1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 08:52:39 +01:00

Mostly modernize post edit/detail UIs

Summary:
Still some big chunks left but this moves us a bit closer to getting everything device-ready.

Stuff not addressed here but which I'm planning to do soon:

  - Posts don't have a live URI yet.
  - Post detail pages don't actually show the post content. I'm going to tweak PhabricatorObjectPropertyListView for this since we need it some other places.
  - Some of the hinting about use/states is gone (e.g., "This post is a draft, publish it to make it live to the world."); I'm planning to restore it.
  - Left nav is still a bit of a mess with states/highlighting.

Major changes are:

  - If you click "New Post" you get a screen asking you to pick a blog to post to.
  - "Publish/Preview" and Unpublish are now separate actions from the post detail screen.
  - "Publish/Preview" renders a preview of the post in an iframe and gives you a "Publish" button.

Test Plan: Will attach screenshots.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1373

Differential Revision: https://secure.phabricator.com/D3697
This commit is contained in:
epriestley 2012-10-15 14:50:45 -07:00
parent c37202dd48
commit 0e07ef8d56
14 changed files with 520 additions and 340 deletions

View file

@ -1162,10 +1162,14 @@ phutil_register_library_map(array(
'PhamePostDeleteController' => 'applications/phame/controller/post/PhamePostDeleteController.php', 'PhamePostDeleteController' => 'applications/phame/controller/post/PhamePostDeleteController.php',
'PhamePostDetailView' => 'applications/phame/view/PhamePostDetailView.php', 'PhamePostDetailView' => 'applications/phame/view/PhamePostDetailView.php',
'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php',
'PhamePostFramedController' => 'applications/phame/controller/post/PhamePostFramedController.php',
'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php', 'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php',
'PhamePostListView' => 'applications/phame/view/PhamePostListView.php', 'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
'PhamePostNewController' => 'applications/phame/controller/post/PhamePostNewController.php',
'PhamePostPreviewController' => 'applications/phame/controller/post/PhamePostPreviewController.php', 'PhamePostPreviewController' => 'applications/phame/controller/post/PhamePostPreviewController.php',
'PhamePostPublishController' => 'applications/phame/controller/post/PhamePostPublishController.php',
'PhamePostQuery' => 'applications/phame/query/PhamePostQuery.php', 'PhamePostQuery' => 'applications/phame/query/PhamePostQuery.php',
'PhamePostUnpublishController' => 'applications/phame/controller/post/PhamePostUnpublishController.php',
'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php', 'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php',
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php', 'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/PhortuneStripeBaseController.php', 'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/PhortuneStripeBaseController.php',
@ -2291,10 +2295,14 @@ phutil_register_library_map(array(
'PhamePostDeleteController' => 'PhameController', 'PhamePostDeleteController' => 'PhameController',
'PhamePostDetailView' => 'AphrontView', 'PhamePostDetailView' => 'AphrontView',
'PhamePostEditController' => 'PhameController', 'PhamePostEditController' => 'PhameController',
'PhamePostFramedController' => 'PhameController',
'PhamePostListController' => 'PhameController', 'PhamePostListController' => 'PhameController',
'PhamePostListView' => 'AphrontView', 'PhamePostListView' => 'AphrontView',
'PhamePostNewController' => 'PhameController',
'PhamePostPreviewController' => 'PhameController', 'PhamePostPreviewController' => 'PhameController',
'PhamePostPublishController' => 'PhameController',
'PhamePostQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhamePostQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhamePostUnpublishController' => 'PhameController',
'PhamePostViewController' => 'PhameController', 'PhamePostViewController' => 'PhameController',
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl', 'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
'PhortuneStripeBaseController' => 'PhabricatorController', 'PhortuneStripeBaseController' => 'PhabricatorController',

View file

@ -49,11 +49,14 @@ final class PhabricatorApplicationPhame extends PhabricatorApplication {
'post/' => array( 'post/' => array(
'(?:(?P<filter>draft|all)/)?' => 'PhamePostListController', '(?:(?P<filter>draft|all)/)?' => 'PhamePostListController',
'blogger/(?P<bloggername>[\w\.-_]+)/' => 'PhamePostListController', 'blogger/(?P<bloggername>[\w\.-_]+)/' => 'PhamePostListController',
'delete/(?P<phid>[^/]+)/' => 'PhamePostDeleteController', 'delete/(?P<id>[^/]+)/' => 'PhamePostDeleteController',
'edit/(?P<phid>[^/]+)/' => 'PhamePostEditController', 'edit/(?:(?P<id>[^/]+)/)?' => 'PhamePostEditController',
'new/' => 'PhamePostEditController', 'view/(?P<id>\d+)/' => 'PhamePostViewController',
'publish/(?P<id>\d+)/' => 'PhamePostPublishController',
'unpublish/(?P<id>\d+)/' => 'PhamePostUnpublishController',
'preview/' => 'PhamePostPreviewController', 'preview/' => 'PhamePostPreviewController',
'view/(?P<phid>[^/]+)/' => 'PhamePostViewController', 'framed/(?P<id>\d+)/' => 'PhamePostFramedController',
'new/' => 'PhamePostNewController',
), ),
'blog/' => array( 'blog/' => array(
'(?:(?P<filter>user|all)/)?' => 'PhameBlogListController', '(?:(?P<filter>user|all)/)?' => 'PhameBlogListController',

View file

@ -76,7 +76,7 @@ abstract class PhameController extends PhabricatorController {
$nav = new AphrontSideNavFilterView(); $nav = new AphrontSideNavFilterView();
$nav->setBaseURI($base_uri); $nav->setBaseURI($base_uri);
$nav->addLabel('Create'); $nav->addLabel('Create');
$nav->addFilter('post/new', 'New Draft'); $nav->addFilter('post/new', 'New Post');
$nav->addFilter('blog/new', 'New Blog'); $nav->addFilter('blog/new', 'New Blog');
$nav->addSpacer(); $nav->addSpacer();
@ -164,7 +164,7 @@ abstract class PhameController extends PhabricatorController {
foreach ($posts as $post) { foreach ($posts as $post) {
$item = id(new PhabricatorObjectItemView()) $item = id(new PhabricatorObjectItemView())
->setHeader($post->getTitle()) ->setHeader($post->getTitle())
->setHref($this->getApplicationURI('post/view/'.$post->getPHID())) ->setHref($this->getApplicationURI('post/view/'.$post->getID().'/'))
->addDetail( ->addDetail(
pht('Blogger'), pht('Blogger'),
$this->getHandle($post->getBloggerPHID())->renderLink()) $this->getHandle($post->getBloggerPHID())->renderLink())

View file

@ -23,20 +23,6 @@ final class PhameBlogDeleteController extends PhameController {
private $id; private $id;
protected function getSideNavFilter() {
return 'blog/delete/'.$this->getBlogPHID();
}
protected function getSideNavExtraBlogFilters() {
$filters = array(
array('key' => $this->getSideNavFilter(),
'name' => 'Delete Blog')
);
return $filters;
}
public function willProcessRequest(array $data) { public function willProcessRequest(array $data) {
$this->id = $data['id']; $this->id = $data['id'];
} }
@ -67,11 +53,11 @@ final class PhameBlogDeleteController extends PhameController {
$dialog = id(new AphrontDialogView()) $dialog = id(new AphrontDialogView())
->setUser($user) ->setUser($user)
->setTitle(pht('Delete blog?')) ->setTitle(pht('Delete Blog?'))
->appendChild( ->appendChild(
pht( pht(
'Really delete the blog "%s"? It will be gone forever.', 'Really delete the blog "%s"? It will be gone forever.',
$blog->getName())) phutil_escape_html($blog->getName())))
->addSubmitButton(pht('Delete')) ->addSubmitButton(pht('Delete'))
->addCancelButton($cancel_uri); ->addCancelButton($cancel_uri);

View file

@ -24,11 +24,6 @@ final class PhameBlogEditController
private $id; private $id;
protected function getSideNavFilter() {
return 'blog/edit/'.$this->id;
}
public function willProcessRequest(array $data) { public function willProcessRequest(array $data) {
$this->id = idx($data, 'id'); $this->id = idx($data, 'id');
} }

View file

@ -23,19 +23,6 @@ final class PhameBlogViewController extends PhameController {
private $id; private $id;
protected function getSideNavFilter() {
$filter = 'blog/view/'.$this->getBlogPHID();
return $filter;
}
protected function getSideNavExtraBlogFilters() {
$filters = array(
array('key' => $this->getSideNavFilter(),
'name' => $this->getPhameTitle())
);
return $filters;
}
public function willProcessRequest(array $data) { public function willProcessRequest(array $data) {
$this->id = $data['id']; $this->id = $data['id'];
} }

View file

@ -19,31 +19,21 @@
/** /**
* @group phame * @group phame
*/ */
final class PhamePostDeleteController final class PhamePostDeleteController extends PhameController {
extends PhameController {
private $phid; private $id;
private function setPostPHID($phid) {
$this->phid = $phid;
return $this;
}
private function getPostPHID() {
return $this->phid;
}
public function willProcessRequest(array $data) { public function willProcessRequest(array $data) {
$phid = $data['phid']; $this->id = $data['id'];
$this->setPostPHID($phid);
} }
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$post = id(new PhamePostQuery()) $post = id(new PhamePostQuery())
->setViewer($user) ->setViewer($user)
->withPHIDs(array($this->getPostPHID())) ->withIDs(array($this->id))
->requireCapabilities( ->requireCapabilities(
array( array(
PhabricatorPolicyCapability::CAN_EDIT, PhabricatorPolicyCapability::CAN_EDIT,
@ -52,37 +42,24 @@ extends PhameController {
if (!$post) { if (!$post) {
return new Aphront404Response(); return new Aphront404Response();
} }
$post_noun = $post->getHumanName();
if ($request->isFormPost()) { if ($request->isFormPost()) {
$edge_type = PhabricatorEdgeConfig::TYPE_POST_HAS_BLOG;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($post->getPHID()))
->withEdgeTypes(array($edge_type))
->execute();
$blog_edges = $edges[$post->getPHID()][$edge_type];
$blog_phids = array_keys($blog_edges);
$editor = id(new PhabricatorEdgeEditor())
->setActor($user);
foreach ($blog_phids as $phid) {
$editor->removeEdge($post->getPHID(), $edge_type, $phid);
}
$editor->save();
$post->delete(); $post->delete();
return id(new AphrontRedirectResponse()) return id(new AphrontRedirectResponse())
->setURI('/phame/'.$post_noun.'/?deleted'); ->setURI('/phame/post/');
} }
$edit_uri = $post->getEditURI(); $cancel_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/');
$dialog = id(new AphrontDialogView())
$dialog = id(new AphrontDialogView())
->setUser($user) ->setUser($user)
->setTitle('Delete '.$post_noun.'?') ->setTitle(pht('Delete Post?'))
->appendChild('Really delete this '.$post_noun.'? '. ->appendChild(
'It will be gone forever.') pht(
->addSubmitButton('Delete') 'Really delete the post "%s"? It will be gone forever.',
->addCancelButton($edit_uri); phutil_escape_html($post->getTitle())))
->addSubmitButton(pht('Delete'))
->addCancelButton($cancel_uri);
return id(new AphrontDialogResponse())->setDialog($dialog); return id(new AphrontDialogResponse())->setDialog($dialog);
} }

View file

@ -22,102 +22,20 @@
final class PhamePostEditController final class PhamePostEditController
extends PhameController { extends PhameController {
private $phid; private $id;
private $isPostEdit;
private $userBlogs;
private $postBlogs;
private $post;
private function setPost(PhamePost $post) {
$this->post = $post;
return $this;
}
private function getPost() {
return $this->post;
}
private function setPostPHID($phid) {
$this->phid = $phid;
return $this;
}
private function getPostPHID() {
return $this->phid;
}
private function setIsPostEdit($is_post_edit) {
$this->isPostEdit = $is_post_edit;
return $this;
}
private function isPostEdit() {
return $this->isPostEdit;
}
private function setUserBlogs(array $blogs) {
assert_instances_of($blogs, 'PhameBlog');
$this->userBlogs = $blogs;
return $this;
}
private function getUserBlogs() {
return $this->userBlogs;
}
private function setPostBlogs(array $blogs) {
assert_instances_of($blogs, 'PhameBlog');
$this->postBlogs = $blogs;
return $this;
}
private function getPostBlogs() {
return $this->postBlogs;
}
protected function getSideNavFilter() {
if ($this->isPostEdit()) {
$post_noun = $this->getPost()->getHumanName();
$filter = $post_noun.'/edit/'.$this->getPostPHID();
} else {
$filter = 'post/new';
}
return $filter;
}
protected function getSideNavExtraPostFilters() {
if ($this->isPostEdit() && !$this->getPost()->isDraft()) {
$filters = array(
array('key' => 'post/edit/'.$this->getPostPHID(),
'name' => 'Edit Post')
);
} else {
$filters = array();
}
return $filters;
}
protected function getSideNavExtraDraftFilters() {
if ($this->isPostEdit() && $this->getPost()->isDraft()) {
$filters = array(
array('key' => 'draft/edit/'.$this->getPostPHID(),
'name' => 'Edit Draft')
);
} else {
$filters = array();
}
return $filters;
}
public function willProcessRequest(array $data) { public function willProcessRequest(array $data) {
$phid = idx($data, 'phid'); $this->id = idx($data, 'id');
$this->setPostPHID($phid);
$this->setIsPostEdit((bool) $phid);
} }
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$e_phame_title = null;
$e_title = null;
$errors = array();
if ($this->isPostEdit()) { if ($this->id) {
$post = id(new PhamePostQuery()) $post = id(new PhamePostQuery())
->setViewer($user) ->setViewer($user)
->withPHIDs(array($this->getPostPHID())) ->withIDs(array($this->id))
->requireCapabilities( ->requireCapabilities(
array( array(
PhabricatorPolicyCapability::CAN_EDIT, PhabricatorPolicyCapability::CAN_EDIT,
@ -127,45 +45,35 @@ final class PhamePostEditController
return new Aphront404Response(); return new Aphront404Response();
} }
$post_noun = ucfirst($post->getHumanName()); $cancel_uri = $this->getApplicationURI('/post/view/'.$this->id.'/');
$cancel_uri = $post->getViewURI($user->getUsername()); $submit_button = pht('Save Changes');
$submit_button = 'Save Changes'; $page_title = pht('Edit Post');
$delete_button = javelin_render_tag(
'a',
array(
'href' => $post->getDeleteURI(),
'class' => 'grey button',
'sigil' => 'workflow',
),
'Delete '.$post_noun);
$page_title = 'Edit '.$post_noun;
} else { } else {
$blog = id(new PhameBlogQuery()) $blog = id(new PhameBlogQuery())
->setViewer($user) ->setViewer($user)
->withIDs(array($request->getInt('blog'))) ->withIDs(array($request->getInt('blog')))
->executeOne(); ->executeOne();
if (!$blog) { if (!$blog) {
return new Aphront404Response(); return new Aphront404Response();
} }
$post = id(new PhamePost()) $post = id(new PhamePost())
->setBloggerPHID($user->getPHID()) ->setBloggerPHID($user->getPHID())
->setBlog($blog)
->setBlogPHID($blog->getPHID()) ->setBlogPHID($blog->getPHID())
->setBlog($blog)
->setDatePublished(0)
->setVisibility(PhamePost::VISIBILITY_DRAFT); ->setVisibility(PhamePost::VISIBILITY_DRAFT);
$cancel_uri = $this->getApplicationURI('/blog/view/'.$blog->getID().'/'); $cancel_uri = $this->getApplicationURI('/blog/view/'.$blog->getID().'/');
$submit_button = pht('Create Draft'); $submit_button = pht('Save Draft');
$delete_button = null; $page_title = pht('Create Post');
$page_title = pht('Create Draft');
} }
$this->setPost($post); $e_phame_title = null;
$e_title = true;
$errors = array();
if ($request->isFormPost()) { if ($request->isFormPost()) {
$saved = true;
$visibility = $request->getInt('visibility');
$comments = $request->getStr('comments_widget'); $comments = $request->getStr('comments_widget');
$data = array('comments_widget' => $comments); $data = array('comments_widget' => $comments);
$phame_title = $request->getStr('phame_title'); $phame_title = $request->getStr('phame_title');
@ -174,34 +82,26 @@ final class PhamePostEditController
$post->setTitle($title); $post->setTitle($title);
$post->setPhameTitle($phame_title); $post->setPhameTitle($phame_title);
$post->setBody($request->getStr('body')); $post->setBody($request->getStr('body'));
$post->setVisibility($visibility);
$post->setConfigData($data); $post->setConfigData($data);
// only publish once...!
if ($visibility == PhamePost::VISIBILITY_PUBLISHED) {
if (!$post->getDatePublished()) {
$post->setDatePublished(time());
}
// this is basically a cast of null to 0 if its a new post
} else if (!$post->getDatePublished()) {
$post->setDatePublished(0);
}
if ($phame_title == '/') { if ($phame_title == '/') {
$errors[] = 'Phame title must be nonempty.'; $errors[] = 'Phame title must be nonempty.';
$e_phame_title = 'Required'; $e_phame_title = 'Required';
} }
if (empty($title)) {
if (!strlen($title)) {
$errors[] = 'Title must be nonempty.'; $errors[] = 'Title must be nonempty.';
$e_title = 'Required'; $e_title = 'Required';
} else {
$e_title = null;
} }
if (!$errors) { if (!$errors) {
try { try {
$post->save(); $post->save();
$uri = new PhutilURI($post->getViewURI($user->getUsername())); $uri = $this->getApplicationURI('/post/view/'.$post->getID().'/');
$uri->setQueryParam('saved', true); return id(new AphrontRedirectResponse())->setURI($uri);
return id(new AphrontRedirectResponse())
->setURI($uri);
} catch (AphrontQueryDuplicateKeyException $e) { } catch (AphrontQueryDuplicateKeyException $e) {
$e_phame_title = 'Not Unique'; $e_phame_title = 'Not Unique';
$errors[] = 'Another post already uses this slug. '. $errors[] = 'Another post already uses this slug. '.
@ -210,19 +110,14 @@ final class PhamePostEditController
} }
} }
$panel = new AphrontPanelView();
$panel->setHeader($page_title);
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
if ($delete_button) {
$panel->addButton($delete_button);
}
$handle = PhabricatorObjectHandleData::loadOneHandle( $handle = PhabricatorObjectHandleData::loadOneHandle(
$post->getBlogPHID(), $post->getBlogPHID(),
$user); $user);
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($user) ->setUser($user)
->setFlexible(true)
->addHiddenInput('blog', $request->getInt('blog'))
->appendChild( ->appendChild(
id(new AphrontFormMarkupControl()) id(new AphrontFormMarkupControl())
->setLabel('Blog') ->setLabel('Blog')
@ -254,14 +149,6 @@ final class PhamePostEditController
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setID('post-body') ->setID('post-body')
) )
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Visibility')
->setName('visibility')
->setValue($post->getVisibility())
->setOptions(PhamePost::getVisibilityOptionsForSelect())
->setID('post-visibility')
)
->appendChild( ->appendChild(
id(new AphrontFormSelectControl()) id(new AphrontFormSelectControl())
->setLabel('Comments Widget') ->setLabel('Comments Widget')
@ -275,10 +162,8 @@ final class PhamePostEditController
->setValue($submit_button) ->setValue($submit_button)
); );
$panel->appendChild($form);
$preview_panel = $preview_panel =
'<div class="aphront-panel-preview "> '<div class="aphront-panel-preview">
<div class="phame-post-preview-header"> <div class="phame-post-preview-header">
Post Preview Post Preview
</div> </div>
@ -300,6 +185,8 @@ final class PhamePostEditController
'uri' => '/phame/post/preview/', 'uri' => '/phame/post/preview/',
)); ));
$header = id(new PhabricatorHeaderView())->setHeader($page_title);
if ($errors) { if ($errors) {
$error_view = id(new AphrontErrorView()) $error_view = id(new AphrontErrorView())
->setTitle('Errors saving post.') ->setTitle('Errors saving post.')
@ -308,15 +195,20 @@ final class PhamePostEditController
$error_view = null; $error_view = null;
} }
$this->setShowSideNav(true); $nav = $this->renderSideNavFilterView(null);
return $this->buildStandardPageResponse( $nav->appendChild(
array( array(
$header,
$error_view, $error_view,
$panel, $form,
$preview_panel, $preview_panel,
), ));
return $this->buildApplicationPage(
$nav,
array( array(
'title' => $page_title, 'title' => $page_title,
'device' => true,
)); ));
} }

View file

@ -0,0 +1,71 @@
<?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 PhamePostFramedController extends PhameController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$post = id(new PhamePostQuery())
->setViewer($user)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$post) {
return new Aphront404Response();
}
$skin = $post->getBlog()->getSkinRenderer();
$handles = $this->loadViewerHandles(
array(
$post->getBloggerPHID(),
));
$skin
->setUser($user)
->setBlog($post->getBlog())
->setPosts(array($post))
->setBloggers($handles)
->setRequestURI($this->getRequest()->getRequestURI());
$page = $this->buildStandardPageView();
$page->setFrameable(true);
$page->setShowChrome(false);
$page->appendChild($skin);
$response = new AphrontWebpageResponse();
$response->setFrameable(true);
$response->setContent($page->render());
return $response;
}
}

View file

@ -0,0 +1,70 @@
<?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 PhamePostNewController extends PhameController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$blogs = id(new PhameBlogQuery())
->setViewer($user)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_JOIN,
))
->execute();
$nav = $this->renderSideNavFilterView(null);
$nav->appendChild(
id(new PhabricatorHeaderView())->setHeader(
pht('Create Post')));
if (!$blogs) {
} else {
$options = mpull($blogs, 'getName', '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'));
$nav->appendChild($form);
}
return $this->buildApplicationPage(
$nav,
array(
'title' => 'Create Post',
'device' => true,
));
}
}

View file

@ -0,0 +1,103 @@
<?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 PhamePostPublishController extends PhameController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$post = id(new PhamePostQuery())
->setViewer($user)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$post) {
return new Aphront404Response();
}
$view_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/');
if ($request->isFormPost()) {
$post->setVisibility(PhamePost::VISIBILITY_PUBLISHED);
$post->setDatePublished(time());
$post->save();
return id(new AphrontRedirectResponse())->setURI($view_uri);
}
$header = id(new PhabricatorHeaderView())
->setHeader('Preview Post');
$form = id(new AphrontFormView())
->setUser($user)
->setFlexible(true)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Publish Post'))
->addCancelButton($view_uri));
$frame = $this->renderPreviewFrame($post);
$nav = $this->renderSideNavFilterView(null);
$nav->appendChild(
array(
$header,
$form,
$frame,
));
return $this->buildApplicationPage(
$nav,
array(
'title' => pht('Preview Post'),
'device' => true,
));
}
private function renderPreviewFrame(PhamePost $post) {
// TODO: Clean up this CSS.
return phutil_render_tag(
'div',
array(
'style' => 'text-align: center; padding: 1em;',
),
phutil_render_tag(
'iframe',
array(
'style' => 'width: 100%; height: 600px; '.
'border: 1px solid #303030; background: #303030;',
'src' => $this->getApplicationURI('/post/framed/'.$post->getID().'/'),
),
''));
}
}

View file

@ -0,0 +1,70 @@
<?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 PhamePostUnpublishController extends PhameController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$post = id(new PhamePostQuery())
->setViewer($user)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$post) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
$post->setVisibility(PhamePost::VISIBILITY_DRAFT);
$post->setDatePublished(0);
$post->save();
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI('/post/view/'.$post->getID().'/'));
}
$cancel_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/');
$dialog = id(new AphrontDialogView())
->setUser($user)
->setTitle(pht('Unpublish Post?'))
->appendChild(
pht(
'The post "%s" will no longer be visible to other users until you '.
'republish it.',
phutil_escape_html($post->getTitle())))
->addSubmitButton(pht('Unpublish'))
->addCancelButton($cancel_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -21,111 +21,25 @@
*/ */
final class PhamePostViewController extends PhameController { final class PhamePostViewController extends PhameController {
private $postPHID; private $id;
private $phameTitle;
private $bloggerName;
private function setPostPHID($post_phid) {
$this->postPHID = $post_phid;
return $this;
}
private function getPostPHID() {
return $this->postPHID;
}
private function setPhameTitle($phame_title) {
$this->phameTitle = $phame_title;
return $this;
}
private function getPhameTitle() {
return $this->phameTitle;
}
private function setBloggerName($blogger_name) {
$this->bloggerName = $blogger_name;
return $this;
}
private function getBloggerName() {
return $this->bloggerName;
}
protected function getSideNavFilter() {
$filter = 'post/view/'.$this->getPostPHID();
return $filter;
}
protected function getSideNavExtraPostFilters() {
$filters = array(
array('key' => $this->getSideNavFilter(),
'name' => $this->getPhameTitle())
);
return $filters;
}
public function shouldRequireLogin() {
// TODO -- get policy logic going
// return PhabricatorEnv::getEnvConfig('policy.allow-public');
return true;
}
public function willProcessRequest(array $data) { public function willProcessRequest(array $data) {
$this->setPostPHID(idx($data, 'phid')); $this->id = $data['id'];
$this->setPhameTitle(idx($data, 'phametitle'));
$this->setBloggerName(idx($data, 'bloggername'));
} }
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
if ($this->getPostPHID()) { $post = id(new PhamePostQuery())
$post = id(new PhamePostQuery()) ->setViewer($user)
->setViewer($user) ->withIDs(array($this->id))
->withPHIDs(array($this->getPostPHID())) ->executeOne();
->executeOne();
if (!$post) {
return new Aphront404Response();
}
$this->setPhameTitle($post->getPhameTitle());
$blogger = PhabricatorObjectHandleData::loadOneHandle(
$post->getBloggerPHID(),
$user);
} else if ($this->getBloggerName() && $this->getPhameTitle()) {
$phame_title = $this->getPhameTitle();
$phame_title = PhabricatorSlug::normalize($phame_title);
$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();
}
$posts = id(new PhamePostQuery())
->setViewer($user)
->withBloggerPHIDs(array($blogger->getPHID()))
->withPhameTitles(array($phame_title))
->execute();
$post = reset($posts);
if ($post && $phame_title != $this->getPhameTitle()) {
$uri = $post->getViewURI($this->getBloggerName());
return id(new AphrontRedirectResponse())->setURI($uri);
}
}
if (!$post) { if (!$post) {
return new Aphront404Response(); return new Aphront404Response();
} }
if ($post->isDraft() &&
$post->getBloggerPHID() != $user->getPHID()) {
return new Aphront404Response();
}
if ($post->isDraft()) { if ($post->isDraft()) {
$notice = array( $notice = array(
'title' => 'You are previewing a draft.', 'title' => 'You are previewing a draft.',
@ -150,32 +64,123 @@ final class PhamePostViewController extends PhameController {
$notice = array(); $notice = array();
} }
$actions = array('more'); $this->loadHandles(
if ($post->getBloggerPHID() == $user->getPHID()) {
$actions[] = 'edit';
}
$skin = new PhabricatorBlogSkin();
$skin
->setUser($user)
->setRequestURI($request->getRequestURI())
->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( array(
$skin $post->getBlogPHID(),
), $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,
));
return $this->buildApplicationPage(
$nav,
array( array(
'title' => $post->getTitle(), 'title' => $post->getTitle(),
'device' => true,
)); ));
} }
private function renderActions(
PhamePost $post,
PhabricatorUser $user) {
$actions = id(new PhabricatorActionListView())
->setObject($post)
->setUser($user);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$post,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $post->getID();
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('edit')
->setHref($this->getApplicationURI('post/edit/'.$id.'/'))
->setName('Edit Post')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$can_view_live = $post->getBlog() && !$post->isDraft();
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('world')
->setHref($this->getApplicationURI('post/live/'.$id.'/'))
->setName(pht('View Live'))
->setDisabled(!$can_view_live)
->setWorkflow(true));
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;
}
private function renderProperties(
PhamePost $post,
PhabricatorUser $user) {
$properties = new PhabricatorPropertyListView();
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$user,
$post);
$properties->addProperty(
pht('Blog'),
$post->getBlogPHID()
? $this->getHandle($post->getBlogPHID())->renderLink()
: null);
$properties->addProperty(
pht('Blogger'),
$this->getHandle($post->getBloggerPHID())->renderLink());
$properties->addProperty(
pht('Visible To'),
$descriptions[PhabricatorPolicyCapability::CAN_VIEW]);
$properties->addProperty(
pht('Published'),
$post->isDraft()
? pht('Draft')
: phabricator_datetime($post->getDatePublished(), $user));
return $properties;
}
} }

View file

@ -21,12 +21,18 @@
*/ */
final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery { final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $blogPHIDs; private $blogPHIDs;
private $bloggerPHIDs; private $bloggerPHIDs;
private $phameTitles; private $phameTitles;
private $visibility; private $visibility;
private $phids; private $phids;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) { public function withPHIDs(array $phids) {
$this->phids = $phids; $this->phids = $phids;
return $this; return $this;
@ -91,6 +97,13 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private function buildWhereClause(AphrontDatabaseConnection $conn_r) { private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array(); $where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'p.id IN (%Ld)',
$this->ids);
}
if ($this->phids) { if ($this->phids) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,