From 8355f3592f984265e31acbd66c0b262657ef0b61 Mon Sep 17 00:00:00 2001 From: Bob Trahan Date: Fri, 12 Oct 2012 16:01:33 -0700 Subject: [PATCH] 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 --- src/__celerity_resource_map__.php | 62 +++- src/__phutil_library_map__.php | 4 + .../AphrontApplicationConfiguration.php | 10 +- .../phame/controller/PhameController.php | 20 ++ .../blog/PhameBlogEditController.php | 9 + .../blog/PhameBlogViewController.php | 47 ++- .../post/PhamePostEditController.php | 1 + .../post/PhamePostPreviewController.php | 4 +- .../post/PhamePostViewController.php | 62 ++-- .../post/list/PhameAllPostListController.php | 2 +- .../post/list/PhamePostListBaseController.php | 8 +- src/applications/phame/storage/PhameBlog.php | 41 +++ .../phame/view/PhameBlogDetailView.php | 72 +++-- .../phame/view/PhameBlogListView.php | 74 ++--- .../phame/view/PhamePostDetailView.php | 291 +++++++++++++----- .../phame/view/PhamePostListView.php | 100 +++--- .../phame/view/skins/PhabricatorBlogSkin.php | 44 +++ .../phame/view/skins/PhameBlogSkin.php | 200 ++++++++++++ .../css/application/phame/blog-detail.css | 18 -- .../css/application/phame/blog-post-list.css | 18 -- webroot/rsrc/css/application/phame/phame.css | 114 +++++++ 21 files changed, 886 insertions(+), 315 deletions(-) create mode 100644 src/applications/phame/view/skins/PhabricatorBlogSkin.php create mode 100644 src/applications/phame/view/skins/PhameBlogSkin.php delete mode 100644 webroot/rsrc/css/application/phame/blog-detail.css delete mode 100644 webroot/rsrc/css/application/phame/blog-post-list.css create mode 100644 webroot/rsrc/css/application/phame/phame.css diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 8d87fe18a4..0299ad68c8 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -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( diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index cd07a4e727..b2a7c296a8 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -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', diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index e9dabb6695..f3bbeaeaf4 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -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//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); } diff --git a/src/applications/phame/controller/PhameController.php b/src/applications/phame/controller/PhameController.php index 8b12a8a323..c6d7f39b54 100644 --- a/src/applications/phame/controller/PhameController.php +++ b/src/applications/phame/controller/PhameController.php @@ -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()); diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php index 9d55abc8be..4c0216887c 100644 --- a/src/applications/phame/controller/blog/PhameBlogEditController.php +++ b/src/applications/phame/controller/blog/PhameBlogEditController.php @@ -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/') diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index b84c62ba8d..9ad099ec5f 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -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(), diff --git a/src/applications/phame/controller/post/PhamePostEditController.php b/src/applications/phame/controller/post/PhamePostEditController.php index f094c5779d..013632bf9a 100644 --- a/src/applications/phame/controller/post/PhamePostEditController.php +++ b/src/applications/phame/controller/post/PhamePostEditController.php @@ -299,6 +299,7 @@ final class PhamePostEditController '; + require_celerity_resource('phame-css'); Javelin::initBehavior( 'phame-post-preview', array( diff --git a/src/applications/phame/controller/post/PhamePostPreviewController.php b/src/applications/phame/controller/post/PhamePostPreviewController.php index 3b2f03dff1..7f0252b4b4 100644 --- a/src/applications/phame/controller/post/PhamePostPreviewController.php +++ b/src/applications/phame/controller/post/PhamePostPreviewController.php @@ -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(); diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index b1123a1d44..085b7ccb8b 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -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('

Saved post successfully.

