1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-19 16:58:48 +02:00

Add Profile Images to PhameBlog

Summary: Will use these more in the upcoming unbeta design of PhameBlog, likely. Also curious how this works.

Test Plan: Add an image to a blog, remove an image from a blog.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Differential Revision: https://secure.phabricator.com/D14587
This commit is contained in:
Chad Little 2015-11-28 12:42:36 -08:00
parent 6507e84629
commit 5eada3d89c
8 changed files with 291 additions and 1 deletions

BIN
resources/builtin/blog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,010 B

View file

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

View file

@ -3285,6 +3285,7 @@ phutil_register_library_map(array(
'PhameBlogFeedController' => 'applications/phame/controller/blog/PhameBlogFeedController.php',
'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php',
'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php',
'PhameBlogProfilePictureController' => 'applications/phame/controller/blog/PhameBlogProfilePictureController.php',
'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php',
'PhameBlogReplyHandler' => 'applications/phame/mail/PhameBlogReplyHandler.php',
'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php',
@ -7592,6 +7593,7 @@ phutil_register_library_map(array(
'PhameBlogFeedController' => 'PhameBlogController',
'PhameBlogListController' => 'PhameBlogController',
'PhameBlogLiveController' => 'PhameBlogController',
'PhameBlogProfilePictureController' => 'PhameBlogController',
'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhameBlogReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine',

View file

@ -63,6 +63,7 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
'view/(?P<id>[^/]+)/' => 'PhameBlogViewController',
'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController',
'new/' => 'PhameBlogEditController',
'picture/(?P<id>[1-9]\d*)/' => 'PhameBlogProfilePictureController',
),
) + $this->getResourceSubroutes(),
);

View file

@ -0,0 +1,218 @@
<?php
final class PhameBlogProfilePictureController
extends PhameBlogController {
public function shouldRequireAdmin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$blog = id(new PhameBlogQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfileImage(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$blog) {
return new Aphront404Response();
}
$blog_uri = '/phame/blog/view/'.$id;
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$e_file = true;
$errors = array();
if ($request->isFormPost()) {
$phid = $request->getStr('phid');
$is_default = false;
if ($phid == PhabricatorPHIDConstants::PHID_VOID) {
$phid = null;
$is_default = true;
} else if ($phid) {
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($phid))
->executeOne();
} else {
if ($request->getFileExists('picture')) {
$file = PhabricatorFile::newFromPHPUpload(
$_FILES['picture'],
array(
'authorPHID' => $viewer->getPHID(),
'canCDN' => true,
));
} else {
$e_file = pht('Required');
$errors[] = pht(
'You must choose a file when uploading a new blog picture.');
}
}
if (!$errors && !$is_default) {
if (!$file->isTransformableImage()) {
$e_file = pht('Not Supported');
$errors[] = pht(
'This server only supports these image formats: %s.',
implode(', ', $supported_formats));
} else {
$xform = PhabricatorFileTransform::getTransformByKey(
PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE);
$xformed = $xform->executeTransform($file);
}
}
if (!$errors) {
if ($is_default) {
$blog->setProfileImagePHID(null);
} else {
$blog->setProfileImagePHID($xformed->getPHID());
$xformed->attachToObject($blog->getPHID());
}
$blog->save();
return id(new AphrontRedirectResponse())->setURI($blog_uri);
}
}
$title = pht('Edit Blog Picture');
$form = id(new PHUIFormLayoutView())
->setUser($viewer);
$default_image = PhabricatorFile::loadBuiltin($viewer, 'blog.png');
$images = array();
$current = $blog->getProfileImagePHID();
$has_current = false;
if ($current) {
$files = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($current))
->execute();
if ($files) {
$file = head($files);
if ($file->isTransformableImage()) {
$has_current = true;
$images[$current] = array(
'uri' => $file->getBestURI(),
'tip' => pht('Current Picture'),
);
}
}
}
$images[PhabricatorPHIDConstants::PHID_VOID] = array(
'uri' => $default_image->getBestURI(),
'tip' => pht('Default Picture'),
);
require_celerity_resource('people-profile-css');
Javelin::initBehavior('phabricator-tooltips', array());
$buttons = array();
foreach ($images as $phid => $spec) {
$button = javelin_tag(
'button',
array(
'class' => 'grey profile-image-button',
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $spec['tip'],
'size' => 300,
),
),
phutil_tag(
'img',
array(
'height' => 50,
'width' => 50,
'src' => $spec['uri'],
)));
$button = array(
phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => 'phid',
'value' => $phid,
)),
$button,
);
$button = phabricator_form(
$viewer,
array(
'class' => 'profile-image-form',
'method' => 'POST',
),
$button);
$buttons[] = $button;
}
if ($has_current) {
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Current Picture'))
->setValue(array_shift($buttons)));
}
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Use Picture'))
->setValue($buttons));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setForm($form);
$upload_form = id(new AphrontFormView())
->setUser($viewer)
->setEncType('multipart/form-data')
->appendChild(
id(new AphrontFormFileControl())
->setName('picture')
->setLabel(pht('Upload Picture'))
->setError($e_file)
->setCaption(
pht('Supported formats: %s', implode(', ', $supported_formats))))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($blog_uri)
->setValue(pht('Upload Picture')));
$upload_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Upload New Picture'))
->setForm($upload_form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Blogs'),
$this->getApplicationURI('blog/'));
$crumbs->addTextCrumb(
$blog->getName(),
$this->getApplicationURI('blog/view/'.$id));
$crumbs->addTextCrumb(pht('Blog Picture'));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$form_box,
$upload_box,
));
}
}

