1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Phame - add an "Everyone but Me" view and make published posts truly accessible to the whole world.

Summary:
accessibility covers not only a given post but also the various "published" views.

to keep the code relative clean, this diff also splits up the post list controller logic quite a bit. this also feels like good preparation for some other work around introducing "blogs" which are collections of published posts from bloggers with some fancy features around that.

Test Plan: clicked around various parts of the Phame application as a logged in user, a logged in user with no personal posts, and without any user logged in at all. various views all seemed reasonable.

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T1373

Differential Revision: https://secure.phabricator.com/D2898
This commit is contained in:
Bob Trahan 2012-07-05 15:43:14 -07:00
parent 4dd5bcf1cd
commit 67691c196e
14 changed files with 394 additions and 116 deletions

View file

@ -1040,6 +1040,8 @@ phutil_register_library_map(array(
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/PhabricatorXHProfProfileSymbolView.php', 'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/PhabricatorXHProfProfileSymbolView.php',
'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/PhabricatorXHProfProfileTopLevelView.php', 'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/PhabricatorXHProfProfileTopLevelView.php',
'PhabricatorXHProfProfileView' => 'applications/xhprof/view/PhabricatorXHProfProfileView.php', 'PhabricatorXHProfProfileView' => 'applications/xhprof/view/PhabricatorXHProfProfileView.php',
'PhameAllBloggersPostListController' => 'applications/phame/controller/post/list/PhameAllBloggersPostListController.php',
'PhameBloggerPostListController' => 'applications/phame/controller/post/list/PhameBloggerPostListController.php',
'PhameController' => 'applications/phame/controller/PhameController.php', 'PhameController' => 'applications/phame/controller/PhameController.php',
'PhameDAO' => 'applications/phame/storage/PhameDAO.php', 'PhameDAO' => 'applications/phame/storage/PhameDAO.php',
'PhameDraftListController' => 'applications/phame/controller/post/list/PhameDraftListController.php', 'PhameDraftListController' => 'applications/phame/controller/post/list/PhameDraftListController.php',
@ -1048,11 +1050,11 @@ phutil_register_library_map(array(
'PhamePostDetailView' => 'applications/phame/view/PhamePostDetailView.php', 'PhamePostDetailView' => 'applications/phame/view/PhamePostDetailView.php',
'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php',
'PhamePostListBaseController' => 'applications/phame/controller/post/list/PhamePostListBaseController.php', 'PhamePostListBaseController' => 'applications/phame/controller/post/list/PhamePostListBaseController.php',
'PhamePostListController' => 'applications/phame/controller/post/list/PhamePostListController.php',
'PhamePostListView' => 'applications/phame/view/PhamePostListView.php', 'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
'PhamePostPreviewController' => 'applications/phame/controller/post/PhamePostPreviewController.php', 'PhamePostPreviewController' => 'applications/phame/controller/post/PhamePostPreviewController.php',
'PhamePostQuery' => 'applications/phame/query/PhamePostQuery.php', 'PhamePostQuery' => 'applications/phame/query/PhamePostQuery.php',
'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php', 'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php',
'PhameUserPostListController' => 'applications/phame/controller/post/list/PhameUserPostListController.php',
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php', 'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/PhortuneStripeBaseController.php', 'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/PhortuneStripeBaseController.php',
'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/PhortuneStripePaymentFormView.php', 'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/PhortuneStripePaymentFormView.php',
@ -2001,6 +2003,8 @@ phutil_register_library_map(array(
'PhabricatorXHProfProfileSymbolView' => 'PhabricatorXHProfProfileView', 'PhabricatorXHProfProfileSymbolView' => 'PhabricatorXHProfProfileView',
'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView', 'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
'PhabricatorXHProfProfileView' => 'AphrontView', 'PhabricatorXHProfProfileView' => 'AphrontView',
'PhameAllBloggersPostListController' => 'PhamePostListBaseController',
'PhameBloggerPostListController' => 'PhamePostListBaseController',
'PhameController' => 'PhabricatorController', 'PhameController' => 'PhabricatorController',
'PhameDAO' => 'PhabricatorLiskDAO', 'PhameDAO' => 'PhabricatorLiskDAO',
'PhameDraftListController' => 'PhamePostListBaseController', 'PhameDraftListController' => 'PhamePostListBaseController',
@ -2009,11 +2013,11 @@ phutil_register_library_map(array(
'PhamePostDetailView' => 'AphrontView', 'PhamePostDetailView' => 'AphrontView',
'PhamePostEditController' => 'PhameController', 'PhamePostEditController' => 'PhameController',
'PhamePostListBaseController' => 'PhameController', 'PhamePostListBaseController' => 'PhameController',
'PhamePostListController' => 'PhamePostListBaseController',
'PhamePostListView' => 'AphrontView', 'PhamePostListView' => 'AphrontView',
'PhamePostPreviewController' => 'PhameController', 'PhamePostPreviewController' => 'PhameController',
'PhamePostQuery' => 'PhabricatorOffsetPagedQuery', 'PhamePostQuery' => 'PhabricatorOffsetPagedQuery',
'PhamePostViewController' => 'PhameController', 'PhamePostViewController' => 'PhameController',
'PhameUserPostListController' => 'PhamePostListBaseController',
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl', 'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
'PhortuneStripeBaseController' => 'PhabricatorController', 'PhortuneStripeBaseController' => 'PhabricatorController',
'PhortuneStripePaymentFormView' => 'AphrontView', 'PhortuneStripePaymentFormView' => 'AphrontView',

View file

@ -381,9 +381,9 @@ class AphrontDefaultApplicationConfiguration
), ),
'/phame/' => array( '/phame/' => array(
'' => 'PhamePostListController', '' => 'PhameAllBloggersPostListController',
'post/' => array( 'post/' => array(
'' => 'PhamePostListController', '' => 'PhameUserPostListController',
'delete/(?P<phid>[^/]+)/' => 'PhamePostDeleteController', 'delete/(?P<phid>[^/]+)/' => 'PhamePostDeleteController',
'edit/(?P<phid>[^/]+)/' => 'PhamePostEditController', 'edit/(?P<phid>[^/]+)/' => 'PhamePostEditController',
'new/' => 'PhamePostEditController', 'new/' => 'PhamePostEditController',
@ -395,8 +395,8 @@ class AphrontDefaultApplicationConfiguration
'new/' => 'PhamePostEditController', 'new/' => 'PhamePostEditController',
), ),
'posts/' => array( 'posts/' => array(
'' => 'PhamePostListController', '' => 'PhameUserPostListController',
'(?P<bloggername>\w+)/' => 'PhamePostListController', '(?P<bloggername>\w+)/' => 'PhameBloggerPostListController',
'(?P<bloggername>\w+)/(?P<phametitle>.+/)' '(?P<bloggername>\w+)/(?P<phametitle>.+/)'
=> 'PhamePostViewController', => 'PhamePostViewController',
), ),

View file

@ -60,8 +60,9 @@ abstract class PhameController extends PhabricatorController {
} }
private function renderSideNavFilterView($filter) { private function renderSideNavFilterView($filter) {
$base_uri = new PhutilURI('/phame/');
$nav = new AphrontSideNavFilterView(); $nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/phame/')); $nav->setBaseURI($base_uri);
$nav->addLabel('Drafts'); $nav->addLabel('Drafts');
$nav->addFilter('post/new', $nav->addFilter('post/new',
'New Draft'); 'New Draft');
@ -71,12 +72,16 @@ abstract class PhameController extends PhabricatorController {
$nav->addLabel('Posts'); $nav->addLabel('Posts');
$nav->addFilter('post', $nav->addFilter('post',
'My Posts'); 'My Posts');
$nav->addFilter('everyone',
'Everyone',
$base_uri);
foreach ($this->getSideNavExtraPostFilters() as $post_filter) { foreach ($this->getSideNavExtraPostFilters() as $post_filter) {
$nav->addFilter($post_filter['key'], $nav->addFilter($post_filter['key'],
$post_filter['name']); $post_filter['name'],
idx($post_filter, 'uri'));
} }
$nav->selectFilter($filter, 'post'); $nav->selectFilter($filter);
return $nav; return $nav;
} }

View file

@ -99,7 +99,7 @@ extends PhameController {
$post = id(new PhamePost()) $post = id(new PhamePost())
->setBloggerPHID($user->getPHID()) ->setBloggerPHID($user->getPHID())
->setVisibility(PhamePost::VISIBILITY_DRAFT); ->setVisibility(PhamePost::VISIBILITY_DRAFT);
$cancel_uri = '/phame'; $cancel_uri = '/phame/';
$submit_button = 'Create Post'; $submit_button = 'Create Post';
$delete_button = null; $delete_button = null;
$page_title = 'Create Post'; $page_title = 'Create Post';

View file

@ -60,6 +60,12 @@ extends PhameController {
return $filters; return $filters;
} }
public function shouldRequireLogin() {
// TODO -- get policy logic going
// return PhabricatorEnv::getEnvConfig('policy.allow-public');
return true;
}
public function willProcessRequest(array $data) { public function willProcessRequest(array $data) {
$this->setPostPHID(idx($data, 'phid')); $this->setPostPHID(idx($data, 'phid'));
$this->setPhameTitle(idx($data, 'phametitle')); $this->setPhameTitle(idx($data, 'phametitle'));

View file

@ -0,0 +1,95 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group phame
*/
final class PhameAllBloggersPostListController
extends PhamePostListBaseController {
public function shouldRequireLogin() {
return true;
}
protected function getSideNavFilter() {
return 'everyone';
}
protected function getNoticeView() {
$user = $this->getRequest()->getUser();
$new_link = phutil_render_tag(
'a',
array(
'href' => '/phame/post/new/',
'class' => 'button green',
),
'write a blog post'
);
$remarkup_link = phutil_render_tag(
'a',
array(
'href' =>
PhabricatorEnv::getDoclink('article/Remarkup_Reference.html'),
),
'remarkup'
);
$guide_link = phutil_render_tag(
'a',
array(
'href' => PhabricatorEnv::getDoclink('article/Phame_User_Guide.html'),
),
'Phame user guide'
);
$notices = array(
'Seek phame and '.$new_link,
'Use '.$remarkup_link.' for maximal elegance, grace, and style. ',
'If you need more help try the '.$guide_link.'.',
);
$notice_view = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle('Meta thoughts and feelings');
foreach ($notices as $notice) {
$notice_view->appendChild('<p>'.$notice.'</p>');
}
return $notice_view;
}
public function processRequest() {
$user = $this->getRequest()->getUser();
$query = new PhamePostQuery();
$query->withVisibility(PhamePost::VISIBILITY_PUBLISHED);
$this->setPhamePostQuery($query);
$this->setActions(array('view'));
$page_title = 'Posts by Everyone';
$this->setPageTitle($page_title);
$this->setShowSideNav(true);
return $this->buildPostListPageResponse();
}
}

View file

@ -0,0 +1,74 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group phame
*/
final class PhameBloggerPostListController
extends PhamePostListBaseController {
private $bloggerName;
private function setBloggerName($blogger_name) {
$this->bloggerName = $blogger_name;
return $this;
}
private function getBloggerName() {
return $this->bloggerName;
}
public function shouldRequireLogin() {
// TODO -- get policy logic going
// return PhabricatorEnv::getEnvConfig('policy.allow-public');
return true;
}
public function willProcessRequest(array $data) {
$this->setBloggerName(idx($data, 'bloggername'));
}
public function processRequest() {
$user = $this->getRequest()->getUser();
$blogger = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$this->getBloggerName());
if (!$blogger) {
return new Aphront404Response();
}
$blogger_phid = $blogger->getPHID();
if ($blogger_phid == $user->getPHID()) {
$actions = array('view', 'edit');
} else {
$actions = array('view');
}
$this->setActions($actions);
$query = new PhamePostQuery();
$query->withBloggerPHID($blogger_phid);
$query->withVisibility(PhamePost::VISIBILITY_PUBLISHED);
$this->setPhamePostQuery($query);
$page_title = 'Posts by '.$this->getBloggerName();
$this->setPageTitle($page_title);
$this->setShowSideNav(false);
return $this->buildPostListPageResponse();
}
}

View file

@ -22,8 +22,34 @@
final class PhameDraftListController final class PhameDraftListController
extends PhamePostListBaseController { extends PhamePostListBaseController {
public function shouldRequireLogin() {
return true;
}
protected function getSideNavFilter() {
return 'draft';
}
protected function isDraft() {
return true;
}
public function processRequest() { public function processRequest() {
$this->setIsDraft(true); $user = $this->getRequest()->getUser();
return parent::processRequest(); $phid = $user->getPHID();
$query = new PhamePostQuery();
$query->withBloggerPHID($phid);
$query->withVisibility(PhamePost::VISIBILITY_DRAFT);
$this->setPhamePostQuery($query);
$actions = array('view', 'edit');
$this->setActions($actions);
$this->setPageTitle('My Drafts');
$this->setShowSideNav(true);
return $this->buildPostListPageResponse();
} }
} }

View file

@ -22,106 +22,87 @@
abstract class PhamePostListBaseController abstract class PhamePostListBaseController
extends PhameController { extends PhameController {
private $bloggerName; private $phamePostQuery;
private $isDraft; private $actions;
private $pageTitle;
private function setBloggerName($blogger_name) { protected function setPageTitle($page_title) {
$this->bloggerName = $blogger_name; $this->pageTitle = $page_title;
return $this; return $this;
} }
private function getBloggerName() { private function getPageTitle() {
return $this->bloggerName; return $this->pageTitle;
} }
protected function getSideNavExtraPostFilters() { protected function setActions($actions) {
if ($this->isDraft() || !$this->getBloggerName()) { $this->actions = $actions;
return array();
}
return
array(array('key' => $this->getSideNavFilter(),
'name' => 'Posts by '.$this->getBloggerName()));
}
protected function getSideNavFilter() {
if ($this->getBloggerName()) {
$filter = 'posts/'.$this->getBloggerName();
} else if ($this->isDraft()) {
$filter = 'draft';
} else {
$filter = 'posts';
}
return $filter;
}
private function isDraft() {
return (bool) $this->isDraft;
}
protected function setIsDraft($is_draft) {
$this->isDraft = $is_draft;
return $this; return $this;
} }
private function getActions() {
public function willProcessRequest(array $data) { return $this->actions;
$this->setBloggerName(idx($data, 'bloggername'));
} }
public function processRequest() { protected function setPhamePostQuery(PhamePostQuery $query) {
$this->phamePostQuery = $query;
return $this;
}
private function getPhamePostQuery() {
return $this->phamePostQuery;
}
protected function isDraft() {
return false;
}
protected function getPager() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser();
$pager = new AphrontPagerView(); $pager = new AphrontPagerView();
$page_size = 50; $page_size = 50;
$pager->setURI($request->getRequestURI(), 'offset'); $pager->setURI($request->getRequestURI(), 'offset');
$pager->setPageSize($page_size); $pager->setPageSize($page_size);
$pager->setOffset($request->getInt('offset')); $pager->setOffset($request->getInt('offset'));
if ($this->getBloggerName()) { return $pager;
$blogger = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$this->getBloggerName());
if (!$blogger) {
return new Aphront404Response();
} }
$page_title = 'Posts by '.$this->getBloggerName();
if ($blogger->getPHID() == $user->getPHID()) { protected function getNoticeView() {
$actions = array('view', 'edit'); return null;
} else {
$actions = array('view');
} }
$this->setShowSideNav(false);
} else { private function loadBloggersFromPosts(array $posts) {
$blogger = $user; assert_instances_of($posts, 'PhamePost');
$page_title = 'Posts by '.$user->getUserName(); if (empty($posts)) {
$actions = array('view', 'edit'); return array();
$this->setShowSideNav(true);
} }
$phid = $blogger->getPHID();
// user gets to see their own unpublished stuff $blogger_phids = mpull($posts, 'getBloggerPHID', 'getBloggerPHID');
if ($phid == $user->getPHID() && $this->isDraft()) {
$post_visibility = PhamePost::VISIBILITY_DRAFT; return
} else { id(new PhabricatorObjectHandleData($blogger_phids))->loadHandles();
$post_visibility = PhamePost::VISIBILITY_PUBLISHED;
} }
$query = new PhamePostQuery();
$query->withBloggerPHID($phid); protected function buildPostListPageResponse() {
$query->withVisibility($post_visibility); $pager = $this->getPager();
$query = $this->getPhamePostQuery();
$posts = $query->executeWithPager($pager); $posts = $query->executeWithPager($pager);
$bloggers = array($blogger->getPHID() => $blogger);
$bloggers = $this->loadBloggersFromPosts($posts);
$panel = id(new PhamePostListView()) $panel = id(new PhamePostListView())
->setUser($user) ->setUser($this->getRequest()->getUser())
->setBloggers($bloggers) ->setBloggers($bloggers)
->setPosts($posts) ->setPosts($posts)
->setActions($actions) ->setActions($this->getActions())
->setDraftList($this->isDraft()); ->setDraftList($this->isDraft());
return $this->buildStandardPageResponse( return $this->buildStandardPageResponse(
array( array(
$this->getNoticeView(),
$panel, $panel,
$pager $pager
), ),
array( array(
'title' => $page_title, 'title' => $this->getPageTitle(),
)); ));
} }
} }

View file

@ -1,29 +0,0 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group phame
*/
final class PhamePostListController
extends PhamePostListBaseController {
public function processRequest() {
$this->setIsDraft(false);
return parent::processRequest();
}
}

View file

@ -0,0 +1,89 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group phame
*/
final class PhameUserPostListController
extends PhamePostListBaseController {
public function shouldRequireLogin() {
return true;
}
protected function getSideNavFilter() {
return 'post';
}
protected function getNoticeView() {
$user = $this->getRequest()->getUser();
$new_link = phutil_render_tag(
'a',
array(
'href' => '/phame/post/new/',
'class' => 'button green',
),
'write another blog post'
);
$pretty_uri = PhabricatorEnv::getProductionURI(
'/phame/posts/'.$user->getUserName().'/');
$pretty_link = phutil_render_tag(
'a',
array(
'href' => (string) $pretty_uri
),
(string) $pretty_uri
);
$notices = array(
'Seek even more phame and '.$new_link,
'Published posts also appear at the awesome, world-accessible '.
'URI: '.$pretty_link
);
$notice_view = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle('Meta thoughts and feelings');
foreach ($notices as $notice) {
$notice_view->appendChild('<p>'.$notice.'</p>');
}
return $notice_view;
}
public function processRequest() {
$user = $this->getRequest()->getUser();
$phid = $user->getPHID();
$query = new PhamePostQuery();
$query->withBloggerPHID($phid);
$query->withVisibility(PhamePost::VISIBILITY_PUBLISHED);
$this->setPhamePostQuery($query);
$actions = array('view', 'edit');
$this->setActions($actions);
$this->setPageTitle('My Posts');
$this->setShowSideNav(true);
return $this->buildPostListPageResponse();
}
}

View file

@ -19,12 +19,24 @@
final class PhamePostQuery extends PhabricatorOffsetPagedQuery { final class PhamePostQuery extends PhabricatorOffsetPagedQuery {
private $bloggerPHID; private $bloggerPHID;
private $withoutBloggerPHID;
private $visibility; private $visibility;
/**
* Mutually exlusive with @{method:withoutBloggerPHID}.
*
* @{method:withBloggerPHID} wins because being positive and inclusive is
* cool.
*/
public function withBloggerPHID($blogger_phid) { public function withBloggerPHID($blogger_phid) {
$this->bloggerPHID = $blogger_phid; $this->bloggerPHID = $blogger_phid;
return $this; return $this;
} }
public function withoutBloggerPHID($blogger_phid) {
$this->withoutBloggerPHID = $blogger_phid;
return $this;
}
public function withVisibility($visibility) { public function withVisibility($visibility) {
$this->visibility = $visibility; $this->visibility = $visibility;
return $this; return $this;
@ -60,6 +72,12 @@ final class PhamePostQuery extends PhabricatorOffsetPagedQuery {
'bloggerPHID = %s', 'bloggerPHID = %s',
$this->bloggerPHID $this->bloggerPHID
); );
} else if ($this->withoutBloggerPHID) {
$where[] = qsprintf(
$conn_r,
'bloggerPHID != %s',
$this->withoutBloggerPHID
);
} }
if ($this->visibility !== null) { if ($this->visibility !== null) {

View file

@ -79,7 +79,7 @@ final class PhamePostDetailView extends AphrontView {
$uri = '/phame/draft/'; $uri = '/phame/draft/';
$label = 'Back to Your Drafts'; $label = 'Back to Your Drafts';
} else { } else {
$uri = '/phame/posts/'.$blogger->getUsername(); $uri = '/phame/posts/'.$blogger->getUsername().'/';
$label = 'More Posts by '.phutil_escape_html($blogger->getUsername()); $label = 'More Posts by '.phutil_escape_html($blogger->getUsername());
} }
$button = phutil_render_tag( $button = phutil_render_tag(
@ -101,6 +101,14 @@ final class PhamePostDetailView extends AphrontView {
phabricator_datetime($post->getDateModified(), phabricator_datetime($post->getDateModified(),
$user); $user);
} }
$caption .= ' by '.phutil_render_tag(
'a',
array(
'href' => new PhutilURI('/p/'.$blogger->getUsername().'/'),
),
phutil_escape_html($blogger->getUsername())
).'.';
if ($this->isPreview()) { if ($this->isPreview()) {
$width = AphrontPanelView::WIDTH_FULL; $width = AphrontPanelView::WIDTH_FULL;
} else { } else {

View file

@ -59,7 +59,7 @@ final class PhamePostListView extends AphrontView {
return $this->posts; return $this->posts;
} }
public function setBloggers(array $bloggers) { public function setBloggers(array $bloggers) {
assert_instances_of($bloggers, 'PhabricatorUser'); assert_instances_of($bloggers, 'PhabricatorObjectHandle');
$this->bloggers = $bloggers; $this->bloggers = $bloggers;
return $this; return $this;
} }
@ -99,17 +99,18 @@ final class PhamePostListView extends AphrontView {
foreach ($posts as $post) { foreach ($posts as $post) {
$blogger_phid = $post->getBloggerPHID(); $blogger_phid = $post->getBloggerPHID();
$blogger = $bloggers[$blogger_phid]; $blogger = $bloggers[$blogger_phid];
$blogger_link = $blogger->renderLink();
$updated = phabricator_datetime($post->getDateModified(), $updated = phabricator_datetime($post->getDateModified(),
$user); $user);
$body = $engine->markupText($post->getBody()); $body = $engine->markupText($post->getBody());
$panel = id(new AphrontPanelView()) $panel = id(new AphrontPanelView())
->setHeader(phutil_escape_html($post->getTitle())) ->setHeader(phutil_escape_html($post->getTitle()))
->setCaption('Last updated '.$updated) ->setCaption('Last updated '.$updated.' by '.$blogger_link.'.')
->appendChild('<div class="phabricator-remarkup">'.$body.'</div>'); ->appendChild('<div class="phabricator-remarkup">'.$body.'</div>');
foreach ($actions as $action) { foreach ($actions as $action) {
switch ($action) { switch ($action) {
case 'view': case 'view':
$uri = $post->getViewURI($blogger->getUsername()); $uri = $post->getViewURI($blogger->getName());
$label = 'View '.$noun; $label = 'View '.$noun;
break; break;
case 'edit': case 'edit':