Page MenuHomePhabricator

D14587.id35288.diff
No OneTemporary

D14587.id35288.diff

diff --git a/resources/builtin/blog.png b/resources/builtin/blog.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d0a838ef3dfb7e50c525ab4a2571c0ff84ac56c
GIT binary patch
literal 1010
zc$@+90}cF%P)<h;3K|Lk000e1NJLTq001%o001%w1^@s69zTe&00004XF*Lt006O%
z3;baP0000WV@Og>004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006
zVoOIv00000008+zyMF)x010qNS#tmY3labT3lag+-G2N400UM@L_t(&-tC&%a@sHy
zhJSgLZEQoDg%C2G_Wd8Dx4mteVW3G!3N@SK1@HpyMGF%e9Izt?(&4|fb!2@yI*S;{
zm#;So{sVElUcdq50J7&Q97Nzbe7-ya&yn{Ha&czh{6xpcTIKzM@En1uQvg6olOXWL
zeXdZD+Nl9ulK}v_CgbAFcz+-~M^G`9m1m|-3-d~0AWyxqP?26K1Z15z7Andsg@9B~
z^k?2!NY`YXpR)G~a}bUr_)u0~98)nU^SZ(uq<Wn0jfHegMvbvon1XN|!Er_1YHY<+
zn0kd72qD15noN}@U6WC-njnM?jF4@y1OiVWO9H4QK%pWi6+tBdMrk{Mr>a*?)T<^O
zFF+W@7*8Thf(VmogfN;Ro+U_<?e2Z4AcO#Bi9%B(sEP<l6i_xOlimAQe>On|*0f``
zJ4G1A7*8Yk;}DNtuof;A<ora(r}|ML3(ummzPfpLKW((TkI%-X#z?2<!FHy*=2(&K
zO!4r`S^F*(BuNrnH7$&s$*$MGgt%&1n5V?di&<-)B)Dq!FtEovg%~*#d}~^m&(i~c
zWBN^!1lK=y2Q#q8xN2G(v)5L2BuRkptsXi(_e~)>mW%6F58GtmOm(Ey9)jmNoE~X~
z0=Rwv&kxbMA8u`vxw1EI`mhFGp&*_gWP->A0sxKML1D+t8hB{kk22fk?zLL&A*`X#
z6rykYXxt8R*}kf2oXzG;LFTjkzo)+nL{lWDAXFCL9)u$#SI7I3jFO!Dj*=Thl6a;N
z%X!6272)+DRLb|kO;YF^gJ>$-_*l-HGwyPOsEh-BIj@>8;B%jkZH|`f2e1Yn`t}&d
z6%D6H+QvJ*B;;FCE-RjUIb*qgfRQsr$8r$=tu@`Ai{8*f?ZiOER8oPAk_<wyRipT7
z5K&y8u@=o<yK5tgpBKy7EJ3^bh;HA-#hD4CB(DV$cy6sda}brTo@3V!FquZU>(~gw
z*+%;)p5x|W1WDvjua;3#r4=BBiio_Rco0pMmjFQ&<L<%6B#1N56Gn5i+CxxTz~y-v
ziY!3aWT;ev^A;ffIDj?u(XkwO<1o(@E1Cu|8aI7tiip~o34f9e0szS2pAUPc1IWJV
g0J3j7fb5%o0}z@p?jaHn2mk;807*qoM6N<$f@`X^B>(^b
literal 0
Hc$@<O00001
diff --git a/resources/sql/autopatches/20151128.phame.blog.picture.1.sql b/resources/sql/autopatches/20151128.phame.blog.picture.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20151128.phame.blog.picture.1.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_phame.phame_blog
+ ADD profileImagePHID 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
@@ -3285,6 +3285,7 @@
'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',
@@ -7591,6 +7592,7 @@
'PhameBlogFeedController' => 'PhameBlogController',
'PhameBlogListController' => 'PhameBlogController',
'PhameBlogLiveController' => 'PhameBlogController',
+ 'PhameBlogProfilePictureController' => 'PhameBlogController',
'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhameBlogReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine',
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
@@ -63,6 +63,7 @@
'view/(?P<id>[^/]+)/' => 'PhameBlogViewController',
'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController',
'new/' => 'PhameBlogEditController',
+ 'picture/(?P<id>[1-9]\d*)/' => 'PhameBlogProfilePictureController',
),
) + $this->getResourceSubroutes(),
);
diff --git a/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php b/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php
@@ -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,
+ ));
+
+ }
+}
diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php
--- a/src/applications/phame/controller/blog/PhameBlogViewController.php
+++ b/src/applications/phame/controller/blog/PhameBlogViewController.php
@@ -9,6 +9,7 @@
$blog = id(new PhameBlogQuery())
->setViewer($viewer)
->withIDs(array($id))
+ ->needProfileImage(true)
->executeOne();
if (!$blog) {
return new Aphront404Response();
@@ -32,10 +33,13 @@
$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 @@
->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())
diff --git a/src/applications/phame/query/PhameBlogQuery.php b/src/applications/phame/query/PhameBlogQuery.php
--- a/src/applications/phame/query/PhameBlogQuery.php
+++ b/src/applications/phame/query/PhameBlogQuery.php
@@ -6,7 +6,9 @@
private $phids;
private $domain;
private $statuses;
+
private $needBloggers;
+ private $needProfileImage;
public function withIDs(array $ids) {
$this->ids = $ids;
@@ -28,6 +30,11 @@
return $this;
}
+ public function needProfileImage($need) {
+ $this->needProfileImage = $need;
+ return $this;
+ }
+
public function newResultObject() {
return new PhameBlog();
}
@@ -70,6 +77,39 @@
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;
diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php
--- a/src/applications/phame/storage/PhameBlog.php
+++ b/src/applications/phame/storage/PhameBlog.php
@@ -10,7 +10,6 @@
PhabricatorApplicationTransactionInterface {
const MARKUP_FIELD_DESCRIPTION = 'markup:description';
-
const SKIN_DEFAULT = 'oblivious';
protected $name;
@@ -22,7 +21,9 @@
protected $editPolicy;
protected $status;
protected $mailKey;
+ protected $profileImagePHID;
+ private $profileImageFile = self::ATTACHABLE;
private static $requestBlog;
const STATUS_ACTIVE = 'active';
@@ -40,6 +41,7 @@
'domain' => 'text128?',
'status' => 'text32',
'mailKey' => 'bytes20',
+ 'profileImagePHID' => 'phid?',
// T6203/NULLABILITY
// These policies should always be non-null.
@@ -242,6 +244,19 @@
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 )-------------------------- */

File Metadata

Mime Type
text/plain
Expires
Mar 16 2025, 12:45 AM (4 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7704752
Default Alt Text
D14587.id35288.diff (14 KB)

Event Timeline