From 1a63938b334c902e10cf9602a379f99959077454 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 16 Oct 2012 09:44:43 -0700 Subject: [PATCH] Further improve various Phame UI things Summary: - Better icons and action order. - "Move Post" action. - (Bugfix) Allow multiple blogs to be set to not having custom domains. - Make "Write Post" skip the "select a blog" step when coming from a blog view. - Sort blog list on "Write Post". - Show messages when a post is a draft or not on a blog. Test Plan: Created posts, blogs, moved posts, preview/live'd posts, etc. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T1373 Differential Revision: https://secure.phabricator.com/D3708 --- scripts/celerity/generate_sprites.php | 5 +- .../PhabricatorApplicationPhame.php | 1 + .../blog/PhameBlogEditController.php | 2 +- .../blog/PhameBlogViewController.php | 2 +- .../post/PhamePostNewController.php | 81 ++++++++++++-- .../post/PhamePostNotLiveController.php | 4 +- .../post/PhamePostViewController.php | 103 +++++++++--------- src/view/form/AphrontErrorView.php | 21 ++-- webroot/rsrc/css/aphront/error-view.css | 1 + webroot/rsrc/css/autosprite.css | 30 +++-- .../layout/phabricator-action-list-view.css | 2 +- .../layout/phabricator-property-list-view.css | 24 ++++ webroot/rsrc/image/autosprite.png | Bin 145081 -> 146324 bytes 13 files changed, 192 insertions(+), 84 deletions(-) diff --git a/scripts/celerity/generate_sprites.php b/scripts/celerity/generate_sprites.php index a6f2b109b5..b44e61f1e3 100755 --- a/scripts/celerity/generate_sprites.php +++ b/scripts/celerity/generate_sprites.php @@ -220,7 +220,10 @@ $action_map = array( 'subscribe-delete' => 'icon/unsubscribe.png', 'new' => 'icon/page_white_put.png', 'world' => 'icon/world.png', - 'delete' => 'icon/delete.png', + 'delete' => 'icon/page_delete.png', + 'move' => 'icon/page_go.png', + 'preview' => 'icon/page_world.png', + 'unpublish' => 'icon/page_error.png', ); foreach ($action_map as $icon => $source) { diff --git a/src/applications/phame/application/PhabricatorApplicationPhame.php b/src/applications/phame/application/PhabricatorApplicationPhame.php index 5867f9d194..84ca525a9c 100644 --- a/src/applications/phame/application/PhabricatorApplicationPhame.php +++ b/src/applications/phame/application/PhabricatorApplicationPhame.php @@ -59,6 +59,7 @@ final class PhabricatorApplicationPhame extends PhabricatorApplication { 'preview/' => 'PhamePostPreviewController', 'framed/(?P\d+)/' => 'PhamePostFramedController', 'new/' => 'PhamePostNewController', + 'move/(?P\d+)/' => 'PhamePostNewController' ), 'blog/' => array( '(?:(?Puser|all)/)?' => 'PhameBlogListController', diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php index 35cddc9b52..4b953d2bd0 100644 --- a/src/applications/phame/controller/blog/PhameBlogEditController.php +++ b/src/applications/phame/controller/blog/PhameBlogEditController.php @@ -80,7 +80,7 @@ final class PhameBlogEditController $blog->setName($name); $blog->setDescription($description); - $blog->setDomain($custom_domain); + $blog->setDomain(nonempty($custom_domain, null)); $blog->setSkin($skin); if (!empty($custom_domain)) { diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 7533d0b86b..8475cee454 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -139,7 +139,7 @@ final class PhameBlogViewController extends PhameController { $actions->addAction( id(new PhabricatorActionView()) ->setIcon('new') - ->setHref($this->getApplicationURI('post/new/?blog='.$blog->getID())) + ->setHref($this->getApplicationURI('post/edit/?blog='.$blog->getID())) ->setName(pht('Write Post')) ->setDisabled(!$can_join) ->setWorkflow(!$can_join)); diff --git a/src/applications/phame/controller/post/PhamePostNewController.php b/src/applications/phame/controller/post/PhamePostNewController.php index 6393c2d9b0..60b7056e67 100644 --- a/src/applications/phame/controller/post/PhamePostNewController.php +++ b/src/applications/phame/controller/post/PhamePostNewController.php @@ -21,10 +21,57 @@ */ final class PhamePostNewController extends PhameController { + private $id; + + public function willProcessRequest(array $data) { + $this->id = idx($data, 'id'); + } + public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); + $post = null; + $view_uri = null; + if ($this->id) { + $post = id(new PhamePostQuery()) + ->setViewer($user) + ->withIDs(array($this->id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$post) { + return new Aphront404Response(); + } + + $view_uri = '/post/view/'.$post->getID().'/'; + $view_uri = $this->getApplicationURI($view_uri); + + if ($request->isFormPost()) { + $blog = id(new PhameBlogQuery()) + ->setViewer($user) + ->withIDs(array($request->getInt('blog'))) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_JOIN, + )) + ->executeOne(); + + if ($blog) { + $post->setBlogPHID($blog->getPHID()); + $post->save(); + + return id(new AphrontRedirectResponse())->setURI($view_uri); + } + } + + $title = pht('Move Post'); + } else { + $title = pht('Create Post'); + } + $blogs = id(new PhameBlogQuery()) ->setViewer($user) ->requireCapabilities( @@ -36,8 +83,7 @@ final class PhamePostNewController extends PhameController { $nav = $this->renderSideNavFilterView(); $nav->selectFilter('post/new'); $nav->appendChild( - id(new PhabricatorHeaderView())->setHeader( - pht('Create Post'))); + id(new PhabricatorHeaderView())->setHeader($title)); if (!$blogs) { $notification = id(new AphrontErrorView()) @@ -49,20 +95,37 @@ final class PhamePostNewController extends PhameController { $nav->appendChild($notification); } else { $options = mpull($blogs, 'getName', 'getID'); + asort($options); + + $selected_value = null; + if ($post && $post->getBlog()) { + $selected_value = $post->getBlog()->getID(); + } $form = id(new AphrontFormView()) ->setUser($user) - ->setMethod('GET') ->setFlexible(true) - ->setAction($this->getApplicationURI('post/edit/')) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Blog') ->setName('blog') - ->setOptions($options)) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue('Continue')); + ->setOptions($options) + ->setValue($selected_value)); + + if ($post) { + $form + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Move Post')) + ->addCancelButton($view_uri)); + } else { + $form + ->setAction($this->getApplicationURI('post/edit/')) + ->setMethod('GET') + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Continue'))); + } $nav->appendChild($form); } @@ -70,7 +133,7 @@ final class PhamePostNewController extends PhameController { return $this->buildApplicationPage( $nav, array( - 'title' => 'Create Post', + 'title' => $title, 'device' => true, )); } diff --git a/src/applications/phame/controller/post/PhamePostNotLiveController.php b/src/applications/phame/controller/post/PhamePostNotLiveController.php index 9676a11ad8..8d5cb3715b 100644 --- a/src/applications/phame/controller/post/PhamePostNotLiveController.php +++ b/src/applications/phame/controller/post/PhamePostNotLiveController.php @@ -43,8 +43,8 @@ final class PhamePostNotLiveController extends PhameController { if (!$post->getBlog()) { $reasons[] = '

'.pht('You can not view the live version of this post because it '. - 'is not associated with a blog. Edit the post and choose a blog to '. - 'publish it to.').'

'; + 'is not associated with a blog. Move the post to a blog in order to '. + 'view it live.').'

'; } if ($post->isDraft()) { diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index affbfbda81..626a1704c4 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -40,28 +40,30 @@ final class PhamePostViewController extends PhameController { return new Aphront404Response(); } + $nav = $this->renderSideNavFilterView(); + + $nav->appendChild( + id(new PhabricatorHeaderView()) + ->setHeader($post->getTitle())); + if ($post->isDraft()) { - $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', - array( - 'href' => '/phame/post/new/', - 'class' => 'button green', - ), - 'write another blog post' - ); - $notice = array( - 'title' => 'Saved post successfully.', - 'body' => 'Seek even more phame and '.$new_link.'.' - ); - } else { - $notice = array(); + $nav->appendChild( + id(new AphrontErrorView()) + ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) + ->setTitle(pht('Draft Post')) + ->appendChild( + pht('Only you can see this draft until you publish it. '. + 'Use "Preview / Publish" to publish this post.'))); + } + + if (!$post->getBlog()) { + $nav->appendChild( + id(new AphrontErrorView()) + ->setSeverity(AphrontErrorView::SEVERITY_WARNING) + ->setTitle(pht('Not On A Blog')) + ->appendChild( + pht('This post is not associated with a blog (the blog may have '. + 'been deleted). Use "Move Post" to move it to a new blog.'))); } $this->loadHandles( @@ -70,16 +72,11 @@ final class PhamePostViewController extends PhameController { $post->getBloggerPHID(), )); - $nav = $this->renderSideNavFilterView(null); - - $header = id(new PhabricatorHeaderView())->setHeader($post->getTitle()); - $actions = $this->renderActions($post, $user); $properties = $this->renderProperties($post, $user); $nav->appendChild( array( - $header, $actions, $properties, )); @@ -115,6 +112,37 @@ final class PhamePostViewController extends PhameController { ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('move') + ->setHref($this->getApplicationURI('post/move/'.$id.'/')) + ->setName('Move Post') + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + if ($post->isDraft()) { + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('preview') + ->setHref($this->getApplicationURI('post/publish/'.$id.'/')) + ->setName(pht('Preview / Publish'))); + } else { + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('unpublish') + ->setHref($this->getApplicationURI('post/unpublish/'.$id.'/')) + ->setName(pht('Unpublish')) + ->setWorkflow(true)); + } + + $actions->addAction( + id(new PhabricatorActionView()) + ->setIcon('delete') + ->setHref($this->getApplicationURI('post/delete/'.$id.'/')) + ->setName('Delete Post') + ->setDisabled(!$can_edit) + ->setWorkflow(true)); + $blog = $post->getBlog(); $can_view_live = $blog && !$post->isDraft(); @@ -133,29 +161,6 @@ final class PhamePostViewController extends PhameController { ->setDisabled(!$can_view_live) ->setWorkflow(!$can_view_live)); - if ($post->isDraft()) { - $actions->addAction( - id(new PhabricatorActionView()) - ->setIcon('world') - ->setHref($this->getApplicationURI('post/publish/'.$id.'/')) - ->setName(pht('Preview / Publish'))); - } else { - $actions->addAction( - id(new PhabricatorActionView()) - ->setIcon('delete') - ->setHref($this->getApplicationURI('post/unpublish/'.$id.'/')) - ->setName(pht('Unpublish')) - ->setWorkflow(true)); - } - - $actions->addAction( - id(new PhabricatorActionView()) - ->setIcon('delete') - ->setHref($this->getApplicationURI('post/delete/'.$id.'/')) - ->setName('Delete Post') - ->setDisabled(!$can_edit) - ->setWorkflow(true)); - return $actions; } diff --git a/src/view/form/AphrontErrorView.php b/src/view/form/AphrontErrorView.php index f68dc129cd..dce3e9a974 100644 --- a/src/view/form/AphrontErrorView.php +++ b/src/view/form/AphrontErrorView.php @@ -89,20 +89,19 @@ final class AphrontErrorView extends AphrontView { $more_classes[] = 'aphront-error-severity-'.$this->severity; $more_classes = implode(' ', $more_classes); - return + return phutil_render_tag( + 'div', + array( + 'id' => $this->id, + 'class' => 'aphront-error-view '.$more_classes, + ), + $title. phutil_render_tag( 'div', array( - 'id' => $this->id, - 'class' => 'aphront-error-view '.$more_classes, + 'class' => 'aphront-error-view-body', ), - $title. - phutil_render_tag( - 'div', - array( - 'class' => 'aphront-error-view-body', - ), - $this->renderChildren(). - $list)); + $this->renderChildren(). + $list)); } } diff --git a/webroot/rsrc/css/aphront/error-view.css b/webroot/rsrc/css/aphront/error-view.css index 02056f45cf..21f6e36156 100644 --- a/webroot/rsrc/css/aphront/error-view.css +++ b/webroot/rsrc/css/aphront/error-view.css @@ -66,3 +66,4 @@ .aphront-error-severity-nodata .aphront-error-view-head { background: #e3e3e3; } + diff --git a/webroot/rsrc/css/autosprite.css b/webroot/rsrc/css/autosprite.css index e26de1e87e..5b93e88883 100644 --- a/webroot/rsrc/css/autosprite.css +++ b/webroot/rsrc/css/autosprite.css @@ -760,38 +760,50 @@ background-position: 0px -7359px; } -.remarkup-assist-b { +.action-move { background-position: 0px -7376px; } +.action-preview { + background-position: 0px -7393px; +} + +.action-unpublish { + background-position: 0px -7410px; +} + +.remarkup-assist-b { + background-position: 0px -7427px; +} + .remarkup-assist-code { - background-position: 0px -7391px; + background-position: 0px -7442px; } .remarkup-assist-i { - background-position: 0px -7406px; + background-position: 0px -7457px; } .remarkup-assist-image { - background-position: 0px -7421px; + background-position: 0px -7472px; } .remarkup-assist-ol { - background-position: 0px -7436px; + background-position: 0px -7487px; } .remarkup-assist-tag { - background-position: 0px -7451px; + background-position: 0px -7502px; } .remarkup-assist-tt { - background-position: 0px -7466px; + background-position: 0px -7517px; } .remarkup-assist-ul { - background-position: 0px -7481px; + background-position: 0px -7532px; } .remarkup-assist-help { - background-position: 0px -7496px; + background-position: 0px -7547px; } diff --git a/webroot/rsrc/css/layout/phabricator-action-list-view.css b/webroot/rsrc/css/layout/phabricator-action-list-view.css index 80083e448a..a8d7ce03d2 100644 --- a/webroot/rsrc/css/layout/phabricator-action-list-view.css +++ b/webroot/rsrc/css/layout/phabricator-action-list-view.css @@ -11,7 +11,7 @@ padding: .5em 0; float: right; - margin-top: -30px; + margin-top: 0px; margin-right: 1%; width: 20%; border-radius: 2px; diff --git a/webroot/rsrc/css/layout/phabricator-property-list-view.css b/webroot/rsrc/css/layout/phabricator-property-list-view.css index 22f3affe7a..dec6656600 100644 --- a/webroot/rsrc/css/layout/phabricator-property-list-view.css +++ b/webroot/rsrc/css/layout/phabricator-property-list-view.css @@ -66,3 +66,27 @@ .phabricator-property-list-text-content { padding: 12px 18px; } + +/* When we follow an action list view on the Desktop, move down 30px so the + action list can start slightly above the property list. This is an unusual + case where we have something between the header and the action/property + lists and we want to give it space. */ +.device-desktop .phabricator-action-list-view + + .phabricator-property-list-view { + clear: left; + margin-top: 30px; +} + + +/* In the common case where we immediately follow a header, move back up 30px + so we snuggle next to the header. */ +.device-desktop .phabricator-header-view + + .phabricator-action-list-view { + margin-top: -30px; +} + +.device-desktop .phabricator-header-view + + .phabricator-action-list-view + + .phabricator-property-list-view { + margin-top: 0px; +} diff --git a/webroot/rsrc/image/autosprite.png b/webroot/rsrc/image/autosprite.png index 35b9978462331503b68147e952b1cc66fd37e7e6..89a7ff31adbcbdc2ddf30e17890916d5d8d90c0b 100644 GIT binary patch delta 6255 zcmV-#7?9_=?Ff|j2#^;JiU$J6nMzg(U&CB?3|-f9C;J)s^+}_l;jN zj>(wB7Da<4iHRMtU>6YqYhs+wl0-ifyT%Mrqe(P?fFZWTj@VF?=gOix`tu>y@-D~Z2?z{KAwchXTQ}@~D++BJnQ5IT?gv0R&wh2aQ za^tu1QSbC~^QC zn&gh{`u1*%2mP~&1K{XXXB?sK=&~;xNf1U1J$Ab(z<9e%a*SGVT(`TXB4gjakiUUA#04NRs#Q~tmizhDh8bCLz z5jgPmSjS1m)0P^WOuk`cWCTM)Lrk1#Af?8}#z`Ea_l7O zz2QfX9D${!mGq|g`q%WP`CWqU zU|r~q9EHN-5()U>!#c_0dU|@eckiC`MGWj?AUQIm}rRdd-ogPD!q2ydV(8| zZQFmsH{WhVVp1|LT(~H19ZvOHh6$6VV}gO9)DlzILw)c>$%0%@`I=j4mWrweShjox zzT3D-$|vOHfzJRiGMNqoLt{5xUEQW<08F0_?r%9|sjYhi)34T0zdVU8Ea({k?#to5 ze`Q$nX8;Tv_Qi}DGwRenUddC=LL{0iZYl6nWuf|Ni}M`}XaF zt*x!&$&)9S+S}XTICkt9jvhVQsC2KEp2@=jz_4CkUN~`r-V;vIvQbe{$j!|~MMVXw ztE*92St(5g30kV-luzgQ@#C{e{pnADe~`c)9v)z;*3{G>h32~P@$tBI>y|X}=d`M- z3P;E>z<_RUZt(V|DP&<`Mxja70H(BP=WoIXO8vc<><7)6=E&O zB&fEU7H?4gW5jd-sDU|c5t9c7<-WJY03SGT05oF)r!4|lvu-MrWknQGf8>RiB}
  • eZ_yXkme~va%$P23w7%ht0wA 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 Z@_%8FNdd3y%w7Nh002ovPDHLkV1mjb7xw@F delta 5002 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 Ug+S0)z5oCK07*qoM6N<$f^zb~sQ>@~