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

Add concept of "skins" to phame, and add a phacility skin

Summary:
introduce an abstract "PhameBlogSkin" class and instantiate two versions -- PhabricatorBlogSkin (Default) and PhacilityBlogSkin.

Most notable hack is including the directory /rsrc/images/phacility - this lets things "work" without messing around with the phacility.com CSS and instead just cutting and pasting most of the file.

Test Plan: played around with Phame a bunch. In particular, created a blog with a custom domain and the phacility skin. Verified it looked good and individual posts looked okay.

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T1373

Differential Revision: https://secure.phabricator.com/D3687
This commit is contained in:
Bob Trahan 2012-10-12 16:01:33 -07:00
parent 421d2d6a08
commit 8355f3592f
21 changed files with 886 additions and 315 deletions

View file

@ -462,6 +462,27 @@ celerity_register_resource_map(array(
'disk' => '/rsrc/image/nyan.gif',
'type' => 'gif',
),
'/rsrc/image/phacility/phacility_logo.jpg' =>
array(
'hash' => '88000de28b8741acc584b24560bd3d4f',
'uri' => '/res/88000de2/rsrc/image/phacility/phacility_logo.jpg',
'disk' => '/rsrc/image/phacility/phacility_logo.jpg',
'type' => 'jpg',
),
'/rsrc/image/phacility/sprites.png' =>
array(
'hash' => 'b018a070d120f689a2beb8ece67e1b1e',
'uri' => '/res/b018a070/rsrc/image/phacility/sprites.png',
'disk' => '/rsrc/image/phacility/sprites.png',
'type' => 'png',
),
'/rsrc/image/phacility/tactile_noise.png' =>
array(
'hash' => '7fb4ca90b8b0919153770b6badb982f0',
'uri' => '/res/7fb4ca90/rsrc/image/phacility/tactile_noise.png',
'disk' => '/rsrc/image/phacility/tactile_noise.png',
'type' => 'png',
),
'/rsrc/image/search.png' =>
array(
'hash' => 'ff7da044e6f923b8f569dec11f97e5e5',
@ -2182,6 +2203,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/javelin/lib/Workflow.js',
),
'jquery-js' =>
array(
'uri' => '/res/7f0dd213/rsrc/js/application/phame/skins/phacility/jquery/jquery.min.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/application/phame/skins/phacility/jquery/jquery.min.js',
),
'maniphest-batch-editor' =>
array(
'uri' => '/res/fb15d744/rsrc/css/application/maniphest/batch-editor.css',
@ -2888,23 +2918,43 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/uiexample/ReactorSendPropertiesExample.js',
),
'phame-blog-detail-css' =>
'phacility-bootstrap-css' =>
array(
'uri' => '/res/5a0e24ab/rsrc/css/application/phame/blog-detail.css',
'uri' => '/res/28f0ad0e/rsrc/css/application/phame/skins/phacility/bootstrap/bootstrap.min.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/phame/blog-detail.css',
'disk' => '/rsrc/css/application/phame/skins/phacility/bootstrap/bootstrap.min.css',
),
'phame-blog-post-list-css' =>
'phacility-css' =>
array(
'uri' => '/res/371237fa/rsrc/css/application/phame/blog-post-list.css',
'uri' => '/res/f55275c2/rsrc/css/application/phame/skins/phacility/phacility.css',
'type' => 'css',
'requires' =>
array(
0 => 'phacility-bootstrap-css',
),
'disk' => '/rsrc/css/application/phame/skins/phacility/phacility.css',
),
'phacility-js' =>
array(
'uri' => '/res/f441bc88/rsrc/js/application/phame/skins/phacility/bootstrap/bootstrap.min.js',
'type' => 'js',
'requires' =>
array(
0 => 'jquery-js',
),
'disk' => '/rsrc/js/application/phame/skins/phacility/bootstrap/bootstrap.min.js',
),
'phame-css' =>
array(
'uri' => '/res/2d18adca/rsrc/css/application/phame/phame.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/phame/blog-post-list.css',
'disk' => '/rsrc/css/application/phame/phame.css',
),
'phriction-document-css' =>
array(

View file

@ -602,6 +602,7 @@ phutil_register_library_map(array(
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php',
'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php',
'PhabricatorBaseEnglishTranslation' => 'infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php',
'PhabricatorBlogSkin' => 'applications/phame/view/skins/PhabricatorBlogSkin.php',
'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php',
'PhabricatorCacheDAO' => 'applications/cache/storage/PhabricatorCacheDAO.php',
'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/PhabricatorCalendarBrowseController.php',
@ -1155,6 +1156,7 @@ phutil_register_library_map(array(
'PhameBlogListBaseController' => 'applications/phame/controller/blog/list/PhameBlogListBaseController.php',
'PhameBlogListView' => 'applications/phame/view/PhameBlogListView.php',
'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php',
'PhameBlogSkin' => 'applications/phame/view/skins/PhameBlogSkin.php',
'PhameBlogViewController' => 'applications/phame/controller/blog/PhameBlogViewController.php',
'PhameBloggerPostListController' => 'applications/phame/controller/post/list/PhameBloggerPostListController.php',
'PhameController' => 'applications/phame/controller/PhameController.php',
@ -1784,6 +1786,7 @@ phutil_register_library_map(array(
'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
'PhabricatorAuthController' => 'PhabricatorController',
'PhabricatorBaseEnglishTranslation' => 'PhabricatorTranslation',
'PhabricatorBlogSkin' => 'PhameBlogSkin',
'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList',
'PhabricatorCacheDAO' => 'PhabricatorLiskDAO',
'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
@ -2280,6 +2283,7 @@ phutil_register_library_map(array(
'PhameBlogListBaseController' => 'PhameController',
'PhameBlogListView' => 'AphrontView',
'PhameBlogQuery' => 'PhabricatorOffsetPagedQuery',
'PhameBlogSkin' => 'AphrontView',
'PhameBlogViewController' => 'PhameController',
'PhameBloggerPostListController' => 'PhamePostListBaseController',
'PhameController' => 'PhabricatorController',

View file

@ -153,13 +153,17 @@ abstract class AphrontApplicationConfiguration {
// 2 basic cases
// -- looking at a list of blog posts, path is nothing or '/'
// -- looking at an actual blog post, path is like /btrahan/post_title
// -- we have to fudge the URI in this case
// -- looking at an actual blog post, path is like
// /phame/posts/<author>/post_title
// NOTE: it is possible to get other phame pages, we just do
// not link to them at this time.
if (!$path || $path == '/') {
$path = $blog->getViewURI();
} else {
$path = '/phame/posts/'.trim($path, '/').'/';
}
PhameBlog::setRequestBlog($blog);
$celerity = CelerityAPI::getStaticResourceResponse();
$celerity->setUseFullURI(true);
}

View file

@ -21,6 +21,8 @@
*/
abstract class PhameController extends PhabricatorController {
private $showSideNav;
private $showChrome = true;
private $deviceReady = false;
protected function setShowSideNav($value) {
$this->showSideNav = (bool) $value;
@ -30,6 +32,22 @@ abstract class PhameController extends PhabricatorController {
return $this->showSideNav;
}
protected function setShowChrome($show_chrome) {
$this->showChrome = $show_chrome;
return $this;
}
private function getShowChrome() {
return $this->showChrome;
}
public function setDeviceReady($device_ready) {
$this->deviceReady = $device_ready;
return $this;
}
public function getDeviceReady() {
return $this->deviceReady;
}
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
@ -38,6 +56,8 @@ abstract class PhameController extends PhabricatorController {
$page->setBaseURI('/phame/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xe2\x9c\xa9");
$page->setShowChrome($this->getShowChrome());
$page->setDeviceReady($this->getDeviceReady());
if ($this->showSideNav()) {
$nav = $this->renderSideNavFilterView($this->getSideNavFilter());

View file

@ -123,6 +123,7 @@ final class PhameBlogEditController
$description = $request->getStr('description');
$blogger_arr = $request->getArr('bloggers');
$custom_domain = $request->getStr('custom_domain');
$skin = $request->getStr('skin');
if (empty($blogger_arr)) {
$error = 'Bloggers must be nonempty.';
@ -155,6 +156,7 @@ final class PhameBlogEditController
}
$blog->setDomain($custom_domain);
}
$blog->setSkin($skin);
if (empty($errors)) {
$blog->save();
@ -231,6 +233,13 @@ final class PhameBlogEditController
'blog.example.com')
->setError($e_custom_domain)
)
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Skin')
->setName('skin')
->setValue($blog->getSkin())
->setOptions(PhameBlog::getSkinOptionsForSelect())
)
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/phame/blog/')

View file

@ -99,47 +99,38 @@ final class PhameBlogViewController
$posts = array();
}
$actions = array('view');
$is_admin = false;
// TODO -- make this check use a policy
if (isset($bloggers[$user->getPHID()])) {
$actions[] = 'edit';
$is_admin = true;
}
$notice = array();
if ($request->getExists('new')) {
$notice = $this->buildNoticeView()
->setTitle('Successfully created your blog.')
->appendChild('Time to write some posts.');
$notice =
array(
'title' => 'Successfully created your blog.',
'body' => 'Time to write some posts.'
);
} else if ($request->getExists('edit')) {
$notice = $this->buildNoticeView()
->setTitle('Successfully edited your blog.')
->appendChild('Time to write some posts.');
} else {
$notice = null;
$notice =
array(
'title' => 'Successfully edited your blog.',
'body' => 'Time to write some posts.'
);
}
$panel = id(new PhamePostListView())
->setBlogStyle(true)
$skin = $blog->getSkinRenderer();
$skin
->setUser($this->getRequest()->getUser())
->setNotice($notice)
->setBloggers($bloggers)
->setPosts($posts)
->setActions($actions)
->setDraftList(false);
$details = id(new PhameBlogDetailView())
->setUser($user)
->setBloggers($bloggers)
->setBlog($blog)
->setIsAdmin($is_admin);
->setRequestURI($this->getRequest()->getRequestURI());
$this->setShowSideNav(false);
$this->setShowChrome($skin->getShowChrome());
$this->setDeviceReady($skin->getDeviceReady());
$skin->setIsExternalDomain($blog->getDomain() == $request->getHost());
return $this->buildStandardPageResponse(
array(
$notice,
$details,
$panel,
$skin
),
array(
'title' => $blog->getName(),

View file

@ -299,6 +299,7 @@ final class PhamePostEditController
</div>
</div>';
require_celerity_resource('phame-css');
Javelin::initBehavior(
'phame-post-preview',
array(

View file

@ -39,9 +39,11 @@ extends PhameController {
->setPhameTitle($phame_title)
->setDateModified(time());
$blogger = PhabricatorObjectHandleData::loadOneHandle($user->getPHID());
$post_html = id(new PhamePostDetailView())
->setUser($user)
->setBlogger($user)
->setBlogger($blogger)
->setPost($post)
->setIsPreview(true)
->render();

View file

@ -90,8 +90,9 @@ extends PhameController {
if ($post) {
$this->setPhameTitle($post->getPhameTitle());
$blogger = id(new PhabricatorUser())->loadOneWhere(
'phid = %s', $post->getBloggerPHID());
$blogger = PhabricatorObjectHandleData::loadOneHandle(
$post->getBloggerPHID(),
$user);
if (!$blogger) {
return new Aphront404Response();
}
@ -100,9 +101,15 @@ extends PhameController {
} else if ($this->getBloggerName() && $this->getPhameTitle()) {
$phame_title = $this->getPhameTitle();
$phame_title = PhabricatorSlug::normalize($phame_title);
$blogger = id(new PhabricatorUser())->loadOneWhere(
$blogger_user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$this->getBloggerName());
if (!$blogger_user) {
return new Aphront404Response();
}
$blogger = PhabricatorObjectHandleData::loadOneHandle(
$blogger_user->getPHID(),
$user);
if (!$blogger) {
return new Aphront404Response();
}
@ -128,12 +135,12 @@ extends PhameController {
}
if ($post->isDraft()) {
$notice = $this->buildNoticeView()
->setTitle('You are previewing a draft.')
->setErrors(array(
'Only you can see this draft until you publish it.',
'If you chose a comment widget it will show up when you publish.',
));
$notice = array(
'title' => 'You are previewing a draft.',
'body' => 'Only you can see this draft until you publish it. '.
'If you chose a comment widget it will show up when '.
'you publish.'
);
} else if ($request->getExists('saved')) {
$new_link = phutil_render_tag(
'a',
@ -143,24 +150,43 @@ extends PhameController {
),
'write another blog post'
);
$notice = $this->buildNoticeView()
->appendChild('<p>Saved post successfully.</p>')
->appendChild('Seek even more phame and '.$new_link);
$notice = array(
'title' => 'Saved post successfully.',
'body' => 'Seek even more phame and '.$new_link.'.'
);
} else {
$notice = null;
$notice = array();
}
$page = id(new PhamePostDetailView())
$actions = array('more');
if ($post->getBloggerPHID() == $user->getPHID()) {
$actions[] = 'edit';
}
$blog = PhameBlog::getRequestBlog();
if ($blog) {
$skin = $blog->getSkinRenderer();
$skin->setBlog($blog);
$skin->setIsExternalDomain(true);
} else {
$skin = new PhabricatorBlogSkin();
}
$skin
->setUser($user)
->setRequestURI($request->getRequestURI())
->setBlogger($blogger)
->setPost($post);
->setBloggers(array($blogger->getPHID() => $blogger))
->setPosts(array($post))
->setNotice($notice)
->setShowPostComments(true)
->setActions($actions);
$this->setShowSideNav(false);
$this->setShowChrome($skin->getShowChrome());
$this->setDeviceReady($skin->getDeviceReady());
return $this->buildStandardPageResponse(
array(
$notice,
$page,
$skin
),
array(
'title' => $post->getTitle(),

View file

@ -80,7 +80,7 @@ final class PhameAllPostListController
$query->withVisibility(PhamePost::VISIBILITY_PUBLISHED);
$this->setPhamePostQuery($query);
$this->setActions(array('view'));
$this->setActions(array());
$page_title = 'All Posts';
$this->setPageTitle($page_title);

View file

@ -71,9 +71,10 @@ abstract class PhamePostListBaseController
}
protected function buildPostListPageResponse() {
$pager = $this->getPager();
$query = $this->getPhamePostQuery();
$posts = $query->executeWithOffsetPager($pager);
$request = $this->getRequest();
$pager = $this->getPager();
$query = $this->getPhamePostQuery();
$posts = $query->executeWithOffsetPager($pager);
$bloggers = $this->loadBloggersFromPosts($posts);
@ -82,6 +83,7 @@ abstract class PhamePostListBaseController
->setBloggers($bloggers)
->setPosts($posts)
->setActions($this->getActions())
->setRequestURI($request->getRequestURI())
->setDraftList($this->isDraft());
return $this->buildStandardPageResponse(

View file

@ -21,6 +21,8 @@
*/
final class PhameBlog extends PhameDAO {
const SKIN_DEFAULT = 'PhabricatorBlogSkin';
protected $id;
protected $phid;
protected $name;
@ -29,9 +31,13 @@ final class PhameBlog extends PhameDAO {
protected $configData;
protected $creatorPHID;
private $skin;
private $bloggerPHIDs;
private $bloggers;
static private $requestBlog;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
@ -46,6 +52,12 @@ final class PhameBlog extends PhameDAO {
PhabricatorPHIDConstants::PHID_TYPE_BLOG);
}
public function getSkinRenderer() {
$skin = $this->getSkin();
return new $skin();
}
/**
* Makes sure a given custom blog uri is properly configured in DNS
* to point at this Phabricator instance. If there is an error in
@ -142,6 +154,27 @@ final class PhameBlog extends PhameDAO {
return $this->bloggers;
}
public function getSkin() {
$config = coalesce($this->getConfigData(), array());
return idx($config, 'skin', self::SKIN_DEFAULT);
}
public function setSkin($skin) {
$config = coalesce($this->getConfigData(), array());
$config['skin'] = $skin;
return $this->setConfigData($config);
}
static public function getSkinOptionsForSelect() {
$classes = id(new PhutilSymbolLoader())
->setAncestorClass('PhameBlogSkin')
->setType('class')
->setConcreteOnly(true)
->selectSymbolsWithoutLoading();
return ipull($classes, 'name', 'name');
}
public function getPostListURI() {
return $this->getActionURI('posts');
}
@ -165,4 +198,12 @@ final class PhameBlog extends PhameDAO {
private function getActionURI($action) {
return '/phame/blog/'.$action.'/'.$this->getPHID().'/';
}
public static function setRequestBlog(PhameBlog $blog) {
self::$requestBlog = $blog;
}
public static function getRequestBlog() {
return self::$requestBlog;
}
}

View file

@ -22,19 +22,9 @@
final class PhameBlogDetailView extends AphrontView {
private $user;
private $isAdmin;
private $blog;
private $bloggers;
public function setIsAdmin($is_admin) {
$this->isAdmin = $is_admin;
return $this;
}
private function getIsAdmin() {
return $this->isAdmin;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
@ -60,38 +50,58 @@ final class PhameBlogDetailView extends AphrontView {
return $this->blog;
}
public function render() {
require_celerity_resource('phabricator-remarkup-css');
require_celerity_resource('phame-blog-detail-css');
$user = $this->getUser();
$blog = $this->getBlog();
$bloggers = $this->getBloggers();
$name = phutil_escape_html($blog->getName());
$description = phutil_escape_html($blog->getDescription());
$bloggers_txt = implode(' &middot; ', mpull($bloggers, 'renderLink'));
$panel = id(new AphrontPanelView())
->addClass('blog-detail')
->setHeader($name)
->setCaption($description)
->setWidth(AphrontPanelView::WIDTH_FORM)
->appendChild('Current bloggers: '.$bloggers_txt);
if ($this->getIsAdmin()) {
$panel->addButton(
$detail = phutil_render_tag(
'div',
array(
'class' => 'blog-detail'
),
phutil_render_tag(
'div',
array(
'class' => 'header',
),
phutil_render_tag(
'a',
array(
'href' => $blog->getEditURI(),
'class' => 'button grey',
),
'Edit Blog')
);
'h1',
array(),
$name
)
).
phutil_render_tag(
'div',
array(
'class' => 'description'
),
$description
).
phutil_render_tag(
'div',
array(
'class' => 'bloggers'
),
'Current bloggers: '.$this->getBloggersHTML($bloggers)
)
);
}
return $panel->render();
return $detail;
}
private function getBloggersHTML(array $bloggers) {
assert_instances_of($bloggers, 'PhabricatorObjectHandle');
$arr = array();
foreach ($bloggers as $blogger) {
$arr[] = '<strong>'.phutil_escape_html($blogger->getName()).'</strong>';
}
return implode(' &middot; ', $arr);
}
}

View file

@ -64,61 +64,37 @@ final class PhameBlogListView extends AphrontView {
return $panel->render();
}
$table_data = array();
$view = new PhabricatorObjectItemListView();
foreach ($blogs as $blog) {
$view_link = phutil_render_tag(
'a',
array(
'href' => $blog->getViewURI(),
),
phutil_escape_html($blog->getName()));
$bloggers = $blog->getBloggers();
if (isset($bloggers[$user->getPHID()])) {
$edit = phutil_render_tag(
'a',
array(
'class' => 'button small grey',
'href' => $blog->getEditURI(),
),
'Edit');
} else {
$edit = null;
}
$view = phutil_render_tag(
'a',
array(
'class' => 'button small grey',
'href' => $blog->getViewURI(),
),
'View');
$table_data[] =
array(
$view_link,
implode(', ', mpull($blog->getBloggers(), 'renderLink')),
$view,
$edit,
);
}
$table = new AphrontTableView($table_data);
$table->setHeaders(
array(
'Name',
'Bloggers',
'',
'',
));
$table->setColumnClasses(
array(
null,
null,
'action',
'action',
));
$item = id(new PhabricatorObjectItemView())
->setHeader($blog->getName())
->setHref($blog->getViewURI())
->addDetail(
'Bloggers',
implode(', ', mpull($bloggers, 'renderLink')))
->addDetail(
'Custom Domain',
$blog->getDomain());
if (isset($bloggers[$user->getPHID()])) {
$item->addAttribute(
phutil_render_tag(
'a',
array(
'class' => 'button small grey',
'href' => $blog->getEditURI(),
),
'Edit'));
}
$view->addItem($item);
}
$panel->setCreateButton('Create a Blog', '/phame/blog/new/');
$panel->setHeader($this->getHeader());
$panel->appendChild($table);
$panel->appendChild($view);
return $panel->render();
}

View file

@ -20,18 +20,23 @@
* @group phame
*/
final class PhamePostDetailView extends AphrontView {
private $post;
private $blogger;
private $requestURI;
private $user;
private $isPreview;
public function setIsPreview($is_preview) {
$this->isPreview = $is_preview;
private $user;
private $post;
private $blog;
private $blogger;
private $actions = array();
private $requestURI;
private $isPreview;
private $showComments;
private $shouldShorten;
public function setShouldShorten($should_shorten) {
$this->shouldShorten = $should_shorten;
return $this;
}
private function isPreview() {
return $this->isPreview;
public function getShouldShorten() {
return $this->shouldShorten;
}
public function setUser(PhabricatorUser $user) {
@ -42,6 +47,41 @@ final class PhamePostDetailView extends AphrontView {
return $this->user;
}
public function setPost(PhamePost $post) {
$this->post = $post;
return $this;
}
private function getPost() {
return $this->post;
}
public function setBlog(PhameBlog $blog) {
$this->blog = $blog;
return $this;
}
private function getBlog() {
return $this->blog;
}
public function setBlogger(PhabricatorObjectHandle $blogger) {
$this->blogger = $blogger;
return $this;
}
private function getBlogger() {
return $this->blogger;
}
public function setActions(array $actions) {
$this->actions = $actions;
return $this;
}
private function getActions() {
if ($this->actions) {
return $this->actions;
}
return array();
}
public function setRequestURI(PhutilURI $uri) {
$uri = PhabricatorEnv::getProductionURI($uri->setQueryParams(array()));
$this->requestURI = $uri;
@ -51,45 +91,75 @@ final class PhamePostDetailView extends AphrontView {
return $this->requestURI;
}
public function setBlogger(PhabricatorUser $blogger) {
$this->blogger = $blogger;
public function setIsPreview($is_preview) {
$this->isPreview = $is_preview;
return $this;
}
private function getBlogger() {
return $this->blogger;
private function isPreview() {
return $this->isPreview;
}
public function setPost(PhamePost $post) {
$this->post = $post;
public function setShowComments($show_comments) {
$this->showComments = $show_comments;
return $this;
}
private function getPost() {
return $this->post;
private function getShowComments() {
return $this->showComments;
}
public function render() {
require_celerity_resource('phabricator-remarkup-css');
require_celerity_resource('phame-css');
$user = $this->getUser();
$blogger = $this->getBlogger();
$post = $this->getPost();
$engine = PhabricatorMarkupEngine::newPhameMarkupEngine();
$body = $engine->markupText($post->getBody());
if ($post->isDraft()) {
$uri = '/phame/draft/';
$label = 'Back to Your Drafts';
} else {
$uri = '/phame/posts/'.$blogger->getUsername().'/';
$label = 'More Posts by '.phutil_escape_html($blogger->getUsername());
$actions = $this->getActions();
$noun = $post->isDraft() ? 'Draft' : 'Post';
$buttons = array();
foreach ($actions as $action) {
switch ($action) {
case 'view':
$uri = $post->getViewURI($blogger->getName());
$label = 'View '.$noun;
break;
case 'edit':
$uri = $post->getEditURI();
$label = 'Edit '.$noun;
break;
case 'more':
if ($post->isDraft()) {
$uri = '/phame/draft/';
$label = 'Back to Your Drafts';
} else {
$uri = '/phame/posts/'.$blogger->getName().'/';
$label = 'More Posts by '.phutil_escape_html($blogger->getName());
}
break;
default:
break;
}
$button = phutil_render_tag(
'a',
array(
'href' => $uri,
'class' => 'grey button',
),
$label);
$buttons[] = $button;
}
$button_html = '';
if ($buttons) {
$button_html = phutil_render_tag(
'div',
array(
'class' => 'buttons'
),
implode('', $buttons)
);
}
$button = phutil_render_tag(
'a',
array(
'href' => $uri,
'class' => 'grey button',
),
$label
);
$publish_date = $post->getDatePublished();
if ($publish_date) {
@ -101,55 +171,114 @@ final class PhamePostDetailView extends AphrontView {
phabricator_datetime($post->getDateModified(),
$user);
}
$caption .= ' by '.phutil_render_tag(
'a',
array(
'href' => new PhutilURI('/p/'.$blogger->getUsername().'/'),
),
phutil_escape_html($blogger->getUsername())
).'.';
$caption .= ' by <b>'.
phutil_escape_html($blogger->getName()).'</b>.';
if ($this->isPreview()) {
$width = AphrontPanelView::WIDTH_FULL;
} else {
$width = AphrontPanelView::WIDTH_WIDE;
$shortened = false;
$body_text = $post->getBody();
if ($this->getShouldShorten()) {
$body_length = phutil_utf8_strlen($body_text);
$body_text = phutil_utf8_shorten($body_text, 5000);
$shortened = $body_length > phutil_utf8_strlen($body_text);
}
$panel = id(new AphrontPanelView())
->setHeader(phutil_escape_html($post->getTitle()))
->appendChild('<div class="phabricator-remarkup">'.$body.'</div>')
->setWidth($width)
->addButton($button)
->setCaption($caption);
if ($user->getPHID() == $post->getBloggerPHID()) {
if ($post->isDraft()) {
$label = 'Edit Draft';
} else {
$label = 'Edit Post';
$engine = PhabricatorMarkupEngine::newPhameMarkupEngine();
$body = $engine->markupText($body_text);
$comments = null;
if ($this->getShowComments()) {
switch ($post->getCommentsWidget()) {
case 'facebook':
$comments = $this->renderFacebookComments();
break;
case 'disqus':
$comments = $this->renderDisqusComments();
break;
case 'none':
default:
$comments = null;
break;
}
$button = phutil_render_tag(
'a',
array(
'href' => $post->getEditURI(),
'class' => 'grey button',
),
$label);
$panel->addButton($button);
}
switch ($post->getCommentsWidget()) {
case 'facebook':
$comments = $this->renderFacebookComments();
break;
case 'disqus':
$comments = $this->renderDisqusComments();
break;
case 'none':
default:
$comments = null;
break;
}
$panel->appendChild($comments);
return $panel->render();
$more_to_do = null;
if (!$comments) {
if ($shortened) {
$more_to_do =
phutil_render_tag(
'div',
array(
'class' => 'more-and-comments'
),
phutil_render_tag(
'a',
array(
'href' => $post->getViewURI()
),
'&#8594; Read More'
)
);
} else if ($post->getCommentsWidget() &&
$post->getCommentsWidget() != 'none') {
$more_to_do =
phutil_render_tag(
'div',
array(
'class' => 'more-and-comments'
),
phutil_render_tag(
'a',
array(
'href' => $post->getViewURI()
),
'&#8594; Comment'
)
);
}
}
$post_html =
phutil_render_tag(
'div',
array(
'class' => 'blog-post'
),
phutil_render_tag(
'div',
array(
'class' => 'header',
),
$button_html .
phutil_render_tag(
'h1',
array(),
phutil_render_tag('a',
array(
'href' => $post->getViewURI($blogger->getName())
),
phutil_escape_html($post->getTitle())
)
).
phutil_render_tag(
'div',
array(
'class' => 'last-updated'
),
$caption
)
).
phutil_render_tag(
'div',
array(
'class' => 'phabricator-remarkup'
),
$body
).
$comments.$more_to_do
);
return $post_html;
}
private function renderFacebookComments() {
@ -161,7 +290,8 @@ final class PhamePostDetailView extends AphrontView {
$fb_root = phutil_render_tag('div',
array(
'id' => 'fb-root',
)
),
''
);
$c_uri = '//connect.facebook.net/en_US/all.js#xfbml=1&appId='.$fb_id;
@ -181,9 +311,10 @@ final class PhamePostDetailView extends AphrontView {
'class' => 'fb-comments',
'data-href' => $this->getRequestURI(),
'data-num-posts' => 5,
'data-width' => 1080,
'data-width' => 1080, // we hack this to fluid in css
'data-colorscheme' => 'dark',
)
),
''
);
return '<hr />' . $fb_root . $fb_js . $fb_comments;

View file

@ -24,9 +24,26 @@ final class PhamePostListView extends AphrontView {
private $user;
private $posts;
private $bloggers;
private $actions;
private $actions = array();
private $draftList;
private $blogStyle;
private $requestURI;
private $showPostComments;
public function setShowPostComments($show_post_comments) {
$this->showPostComments = $show_post_comments;
return $this;
}
private function getShowPostComments() {
return $this->showPostComments;
}
public function setRequestURI($request_uri) {
$this->requestURI = $request_uri;
return $this;
}
public function getRequestURI() {
return $this->requestURI;
}
public function setDraftList($draft_list) {
$this->draftList = $draft_list;
@ -72,28 +89,14 @@ final class PhamePostListView extends AphrontView {
return $this;
}
private function getActions() {
if ($this->actions) {
return $this->actions;
}
return array();
}
public function setBlogStyle($style) {
$this->blogStyle = $style;
return $this;
}
private function getBlogStyle() {
return $this->blogStyle;
return $this->actions;
}
public function render() {
$user = $this->getUser();
$posts = $this->getPosts();
$bloggers = $this->getBloggers();
$noun = $this->getPostNoun();
// TODO -- change this from a boolean to a string
// this string will represent a more specific "style" below
$blog_style = $this->getBlogStyle();
$user = $this->getUser();
$posts = $this->getPosts();
$bloggers = $this->getBloggers();
$noun = $this->getPostNoun();
if (empty($posts)) {
$panel = id(new AphrontPanelView())
@ -103,10 +106,9 @@ final class PhamePostListView extends AphrontView {
sprintf('/phame/%s/new', strtolower($noun)));
return $panel->render();
}
require_celerity_resource('phabricator-remarkup-css');
if ($blog_style) {
require_celerity_resource('phame-blog-post-list-css');
}
require_celerity_resource('phame-css');
$engine = PhabricatorMarkupEngine::newPhameMarkupEngine();
$html = array();
@ -114,43 +116,23 @@ final class PhamePostListView extends AphrontView {
foreach ($posts as $post) {
$blogger_phid = $post->getBloggerPHID();
$blogger = $bloggers[$blogger_phid];
$blogger_link = $blogger->renderLink();
$updated = phabricator_datetime($post->getDateModified(),
$user);
$body = $engine->markupText($post->getBody());
$panel = id(new AphrontPanelView())
->setHeader(phutil_escape_html($post->getTitle()))
->setCaption('Last updated '.$updated.' by '.$blogger_link.'.')
->appendChild('<div class="phabricator-remarkup">'.$body.'</div>');
if ($blog_style) {
$panel->addClass('blog-post-list');
}
foreach ($actions as $action) {
switch ($action) {
case 'view':
$uri = $post->getViewURI($blogger->getName());
$label = 'View '.$noun;
break;
case 'edit':
$uri = $post->getEditURI();
$label = 'Edit '.$noun;
break;
default:
break;
}
$button = phutil_render_tag(
'a',
array(
'href' => $uri,
'class' => 'grey button',
),
$label);
$panel->addButton($button);
}
$detail = id(new PhamePostDetailView())
->setUser($user)
->setPost($post)
->setBlogger($blogger)
->setActions($actions)
->setRequestURI($this->getRequestURI())
->setShowComments($this->getShowPostComments());
$html[] = $panel->render();
$html[] = $detail->render();
}
return implode('', $html);
return phutil_render_tag(
'div',
array(
'class' => 'blog-post-list'
),
implode('', $html)
);
}
}

View file

@ -0,0 +1,44 @@
<?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 PhabricatorBlogSkin extends PhameBlogSkin {
public function __construct() {
$this->setShowChrome(true);
}
protected function renderHeader() {
return '';
}
protected function renderFooter() {
return '';
}
protected function renderBody() {
require_celerity_resource('phame-css');
return
$this->renderNotice() .
$this->renderPosts() .
$this->renderBlogDetails();
}
}

View file

@ -0,0 +1,200 @@
<?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
*/
abstract class PhameBlogSkin extends AphrontView {
private $user;
private $blog;
private $bloggers;
private $posts;
private $actions;
private $notice;
private $showChrome;
private $deviceReady;
private $isExternalDomain;
private $requestURI;
private $showPostComments;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->user;
}
public function setBlog(PhameBlog $blog) {
$this->blog = $blog;
return $this;
}
public function getBlog() {
return $this->blog;
}
public function setBloggers(array $bloggers) {
assert_instances_of($bloggers, 'PhabricatorObjectHandle');
$this->bloggers = $bloggers;
return $this;
}
public function getBloggers() {
return $this->bloggers;
}
public function setPosts(array $posts) {
assert_instances_of($posts, 'PhamePost');
$this->posts = $posts;
return $this;
}
protected function getPosts() {
return $this->posts;
}
public function setActions($actions) {
$this->actions = $actions;
return $this;
}
public function getActions() {
return $this->actions;
}
public function setNotice(array $notice) {
$this->notice = $notice;
return $this;
}
protected function getNotice() {
return $this->notice;
}
public function setIsExternalDomain($is_external_domain) {
$this->isExternalDomain = $is_external_domain;
return $this;
}
protected function getIsExternalDomain() {
return $this->isExternalDomain;
}
public function setRequestURI($request_uri) {
$this->requestURI = $request_uri;
return $this;
}
private function getRequestURI() {
return $this->requestURI;
}
protected function setShowChrome($show_chrome) {
$this->showChrome = $show_chrome;
return $this;
}
public function getShowChrome() {
return $this->showChrome;
}
protected function setDeviceReady($device_ready) {
$this->deviceReady = $device_ready;
return $this;
}
public function getDeviceReady() {
return $this->deviceReady;
}
public function setShowPostComments($show_post_comments) {
$this->showPostComments = $show_post_comments;
return $this;
}
public function getShowPostComments() {
return $this->showPostComments;
}
public function __construct() {
// set any options here
}
public function render() {
return
$this->renderHeader().
$this->renderBody().
$this->renderFooter();
}
abstract protected function renderHeader();
abstract protected function renderBody();
abstract protected function renderFooter();
final public function renderPosts() {
$list = id(new PhamePostListView())
->setUser($this->getUser())
->setPosts($this->getPosts())
->setBloggers($this->getBloggers())
->setRequestURI($this->getRequestURI())
->setShowPostComments($this->getShowPostComments())
->setDraftList(false);
return $list->render();
}
final protected function renderBlogDetails() {
$details = array();
$blog = $this->getBlog();
if ($blog) {
$detail = id(new PhameBlogDetailView())
->setUser($this->getUser())
->setBlog($this->getBlog())
->setBloggers($this->getBloggers());
$details[] = $detail->render();
}
// TODO - more cool boxes for the right hand column...!
return implode('', $details);
}
final protected function renderNotice() {
$notice_html = '';
if ($this->getIsExternalDomain()) {
return $notice_html;
}
$notice = $this->getNotice();
if ($notice) {
$header = $notice['title'];
$details = $notice['body'];
$notice_html = phutil_render_tag(
'div',
array(
'class' => 'notice'
),
phutil_render_tag(
'h3',
array(),
$header
).phutil_render_tag(
'h4',
array(),
$details
)
);
}
return $notice_html;
}
}

View file

@ -1,18 +0,0 @@
/**
* @provides phame-blog-detail-css
*/
.blog-detail {
clear: right;
float: right;
width: 20%;
margin: 16px 16px 16px 0px;
padding: 12px 8px 12px 8px;
}
.device-tablet .blog-detail,
.device-phone .blog-detail {
float: none;
margin: 16px auto;
width: 90%;
}

View file

@ -1,18 +0,0 @@
/**
* @provides phame-blog-post-list-css
*/
.blog-post-list {
clear: left;
float: left;
width: 70%;
margin: 16px 0px 16px 16px;
padding: 12px 8px 12px 8px;
}
.device-tablet .blog-post-list,
.device-phone .blog-post-list {
float: none;
width: 90%;
margin: 16px auto;
}

View file

@ -0,0 +1,114 @@
/**
* @provides phame-css
*/
.notice {
background: #F3F3FF;
border: 1px solid #008;
margin: 16px 16px 4px 26px;
}
.notice h3 {
background: #E3E3FF;
padding: 8px;
}
.notice h4 {
font-weight: normal;
padding: 8px;
}
.phame-post-preview-header {
margin: 0px 0px 16px 0px;
}
.blog-post-list {
clear: left;
float: left;
width: 70%;
margin: 16px 0px 16px 16px;
padding: 0px 8px 12px 8px;
}
.device-tablet .blog-post-list,
.device-phone .blog-post-list {
float: none;
width: 90%;
margin: 16px auto;
}
.blog-post-list-full {
clear: left;
float: left;
margin: 16px 0px 0px 0px;
padding: 0px 16px 0px 16px;
}
.device-tablet .blog-post-list-full,
.device-phone .blog-post-list-full {
float: none;
margin: 16px auto;
}
.blog-detail {
float: right;
clear: right;
width: 20%;
margin: 16px 16px 16px 0px;
}
.device-tablet .blog-detail,
.device-phone .blog-detail {
float: none;
margin: 16px auto;
width: 90%;
}
.blog-detail .description {
margin: 16px 0px 16px 0px;
}
.blog-detail .bloggers {
font-size: 11px;
}
.blog-post,
.blog-detail {
border: 1px solid #DBDBDB;
background: #F9F9F9;
padding: 20px;
}
.blog-post {
margin: 0px 0px 20px 0px;
}
.blog-post .header {
padding: 0px 0px 16px 0px;
}
.blog-post .header h1 {
clear: none;
}
.blog-post .header .last-updated {
color: #666;
clear: none;
font-size: 11px;
}
.blog-post .header .buttons {
float: right;
}
.blog-post .header .buttons a {
margin: 0px 0px 0px 12px;
}
.more-and-comments {
padding: 12px 0px 12px 0px;
}
.fb-comments,
.fb-comments span,
.fb-comments iframe[style] {
width: 100% !important;
}