1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-28 16:30:59 +01:00

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
This commit is contained in:
Chad Little 2015-08-08 12:20:01 -07:00
parent 22de31c56d
commit f98f5a081e
13 changed files with 137 additions and 78 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_ponder.ponder_answer
DROP COLUMN contentSource;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_ponder.ponder_question
DROP COLUMN editPolicy;

View file

@ -3402,11 +3402,11 @@ phutil_register_library_map(array(
'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php',
'PonderController' => 'applications/ponder/controller/PonderController.php', 'PonderController' => 'applications/ponder/controller/PonderController.php',
'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php',
'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php',
'PonderEditor' => 'applications/ponder/editor/PonderEditor.php', 'PonderEditor' => 'applications/ponder/editor/PonderEditor.php',
'PonderModerateCapability' => 'applications/ponder/capability/PonderModerateCapability.php',
'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php',
'PonderQuestionCommentController' => 'applications/ponder/controller/PonderQuestionCommentController.php', 'PonderQuestionCommentController' => 'applications/ponder/controller/PonderQuestionCommentController.php',
'PonderQuestionDefaultEditCapability' => 'applications/ponder/capability/PonderQuestionDefaultEditCapability.php',
'PonderQuestionDefaultViewCapability' => 'applications/ponder/capability/PonderQuestionDefaultViewCapability.php',
'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php', 'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php',
'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php', 'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php',
'PonderQuestionHasVotingUserEdgeType' => 'applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php', 'PonderQuestionHasVotingUserEdgeType' => 'applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php',
@ -7591,7 +7591,9 @@ phutil_register_library_map(array(
'PonderConstants' => 'Phobject', 'PonderConstants' => 'Phobject',
'PonderController' => 'PhabricatorController', 'PonderController' => 'PhabricatorController',
'PonderDAO' => 'PhabricatorLiskDAO', 'PonderDAO' => 'PhabricatorLiskDAO',
'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PonderEditor' => 'PhabricatorApplicationTransactionEditor', 'PonderEditor' => 'PhabricatorApplicationTransactionEditor',
'PonderModerateCapability' => 'PhabricatorPolicyCapability',
'PonderQuestion' => array( 'PonderQuestion' => array(
'PonderDAO', 'PonderDAO',
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
@ -7606,8 +7608,6 @@ phutil_register_library_map(array(
'PhabricatorSpacesInterface', 'PhabricatorSpacesInterface',
), ),
'PonderQuestionCommentController' => 'PonderController', 'PonderQuestionCommentController' => 'PonderController',
'PonderQuestionDefaultEditCapability' => 'PhabricatorPolicyCapability',
'PonderQuestionDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PonderQuestionEditController' => 'PonderController', 'PonderQuestionEditController' => 'PonderController',
'PonderQuestionEditor' => 'PonderEditor', 'PonderQuestionEditor' => 'PonderEditor',
'PonderQuestionHasVotingUserEdgeType' => 'PhabricatorEdgeType', 'PonderQuestionHasVotingUserEdgeType' => 'PhabricatorEdgeType',

View file

@ -93,11 +93,12 @@ final class PhabricatorPonderApplication extends PhabricatorApplication {
protected function getCustomCapabilities() { protected function getCustomCapabilities() {
return array( return array(
PonderQuestionDefaultViewCapability::CAPABILITY => array( PonderDefaultViewCapability::CAPABILITY => array(
'template' => PonderQuestionPHIDType::TYPECONST, 'template' => PonderQuestionPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW, 'capability' => PhabricatorPolicyCapability::CAN_VIEW,
), ),
PonderQuestionDefaultEditCapability::CAPABILITY => array( PonderModerateCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
'template' => PonderQuestionPHIDType::TYPECONST, 'template' => PonderQuestionPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT, 'capability' => PhabricatorPolicyCapability::CAN_EDIT,
), ),

View file

@ -1,12 +1,12 @@
<?php <?php
final class PonderQuestionDefaultViewCapability final class PonderDefaultViewCapability
extends PhabricatorPolicyCapability { extends PhabricatorPolicyCapability {
const CAPABILITY = 'ponder.question.default.view'; const CAPABILITY = 'ponder.default.view';
public function getCapabilityName() { public function getCapabilityName() {
return pht('Default Question View Policy'); return pht('Default View Policy');
} }
public function shouldAllowPublicPolicySetting() { public function shouldAllowPublicPolicySetting() {

View file

@ -0,0 +1,12 @@
<?php
final class PonderModerateCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'ponder.moderate';
public function getCapabilityName() {
return pht('Moderate Policy');
}
}

View file

@ -1,12 +0,0 @@
<?php
final class PonderQuestionDefaultEditCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'ponder.question.default.edit';
public function getCapabilityName() {
return pht('Default Question Edit Policy');
}
}

View file

@ -19,9 +19,9 @@ final class PonderAnswerSaveController extends PonderController {
return new Aphront404Response(); return new Aphront404Response();
} }
$answer = $request->getStr('answer'); $content = $request->getStr('answer');
if (!strlen(trim($answer))) { if (!strlen(trim($content))) {
$dialog = id(new AphrontDialogView()) $dialog = id(new AphrontDialogView())
->setUser($viewer) ->setUser($viewer)
->setTitle(pht('Empty Answer')) ->setTitle(pht('Empty Answer'))
@ -32,18 +32,9 @@ final class PonderAnswerSaveController extends PonderController {
return id(new AphrontDialogResponse())->setDialog($dialog); return id(new AphrontDialogResponse())->setDialog($dialog);
} }
$content_source = PhabricatorContentSource::newForSource( $answer = PonderAnswer::initializeNewAnswer($viewer);
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr(),
));
$res = id(new PonderAnswer()) // Question Editor
->setAuthorPHID($viewer->getPHID())
->setQuestionID($question->getID())
->setContent($answer)
->setVoteCount(0)
->setContentSource($content_source);
$xactions = array(); $xactions = array();
$xactions[] = id(new PonderQuestionTransaction()) $xactions[] = id(new PonderQuestionTransaction())
@ -51,7 +42,7 @@ final class PonderAnswerSaveController extends PonderController {
->setNewValue( ->setNewValue(
array( array(
'+' => array( '+' => array(
array('answer' => $res), array('answer' => $answer),
), ),
)); ));
@ -61,6 +52,27 @@ final class PonderAnswerSaveController extends PonderController {
$editor->applyTransactions($question, $xactions); $editor->applyTransactions($question, $xactions);
// Answer Editor
$template = id(new PonderAnswerTransaction());
$xactions = array();
$xactions[] = id(clone $template)
->setTransactionType(PonderAnswerTransaction::TYPE_QUESTION_ID)
->setNewValue($question->getID());
$xactions[] = id(clone $template)
->setTransactionType(PonderAnswerTransaction::TYPE_CONTENT)
->setNewValue($content);
$editor = id(new PonderAnswerEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
$editor->applyTransactions($answer, $xactions);
return id(new AphrontRedirectResponse())->setURI( return id(new AphrontRedirectResponse())->setURI(
id(new PhutilURI('/Q'.$question->getID()))); id(new PhutilURI('/Q'.$question->getID())));
} }

View file

@ -31,7 +31,6 @@ final class PonderQuestionEditController extends PonderController {
$v_title = $question->getTitle(); $v_title = $question->getTitle();
$v_content = $question->getContent(); $v_content = $question->getContent();
$v_view = $question->getViewPolicy(); $v_view = $question->getViewPolicy();
$v_edit = $question->getEditPolicy();
$v_space = $question->getSpacePHID(); $v_space = $question->getSpacePHID();
$v_status = $question->getStatus(); $v_status = $question->getStatus();
@ -43,7 +42,6 @@ final class PonderQuestionEditController extends PonderController {
$v_content = $request->getStr('content'); $v_content = $request->getStr('content');
$v_projects = $request->getArr('projects'); $v_projects = $request->getArr('projects');
$v_view = $request->getStr('viewPolicy'); $v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_space = $request->getStr('spacePHID'); $v_space = $request->getStr('spacePHID');
$v_status = $request->getStr('status'); $v_status = $request->getStr('status');
@ -76,10 +74,6 @@ final class PonderQuestionEditController extends PonderController {
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($v_view); ->setNewValue($v_view);
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit);
$xactions[] = id(clone $template) $xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_SPACE) ->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
->setNewValue($v_space); ->setNewValue($v_space);
@ -131,13 +125,6 @@ final class PonderQuestionEditController extends PonderController {
->setPolicies($policies) ->setPolicies($policies)
->setValue($v_view) ->setValue($v_view)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
->appendControl(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($question)
->setPolicies($policies)
->setValue($v_edit)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT))
->appendChild( ->appendChild(
id(new AphrontFormSelectControl()) id(new AphrontFormSelectControl())
->setLabel(pht('Status')) ->setLabel(pht('Status'))

View file

@ -10,7 +10,9 @@ final class PonderAnswerEditor extends PonderEditor {
$types = parent::getTransactionTypes(); $types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PonderAnswerTransaction::TYPE_CONTENT; $types[] = PonderAnswerTransaction::TYPE_CONTENT;
$types[] = PonderAnswerTransaction::TYPE_QUESTION_ID;
return $types; return $types;
} }
@ -22,6 +24,8 @@ final class PonderAnswerEditor extends PonderEditor {
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case PonderAnswerTransaction::TYPE_CONTENT: case PonderAnswerTransaction::TYPE_CONTENT:
return $object->getContent(); return $object->getContent();
case PonderAnswerTransaction::TYPE_QUESTION_ID:
return $object->getQuestionID();
} }
} }
@ -31,6 +35,7 @@ final class PonderAnswerEditor extends PonderEditor {
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case PonderAnswerTransaction::TYPE_CONTENT: case PonderAnswerTransaction::TYPE_CONTENT:
case PonderAnswerTransaction::TYPE_QUESTION_ID:
return $xaction->getNewValue(); return $xaction->getNewValue();
} }
} }
@ -43,6 +48,9 @@ final class PonderAnswerEditor extends PonderEditor {
case PonderAnswerTransaction::TYPE_CONTENT: case PonderAnswerTransaction::TYPE_CONTENT:
$object->setContent($xaction->getNewValue()); $object->setContent($xaction->getNewValue());
break; break;
case PonderAnswerTransaction::TYPE_QUESTION_ID:
$object->setQuestionID($xaction->getNewValue());
break;
} }
} }

View file

@ -17,7 +17,6 @@ final class PonderAnswer extends PonderDAO
protected $questionID; protected $questionID;
protected $content; protected $content;
protected $contentSource;
protected $mailKey; protected $mailKey;
protected $voteCount; protected $voteCount;
@ -27,6 +26,20 @@ final class PonderAnswer extends PonderDAO
private $userVotes = array(); private $userVotes = array();
public static function initializeNewAnswer(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorPonderApplication'))
->executeOne();
return id(new PonderAnswer())
->setQuestionID(0)
->setContent('')
->setAuthorPHID($actor->getPHID())
->setVoteCount(0);
}
public function attachQuestion(PonderQuestion $question = null) { public function attachQuestion(PonderQuestion $question = null) {
$this->question = $question; $this->question = $question;
return $this; return $this;
@ -73,10 +86,6 @@ final class PonderAnswer extends PonderDAO
'voteCount' => 'sint32', 'voteCount' => 'sint32',
'content' => 'text', 'content' => 'text',
'mailKey' => 'bytes20', 'mailKey' => 'bytes20',
// T6203/NULLABILITY
// This should always exist.
'contentSource' => 'text?',
), ),
self::CONFIG_KEY_SCHEMA => array( self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null, 'key_phid' => null,
@ -102,15 +111,6 @@ final class PonderAnswer extends PonderDAO
return PhabricatorPHID::generateNewPHID(PonderAnswerPHIDType::TYPECONST); return PhabricatorPHID::generateNewPHID(PonderAnswerPHIDType::TYPECONST);
} }
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source->serialize();
return $this;
}
public function getContentSource() {
return PhabricatorContentSource::newFromSerialized($this->contentSource);
}
public function getMarkupField() { public function getMarkupField() {
return self::MARKUP_FIELD_CONTENT; return self::MARKUP_FIELD_CONTENT;
} }
@ -198,7 +198,9 @@ final class PonderAnswer extends PonderDAO
case PhabricatorPolicyCapability::CAN_VIEW: case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getQuestion()->getPolicy($capability); return $this->getQuestion()->getPolicy($capability);
case PhabricatorPolicyCapability::CAN_EDIT: case PhabricatorPolicyCapability::CAN_EDIT:
return PhabricatorPolicies::POLICY_NOONE; $app = PhabricatorApplication::getByClass(
'PhabricatorPonderApplication');
return $app->getPolicy(PonderModerateCapability::CAPABILITY);
} }
} }
@ -224,6 +226,8 @@ final class PonderAnswer extends PonderDAO
case PhabricatorPolicyCapability::CAN_VIEW: case PhabricatorPolicyCapability::CAN_VIEW:
$out[] = pht( $out[] = pht(
'The user who asks a question can always view the answers.'); 'The user who asks a question can always view the answers.');
$out[] = pht(
'A moderator can always view the answers.');
break; break;
} }
return $out; return $out;

View file

@ -4,6 +4,7 @@ final class PonderAnswerTransaction
extends PhabricatorApplicationTransaction { extends PhabricatorApplicationTransaction {
const TYPE_CONTENT = 'ponder.answer:content'; const TYPE_CONTENT = 'ponder.answer:content';
const TYPE_QUESTION_ID = 'ponder.answer:question-id';
public function getApplicationName() { public function getApplicationName() {
return 'ponder'; return 'ponder';
@ -45,17 +46,36 @@ final class PonderAnswerTransaction
return $blocks; return $blocks;
} }
public function shouldHide() {
switch ($this->getTransactionType()) {
case self::TYPE_QUESTION_ID:
return true;
}
return parent::shouldHide();
}
public function getTitle() { public function getTitle() {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) { switch ($this->getTransactionType()) {
case self::TYPE_CONTENT: case self::TYPE_CONTENT:
if ($old === '') {
return pht(
'%s added %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht( return pht(
'%s edited %s.', '%s edited %s.',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $this->renderHandleLink($object_phid));
} }
break;
}
return parent::getTitle(); return parent::getTitle();
} }
@ -64,13 +84,24 @@ final class PonderAnswerTransaction
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) { switch ($this->getTransactionType()) {
case self::TYPE_CONTENT: case self::TYPE_CONTENT:
if ($old === '') {
return pht(
'%s added %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht( return pht(
'%s updated %s.', '%s updated %s.',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $this->renderHandleLink($object_phid));
} }
break;
}
return parent::getTitleForFeed(); return parent::getTitleForFeed();
} }

View file

@ -23,7 +23,6 @@ final class PonderQuestion extends PonderDAO
protected $content; protected $content;
protected $contentSource; protected $contentSource;
protected $viewPolicy; protected $viewPolicy;
protected $editPolicy;
protected $spacePHID; protected $spacePHID;
protected $voteCount; protected $voteCount;
@ -44,14 +43,11 @@ final class PonderQuestion extends PonderDAO
->executeOne(); ->executeOne();
$view_policy = $app->getPolicy( $view_policy = $app->getPolicy(
PonderQuestionDefaultViewCapability::CAPABILITY); PonderDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(
PonderQuestionDefaultEditCapability::CAPABILITY);
return id(new PonderQuestion()) return id(new PonderQuestion())
->setAuthorPHID($actor->getPHID()) ->setAuthorPHID($actor->getPHID())
->setViewPolicy($view_policy) ->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->setStatus(PonderQuestionStatus::STATUS_OPEN) ->setStatus(PonderQuestionStatus::STATUS_OPEN)
->setVoteCount(0) ->setVoteCount(0)
->setAnswerCount(0) ->setAnswerCount(0)
@ -275,17 +271,33 @@ final class PonderQuestion extends PonderDAO
case PhabricatorPolicyCapability::CAN_VIEW: case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy(); return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT: case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy(); $app = PhabricatorApplication::getByClass(
'PhabricatorPonderApplication');
return $app->getPolicy(PonderModerateCapability::CAPABILITY);
} }
} }
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
if (PhabricatorPolicyFilter::hasCapability(
$viewer, $this, PhabricatorPolicyCapability::CAN_EDIT)) {
return true;
}
}
return ($viewer->getPHID() == $this->getAuthorPHID()); return ($viewer->getPHID() == $this->getAuthorPHID());
} }
public function describeAutomaticCapability($capability) { public function describeAutomaticCapability($capability) {
return pht('The user who asked a question can always view and edit it.'); $out = array();
$out[] = pht('The user who asked a question can always view and edit it.');
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
$out[] = pht(
'A moderator can always view the question.');
break;
}
return $out;
} }