View file

@ -9,6 +9,7 @@ final class PhameBlogViewController extends PhameBlogController {
$blog = id(new PhameBlogQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfileImage(true)
->executeOne();
if (!$blog) {
return new Aphront404Response();
@ -32,10 +33,13 @@ final class PhameBlogViewController extends PhameBlogController {
$header_color = 'bluegrey';
}
$picture = $blog->getProfileImageURI();
$header = id(new PHUIHeaderView())
->setHeader($blog->getName())
->setUser($viewer)
->setPolicyObject($blog)
->setImage($picture)
->setStatus($header_icon, $header_color, $header_name);
$actions = $this->renderActions($blog, $viewer);
@ -169,6 +173,14 @@ final class PhameBlogViewController extends PhameBlogController {
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-picture-o')
->setHref($this->getApplicationURI('blog/picture/'.$blog->getID().'/'))
->setName(pht('Edit Blog Picture'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($blog->isArchived()) {
$actions->addAction(
id(new PhabricatorActionView())

View file

@ -6,7 +6,9 @@ final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids;
private $domain;
private $statuses;
private $needBloggers;
private $needProfileImage;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -28,6 +30,11 @@ final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
return $this;
}
public function needProfileImage($need) {
$this->needProfileImage = $need;
return $this;
}
public function newResultObject() {
return new PhameBlog();
}
@ -70,6 +77,39 @@ final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
return $where;
}
protected function didFilterPage(array $blogs) {
if ($this->needProfileImage) {
$default = null;
$file_phids = mpull($blogs, 'getProfileImagePHID');
$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 ($blogs as $blog) {
$file = idx($files, $blog->getProfileImagePHID());
if (!$file) {
if (!$default) {
$default = PhabricatorFile::loadBuiltin(
$this->getViewer(),
'blog.png');
}
$file = $default;
}
$blog->attachProfileImageFile($file);
}
}
return $blogs;
}
public function getQueryApplicationClass() {
// TODO: Can we set this without breaking public blogs?
return null;

View file

@ -11,7 +11,6 @@ final class PhameBlog extends PhameDAO
PhabricatorApplicationTransactionInterface {
const MARKUP_FIELD_DESCRIPTION = 'markup:description';
const SKIN_DEFAULT = 'oblivious';
protected $name;
@ -23,7 +22,9 @@ final class PhameBlog extends PhameDAO
protected $editPolicy;
protected $status;
protected $mailKey;
protected $profileImagePHID;
private $profileImageFile = self::ATTACHABLE;
private static $requestBlog;
const STATUS_ACTIVE = 'active';
@ -41,6 +42,7 @@ final class PhameBlog extends PhameDAO
'domain' => 'text128?',
'status' => 'text32',
'mailKey' => 'bytes20',
'profileImagePHID' => 'phid?',
// T6203/NULLABILITY
// These policies should always be non-null.
@ -243,6 +245,19 @@ final class PhameBlog extends PhameDAO
return PhabricatorEnv::getProductionURI($uri);
}
public function getProfileImageURI() {
return $this->getProfileImageFile()->getBestURI();
}
public function attachProfileImageFile(PhabricatorFile $file) {
$this->profileImageFile = $file;
return $this;
}
public function getProfileImageFile() {
return $this->assertAttached($this->profileImageFile);
}
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */