1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 11:30:55 +01:00

Add Hero Image to Phame Post

Summary: Adds a headerimage and lets you set it on posts for added reverence. Is that a word?

Test Plan:
Add an image, see an image.

{F1923010}

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Differential Revision: https://secure.phabricator.com/D16873
This commit is contained in:
Chad Little 2016-11-15 13:20:13 -08:00
parent c7f2e4a924
commit ce0cb115ca
10 changed files with 249 additions and 1 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phame.phame_post
ADD headerImagePHID VARBINARY(64);

View file

@ -4085,6 +4085,7 @@ phutil_register_library_map(array(
'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php', 'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php',
'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php', 'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php',
'PhamePostFulltextEngine' => 'applications/phame/search/PhamePostFulltextEngine.php', 'PhamePostFulltextEngine' => 'applications/phame/search/PhamePostFulltextEngine.php',
'PhamePostHeaderPictureController' => 'applications/phame/controller/post/PhamePostHeaderPictureController.php',
'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php', 'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.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',
@ -9324,6 +9325,7 @@ phutil_register_library_map(array(
'PhamePostEditEngine' => 'PhabricatorEditEngine', 'PhamePostEditEngine' => 'PhabricatorEditEngine',
'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor', 'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
'PhamePostFulltextEngine' => 'PhabricatorFulltextEngine', 'PhamePostFulltextEngine' => 'PhabricatorFulltextEngine',
'PhamePostHeaderPictureController' => 'PhamePostController',
'PhamePostHistoryController' => 'PhamePostController', 'PhamePostHistoryController' => 'PhamePostController',
'PhamePostListController' => 'PhamePostController', 'PhamePostListController' => 'PhamePostController',
'PhamePostListView' => 'AphrontTagView', 'PhamePostListView' => 'AphrontTagView',

View file

@ -53,6 +53,7 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
'preview/' => 'PhabricatorMarkupPreviewController', 'preview/' => 'PhabricatorMarkupPreviewController',
'move/(?P<id>\d+)/' => 'PhamePostMoveController', 'move/(?P<id>\d+)/' => 'PhamePostMoveController',
'archive/(?P<id>\d+)/' => 'PhamePostArchiveController', 'archive/(?P<id>\d+)/' => 'PhamePostArchiveController',
'header/(?P<id>[1-9]\d*)/' => 'PhamePostHeaderPictureController',
), ),
'blog/' => array( 'blog/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhameBlogListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhameBlogListController',

View file

@ -90,6 +90,7 @@ abstract class PhameLiveController extends PhameController {
if (strlen($post_id)) { if (strlen($post_id)) {
$post_query = id(new PhamePostQuery()) $post_query = id(new PhamePostQuery())
->setViewer($viewer) ->setViewer($viewer)
->needHeaderImage(true)
->withIDs(array($post_id)); ->withIDs(array($post_id));
if ($blog) { if ($blog) {

View file

@ -0,0 +1,136 @@
<?php
final class PhamePostHeaderPictureController
extends PhamePostController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$post = id(new PhamePostQuery())
->setViewer($viewer)
->withIDs(array($id))
->needHeaderImage(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$post) {
return new Aphront404Response();
}
$post_uri = '/phame/post/view/'.$id;
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$e_file = true;
$errors = array();
$delete_header = ($request->getInt('delete') == 1);
if ($request->isFormPost()) {
if ($request->getFileExists('header')) {
$file = PhabricatorFile::newFromPHPUpload(
$_FILES['header'],
array(
'authorPHID' => $viewer->getPHID(),
'canCDN' => true,
));
} else if (!$delete_header) {
$e_file = pht('Required');
$errors[] = pht(
'You must choose a file when uploading a new post header.');
}
if (!$errors && !$delete_header) {
if (!$file->isTransformableImage()) {
$e_file = pht('Not Supported');
$errors[] = pht(
'This server only supports these image formats: %s.',
implode(', ', $supported_formats));
}
}
if (!$errors) {
if ($delete_header) {
$new_value = null;
} else {
$file->attachToObject($post->getPHID());
$new_value = $file->getPHID();
}
$xactions = array();
$xactions[] = id(new PhamePostTransaction())
->setTransactionType(PhamePostTransaction::TYPE_HEADERIMAGE)
->setNewValue($new_value);
$editor = id(new PhamePostEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($post, $xactions);
return id(new AphrontRedirectResponse())->setURI($post_uri);
}
}
$title = pht('Edit Post Header');
$upload_form = id(new AphrontFormView())
->setUser($viewer)
->setEncType('multipart/form-data')
->appendChild(
id(new AphrontFormFileControl())
->setName('header')
->setLabel(pht('Upload Header'))
->setError($e_file)
->setCaption(
pht('Supported formats: %s', implode(', ', $supported_formats))))
->appendChild(
id(new AphrontFormCheckboxControl())
->setName('delete')
->setLabel(pht('Delete Header'))
->addCheckbox(
'delete',
1,
null,
null))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($post_uri)
->setValue(pht('Upload Header')));
$upload_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Upload New Header'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($upload_form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$post->getTitle(),
$this->getApplicationURI('post/view/'.$id));
$crumbs->addTextCrumb(pht('Post Header'));
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Edit Post Header'))
->setHeaderIcon('fa-camera');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$upload_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
}

View file

@ -19,9 +19,11 @@ final class PhamePostViewController
$is_external = $this->getIsExternal(); $is_external = $this->getIsExternal();
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader($post->getTitle()) ->addClass('phame-header-bar')
->setUser($viewer); ->setUser($viewer);
$hero = $this->buildPhamePostHeader($post);
if (!$is_external) { if (!$is_external) {
$actions = $this->renderActions($post); $actions = $this->renderActions($post);
$header->setPolicyObject($post); $header->setPolicyObject($post);
@ -167,6 +169,7 @@ final class PhamePostViewController
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->appendChild( ->appendChild(
array( array(
$hero,
$document, $document,
$about, $about,
$properties, $properties,
@ -204,6 +207,13 @@ final class PhamePostViewController
->setName(pht('Edit Post')) ->setName(pht('Edit Post'))
->setDisabled(!$can_edit)); ->setDisabled(!$can_edit));
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-camera-retro')
->setHref($this->getApplicationURI('post/header/'.$id.'/'))
->setName(pht('Edit Header Image'))
->setDisabled(!$can_edit));
$actions->addAction( $actions->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setIcon('fa-arrows') ->setIcon('fa-arrows')
@ -307,4 +317,33 @@ final class PhamePostViewController
return array(head($prev), head($next)); return array(head($prev), head($next));
} }
private function buildPhamePostHeader(
PhamePost $post) {
$image = null;
if ($post->getHeaderImagePHID()) {
$image = phutil_tag(
'div',
array(
'class' => 'phame-header-hero',
),
phutil_tag(
'img',
array(
'src' => $post->getHeaderImageURI(),
'class' => 'phame-header-image',
)));
}
$title = phutil_tag_div('phame-header-title', $post->getTitle());
$subtitle = null;
if ($post->getSubtitle()) {
$subtitle = phutil_tag_div('phame-header-subtitle', $post->getSubtitle());
}
return phutil_tag_div(
'phame-mega-header', array($image, $title, $subtitle));
}
} }

View file

@ -19,6 +19,7 @@ final class PhamePostEditor
$types[] = PhamePostTransaction::TYPE_SUBTITLE; $types[] = PhamePostTransaction::TYPE_SUBTITLE;
$types[] = PhamePostTransaction::TYPE_BODY; $types[] = PhamePostTransaction::TYPE_BODY;
$types[] = PhamePostTransaction::TYPE_VISIBILITY; $types[] = PhamePostTransaction::TYPE_VISIBILITY;
$types[] = PhamePostTransaction::TYPE_HEADERIMAGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_COMMENT;
return $types; return $types;
@ -39,6 +40,8 @@ final class PhamePostEditor
return $object->getBody(); return $object->getBody();
case PhamePostTransaction::TYPE_VISIBILITY: case PhamePostTransaction::TYPE_VISIBILITY:
return $object->getVisibility(); return $object->getVisibility();
case PhamePostTransaction::TYPE_HEADERIMAGE:
return $object->getHeaderImagePHID();
} }
} }
@ -51,6 +54,7 @@ final class PhamePostEditor
case PhamePostTransaction::TYPE_SUBTITLE: case PhamePostTransaction::TYPE_SUBTITLE:
case PhamePostTransaction::TYPE_BODY: case PhamePostTransaction::TYPE_BODY:
case PhamePostTransaction::TYPE_VISIBILITY: case PhamePostTransaction::TYPE_VISIBILITY:
case PhamePostTransaction::TYPE_HEADERIMAGE:
case PhamePostTransaction::TYPE_BLOG: case PhamePostTransaction::TYPE_BLOG:
return $xaction->getNewValue(); return $xaction->getNewValue();
} }
@ -69,6 +73,8 @@ final class PhamePostEditor
return $object->setBody($xaction->getNewValue()); return $object->setBody($xaction->getNewValue());
case PhamePostTransaction::TYPE_BLOG: case PhamePostTransaction::TYPE_BLOG:
return $object->setBlogPHID($xaction->getNewValue()); return $object->setBlogPHID($xaction->getNewValue());
case PhamePostTransaction::TYPE_HEADERIMAGE:
return $object->setHeaderImagePHID($xaction->getNewValue());
case PhamePostTransaction::TYPE_VISIBILITY: case PhamePostTransaction::TYPE_VISIBILITY:
if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) { if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) {
$object->setDatePublished(0); $object->setDatePublished(0);
@ -93,6 +99,7 @@ final class PhamePostEditor
case PhamePostTransaction::TYPE_SUBTITLE: case PhamePostTransaction::TYPE_SUBTITLE:
case PhamePostTransaction::TYPE_BODY: case PhamePostTransaction::TYPE_BODY:
case PhamePostTransaction::TYPE_VISIBILITY: case PhamePostTransaction::TYPE_VISIBILITY:
case PhamePostTransaction::TYPE_HEADERIMAGE:
case PhamePostTransaction::TYPE_BLOG: case PhamePostTransaction::TYPE_BLOG:
return; return;
} }

View file

@ -9,6 +9,8 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $publishedAfter; private $publishedAfter;
private $phids; private $phids;
private $needHeaderImage;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
return $this; return $this;
@ -39,6 +41,11 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
return $this; return $this;
} }
public function needHeaderImage($need) {
$this->needHeaderImage = $need;
return $this;
}
public function newResultObject() { public function newResultObject() {
return new PhamePost(); return new PhamePost();
} }
@ -71,6 +78,28 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
$post->attachBlog($blog); $post->attachBlog($blog);
} }
if ($this->needHeaderImage) {
$file_phids = mpull($posts, 'getHeaderImagePHID');
$file_phids = array_filter($file_phids);
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
}
foreach ($posts as $post) {
$file = idx($files, $post->getHeaderImagePHID());
if ($file) {
$post->attachHeaderImageFile($file);
}
}
}
return $posts; return $posts;
} }

View file

@ -26,8 +26,10 @@ final class PhamePost extends PhameDAO
protected $datePublished; protected $datePublished;
protected $blogPHID; protected $blogPHID;
protected $mailKey; protected $mailKey;
protected $headerImagePHID;
private $blog = self::ATTACHABLE; private $blog = self::ATTACHABLE;
private $headerImageFile = self::ATTACHABLE;
public static function initializePost( public static function initializePost(
PhabricatorUser $blogger, PhabricatorUser $blogger,
@ -127,6 +129,7 @@ final class PhamePost extends PhameDAO
'phameTitle' => 'sort64?', 'phameTitle' => 'sort64?',
'visibility' => 'uint32', 'visibility' => 'uint32',
'mailKey' => 'bytes20', 'mailKey' => 'bytes20',
'headerImagePHID' => 'phid?',
// T6203/NULLABILITY // T6203/NULLABILITY
// These seem like they should always be non-null? // These seem like they should always be non-null?
@ -172,6 +175,19 @@ final class PhamePost extends PhameDAO
return PhabricatorSlug::normalizeProjectSlug($this->getTitle(), true); return PhabricatorSlug::normalizeProjectSlug($this->getTitle(), true);
} }
public function getHeaderImageURI() {
return $this->getHeaderImageFile()->getBestURI();
}
public function attachHeaderImageFile(PhabricatorFile $file) {
$this->headerImageFile = $file;
return $this;
}
public function getHeaderImageFile() {
return $this->assertAttached($this->headerImageFile);
}
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */ /* -( PhabricatorPolicyInterface Implementation )-------------------------- */

