@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 a default moderation policy to Ponder

Summary: This allows installs to essentially set a "moderator" for Ponder, who can clean up answers. Fixes T9098

Test Plan: Edit an answer I don't own.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Maniphest Tasks: T9098

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

+150 -91
+2
resources/sql/autopatches/20150806.ponder.answer.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_ponder.ponder_answer 2 + DROP COLUMN contentSource;
+2
resources/sql/autopatches/20150806.ponder.editpolicy.2.sql
··· 1 + ALTER TABLE {$NAMESPACE}_ponder.ponder_question 2 + DROP COLUMN editPolicy;
+4 -4
src/__phutil_library_map__.php
··· 3402 3402 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 3403 3403 'PonderController' => 'applications/ponder/controller/PonderController.php', 3404 3404 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 3405 + 'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php', 3405 3406 'PonderEditor' => 'applications/ponder/editor/PonderEditor.php', 3407 + 'PonderModerateCapability' => 'applications/ponder/capability/PonderModerateCapability.php', 3406 3408 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', 3407 3409 'PonderQuestionCommentController' => 'applications/ponder/controller/PonderQuestionCommentController.php', 3408 - 'PonderQuestionDefaultEditCapability' => 'applications/ponder/capability/PonderQuestionDefaultEditCapability.php', 3409 - 'PonderQuestionDefaultViewCapability' => 'applications/ponder/capability/PonderQuestionDefaultViewCapability.php', 3410 3410 'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php', 3411 3411 'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php', 3412 3412 'PonderQuestionHasVotingUserEdgeType' => 'applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php', ··· 7591 7591 'PonderConstants' => 'Phobject', 7592 7592 'PonderController' => 'PhabricatorController', 7593 7593 'PonderDAO' => 'PhabricatorLiskDAO', 7594 + 'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability', 7594 7595 'PonderEditor' => 'PhabricatorApplicationTransactionEditor', 7596 + 'PonderModerateCapability' => 'PhabricatorPolicyCapability', 7595 7597 'PonderQuestion' => array( 7596 7598 'PonderDAO', 7597 7599 'PhabricatorApplicationTransactionInterface', ··· 7606 7608 'PhabricatorSpacesInterface', 7607 7609 ), 7608 7610 'PonderQuestionCommentController' => 'PonderController', 7609 - 'PonderQuestionDefaultEditCapability' => 'PhabricatorPolicyCapability', 7610 - 'PonderQuestionDefaultViewCapability' => 'PhabricatorPolicyCapability', 7611 7611 'PonderQuestionEditController' => 'PonderController', 7612 7612 'PonderQuestionEditor' => 'PonderEditor', 7613 7613 'PonderQuestionHasVotingUserEdgeType' => 'PhabricatorEdgeType',
+3 -2
src/applications/ponder/application/PhabricatorPonderApplication.php
··· 93 93 94 94 protected function getCustomCapabilities() { 95 95 return array( 96 - PonderQuestionDefaultViewCapability::CAPABILITY => array( 96 + PonderDefaultViewCapability::CAPABILITY => array( 97 97 'template' => PonderQuestionPHIDType::TYPECONST, 98 98 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 99 99 ), 100 - PonderQuestionDefaultEditCapability::CAPABILITY => array( 100 + PonderModerateCapability::CAPABILITY => array( 101 + 'default' => PhabricatorPolicies::POLICY_ADMIN, 101 102 'template' => PonderQuestionPHIDType::TYPECONST, 102 103 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 103 104 ),
+16
src/applications/ponder/capability/PonderDefaultViewCapability.php
··· 1 + <?php 2 + 3 + final class PonderDefaultViewCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'ponder.default.view'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Default View Policy'); 10 + } 11 + 12 + public function shouldAllowPublicPolicySetting() { 13 + return true; 14 + } 15 + 16 + }
+12
src/applications/ponder/capability/PonderModerateCapability.php
··· 1 + <?php 2 + 3 + final class PonderModerateCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'ponder.moderate'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Moderate Policy'); 10 + } 11 + 12 + }
-12
src/applications/ponder/capability/PonderQuestionDefaultEditCapability.php
··· 1 - <?php 2 - 3 - final class PonderQuestionDefaultEditCapability 4 - extends PhabricatorPolicyCapability { 5 - 6 - const CAPABILITY = 'ponder.question.default.edit'; 7 - 8 - public function getCapabilityName() { 9 - return pht('Default Question Edit Policy'); 10 - } 11 - 12 - }
-16
src/applications/ponder/capability/PonderQuestionDefaultViewCapability.php
··· 1 - <?php 2 - 3 - final class PonderQuestionDefaultViewCapability 4 - extends PhabricatorPolicyCapability { 5 - 6 - const CAPABILITY = 'ponder.question.default.view'; 7 - 8 - public function getCapabilityName() { 9 - return pht('Default Question View Policy'); 10 - } 11 - 12 - public function shouldAllowPublicPolicySetting() { 13 - return true; 14 - } 15 - 16 - }
+26 -14
src/applications/ponder/controller/PonderAnswerSaveController.php
··· 19 19 return new Aphront404Response(); 20 20 } 21 21 22 - $answer = $request->getStr('answer'); 22 + $content = $request->getStr('answer'); 23 23 24 - if (!strlen(trim($answer))) { 24 + if (!strlen(trim($content))) { 25 25 $dialog = id(new AphrontDialogView()) 26 26 ->setUser($viewer) 27 27 ->setTitle(pht('Empty Answer')) ··· 32 32 return id(new AphrontDialogResponse())->setDialog($dialog); 33 33 } 34 34 35 - $content_source = PhabricatorContentSource::newForSource( 36 - PhabricatorContentSource::SOURCE_WEB, 37 - array( 38 - 'ip' => $request->getRemoteAddr(), 39 - )); 35 + $answer = PonderAnswer::initializeNewAnswer($viewer); 40 36 41 - $res = id(new PonderAnswer()) 42 - ->setAuthorPHID($viewer->getPHID()) 43 - ->setQuestionID($question->getID()) 44 - ->setContent($answer) 45 - ->setVoteCount(0) 46 - ->setContentSource($content_source); 37 + // Question Editor 47 38 48 39 $xactions = array(); 49 40 $xactions[] = id(new PonderQuestionTransaction()) ··· 51 42 ->setNewValue( 52 43 array( 53 44 '+' => array( 54 - array('answer' => $res), 45 + array('answer' => $answer), 55 46 ), 56 47 )); 57 48 ··· 60 51 ->setContentSourceFromRequest($request); 61 52 62 53 $editor->applyTransactions($question, $xactions); 54 + 55 + // Answer Editor 56 + 57 + $template = id(new PonderAnswerTransaction()); 58 + $xactions = array(); 59 + 60 + $xactions[] = id(clone $template) 61 + ->setTransactionType(PonderAnswerTransaction::TYPE_QUESTION_ID) 62 + ->setNewValue($question->getID()); 63 + 64 + $xactions[] = id(clone $template) 65 + ->setTransactionType(PonderAnswerTransaction::TYPE_CONTENT) 66 + ->setNewValue($content); 67 + 68 + $editor = id(new PonderAnswerEditor()) 69 + ->setActor($viewer) 70 + ->setContentSourceFromRequest($request) 71 + ->setContinueOnNoEffect(true); 72 + 73 + $editor->applyTransactions($answer, $xactions); 74 + 63 75 64 76 return id(new AphrontRedirectResponse())->setURI( 65 77 id(new PhutilURI('/Q'.$question->getID())));
-13
src/applications/ponder/controller/PonderQuestionEditController.php
··· 31 31 $v_title = $question->getTitle(); 32 32 $v_content = $question->getContent(); 33 33 $v_view = $question->getViewPolicy(); 34 - $v_edit = $question->getEditPolicy(); 35 34 $v_space = $question->getSpacePHID(); 36 35 $v_status = $question->getStatus(); 37 36 ··· 43 42 $v_content = $request->getStr('content'); 44 43 $v_projects = $request->getArr('projects'); 45 44 $v_view = $request->getStr('viewPolicy'); 46 - $v_edit = $request->getStr('editPolicy'); 47 45 $v_space = $request->getStr('spacePHID'); 48 46 $v_status = $request->getStr('status'); 49 47 ··· 75 73 $xactions[] = id(clone $template) 76 74 ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) 77 75 ->setNewValue($v_view); 78 - 79 - $xactions[] = id(clone $template) 80 - ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) 81 - ->setNewValue($v_edit); 82 76 83 77 $xactions[] = id(clone $template) 84 78 ->setTransactionType(PhabricatorTransactions::TYPE_SPACE) ··· 131 125 ->setPolicies($policies) 132 126 ->setValue($v_view) 133 127 ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) 134 - ->appendControl( 135 - id(new AphrontFormPolicyControl()) 136 - ->setName('editPolicy') 137 - ->setPolicyObject($question) 138 - ->setPolicies($policies) 139 - ->setValue($v_edit) 140 - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)) 141 128 ->appendChild( 142 129 id(new AphrontFormSelectControl()) 143 130 ->setLabel(pht('Status'))
+8
src/applications/ponder/editor/PonderAnswerEditor.php
··· 10 10 $types = parent::getTransactionTypes(); 11 11 12 12 $types[] = PhabricatorTransactions::TYPE_COMMENT; 13 + 13 14 $types[] = PonderAnswerTransaction::TYPE_CONTENT; 15 + $types[] = PonderAnswerTransaction::TYPE_QUESTION_ID; 14 16 15 17 return $types; 16 18 } ··· 22 24 switch ($xaction->getTransactionType()) { 23 25 case PonderAnswerTransaction::TYPE_CONTENT: 24 26 return $object->getContent(); 27 + case PonderAnswerTransaction::TYPE_QUESTION_ID: 28 + return $object->getQuestionID(); 25 29 } 26 30 } 27 31 ··· 31 35 32 36 switch ($xaction->getTransactionType()) { 33 37 case PonderAnswerTransaction::TYPE_CONTENT: 38 + case PonderAnswerTransaction::TYPE_QUESTION_ID: 34 39 return $xaction->getNewValue(); 35 40 } 36 41 } ··· 42 47 switch ($xaction->getTransactionType()) { 43 48 case PonderAnswerTransaction::TYPE_CONTENT: 44 49 $object->setContent($xaction->getNewValue()); 50 + break; 51 + case PonderAnswerTransaction::TYPE_QUESTION_ID: 52 + $object->setQuestionID($xaction->getNewValue()); 45 53 break; 46 54 } 47 55 }
+19 -15
src/applications/ponder/storage/PonderAnswer.php
··· 17 17 protected $questionID; 18 18 19 19 protected $content; 20 - protected $contentSource; 21 20 protected $mailKey; 22 21 23 22 protected $voteCount; ··· 26 25 private $comments; 27 26 28 27 private $userVotes = array(); 28 + 29 + public static function initializeNewAnswer(PhabricatorUser $actor) { 30 + $app = id(new PhabricatorApplicationQuery()) 31 + ->setViewer($actor) 32 + ->withClasses(array('PhabricatorPonderApplication')) 33 + ->executeOne(); 34 + 35 + return id(new PonderAnswer()) 36 + ->setQuestionID(0) 37 + ->setContent('') 38 + ->setAuthorPHID($actor->getPHID()) 39 + ->setVoteCount(0); 40 + 41 + } 29 42 30 43 public function attachQuestion(PonderQuestion $question = null) { 31 44 $this->question = $question; ··· 73 86 'voteCount' => 'sint32', 74 87 'content' => 'text', 75 88 'mailKey' => 'bytes20', 76 - 77 - // T6203/NULLABILITY 78 - // This should always exist. 79 - 'contentSource' => 'text?', 80 89 ), 81 90 self::CONFIG_KEY_SCHEMA => array( 82 91 'key_phid' => null, ··· 100 109 101 110 public function generatePHID() { 102 111 return PhabricatorPHID::generateNewPHID(PonderAnswerPHIDType::TYPECONST); 103 - } 104 - 105 - public function setContentSource(PhabricatorContentSource $content_source) { 106 - $this->contentSource = $content_source->serialize(); 107 - return $this; 108 - } 109 - 110 - public function getContentSource() { 111 - return PhabricatorContentSource::newFromSerialized($this->contentSource); 112 112 } 113 113 114 114 public function getMarkupField() { ··· 198 198 case PhabricatorPolicyCapability::CAN_VIEW: 199 199 return $this->getQuestion()->getPolicy($capability); 200 200 case PhabricatorPolicyCapability::CAN_EDIT: 201 - return PhabricatorPolicies::POLICY_NOONE; 201 + $app = PhabricatorApplication::getByClass( 202 + 'PhabricatorPonderApplication'); 203 + return $app->getPolicy(PonderModerateCapability::CAPABILITY); 202 204 } 203 205 } 204 206 ··· 224 226 case PhabricatorPolicyCapability::CAN_VIEW: 225 227 $out[] = pht( 226 228 'The user who asks a question can always view the answers.'); 229 + $out[] = pht( 230 + 'A moderator can always view the answers.'); 227 231 break; 228 232 } 229 233 return $out;
+39 -8
src/applications/ponder/storage/PonderAnswerTransaction.php
··· 4 4 extends PhabricatorApplicationTransaction { 5 5 6 6 const TYPE_CONTENT = 'ponder.answer:content'; 7 + const TYPE_QUESTION_ID = 'ponder.answer:question-id'; 7 8 8 9 public function getApplicationName() { 9 10 return 'ponder'; ··· 45 46 return $blocks; 46 47 } 47 48 49 + public function shouldHide() { 50 + switch ($this->getTransactionType()) { 51 + case self::TYPE_QUESTION_ID: 52 + return true; 53 + } 54 + return parent::shouldHide(); 55 + } 56 + 48 57 public function getTitle() { 49 58 $author_phid = $this->getAuthorPHID(); 50 59 $object_phid = $this->getObjectPHID(); 51 60 61 + $old = $this->getOldValue(); 62 + $new = $this->getNewValue(); 63 + 52 64 switch ($this->getTransactionType()) { 53 65 case self::TYPE_CONTENT: 54 - return pht( 55 - '%s edited %s.', 56 - $this->renderHandleLink($author_phid), 57 - $this->renderHandleLink($object_phid)); 66 + if ($old === '') { 67 + return pht( 68 + '%s added %s.', 69 + $this->renderHandleLink($author_phid), 70 + $this->renderHandleLink($object_phid)); 71 + } else { 72 + return pht( 73 + '%s edited %s.', 74 + $this->renderHandleLink($author_phid), 75 + $this->renderHandleLink($object_phid)); 76 + } 77 + break; 58 78 } 59 79 60 80 return parent::getTitle(); ··· 64 84 $author_phid = $this->getAuthorPHID(); 65 85 $object_phid = $this->getObjectPHID(); 66 86 87 + $old = $this->getOldValue(); 88 + $new = $this->getNewValue(); 89 + 67 90 switch ($this->getTransactionType()) { 68 91 case self::TYPE_CONTENT: 69 - return pht( 70 - '%s updated %s.', 71 - $this->renderHandleLink($author_phid), 72 - $this->renderHandleLink($object_phid)); 92 + if ($old === '') { 93 + return pht( 94 + '%s added %s.', 95 + $this->renderHandleLink($author_phid), 96 + $this->renderHandleLink($object_phid)); 97 + } else { 98 + return pht( 99 + '%s updated %s.', 100 + $this->renderHandleLink($author_phid), 101 + $this->renderHandleLink($object_phid)); 102 + } 103 + break; 73 104 } 74 105 75 106 return parent::getTitleForFeed();
+19 -7
src/applications/ponder/storage/PonderQuestion.php
··· 23 23 protected $content; 24 24 protected $contentSource; 25 25 protected $viewPolicy; 26 - protected $editPolicy; 27 26 protected $spacePHID; 28 27 29 28 protected $voteCount; ··· 44 43 ->executeOne(); 45 44 46 45 $view_policy = $app->getPolicy( 47 - PonderQuestionDefaultViewCapability::CAPABILITY); 48 - $edit_policy = $app->getPolicy( 49 - PonderQuestionDefaultEditCapability::CAPABILITY); 46 + PonderDefaultViewCapability::CAPABILITY); 50 47 51 48 return id(new PonderQuestion()) 52 49 ->setAuthorPHID($actor->getPHID()) 53 50 ->setViewPolicy($view_policy) 54 - ->setEditPolicy($edit_policy) 55 51 ->setStatus(PonderQuestionStatus::STATUS_OPEN) 56 52 ->setVoteCount(0) 57 53 ->setAnswerCount(0) ··· 275 271 case PhabricatorPolicyCapability::CAN_VIEW: 276 272 return $this->getViewPolicy(); 277 273 case PhabricatorPolicyCapability::CAN_EDIT: 278 - return $this->getEditPolicy(); 274 + $app = PhabricatorApplication::getByClass( 275 + 'PhabricatorPonderApplication'); 276 + return $app->getPolicy(PonderModerateCapability::CAPABILITY); 279 277 } 280 278 } 281 279 282 280 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 281 + if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { 282 + if (PhabricatorPolicyFilter::hasCapability( 283 + $viewer, $this, PhabricatorPolicyCapability::CAN_EDIT)) { 284 + return true; 285 + } 286 + } 283 287 return ($viewer->getPHID() == $this->getAuthorPHID()); 284 288 } 285 289 286 290 287 291 public function describeAutomaticCapability($capability) { 288 - return pht('The user who asked a question can always view and edit it.'); 292 + $out = array(); 293 + $out[] = pht('The user who asked a question can always view and edit it.'); 294 + switch ($capability) { 295 + case PhabricatorPolicyCapability::CAN_VIEW: 296 + $out[] = pht( 297 + 'A moderator can always view the question.'); 298 + break; 299 + } 300 + return $out; 289 301 } 290 302 291 303