@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.

Make PhameBlogs respect policies

Summary:
Adds "can view" and "can edit" policies to blogs. Replaces "bloggers" with "can join".

This doesn't fully remove "bloggers" because I didn't want this to get too crazy/huge.

Test Plan: Created, edited, deleted blogs.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1373

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

+182 -130
+18
resources/sql/patches/phamepolicy.sql
··· 1 + ALTER TABLE `{$NAMESPACE}_phame`.`phame_blog` 2 + ADD `viewPolicy` varchar(64) COLLATE utf8_bin; 3 + 4 + UPDATE `{$NAMESPACE}_phame`.`phame_blog` SET viewPolicy = 'users' 5 + WHERE viewPolicy IS NULL; 6 + 7 + ALTER TABLE `{$NAMESPACE}_phame`.`phame_blog` 8 + ADD `editPolicy` varchar(64) COLLATE utf8_bin; 9 + 10 + UPDATE `{$NAMESPACE}_phame`.`phame_blog` SET editPolicy = 'users' 11 + WHERE editPolicy IS NULL; 12 + 13 + ALTER TABLE `{$NAMESPACE}_phame`.`phame_blog` 14 + ADD `joinPolicy` varchar(64) COLLATE utf8_bin; 15 + 16 + UPDATE `{$NAMESPACE}_phame`.`phame_blog` SET joinPolicy = 'users' 17 + WHERE joinPolicy IS NULL; 18 +
+6 -2
src/__phutil_library_map__.php
··· 2276 2276 'PhabricatorXHProfSampleListView' => 'AphrontView', 2277 2277 'PhameAllBlogListController' => 'PhameBlogListBaseController', 2278 2278 'PhameAllPostListController' => 'PhamePostListBaseController', 2279 - 'PhameBlog' => 'PhameDAO', 2279 + 'PhameBlog' => 2280 + array( 2281 + 0 => 'PhameDAO', 2282 + 1 => 'PhabricatorPolicyInterface', 2283 + ), 2280 2284 'PhameBlogDeleteController' => 'PhameController', 2281 2285 'PhameBlogDetailView' => 'AphrontView', 2282 2286 'PhameBlogEditController' => 'PhameController', 2283 2287 'PhameBlogListBaseController' => 'PhameController', 2284 2288 'PhameBlogListView' => 'AphrontView', 2285 - 'PhameBlogQuery' => 'PhabricatorOffsetPagedQuery', 2289 + 'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2286 2290 'PhameBlogSkin' => 'AphrontView', 2287 2291 'PhameBlogViewController' => 'PhameController', 2288 2292 'PhameBloggerPostListController' => 'PhamePostListBaseController',
+12 -2
src/aphront/configuration/AphrontApplicationConfiguration.php
··· 137 137 if ($host != id(new PhutilURI($base_uri))->getDomain() && 138 138 $host != id(new PhutilURI($prod_uri))->getDomain() && 139 139 $host != id(new PhutilURI($file_uri))->getDomain()) { 140 - $blogs = id(new PhameBlogQuery())->withDomain($host)->execute(); 141 - $blog = reset($blogs); 140 + 141 + try { 142 + $blog = id(new PhameBlogQuery()) 143 + ->setViewer($request->getUser()) 144 + ->withDomain($host) 145 + ->executeOne(); 146 + } catch (PhabricatorPolicyException $ex) { 147 + throw new Exception( 148 + "This blog is not visible to logged out users, so it can not be ". 149 + "visited from a custom domain."); 150 + } 151 + 142 152 if (!$blog) { 143 153 if ($prod_uri && $prod_uri != $base_uri) { 144 154 $prod_str = ' or '.$prod_uri;
+13 -43
src/applications/phame/controller/blog/PhameBlogDeleteController.php
··· 51 51 } 52 52 53 53 public function processRequest() { 54 - $blogger_edge_type = PhabricatorEdgeConfig::TYPE_BLOG_HAS_BLOGGER; 55 - $post_edge_type = PhabricatorEdgeConfig::TYPE_BLOG_HAS_POST; 56 - $request = $this->getRequest(); 57 - $user = $request->getUser(); 58 - $blog_phid = $this->getBlogPHID(); 59 - $blogs = id(new PhameBlogQuery()) 60 - ->withPHIDs(array($blog_phid)) 61 - ->execute(); 62 - $blog = reset($blogs); 63 - if (empty($blog)) { 64 - return new Aphront404Response(); 65 - } 66 - 67 - $phids = array($blog_phid); 68 - $edge_types = array( 69 - PhabricatorEdgeConfig::TYPE_BLOG_HAS_BLOGGER, 70 - PhabricatorEdgeConfig::TYPE_BLOG_HAS_POST, 71 - ); 54 + $request = $this->getRequest(); 55 + $user = $request->getUser(); 72 56 73 - $edges = id(new PhabricatorEdgeQuery()) 74 - ->withSourcePHIDs($phids) 75 - ->withEdgeTypes($edge_types) 76 - ->execute(); 57 + $blog = id(new PhameBlogQuery()) 58 + ->setViewer($user) 59 + ->withPHIDs(array($this->getBlogPHID())) 60 + ->requireCapabilities( 61 + array( 62 + PhabricatorPolicyCapability::CAN_EDIT, 63 + )) 64 + ->executeOne(); 77 65 78 - $blogger_edges = $edges[$blog_phid][$blogger_edge_type]; 79 - // TODO -- make this check use a policy 80 - if (!isset($blogger_edges[$user->getPHID()]) && 81 - !$user->isAdmin()) { 82 - return new Aphront403Response(); 66 + if (!$blog) { 67 + return new Aphront404Response(); 83 68 } 84 69 85 - $edit_uri = $blog->getEditURI(); 86 - 87 70 if ($request->isFormPost()) { 88 - $blogger_phids = array_keys($blogger_edges); 89 - $post_edges = $edges[$blog_phid][$post_edge_type]; 90 - $post_phids = array_keys($post_edges); 91 - $editor = id(new PhabricatorEdgeEditor()) 92 - ->setActor($user); 93 - foreach ($blogger_phids as $phid) { 94 - $editor->removeEdge($blog_phid, $blogger_edge_type, $phid); 95 - } 96 - foreach ($post_phids as $phid) { 97 - $editor->removeEdge($blog_phid, $post_edge_type, $phid); 98 - } 99 - $editor->save(); 100 - 101 71 $blog->delete(); 102 72 return id(new AphrontRedirectResponse()) 103 73 ->setURI('/phame/blog/?deleted'); ··· 108 78 ->setTitle('Delete blog?') 109 79 ->appendChild('Really delete this blog? It will be gone forever.') 110 80 ->addSubmitButton('Delete') 111 - ->addCancelButton($edit_uri); 81 + ->addCancelButton($blog->getEditURI()); 112 82 113 83 return id(new AphrontDialogResponse())->setDialog($dialog); 114 84 }
+49 -64
src/applications/phame/controller/blog/PhameBlogEditController.php
··· 74 74 } 75 75 76 76 public function processRequest() { 77 - $request = $this->getRequest(); 78 - $user = $request->getUser(); 79 - $e_name = null; 80 - $e_bloggers = null; 77 + $request = $this->getRequest(); 78 + $user = $request->getUser(); 79 + 80 + $e_name = true; 81 81 $e_custom_domain = null; 82 82 $errors = array(); 83 83 84 84 if ($this->isBlogEdit()) { 85 - $blogs = id(new PhameBlogQuery()) 85 + $blog = id(new PhameBlogQuery()) 86 + ->setViewer($user) 86 87 ->withPHIDs(array($this->getBlogPHID())) 87 - ->execute(); 88 - $blog = reset($blogs); 89 - if (empty($blog)) { 88 + ->requireCapabilities( 89 + array( 90 + PhabricatorPolicyCapability::CAN_EDIT 91 + )) 92 + ->executeOne(); 93 + if (!$blog) { 90 94 return new Aphront404Response(); 91 95 } 92 96 93 - $bloggers = $blog->loadBloggers()->getBloggers(); 94 - 95 - // TODO -- make this check use a policy 96 - if (!isset($bloggers[$user->getPHID()]) && 97 - !$user->isAdmin()) { 98 - return new Aphront403Response(); 99 - } 100 - $blogger_tokens = mpull($bloggers, 'getFullName', 'getPHID'); 101 97 $submit_button = 'Save Changes'; 102 98 $delete_button = javelin_render_tag( 103 99 'a', ··· 118 114 } 119 115 120 116 if ($request->isFormPost()) { 121 - $saved = true; 122 117 $name = $request->getStr('name'); 123 118 $description = $request->getStr('description'); 124 - $blogger_arr = $request->getArr('bloggers'); 125 119 $custom_domain = $request->getStr('custom_domain'); 126 120 $skin = $request->getStr('skin'); 127 121 128 - if (empty($blogger_arr)) { 129 - $error = 'Bloggers must be nonempty.'; 130 - if ($this->isBlogEdit()) { 131 - $error .= ' To delete the blog, use the delete button.'; 132 - } else { 133 - $error .= ' A blog cannot exist without bloggers.'; 134 - } 135 - $e_bloggers = 'Required'; 136 - $errors[] = $error; 137 - } 138 - $new_bloggers = array_values($blogger_arr); 139 - if ($this->isBlogEdit()) { 140 - $old_bloggers = array_keys($blogger_tokens); 141 - } else { 142 - $old_bloggers = array(); 143 - } 144 - 145 122 if (empty($name)) { 146 - $errors[] = 'Name must be nonempty.'; 123 + $errors[] = 'You must give the blog a name.'; 147 124 $e_name = 'Required'; 148 125 } 149 126 $blog->setName($name); ··· 158 135 } 159 136 $blog->setSkin($skin); 160 137 161 - if (empty($errors)) { 162 - $blog->save(); 138 + $blog->setViewPolicy($request->getStr('can_view')); 139 + $blog->setEditPolicy($request->getStr('can_edit')); 140 + $blog->setJoinPolicy($request->getStr('can_join')); 163 141 164 - $add_phids = $new_bloggers; 165 - $rem_phids = array_diff($old_bloggers, $new_bloggers); 166 - $editor = new PhabricatorEdgeEditor(); 167 - $edge_type = PhabricatorEdgeConfig::TYPE_BLOG_HAS_BLOGGER; 168 - $editor->setActor($user); 169 - foreach ($add_phids as $phid) { 170 - $editor->addEdge($blog->getPHID(), $edge_type, $phid); 171 - } 172 - foreach ($rem_phids as $phid) { 173 - $editor->removeEdge($blog->getPHID(), $edge_type, $phid); 174 - } 175 - $editor->save(); 142 + // Don't let users remove their ability to edit blogs. 143 + PhabricatorPolicyFilter::mustRetainCapability( 144 + $user, 145 + $blog, 146 + PhabricatorPolicyCapability::CAN_EDIT); 176 147 177 - } else { 178 - $saved = false; 179 - } 148 + if (!$errors) { 149 + $blog->save(); 180 150 181 - if ($saved) { 182 151 $uri = new PhutilURI($blog->getViewURI()); 183 152 if ($this->isBlogEdit()) { 184 153 $uri->setQueryParam('edit', true); ··· 197 166 $panel->addButton($delete_button); 198 167 } 199 168 169 + $policies = id(new PhabricatorPolicyQuery()) 170 + ->setViewer($user) 171 + ->setObject($blog) 172 + ->execute(); 173 + 200 174 $form = id(new AphrontFormView()) 201 175 ->setUser($user) 202 176 ->appendChild( ··· 212 186 ->setLabel('Description') 213 187 ->setName('description') 214 188 ->setValue($blog->getDescription()) 215 - ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) 216 189 ->setID('blog-description') 217 190 ) 218 191 ->appendChild( 219 - id(new AphrontFormTokenizerControl()) 220 - ->setLabel('Bloggers') 221 - ->setName('bloggers') 222 - ->setValue($blogger_tokens) 223 - ->setUser($user) 224 - ->setDatasource('/typeahead/common/users/') 225 - ->setError($e_bloggers) 226 - ) 192 + id(new AphrontFormPolicyControl()) 193 + ->setUser($user) 194 + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 195 + ->setPolicyObject($blog) 196 + ->setPolicies($policies) 197 + ->setName('can_view')) 198 + ->appendChild( 199 + id(new AphrontFormPolicyControl()) 200 + ->setUser($user) 201 + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 202 + ->setPolicyObject($blog) 203 + ->setPolicies($policies) 204 + ->setName('can_edit')) 205 + ->appendChild( 206 + id(new AphrontFormPolicyControl()) 207 + ->setUser($user) 208 + ->setCapability(PhabricatorPolicyCapability::CAN_JOIN) 209 + ->setPolicyObject($blog) 210 + ->setPolicies($policies) 211 + ->setName('can_join')) 227 212 ->appendChild( 228 213 id(new AphrontFormTextControl()) 229 214 ->setLabel('Custom Domain') ··· 250 235 251 236 if ($errors) { 252 237 $error_view = id(new AphrontErrorView()) 253 - ->setTitle('Errors saving blog.') 238 + ->setTitle('Form Errors') 254 239 ->setErrors($errors); 255 240 } else { 256 241 $error_view = null;
+3 -4
src/applications/phame/controller/blog/PhameBlogViewController.php
··· 71 71 $user = $request->getUser(); 72 72 $blog_phid = $this->getBlogPHID(); 73 73 74 - $blogs = id(new PhameBlogQuery()) 74 + $blog = id(new PhameBlogQuery()) 75 + ->setViewer($user) 75 76 ->withPHIDs(array($blog_phid)) 76 - ->execute(); 77 - $blog = reset($blogs); 78 - 77 + ->executeOne(); 79 78 if (!$blog) { 80 79 return new Aphront404Response(); 81 80 }
+5 -1
src/applications/phame/controller/blog/list/PhameAllBlogListController.php
··· 29 29 public function processRequest() { 30 30 $user = $this->getRequest()->getUser(); 31 31 32 + $pager = id(new AphrontCursorPagerView()) 33 + ->readFromRequest($this->getRequest()); 34 + 32 35 $blogs = id(new PhameBlogQuery()) 36 + ->setViewer($user) 33 37 ->needBloggers(true) 34 - ->executeWithOffsetPager($this->getPager()); 38 + ->executeWithCursorPager($pager); 35 39 $this->setBlogs($blogs); 36 40 37 41 $page_title = 'All Blogs';
+5 -1
src/applications/phame/controller/blog/list/PhameUserBlogListController.php
··· 48 48 PhabricatorEdgeConfig::TYPE_BLOGGER_HAS_BLOG 49 49 ); 50 50 51 + $pager = id(new AphrontCursorPagerView()) 52 + ->readFromRequest($this->getRequest()); 53 + 51 54 $blogs = id(new PhameBlogQuery()) 55 + ->setViewer($user) 52 56 ->withPHIDs($blog_phids) 53 57 ->needBloggers(true) 54 - ->executeWithOffsetPager($this->getPager()); 58 + ->executeWithCursorPager($pager); 55 59 56 60 $this->setBlogs($blogs); 57 61
+1
src/applications/phame/controller/post/PhamePostEditController.php
··· 400 400 } 401 401 402 402 $blogs = id(new PhameBlogQuery()) 403 + ->setViewer($this->getRequest()->getUser()) 403 404 ->withPHIDs(array_keys($all_blogs_assoc)) 404 405 ->execute(); 405 406 $blogs = mpull($blogs, null, 'getPHID');
+6 -9
src/applications/phame/query/PhameBlogQuery.php
··· 19 19 /** 20 20 * @group phame 21 21 */ 22 - final class PhameBlogQuery extends PhabricatorOffsetPagedQuery { 22 + final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery { 23 23 24 24 private $phids; 25 25 private $domain; ··· 40 40 return $this; 41 41 } 42 42 43 - public function execute() { 43 + public function loadPage() { 44 44 $table = new PhameBlog(); 45 45 $conn_r = $table->establishConnection('r'); 46 46 ··· 102 102 $where[] = qsprintf( 103 103 $conn_r, 104 104 'phid IN (%Ls)', 105 - $this->phids 106 - ); 105 + $this->phids); 107 106 } 108 107 109 108 if ($this->domain) { 110 109 $where[] = qsprintf( 111 110 $conn_r, 112 111 'domain = %s', 113 - $this->domain 114 - ); 112 + $this->domain); 115 113 } 116 114 115 + $where[] = $this->buildPagingClause($conn_r); 116 + 117 117 return $this->formatWhereClause($where); 118 118 } 119 119 120 - private function buildOrderClause($conn_r) { 121 - return 'ORDER BY id DESC'; 122 - } 123 120 }
+54 -3
src/applications/phame/storage/PhameBlog.php
··· 19 19 /** 20 20 * @group phame 21 21 */ 22 - final class PhameBlog extends PhameDAO { 22 + final class PhameBlog extends PhameDAO implements PhabricatorPolicyInterface { 23 23 24 24 const SKIN_DEFAULT = 'PhabricatorBlogSkin'; 25 25 ··· 30 30 protected $domain; 31 31 protected $configData; 32 32 protected $creatorPHID; 33 - 34 - private $skin; 33 + protected $viewPolicy; 34 + protected $editPolicy; 35 + protected $joinPolicy; 35 36 36 37 private $bloggerPHIDs; 37 38 private $bloggers; ··· 212 213 public static function getRequestBlog() { 213 214 return self::$requestBlog; 214 215 } 216 + 217 + 218 + /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ 219 + 220 + 221 + public function getCapabilities() { 222 + return array( 223 + PhabricatorPolicyCapability::CAN_VIEW, 224 + PhabricatorPolicyCapability::CAN_EDIT, 225 + PhabricatorPolicyCapability::CAN_JOIN, 226 + ); 227 + } 228 + 229 + 230 + public function getPolicy($capability) { 231 + switch ($capability) { 232 + case PhabricatorPolicyCapability::CAN_VIEW: 233 + return $this->getViewPolicy(); 234 + case PhabricatorPolicyCapability::CAN_EDIT: 235 + return $this->getEditPolicy(); 236 + case PhabricatorPolicyCapability::CAN_JOIN: 237 + return $this->getJoinPolicy(); 238 + } 239 + } 240 + 241 + public function hasAutomaticCapability($capability, PhabricatorUser $user) { 242 + $can_edit = PhabricatorPolicyCapability::CAN_EDIT; 243 + $can_join = PhabricatorPolicyCapability::CAN_JOIN; 244 + 245 + switch ($capability) { 246 + case PhabricatorPolicyCapability::CAN_VIEW: 247 + // Users who can edit or post to a blog can always view it. 248 + if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) { 249 + return true; 250 + } 251 + if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_join)) { 252 + return true; 253 + } 254 + break; 255 + case PhabricatorPolicyCapability::CAN_JOIN: 256 + // Users who can edit a blog can always post to it. 257 + if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) { 258 + return true; 259 + } 260 + break; 261 + } 262 + 263 + return false; 264 + } 265 + 215 266 }
+6 -1
src/applications/phame/view/PhameBlogListView.php
··· 78 78 'Custom Domain', 79 79 $blog->getDomain()); 80 80 81 - if (isset($bloggers[$user->getPHID()])) { 81 + $can_edit = PhabricatorPolicyFilter::hasCapability( 82 + $user, 83 + $blog, 84 + PhabricatorPolicyCapability::CAN_EDIT); 85 + 86 + if ($can_edit) { 82 87 $item->addAttribute( 83 88 phutil_render_tag( 84 89 'a',
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1004 1004 'type' => 'php', 1005 1005 'name' => $this->getPatchPath('ponder-mailkey-populate.php'), 1006 1006 ), 1007 + 'phamepolicy.sql' => array( 1008 + 'type' => 'sql', 1009 + 'name' => $this->getPatchPath('phamepolicy.sql'), 1010 + ), 1007 1011 ); 1008 1012 } 1009 1013