View file

@ -7,6 +7,7 @@ final class PhamePostTransaction
const TYPE_SUBTITLE = 'phame.post.subtitle'; const TYPE_SUBTITLE = 'phame.post.subtitle';
const TYPE_BODY = 'phame.post.body'; const TYPE_BODY = 'phame.post.body';
const TYPE_VISIBILITY = 'phame.post.visibility'; const TYPE_VISIBILITY = 'phame.post.visibility';
const TYPE_HEADERIMAGE = 'phame.post.headerimage';
const TYPE_BLOG = 'phame.post.blog'; const TYPE_BLOG = 'phame.post.blog';
const MAILTAG_CONTENT = 'phame-post-content'; const MAILTAG_CONTENT = 'phame-post-content';
@ -71,6 +72,9 @@ final class PhamePostTransaction
case PhabricatorTransactions::TYPE_CREATE: case PhabricatorTransactions::TYPE_CREATE:
return 'fa-plus'; return 'fa-plus';
break; break;
case self::TYPE_HEADERIMAGE:
return 'fa-camera-retro';
break;
case self::TYPE_VISIBILITY: case self::TYPE_VISIBILITY:
if ($new == PhameConstants::VISIBILITY_PUBLISHED) { if ($new == PhameConstants::VISIBILITY_PUBLISHED) {
return 'fa-globe'; return 'fa-globe';
@ -156,6 +160,11 @@ final class PhamePostTransaction
'%s updated the blog post.', '%s updated the blog post.',
$this->renderHandleLink($author_phid)); $this->renderHandleLink($author_phid));
break; break;
case self::TYPE_HEADERIMAGE:
return pht(
'%s updated the header image.',
$this->renderHandleLink($author_phid));
break;
case self::TYPE_VISIBILITY: case self::TYPE_VISIBILITY:
if ($new == PhameConstants::VISIBILITY_DRAFT) { if ($new == PhameConstants::VISIBILITY_DRAFT) {
return pht( return pht(
@ -222,6 +231,12 @@ final class PhamePostTransaction
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $this->renderHandleLink($object_phid));
break; break;
case self::TYPE_HEADERIMAGE:
return pht(
'%s updated the header image for post %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case self::TYPE_VISIBILITY: case self::TYPE_VISIBILITY:
if ($new == PhameConstants::VISIBILITY_DRAFT) { if ($new == PhameConstants::VISIBILITY_DRAFT) {
return pht( return pht(