Page MenuHomePhabricator

D16873.diff
No OneTemporary

D16873.diff

diff --git a/resources/sql/autopatches/20161115.phamepost.02.header.sql b/resources/sql/autopatches/20161115.phamepost.02.header.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20161115.phamepost.02.header.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_phame.phame_post
+ ADD headerImagePHID VARBINARY(64);
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4085,6 +4085,7 @@
'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php',
'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php',
'PhamePostFulltextEngine' => 'applications/phame/search/PhamePostFulltextEngine.php',
+ 'PhamePostHeaderPictureController' => 'applications/phame/controller/post/PhamePostHeaderPictureController.php',
'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php',
'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php',
'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
@@ -9324,6 +9325,7 @@
'PhamePostEditEngine' => 'PhabricatorEditEngine',
'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
'PhamePostFulltextEngine' => 'PhabricatorFulltextEngine',
+ 'PhamePostHeaderPictureController' => 'PhamePostController',
'PhamePostHistoryController' => 'PhamePostController',
'PhamePostListController' => 'PhamePostController',
'PhamePostListView' => 'AphrontTagView',
diff --git a/src/applications/phame/application/PhabricatorPhameApplication.php b/src/applications/phame/application/PhabricatorPhameApplication.php
--- a/src/applications/phame/application/PhabricatorPhameApplication.php
+++ b/src/applications/phame/application/PhabricatorPhameApplication.php
@@ -53,6 +53,7 @@
'preview/' => 'PhabricatorMarkupPreviewController',
'move/(?P<id>\d+)/' => 'PhamePostMoveController',
'archive/(?P<id>\d+)/' => 'PhamePostArchiveController',
+ 'header/(?P<id>[1-9]\d*)/' => 'PhamePostHeaderPictureController',
),
'blog/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhameBlogListController',
diff --git a/src/applications/phame/controller/PhameLiveController.php b/src/applications/phame/controller/PhameLiveController.php
--- a/src/applications/phame/controller/PhameLiveController.php
+++ b/src/applications/phame/controller/PhameLiveController.php
@@ -90,6 +90,7 @@
if (strlen($post_id)) {
$post_query = id(new PhamePostQuery())
->setViewer($viewer)
+ ->needHeaderImage(true)
->withIDs(array($post_id));
if ($blog) {
diff --git a/src/applications/phame/controller/post/PhamePostHeaderPictureController.php b/src/applications/phame/controller/post/PhamePostHeaderPictureController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phame/controller/post/PhamePostHeaderPictureController.php
@@ -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,
+ ));
+
+ }
+}
diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php
--- a/src/applications/phame/controller/post/PhamePostViewController.php
+++ b/src/applications/phame/controller/post/PhamePostViewController.php
@@ -19,9 +19,11 @@
$is_external = $this->getIsExternal();
$header = id(new PHUIHeaderView())
- ->setHeader($post->getTitle())
+ ->addClass('phame-header-bar')
->setUser($viewer);
+ $hero = $this->buildPhamePostHeader($post);
+
if (!$is_external) {
$actions = $this->renderActions($post);
$header->setPolicyObject($post);
@@ -167,6 +169,7 @@
->setCrumbs($crumbs)
->appendChild(
array(
+ $hero,
$document,
$about,
$properties,
@@ -206,6 +209,13 @@
$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(
+ id(new PhabricatorActionView())
->setIcon('fa-arrows')
->setHref($this->getApplicationURI('post/move/'.$id.'/'))
->setName(pht('Move Post'))
@@ -307,4 +317,33 @@
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));
+
+ }
+
}
diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php
--- a/src/applications/phame/editor/PhamePostEditor.php
+++ b/src/applications/phame/editor/PhamePostEditor.php
@@ -19,6 +19,7 @@
$types[] = PhamePostTransaction::TYPE_SUBTITLE;
$types[] = PhamePostTransaction::TYPE_BODY;
$types[] = PhamePostTransaction::TYPE_VISIBILITY;
+ $types[] = PhamePostTransaction::TYPE_HEADERIMAGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
return $types;
@@ -39,6 +40,8 @@
return $object->getBody();
case PhamePostTransaction::TYPE_VISIBILITY:
return $object->getVisibility();
+ case PhamePostTransaction::TYPE_HEADERIMAGE:
+ return $object->getHeaderImagePHID();
}
}
@@ -51,6 +54,7 @@
case PhamePostTransaction::TYPE_SUBTITLE:
case PhamePostTransaction::TYPE_BODY:
case PhamePostTransaction::TYPE_VISIBILITY:
+ case PhamePostTransaction::TYPE_HEADERIMAGE:
case PhamePostTransaction::TYPE_BLOG:
return $xaction->getNewValue();
}
@@ -69,6 +73,8 @@
return $object->setBody($xaction->getNewValue());
case PhamePostTransaction::TYPE_BLOG:
return $object->setBlogPHID($xaction->getNewValue());
+ case PhamePostTransaction::TYPE_HEADERIMAGE:
+ return $object->setHeaderImagePHID($xaction->getNewValue());
case PhamePostTransaction::TYPE_VISIBILITY:
if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) {
$object->setDatePublished(0);
@@ -93,6 +99,7 @@
case PhamePostTransaction::TYPE_SUBTITLE:
case PhamePostTransaction::TYPE_BODY:
case PhamePostTransaction::TYPE_VISIBILITY:
+ case PhamePostTransaction::TYPE_HEADERIMAGE:
case PhamePostTransaction::TYPE_BLOG:
return;
}
diff --git a/src/applications/phame/query/PhamePostQuery.php b/src/applications/phame/query/PhamePostQuery.php
--- a/src/applications/phame/query/PhamePostQuery.php
+++ b/src/applications/phame/query/PhamePostQuery.php
@@ -9,6 +9,8 @@
private $publishedAfter;
private $phids;
+ private $needHeaderImage;
+
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
@@ -39,6 +41,11 @@
return $this;
}
+ public function needHeaderImage($need) {
+ $this->needHeaderImage = $need;
+ return $this;
+ }
+
public function newResultObject() {
return new PhamePost();
}
@@ -71,6 +78,28 @@
$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;
}
diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php
--- a/src/applications/phame/storage/PhamePost.php
+++ b/src/applications/phame/storage/PhamePost.php
@@ -26,8 +26,10 @@
protected $datePublished;
protected $blogPHID;
protected $mailKey;
+ protected $headerImagePHID;
private $blog = self::ATTACHABLE;
+ private $headerImageFile = self::ATTACHABLE;
public static function initializePost(
PhabricatorUser $blogger,
@@ -127,6 +129,7 @@
'phameTitle' => 'sort64?',
'visibility' => 'uint32',
'mailKey' => 'bytes20',
+ 'headerImagePHID' => 'phid?',
// T6203/NULLABILITY
// These seem like they should always be non-null?
@@ -172,6 +175,19 @@
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 )-------------------------- */
diff --git a/src/applications/phame/storage/PhamePostTransaction.php b/src/applications/phame/storage/PhamePostTransaction.php
--- a/src/applications/phame/storage/PhamePostTransaction.php
+++ b/src/applications/phame/storage/PhamePostTransaction.php
@@ -7,6 +7,7 @@
const TYPE_SUBTITLE = 'phame.post.subtitle';
const TYPE_BODY = 'phame.post.body';
const TYPE_VISIBILITY = 'phame.post.visibility';
+ const TYPE_HEADERIMAGE = 'phame.post.headerimage';
const TYPE_BLOG = 'phame.post.blog';
const MAILTAG_CONTENT = 'phame-post-content';
@@ -71,6 +72,9 @@
case PhabricatorTransactions::TYPE_CREATE:
return 'fa-plus';
break;
+ case self::TYPE_HEADERIMAGE:
+ return 'fa-camera-retro';
+ break;
case self::TYPE_VISIBILITY:
if ($new == PhameConstants::VISIBILITY_PUBLISHED) {
return 'fa-globe';
@@ -156,6 +160,11 @@
'%s updated the blog post.',
$this->renderHandleLink($author_phid));
break;
+ case self::TYPE_HEADERIMAGE:
+ return pht(
+ '%s updated the header image.',
+ $this->renderHandleLink($author_phid));
+ break;
case self::TYPE_VISIBILITY:
if ($new == PhameConstants::VISIBILITY_DRAFT) {
return pht(
@@ -222,6 +231,12 @@
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
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:
if ($new == PhameConstants::VISIBILITY_DRAFT) {
return pht(

File Metadata

Mime Type
text/plain
Expires
Sun, May 12, 5:39 AM (3 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6290099
Default Alt Text
D16873.diff (15 KB)

Event Timeline