') - ->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(), diff --git a/src/applications/phame/controller/post/list/PhameAllPostListController.php b/src/applications/phame/controller/post/list/PhameAllPostListController.php index f21731a12a..1f26c70ec2 100644 --- a/src/applications/phame/controller/post/list/PhameAllPostListController.php +++ b/src/applications/phame/controller/post/list/PhameAllPostListController.php @@ -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); diff --git a/src/applications/phame/controller/post/list/PhamePostListBaseController.php b/src/applications/phame/controller/post/list/PhamePostListBaseController.php index 1c3e12c3de..7c17fae691 100644 --- a/src/applications/phame/controller/post/list/PhamePostListBaseController.php +++ b/src/applications/phame/controller/post/list/PhamePostListBaseController.php @@ -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( diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index f33a7da0a7..1e2141d9b7 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -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; + } } diff --git a/src/applications/phame/view/PhameBlogDetailView.php b/src/applications/phame/view/PhameBlogDetailView.php index c35b4bc0e7..6d5bc95109 100644 --- a/src/applications/phame/view/PhameBlogDetailView.php +++ b/src/applications/phame/view/PhameBlogDetailView.php @@ -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(' · ', 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[] = ''.phutil_escape_html($blogger->getName()).''; + } + + return implode(' · ', $arr); + } } diff --git a/src/applications/phame/view/PhameBlogListView.php b/src/applications/phame/view/PhameBlogListView.php index 452d73a088..6aacc1070e 100644 --- a/src/applications/phame/view/PhameBlogListView.php +++ b/src/applications/phame/view/PhameBlogListView.php @@ -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(); } diff --git a/src/applications/phame/view/PhamePostDetailView.php b/src/applications/phame/view/PhamePostDetailView.php index ebf5089acb..4f56d17cff 100644 --- a/src/applications/phame/view/PhamePostDetailView.php +++ b/src/applications/phame/view/PhamePostDetailView.php @@ -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 '. + phutil_escape_html($blogger->getName()).'.'; - 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('
'.$body.'
') - ->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() + ), + '→ 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() + ), + '→ 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 '
' . $fb_root . $fb_js . $fb_comments; diff --git a/src/applications/phame/view/PhamePostListView.php b/src/applications/phame/view/PhamePostListView.php index dda2357afd..3bb75e4fd2 100644 --- a/src/applications/phame/view/PhamePostListView.php +++ b/src/applications/phame/view/PhamePostListView.php @@ -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('
'.$body.'
'); - 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) + ); } } diff --git a/src/applications/phame/view/skins/PhabricatorBlogSkin.php b/src/applications/phame/view/skins/PhabricatorBlogSkin.php new file mode 100644 index 0000000000..0daa2911c4 --- /dev/null +++ b/src/applications/phame/view/skins/PhabricatorBlogSkin.php @@ -0,0 +1,44 @@ +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(); + } +} diff --git a/src/applications/phame/view/skins/PhameBlogSkin.php b/src/applications/phame/view/skins/PhameBlogSkin.php new file mode 100644 index 0000000000..dcb66b9c28 --- /dev/null +++ b/src/applications/phame/view/skins/PhameBlogSkin.php @@ -0,0 +1,200 @@ +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; + } + +} diff --git a/webroot/rsrc/css/application/phame/blog-detail.css b/webroot/rsrc/css/application/phame/blog-detail.css deleted file mode 100644 index ba222494cb..0000000000 --- a/webroot/rsrc/css/application/phame/blog-detail.css +++ /dev/null @@ -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%; -} diff --git a/webroot/rsrc/css/application/phame/blog-post-list.css b/webroot/rsrc/css/application/phame/blog-post-list.css deleted file mode 100644 index 3524d6b4ca..0000000000 --- a/webroot/rsrc/css/application/phame/blog-post-list.css +++ /dev/null @@ -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; -} diff --git a/webroot/rsrc/css/application/phame/phame.css b/webroot/rsrc/css/application/phame/phame.css new file mode 100644 index 0000000000..0d858cde0a --- /dev/null +++ b/webroot/rsrc/css/application/phame/phame.css @@ -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; +}