public function testSlugNormalization() { $slugs = array('' => '/', '/' => '/', '//' => '/', '/derp/' => 'derp/', 'derp' => 'derp/', 'derp//derp' => 'derp/derp/', 'DERP//DERP' => 'derp/derp/', 'a B c' => 'a_b_c/'); foreach ($slugs as $slug => $normal) { $this->assertEqual($normal, PhrictionDocument::normalizeSlug($slug), "Normalization of '{$slug}'"); } }
public function markupDocumentLink($matches) { $slug = trim($matches[1]); $name = trim(idx($matches, 2, $slug)); // If whatever is being linked to begins with "/" or has "://", treat it // as a URI instead of a wiki page. $is_uri = preg_match('@(^/)|(://)@', $slug); if ($is_uri) { $protocols = $this->getEngine()->getConfig('uri.allowed-protocols', array()); $protocol = id(new PhutilURI($slug))->getProtocol(); if (!idx($protocols, $protocol)) { // Don't treat this as a URI if it's not an allowed protocol. $is_uri = false; } } if ($is_uri) { $uri = $slug; // Leave the name unchanged, i.e. link the whole URI if there's no // explicit name. } else { $name = explode('/', trim($name, '/')); $name = end($name); $slug = PhrictionDocument::normalizeSlug($slug); $uri = PhrictionDocument::getSlugURI($slug); } return $this->getEngine()->storeText(phutil_render_tag('a', array('href' => $uri, 'class' => $is_uri ? null : 'phriction-link'), phutil_escape_html($name))); }
public function markupDocumentLink($matches) { $slug = trim($matches[1]); $name = trim(idx($matches, 2, $slug)); $name = explode('/', $name); $name = end($name); $slug = PhrictionDocument::normalizeSlug($slug); $uri = PhrictionDocument::getSlugURI($slug); return $this->getEngine()->storeText(phutil_render_tag('a', array('href' => $uri, 'class' => 'phriction-link'), phutil_escape_html($name))); }
public function setPhrictionSlug($slug) { // NOTE: We're doing a little magic here and stripping out '/' so that // project pages always appear at top level under projects/ even if the // display name is "Hack / Slash" or similar (it will become // 'hack_slash' instead of 'hack/slash'). $slug = str_replace('/', ' ', $slug); $slug = PhrictionDocument::normalizeSlug($slug); $this->phrictionSlug = $slug; return $this; }
protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); $doc = id(new PhrictionDocument())->loadOneWhere('slug = %s', PhrictionDocument::normalizeSlug($slug)); if (!$doc) { throw new ConduitException('ERR-BAD-DOCUMENT'); } $content = id(new PhrictionContent())->load($doc->getContentID()); $doc->attachContent($content); return $this->buildDocumentInfoDictionary($doc); }
protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); $doc = id(new PhrictionDocument())->loadOneWhere('slug = %s', PhrictionDocument::normalizeSlug($slug)); if (!$doc) { throw new ConduitException('ERR-BAD-DOCUMENT'); } $content = id(new PhrictionContent())->loadAllWhere('documentID = %d ORDER BY version DESC', $doc->getID()); $results = array(); foreach ($content as $version) { $results[] = $this->buildDocumentContentDictionary($doc, $version); } return $results; }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $document = id(new PhrictionDocument())->loadOneWhere('slug = %s', PhrictionDocument::normalizeSlug($this->slug)); if (!$document) { return new Aphront404Response(); } $current = id(new PhrictionContent())->load($document->getContentID()); $pager = new AphrontPagerView(); $pager->setOffset($request->getInt('page')); $pager->setURI($request->getRequestURI(), 'page'); $history = id(new PhrictionContent())->loadAllWhere('documentID = %d ORDER BY version DESC LIMIT %d, %d', $document->getID(), $pager->getOffset(), $pager->getPageSize() + 1); $history = $pager->sliceResults($history); $author_phids = mpull($history, 'getAuthorPHID'); $handles = id(new PhabricatorObjectHandleData($author_phids))->loadHandles(); $rows = array(); foreach ($history as $content) { $uri = PhrictionDocument::getSlugURI($document->getSlug()); $version = $content->getVersion(); $diff_uri = new PhutilURI('/phriction/diff/' . $document->getID() . '/'); $vs_previous = '<em>Created</em>'; if ($content->getVersion() != 1) { $uri = $diff_uri->alter('l', $content->getVersion() - 1)->alter('r', $content->getVersion()); $vs_previous = phutil_render_tag('a', array('href' => $uri), 'Show Change'); } $vs_head = '<em>Current</em>'; if ($content->getID() != $document->getContentID()) { $uri = $diff_uri->alter('l', $content->getVersion())->alter('r', $current->getVersion()); $vs_head = phutil_render_tag('a', array('href' => $uri), 'Show Later Changes'); } $change_type = PhrictionChangeType::getChangeTypeLabel($content->getChangeType()); $rows[] = array(phabricator_date($content->getDateCreated(), $user), phabricator_time($content->getDateCreated(), $user), phutil_render_tag('a', array('href' => $uri . '?v=' . $version), 'Version ' . $version), $handles[$content->getAuthorPHID()]->renderLink(), $change_type, phutil_escape_html($content->getDescription()), $vs_previous, $vs_head); } $crumbs = new AphrontCrumbsView(); $crumbs->setCrumbs(array('Phriction', phutil_render_tag('a', array('href' => PhrictionDocument::getSlugURI($document->getSlug())), phutil_escape_html($current->getTitle())), 'History')); $table = new AphrontTableView($rows); $table->setHeaders(array('Date', 'Time', 'Version', 'Author', 'Type', 'Description', 'Against Previous', 'Against Current')); $table->setColumnClasses(array('', 'right', 'pri', '', '', 'wide', '', '')); $panel = new AphrontPanelView(); $panel->setHeader('Document History'); $panel->appendChild($table); $panel->appendChild($pager); return $this->buildStandardPageResponse(array($crumbs, $panel), array('title' => 'Document History')); }
public static function newForSlug($slug) { $slug = PhrictionDocument::normalizeSlug($slug); $document = id(new PhrictionDocument())->loadOneWhere('slug = %s', $slug); $content = null; if ($document) { $content = id(new PhrictionContent())->load($document->getContentID()); } else { $document = new PhrictionDocument(); $document->setSlug($slug); } if (!$content) { $default_title = PhrictionDocument::getDefaultSlugTitle($slug); $content = new PhrictionContent(); $content->setSlug($slug); $content->setTitle($default_title); } $obj = new PhrictionDocumentEditor(); $obj->document = $document; $obj->content = $content; return $obj; }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $slug = PhrictionDocument::normalizeSlug($this->slug); if ($slug != $this->slug) { $uri = PhrictionDocument::getSlugURI($slug); // Canonicalize pages to their one true URI. return id(new AphrontRedirectResponse())->setURI($uri); } require_celerity_resource('phriction-document-css'); $document = id(new PhrictionDocument())->loadOneWhere('slug = %s', $slug); $breadcrumbs = $this->renderBreadcrumbs($slug); $version_note = null; if (!$document) { $create_uri = '/phriction/edit/?slug=' . $slug; $page_content = '<div class="phriction-content">' . '<em>No content here!</em><br />' . 'No document found at <tt>' . phutil_escape_html($slug) . '</tt>. ' . 'You can <strong>' . phutil_render_tag('a', array('href' => $create_uri), 'create a new document') . '</strong>.' . '</div>'; $page_title = 'Page Not Found'; $button = phutil_render_tag('a', array('href' => $create_uri, 'class' => 'green button'), 'Create Page'); $buttons = $button; } else { $version = $request->getInt('v'); if ($version) { $content = id(new PhrictionContent())->loadOneWhere('documentID = %d AND version = %d', $document->getID(), $version); if (!$content) { return new Aphront404Response(); } if ($content->getID() != $document->getContentID()) { $version_note = new AphrontErrorView(); $version_note->setSeverity(AphrontErrorView::SEVERITY_NOTICE); $version_note->setTitle('Older Version'); $version_note->appendChild('You are viewing an older version of this document, as it ' . 'appeared on ' . phabricator_datetime($content->getDateCreated(), $user) . '.'); } } else { $content = id(new PhrictionContent())->load($document->getContentID()); } $page_title = $content->getTitle(); $project_phid = null; if (PhrictionDocument::isProjectSlug($slug)) { $project = id(new PhabricatorProject())->loadOneWhere('phrictionSlug = %s', PhrictionDocument::getProjectSlugIdentifier($slug)); $project_phid = $project->getPHID(); } $phids = array_filter(array($content->getAuthorPHID(), $project_phid)); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $age = time() - $content->getDateCreated(); $age = floor($age / (60 * 60 * 24)); if ($age < 1) { $when = 'today'; } else { if ($age == 1) { $when = 'yesterday'; } else { $when = "{$age} days ago"; } } $project_info = null; if ($project_phid) { $project_info = '<br />This document is about the project ' . $handles[$project_phid]->renderLink() . '.'; } $byline = '<div class="phriction-byline">' . "Last updated {$when} by " . $handles[$content->getAuthorPHID()]->renderLink() . '.' . $project_info . '</div>'; $doc_status = $document->getStatus(); if ($doc_status == PhrictionDocumentStatus::STATUS_EXISTS) { $core_content = $content->renderContent(); } else { if ($doc_status == PhrictionDocumentStatus::STATUS_DELETED) { $notice = new AphrontErrorView(); $notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE); $notice->setTitle('Document Deleted'); $notice->appendChild('This document has been deleted. You can edit it to put new content ' . 'here, or use history to revert to an earlier version.'); $core_content = $notice->render(); } else { throw new Exception("Unknown document status '{$doc_status}'!"); } } $page_content = '<div class="phriction-content">' . $byline . $core_content . '</div>'; $edit_button = phutil_render_tag('a', array('href' => '/phriction/edit/' . $document->getID() . '/', 'class' => 'button'), 'Edit Document'); $history_button = phutil_render_tag('a', array('href' => PhrictionDocument::getSlugURI($slug, 'history'), 'class' => 'button grey'), 'View History'); // these float right so history_button which is right most goes first $buttons = $history_button . $edit_button; } if ($version_note) { $version_note = $version_note->render(); } $children = $this->renderChildren($slug); $page = '<div class="phriction-header">' . $buttons . '<h1>' . phutil_escape_html($page_title) . '</h1>' . $breadcrumbs . '</div>' . $version_note . $page_content . $children; return $this->buildStandardPageResponse($page, array('title' => 'Phriction - ' . $page_title)); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if ($this->id) { $document = id(new PhrictionDocument())->load($this->id); if (!$document) { return new Aphront404Response(); } $revert = $request->getInt('revert'); if ($revert) { $content = id(new PhrictionContent())->loadOneWhere('documentID = %d AND version = %d', $document->getID(), $revert); if (!$content) { return new Aphront404Response(); } } else { $content = id(new PhrictionContent())->load($document->getContentID()); } } else { $slug = $request->getStr('slug'); $slug = PhrictionDocument::normalizeSlug($slug); if (!$slug) { return new Aphront404Response(); } $document = id(new PhrictionDocument())->loadOneWhere('slug = %s', $slug); if ($document) { $content = id(new PhrictionContent())->load($document->getContentID()); } else { $document = new PhrictionDocument(); $document->setSlug($slug); $content = new PhrictionContent(); $content->setSlug($slug); $default_title = PhrictionDocument::getDefaultSlugTitle($slug); $content->setTitle($default_title); } } require_celerity_resource('phriction-document-css'); $e_title = true; $errors = array(); if ($request->isFormPost()) { $title = $request->getStr('title'); if (!strlen($title)) { $e_title = 'Required'; $errors[] = 'Document title is required.'; } else { $e_title = null; } if (!count($errors)) { $editor = id(PhrictionDocumentEditor::newForSlug($document->getSlug()))->setUser($user)->setTitle($title)->setContent($request->getStr('content'))->setDescription($request->getStr('description')); $editor->save(); $uri = PhrictionDocument::getSlugURI($document->getSlug()); return id(new AphrontRedirectResponse())->setURI($uri); } } $error_view = null; if ($errors) { $error_view = id(new AphrontErrorView())->setTitle('Form Errors')->setErrors($errors); } if ($document->getID()) { $panel_header = 'Edit Phriction Document'; $submit_button = 'Save Changes'; } else { $panel_header = 'Create New Phriction Document'; $submit_button = 'Create Document'; } $uri = $document->getSlug(); $uri = PhrictionDocument::getSlugURI($uri); $uri = PhabricatorEnv::getProductionURI($uri); $remarkup_reference = phutil_render_tag('a', array('href' => PhabricatorEnv::getDoclink('article/Remarkup_Reference.html')), 'Formatting Reference'); $cancel_uri = PhrictionDocument::getSlugURI($document->getSlug()); $form = id(new AphrontFormView())->setUser($user)->setAction($request->getRequestURI()->getPath())->addHiddenInput('slug', $document->getSlug())->appendChild(id(new AphrontFormTextControl())->setLabel('Title')->setValue($content->getTitle())->setError($e_title)->setName('title'))->appendChild(id(new AphrontFormTextControl())->setLabel('Description')->setValue($content->getDescription())->setError(null)->setName('description'))->appendChild(id(new AphrontFormStaticControl())->setLabel('URI')->setValue($uri))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Content')->setValue($content->getContent())->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)->setName('content')->setID('document-textarea')->setEnableDragAndDropFileUploads(true)->setCaption($remarkup_reference))->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($cancel_uri)->setValue($submit_button)); $panel = id(new AphrontPanelView())->setWidth(AphrontPanelView::WIDTH_WIDE)->setHeader($panel_header)->appendChild($form); $preview_panel = '<div class="aphront-panel-preview aphront-panel-preview-wide"> <div class="phriction-document-preview-header"> Document Preview </div> <div id="document-preview"> <div class="aphront-panel-preview-loading-text"> Loading preview... </div> </div> </div>'; Javelin::initBehavior('phriction-document-preview', array('preview' => 'document-preview', 'textarea' => 'document-textarea', 'uri' => '/phriction/preview/')); return $this->buildStandardPageResponse(array($error_view, $panel, $preview_panel), array('title' => 'Edit Document')); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $slug = PhrictionDocument::normalizeSlug($this->slug); if ($slug != $this->slug) { $uri = PhrictionDocument::getSlugURI($slug); // Canonicalize pages to their one true URI. return id(new AphrontRedirectResponse())->setURI($uri); } require_celerity_resource('phriction-document-css'); $document = id(new PhrictionDocument())->loadOneWhere('slug = %s', $slug); $breadcrumbs = $this->renderBreadcrumbs($slug); $version_note = null; if (!$document) { $create_uri = '/phriction/edit/?slug=' . $slug; $page_content = '<div class="phriction-content">' . '<em>No content here!</em><br />' . 'No document found at <tt>' . phutil_escape_html($slug) . '</tt>. ' . 'You can <strong>' . phutil_render_tag('a', array('href' => $create_uri), 'create a new document') . '</strong>.' . '</div>'; $page_title = 'Page Not Found'; $button = phutil_render_tag('a', array('href' => $create_uri, 'class' => 'green button'), 'Create Page'); } else { $version = $request->getInt('v'); if ($version) { $content = id(new PhrictionContent())->loadOneWhere('documentID = %d AND version = %d', $document->getID(), $version); if (!$content) { return new Aphront404Response(); } if ($content->getID() != $document->getContentID()) { $version_note = new AphrontErrorView(); $version_note->setSeverity(AphrontErrorView::SEVERITY_NOTICE); $version_note->setTitle('Older Version'); $version_note->appendChild('You are viewing an older version of this document, as it ' . 'appeared on ' . phabricator_datetime($content->getDateCreated(), $user) . '.'); } } else { $content = id(new PhrictionContent())->load($document->getContentID()); } $page_title = $content->getTitle(); $phids = array($content->getAuthorPHID()); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $age = time() - $content->getDateCreated(); $age = floor($age / (60 * 60 * 24)); if ($age < 1) { $when = 'today'; } else { if ($age == 1) { $when = 'yesterday'; } else { $when = "{$age} days ago"; } } $byline = '<div class="phriction-byline">' . "Last updated {$when} by " . $handles[$content->getAuthorPHID()]->renderLink() . '.' . '</div>'; $engine = PhabricatorMarkupEngine::newPhrictionMarkupEngine(); $page_content = '<div class="phriction-content">' . $byline . '<div class="phabricator-remarkup">' . $engine->markupText($content->getContent()) . '</div>' . '</div>'; $button = phutil_render_tag('a', array('href' => '/phriction/edit/' . $document->getID() . '/', 'class' => 'button'), 'Edit Page'); } if ($version_note) { $version_note = $version_note->render(); } $children = $this->renderChildren($slug); $page = '<div class="phriction-header">' . $button . '<h1>' . phutil_escape_html($page_title) . '</h1>' . $breadcrumbs . '</div>' . $version_note . $page_content . $children; return $this->buildStandardPageResponse($page, array('title' => 'Phriction - ' . $page_title, 'history' => PhrictionDocument::getSlugURI($slug, 'history'))); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $slug = $request->getStr('slug'); $slug = PhrictionDocument::normalizeSlug($slug); if ($this->id) { $document = id(new PhrictionDocument())->load($this->id); if (!$document) { return new Aphront404Response(); } $revert = $request->getInt('revert'); if ($revert) { $content = id(new PhrictionContent())->loadOneWhere('documentID = %d AND version = %d', $document->getID(), $revert); if (!$content) { return new Aphront404Response(); } } else { $content = id(new PhrictionContent())->load($document->getContentID()); } } else { if ($slug) { $document = id(new PhrictionDocument())->loadOneWhere('slug = %s', $slug); if ($document) { $content = id(new PhrictionContent())->load($document->getContentID()); } else { $document = new PhrictionDocument(); $document->setSlug($slug); $content = new PhrictionContent(); $content->setSlug($slug); $default_title = PhrictionDocument::getDefaultSlugTitle($slug); $content->setTitle($default_title); } } else { return new Aphront404Response(); } } require_celerity_resource('phriction-document-css'); $e_title = true; $errors = array(); if ($request->isFormPost()) { $title = $request->getStr('title'); if (!strlen($title)) { $e_title = 'Required'; $errors[] = 'Document title is required.'; } else { $e_title = null; } if (!count($errors)) { // TODO: This should all be transactional. $is_new = false; if (!$document->getID()) { $is_new = true; $document->save(); } $new_content = new PhrictionContent(); $new_content->setSlug($document->getSlug()); $new_content->setTitle($title); $new_content->setDescription($request->getStr('description')); $new_content->setContent($request->getStr('content')); $new_content->setDocumentID($document->getID()); $new_content->setVersion($content->getVersion() + 1); $new_content->setAuthorPHID($user->getPHID()); $new_content->save(); $document->setContentID($new_content->getID()); $document->save(); $document->attachContent($new_content); PhabricatorSearchPhrictionIndexer::indexDocument($document); id(new PhabricatorFeedStoryPublisher())->setRelatedPHIDs(array($document->getPHID(), $user->getPHID()))->setStoryAuthorPHID($user->getPHID())->setStoryTime(time())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_PHRICTION)->setStoryData(array('phid' => $document->getPHID(), 'action' => $is_new ? PhrictionActionConstants::ACTION_CREATE : PhrictionActionConstants::ACTION_EDIT, 'content' => phutil_utf8_shorten($new_content->getContent(), 140)))->publish(); $uri = PhrictionDocument::getSlugURI($document->getSlug()); return id(new AphrontRedirectResponse())->setURI($uri); } } $error_view = null; if ($errors) { $error_view = id(new AphrontErrorView())->setTitle('Form Errors')->setErrors($errors); } if ($document->getID()) { $panel_header = 'Edit Phriction Document'; $submit_button = 'Save Changes'; } else { $panel_header = 'Create New Phriction Document'; $submit_button = 'Create Document'; } $uri = $document->getSlug(); $uri = PhrictionDocument::getSlugURI($uri); $uri = PhabricatorEnv::getProductionURI($uri); $remarkup_reference = phutil_render_tag('a', array('href' => PhabricatorEnv::getDoclink('article/Remarkup_Reference.html')), 'Formatting Reference'); $cancel_uri = PhrictionDocument::getSlugURI($document->getSlug()); $form = id(new AphrontFormView())->setUser($user)->setAction($request->getRequestURI()->getPath())->addHiddenInput('slug', $slug)->appendChild(id(new AphrontFormTextControl())->setLabel('Title')->setValue($content->getTitle())->setError($e_title)->setName('title'))->appendChild(id(new AphrontFormTextControl())->setLabel('Description')->setValue($content->getDescription())->setError(null)->setName('description'))->appendChild(id(new AphrontFormStaticControl())->setLabel('URI')->setValue($uri))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Content')->setValue($content->getContent())->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)->setName('content')->setID('document-textarea')->setCaption($remarkup_reference))->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($cancel_uri)->setValue($submit_button)); $panel = id(new AphrontPanelView())->setWidth(AphrontPanelView::WIDTH_WIDE)->setHeader($panel_header)->appendChild($form); $preview_panel = '<div class="aphront-panel-preview aphront-panel-preview-wide"> <div class="phriction-document-preview-header"> Document Preview </div> <div id="document-preview"> <div class="aphront-panel-preview-loading-text"> Loading preview... </div> </div> </div>'; Javelin::initBehavior('phriction-document-preview', array('preview' => 'document-preview', 'textarea' => 'document-textarea', 'uri' => '/phriction/preview/')); return $this->buildStandardPageResponse(array($error_view, $panel, $preview_panel), array('title' => 'Edit Document')); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $project = id(new PhabricatorProject())->load($this->id); if (!$project) { return new Aphront404Response(); } $profile = $project->loadProfile(); if (!$profile) { $profile = new PhabricatorProjectProfile(); } $src_phid = $profile->getProfileImagePHID(); if (!$src_phid) { $src_phid = $user->getProfileImagePHID(); } $picture = PhabricatorFileURI::getViewURIForPHID($src_phid); $nav_view = new AphrontSideNavFilterView(); $uri = new PhutilURI('/project/view/' . $project->getID() . '/'); $nav_view->setBaseURI($uri); $external_arrow = "↗"; $tasks_uri = '/maniphest/view/all/?projects=' . $project->getPHID(); $slug = PhrictionDocument::normalizeSlug($project->getName()); $phriction_uri = '/w/projects/' . $slug . '/'; $edit_uri = '/project/edit/' . $project->getID() . '/'; $nav_view->addFilter('dashboard', 'Dashboard'); $nav_view->addSpacer(); $nav_view->addFilter('feed', 'Feed'); $nav_view->addFilter(null, 'Tasks ' . $external_arrow, $tasks_uri); $nav_view->addFilter(null, 'Wiki ' . $external_arrow, $phriction_uri); $nav_view->addFilter('people', 'People'); $nav_view->addFilter('about', 'About'); $nav_view->addSpacer(); $nav_view->addFilter(null, "Edit Project…", $edit_uri); $this->page = $nav_view->selectFilter($this->page, 'dashboard'); require_celerity_resource('phabricator-profile-css'); switch ($this->page) { case 'dashboard': $content = $this->renderTasksPage($project, $profile); $query = new PhabricatorFeedQuery(); $query->setFilterPHIDs(array($project->getPHID())); $stories = $query->execute(); $content .= $this->renderStories($stories); break; case 'about': $content = $this->renderAboutPage($project, $profile); break; case 'people': $content = $this->renderPeoplePage($project, $profile); break; case 'feed': $content = $this->renderFeedPage($project, $profile); break; default: throw new Exception("Unimplemented filter '{$this->page}'."); } $content = '<div style="padding: 1em;">' . $content . '</div>'; $nav_view->appendChild($content); $header = new PhabricatorProfileHeaderView(); $header->setName($project->getName()); $header->setDescription(phutil_utf8_shorten($profile->getBlurb(), 1024)); $header->setProfilePicture($picture); $header->appendChild($nav_view); return $this->buildStandardPageResponse($header, array('title' => $project->getName() . ' Project')); }