@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add ability to set a header image per Phame blog

Summary: This is the backend half of uploading an image as a header for Phame Blogs. Allows you to upload image, or delete it. Ref T10901

Test Plan:
Go to Manage Blog, visit Edit Header Image, Upload snarky file. See snarky file on Manage page. Edit Header Image, click delete, save, see file goes away.

{F1690966}

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Maniphest Tasks: T10901

Differential Revision: https://secure.phabricator.com/D16140

+210 -7
+3 -3
resources/celerity/map.php
··· 7 7 */ 8 8 return array( 9 9 'names' => array( 10 - 'core.pkg.css' => '6913fe66', 10 + 'core.pkg.css' => 'c7fc5aec', 11 11 'core.pkg.js' => '10275c16', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => 'b3eea3f5', ··· 155 155 'rsrc/css/phui/phui-spacing.css' => '042804d6', 156 156 'rsrc/css/phui/phui-status.css' => 'd5263e49', 157 157 'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2', 158 - 'rsrc/css/phui/phui-timeline-view.css' => '6e342216', 158 + 'rsrc/css/phui/phui-timeline-view.css' => '8ea41b25', 159 159 'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85', 160 160 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7', 161 161 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', ··· 860 860 'phui-status-list-view-css' => 'd5263e49', 861 861 'phui-tag-view-css' => '6bbd83e2', 862 862 'phui-theme-css' => '027ba77e', 863 - 'phui-timeline-view-css' => '6e342216', 863 + 'phui-timeline-view-css' => '8ea41b25', 864 864 'phui-two-column-view-css' => '9fb86c85', 865 865 'phui-workboard-color-css' => 'ac6fe6a7', 866 866 'phui-workboard-view-css' => 'e6d89647',
+2
resources/sql/autopatches/20160616.phame.blog.header.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_phame.phame_blog 2 + ADD headerImagePHID VARBINARY(64);
+2
src/__phutil_library_map__.php
··· 3777 3777 'PhameBlogEditor' => 'applications/phame/editor/PhameBlogEditor.php', 3778 3778 'PhameBlogFeedController' => 'applications/phame/controller/blog/PhameBlogFeedController.php', 3779 3779 'PhameBlogFulltextEngine' => 'applications/phame/search/PhameBlogFulltextEngine.php', 3780 + 'PhameBlogHeaderPictureController' => 'applications/phame/controller/blog/PhameBlogHeaderPictureController.php', 3780 3781 'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php', 3781 3782 'PhameBlogListView' => 'applications/phame/view/PhameBlogListView.php', 3782 3783 'PhameBlogManageController' => 'applications/phame/controller/blog/PhameBlogManageController.php', ··· 8655 8656 'PhameBlogEditor' => 'PhabricatorApplicationTransactionEditor', 8656 8657 'PhameBlogFeedController' => 'PhameBlogController', 8657 8658 'PhameBlogFulltextEngine' => 'PhabricatorFulltextEngine', 8659 + 'PhameBlogHeaderPictureController' => 'PhameBlogController', 8658 8660 'PhameBlogListController' => 'PhameBlogController', 8659 8661 'PhameBlogListView' => 'AphrontTagView', 8660 8662 'PhameBlogManageController' => 'PhameBlogController',
+1
src/applications/phame/application/PhabricatorPhameApplication.php
··· 67 67 'manage/(?P<id>[^/]+)/' => 'PhameBlogManageController', 68 68 'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController', 69 69 'picture/(?P<id>[1-9]\d*)/' => 'PhameBlogProfilePictureController', 70 + 'header/(?P<id>[1-9]\d*)/' => 'PhameBlogHeaderPictureController', 70 71 ), 71 72 ) + $this->getResourceSubroutes(), 72 73 );
+126
src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php
··· 1 + <?php 2 + 3 + final class PhameBlogHeaderPictureController 4 + extends PhameBlogController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $request->getViewer(); 8 + $id = $request->getURIData('id'); 9 + 10 + $blog = id(new PhameBlogQuery()) 11 + ->setViewer($viewer) 12 + ->withIDs(array($id)) 13 + ->needHeaderImage(true) 14 + ->requireCapabilities( 15 + array( 16 + PhabricatorPolicyCapability::CAN_VIEW, 17 + PhabricatorPolicyCapability::CAN_EDIT, 18 + )) 19 + ->executeOne(); 20 + if (!$blog) { 21 + return new Aphront404Response(); 22 + } 23 + 24 + $blog_uri = '/phame/blog/manage/'.$id; 25 + 26 + $supported_formats = PhabricatorFile::getTransformableImageFormats(); 27 + $e_file = true; 28 + $errors = array(); 29 + $delete_header = ($request->getInt('delete') == 1); 30 + 31 + if ($request->isFormPost()) { 32 + if ($request->getFileExists('header')) { 33 + $file = PhabricatorFile::newFromPHPUpload( 34 + $_FILES['header'], 35 + array( 36 + 'authorPHID' => $viewer->getPHID(), 37 + 'canCDN' => true, 38 + )); 39 + } else if (!$delete_header) { 40 + $e_file = pht('Required'); 41 + $errors[] = pht( 42 + 'You must choose a file when uploading a new blog header.'); 43 + } 44 + 45 + if (!$errors && !$delete_header) { 46 + if (!$file->isTransformableImage()) { 47 + $e_file = pht('Not Supported'); 48 + $errors[] = pht( 49 + 'This server only supports these image formats: %s.', 50 + implode(', ', $supported_formats)); 51 + } 52 + } 53 + 54 + if (!$errors) { 55 + if ($delete_header) { 56 + $blog->setHeaderImagePHID(null); 57 + } else { 58 + $blog->setHeaderImagePHID($file->getPHID()); 59 + $file->attachToObject($blog->getPHID()); 60 + } 61 + $blog->save(); 62 + return id(new AphrontRedirectResponse())->setURI($blog_uri); 63 + } 64 + } 65 + 66 + $title = pht('Edit Blog Header'); 67 + 68 + $upload_form = id(new AphrontFormView()) 69 + ->setUser($viewer) 70 + ->setEncType('multipart/form-data') 71 + ->appendChild( 72 + id(new AphrontFormFileControl()) 73 + ->setName('header') 74 + ->setLabel(pht('Upload Header')) 75 + ->setError($e_file) 76 + ->setCaption( 77 + pht('Supported formats: %s', implode(', ', $supported_formats)))) 78 + ->appendChild( 79 + id(new AphrontFormCheckboxControl()) 80 + ->setName('delete') 81 + ->setLabel(pht('Delete Header')) 82 + ->addCheckbox( 83 + 'delete', 84 + 1, 85 + null, 86 + null)) 87 + ->appendChild( 88 + id(new AphrontFormSubmitControl()) 89 + ->addCancelButton($blog_uri) 90 + ->setValue(pht('Upload Header'))); 91 + 92 + $upload_box = id(new PHUIObjectBoxView()) 93 + ->setHeaderText(pht('Upload New Header')) 94 + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 95 + ->setForm($upload_form); 96 + 97 + $crumbs = $this->buildApplicationCrumbs(); 98 + $crumbs->addTextCrumb( 99 + pht('Blogs'), 100 + $this->getApplicationURI('blog/')); 101 + $crumbs->addTextCrumb( 102 + $blog->getName(), 103 + $this->getApplicationURI('blog/view/'.$id)); 104 + $crumbs->addTextCrumb(pht('Blog Header')); 105 + $crumbs->setBorder(true); 106 + 107 + $header = id(new PHUIHeaderView()) 108 + ->setHeader(pht('Edit Blog Header')) 109 + ->setHeaderIcon('fa-camera'); 110 + 111 + $view = id(new PHUITwoColumnView()) 112 + ->setHeader($header) 113 + ->setFooter(array( 114 + $upload_box, 115 + )); 116 + 117 + return $this->newPage() 118 + ->setTitle($title) 119 + ->setCrumbs($crumbs) 120 + ->appendChild( 121 + array( 122 + $view, 123 + )); 124 + 125 + } 126 + }
+31
src/applications/phame/controller/blog/PhameBlogManageController.php
··· 14 14 ->setViewer($viewer) 15 15 ->withIDs(array($id)) 16 16 ->needProfileImage(true) 17 + ->needHeaderImage(true) 17 18 ->executeOne(); 18 19 if (!$blog) { 19 20 return new Aphront404Response(); ··· 40 41 41 42 $curtain = $this->buildCurtain($blog); 42 43 $properties = $this->buildPropertyView($blog); 44 + $file = $this->buildFileView($blog); 43 45 44 46 $crumbs = $this->buildApplicationCrumbs(); 45 47 $crumbs->addTextCrumb( ··· 62 64 ->setHeader($header) 63 65 ->setCurtain($curtain) 64 66 ->addPropertySection(pht('Details'), $properties) 67 + ->addPropertySection(pht('Header'), $file) 65 68 ->setMainColumn( 66 69 array( 67 70 $timeline, ··· 159 162 160 163 $curtain->addAction( 161 164 id(new PhabricatorActionView()) 165 + ->setIcon('fa-camera') 166 + ->setHref($this->getApplicationURI('blog/header/'.$blog->getID().'/')) 167 + ->setName(pht('Edit Blog Header')) 168 + ->setDisabled(!$can_edit) 169 + ->setWorkflow(!$can_edit)); 170 + 171 + $curtain->addAction( 172 + id(new PhabricatorActionView()) 162 173 ->setIcon('fa-picture-o') 163 174 ->setHref($this->getApplicationURI('blog/picture/'.$blog->getID().'/')) 164 175 ->setName(pht('Edit Blog Picture')) ··· 186 197 } 187 198 188 199 return $curtain; 200 + } 201 + 202 + private function buildFileView( 203 + PhameBlog $blog) { 204 + $viewer = $this->getViewer(); 205 + 206 + $view = id(new PHUIPropertyListView()) 207 + ->setUser($viewer); 208 + 209 + if ($blog->getHeaderImagePHID()) { 210 + $view->addImageContent( 211 + phutil_tag( 212 + 'img', 213 + array( 214 + 'src' => $blog->getHeaderImageURI(), 215 + 'class' => 'phabricator-image-macro-hero', 216 + ))); 217 + return $view; 218 + } 219 + return null; 189 220 } 190 221 191 222 }
-4
src/applications/phame/controller/blog/PhameBlogProfilePictureController.php
··· 3 3 final class PhameBlogProfilePictureController 4 4 extends PhameBlogController { 5 5 6 - public function shouldRequireAdmin() { 7 - return false; 8 - } 9 - 10 6 public function handleRequest(AphrontRequest $request) { 11 7 $viewer = $request->getViewer(); 12 8 $id = $request->getURIData('id');
+28
src/applications/phame/query/PhameBlogQuery.php
··· 9 9 10 10 private $needBloggers; 11 11 private $needProfileImage; 12 + private $needHeaderImage; 12 13 13 14 public function withIDs(array $ids) { 14 15 $this->ids = $ids; ··· 32 33 33 34 public function needProfileImage($need) { 34 35 $this->needProfileImage = $need; 36 + return $this; 37 + } 38 + 39 + public function needHeaderImage($need) { 40 + $this->needHeaderImage = $need; 35 41 return $this; 36 42 } 37 43 ··· 105 111 $file = $default; 106 112 } 107 113 $blog->attachProfileImageFile($file); 114 + } 115 + } 116 + 117 + if ($this->needHeaderImage) { 118 + $file_phids = mpull($blogs, 'getHeaderImagePHID'); 119 + $file_phids = array_filter($file_phids); 120 + if ($file_phids) { 121 + $files = id(new PhabricatorFileQuery()) 122 + ->setParentQuery($this) 123 + ->setViewer($this->getViewer()) 124 + ->withPHIDs($file_phids) 125 + ->execute(); 126 + $files = mpull($files, null, 'getPHID'); 127 + } else { 128 + $files = array(); 129 + } 130 + 131 + foreach ($blogs as $blog) { 132 + $file = idx($files, $blog->getHeaderImagePHID()); 133 + if ($file) { 134 + $blog->attachHeaderImageFile($file); 135 + } 108 136 } 109 137 } 110 138 return $blogs;
+16
src/applications/phame/storage/PhameBlog.php
··· 24 24 protected $status; 25 25 protected $mailKey; 26 26 protected $profileImagePHID; 27 + protected $headerImagePHID; 27 28 28 29 private $profileImageFile = self::ATTACHABLE; 30 + private $headerImageFile = self::ATTACHABLE; 29 31 30 32 const STATUS_ACTIVE = 'active'; 31 33 const STATUS_ARCHIVED = 'archived'; ··· 43 45 'status' => 'text32', 44 46 'mailKey' => 'bytes20', 45 47 'profileImagePHID' => 'phid?', 48 + 'headerImagePHID' => 'phid?', 46 49 47 50 // T6203/NULLABILITY 48 51 // These policies should always be non-null. ··· 210 213 211 214 public function getProfileImageFile() { 212 215 return $this->assertAttached($this->profileImageFile); 216 + } 217 + 218 + public function getHeaderImageURI() { 219 + return $this->getHeaderImageFile()->getBestURI(); 220 + } 221 + 222 + public function attachHeaderImageFile(PhabricatorFile $file) { 223 + $this->headerImageFile = $file; 224 + return $this; 225 + } 226 + 227 + public function getHeaderImageFile() { 228 + return $this->assertAttached($this->headerImageFile); 213 229 } 214 230 215 231
+1
webroot/rsrc/css/phui/phui-timeline-view.css
··· 47 47 height: 9px; 48 48 border-radius: 2px; 49 49 margin-left: 76px; 50 + margin-bottom: 20px; 50 51 } 51 52 52 53 .device-desktop .phui-timeline-wedge {