Page MenuHomePhabricator

D17453.id41964.diff
No OneTemporary

D17453.id41964.diff

diff --git a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php
--- a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php
+++ b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php
@@ -210,6 +210,8 @@
- `claim` //Optional bool.// By default, closing an unassigned task claims
it. You can set this to `false` to disable this behavior for a particular
status.
+ - `locked` //Optional bool.// Lock tasks in this status, preventing users
+ from commenting.
Statuses will appear in the UI in the order specified. Note the status marked
`special` as `duplicate` is not settable directly and will not appear in UI
diff --git a/src/applications/maniphest/constants/ManiphestTaskStatus.php b/src/applications/maniphest/constants/ManiphestTaskStatus.php
--- a/src/applications/maniphest/constants/ManiphestTaskStatus.php
+++ b/src/applications/maniphest/constants/ManiphestTaskStatus.php
@@ -156,6 +156,10 @@
return !self::isOpenStatus($status);
}
+ public static function isLockedStatus($status) {
+ return self::getStatusAttribute($status, 'locked', false);
+ }
+
public static function getStatusActionName($status) {
return self::getStatusAttribute($status, 'name.action');
}
@@ -277,6 +281,7 @@
'keywords' => 'optional list<string>',
'disabled' => 'optional bool',
'claim' => 'optional bool',
+ 'locked' => 'optional bool',
));
}
diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
--- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
@@ -257,6 +257,12 @@
$task,
PhabricatorPolicyCapability::CAN_EDIT);
+ $can_interact = PhabricatorPolicyFilter::canInteract($viewer, $task);
+
+ // We expect a policy dialog if you can't edit the task, and expect a
+ // lock override dialog if you can't interact with it.
+ $workflow_edit = (!$can_edit || !$can_interact);
+
$curtain = $this->newCurtainView($task);
$curtain->addAction(
@@ -265,7 +271,7 @@
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("/task/edit/{$id}/"))
->setDisabled(!$can_edit)
- ->setWorkflow(!$can_edit));
+ ->setWorkflow($workflow_edit));
$edit_config = $edit_engine->loadDefaultEditConfiguration($task);
$can_create = (bool)$edit_config;
diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php
--- a/src/applications/maniphest/storage/ManiphestTask.php
+++ b/src/applications/maniphest/storage/ManiphestTask.php
@@ -213,6 +213,10 @@
return ManiphestTaskStatus::isClosedStatus($this->getStatus());
}
+ public function isLocked() {
+ return ManiphestTaskStatus::isLockedStatus($this->getStatus());
+ }
+
public function setProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
@@ -343,6 +347,7 @@
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_INTERACT,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
@@ -351,6 +356,12 @@
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
+ case PhabricatorPolicyCapability::CAN_INTERACT:
+ if ($this->isLocked()) {
+ return PhabricatorPolicies::POLICY_NOONE;
+ } else {
+ return PhabricatorPolicies::POLICY_USER;
+ }
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
diff --git a/src/applications/policy/capability/PhabricatorPolicyCapability.php b/src/applications/policy/capability/PhabricatorPolicyCapability.php
--- a/src/applications/policy/capability/PhabricatorPolicyCapability.php
+++ b/src/applications/policy/capability/PhabricatorPolicyCapability.php
@@ -5,6 +5,7 @@
const CAN_VIEW = 'view';
const CAN_EDIT = 'edit';
const CAN_JOIN = 'join';
+ const CAN_INTERACT = 'interact';
/**
* Get the unique key identifying this capability. This key must be globally
diff --git a/src/applications/policy/filter/PhabricatorPolicyFilter.php b/src/applications/policy/filter/PhabricatorPolicyFilter.php
--- a/src/applications/policy/filter/PhabricatorPolicyFilter.php
+++ b/src/applications/policy/filter/PhabricatorPolicyFilter.php
@@ -86,6 +86,36 @@
return (count($result) == 1);
}
+ public static function canInteract(
+ PhabricatorUser $user,
+ PhabricatorPolicyInterface $object) {
+
+ $capabilities = $object->getCapabilities();
+ $capabilities = array_fuse($capabilities);
+
+ $can_interact = PhabricatorPolicyCapability::CAN_INTERACT;
+ $can_view = PhabricatorPolicyCapability::CAN_VIEW;
+
+ $require = array();
+
+ // If the object doesn't support a separate "Interact" capability, we
+ // only use the "View" capability: for most objects, you can interact
+ // with them if you can see them.
+ $require[] = $can_view;
+
+ if (isset($capabilities[$can_interact])) {
+ $require[] = $can_interact;
+ }
+
+ foreach ($require as $capability) {
+ if (!self::hasCapability($user, $object, $capability)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
public function setViewer(PhabricatorUser $user) {
$this->viewer = $user;
return $this;
diff --git a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php
--- a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php
+++ b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php
@@ -47,6 +47,19 @@
$handle->getURI());
}
+ if (!PhabricatorPolicyFilter::canInteract($viewer, $object)) {
+ if ($is_add) {
+ $message = pht('You can not subscribe to a locked object.');
+ } else {
+ $message = pht('You can not unsubscribe from a locked object.');
+ }
+
+ return $this->buildErrorResponse(
+ pht('Object Locked'),
+ $message,
+ $handle->getURI());
+ }
+
if ($object instanceof PhabricatorApplicationTransactionInterface) {
if ($is_add) {
$xaction_value = array(
diff --git a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php
--- a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php
+++ b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php
@@ -56,20 +56,24 @@
$subscribed = isset($edges[$src_phid][$edge_type][$user_phid]);
}
+ $can_interact = PhabricatorPolicyFilter::canInteract($user, $object);
+
if ($subscribed) {
$sub_action = id(new PhabricatorActionView())
->setWorkflow(true)
->setRenderAsForm(true)
->setHref('/subscriptions/delete/'.$object->getPHID().'/')
->setName(pht('Unsubscribe'))
- ->setIcon('fa-minus-circle');
+ ->setIcon('fa-minus-circle')
+ ->setDisabled(!$can_interact);
} else {
$sub_action = id(new PhabricatorActionView())
->setWorkflow(true)
->setRenderAsForm(true)
->setHref('/subscriptions/add/'.$object->getPHID().'/')
->setName(pht('Subscribe'))
- ->setIcon('fa-plus-circle');
+ ->setIcon('fa-plus-circle')
+ ->setDisabled(!$can_interact);
}
if (!$user->isLoggedIn()) {
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php
--- a/src/applications/transactions/editengine/PhabricatorEditEngine.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php
@@ -985,9 +985,31 @@
$fields = $this->buildEditFields($object);
$template = $object->getApplicationTransactionTemplate();
+ if ($this->getIsCreate()) {
+ $cancel_uri = $this->getObjectCreateCancelURI($object);
+ $submit_button = $this->getObjectCreateButtonText($object);
+ } else {
+ $cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
+ $submit_button = $this->getObjectEditButtonText($object);
+ }
+
$config = $this->getEditEngineConfiguration()
->attachEngine($this);
+ $can_interact = PhabricatorPolicyFilter::canInteract($viewer, $object);
+ if (!$can_interact &&
+ !$request->getBool('editEngine') &&
+ !$request->getBool('overrideLock')) {
+ return $this->getController()
+ ->newDialog()
+ ->setTitle(pht('Edit Locked Object'))
+ ->appendParagraph(pht('This object is locked. Edit it anyway?'))
+ ->addHiddenInput('overrideLock', true)
+ ->setDisableWorkflowOnSubmit(true)
+ ->addCancelButton($cancel_uri)
+ ->addSubmitButton(pht('Override Lock'));
+ }
+
$validation_exception = null;
if ($request->isFormPost() && $request->getBool('editEngine')) {
$submit_fields = $fields;
@@ -1154,14 +1176,6 @@
$form = $this->buildEditForm($object, $fields);
if ($request->isAjax()) {
- if ($this->getIsCreate()) {
- $cancel_uri = $this->getObjectCreateCancelURI($object);
- $submit_button = $this->getObjectCreateButtonText($object);
- } else {
- $cancel_uri = $this->getEffectiveObjectEditCancelURI($object);
- $submit_button = $this->getObjectEditButtonText($object);
- }
-
return $this->getController()
->newDialog()
->setWidth(AphrontDialogView::WIDTH_FULL)
@@ -1554,8 +1568,14 @@
}
$viewer = $this->getViewer();
- $object_phid = $object->getPHID();
+ $can_interact = PhabricatorPolicyFilter::canInteract($viewer, $object);
+ if (!$can_interact) {
+ return id(new PhabricatorApplicationTransactionCommentView())
+ ->setIsLocked(true);
+ }
+
+ $object_phid = $object->getPHID();
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
if ($is_serious) {
@@ -1761,6 +1781,14 @@
$config->getName()));
}
+ private function buildLockedObjectResponse($object) {
+ return $this->buildError(
+ $object,
+ pht('Object Locked'),
+ pht(
+ 'This object has been locked, so you can not comment on it.'));
+ }
+
private function buildCommentResponse($object) {
$viewer = $this->getViewer();
@@ -1775,6 +1803,11 @@
return new Aphront400Response();
}
+ $can_interact = PhabricatorPolicyFilter::canInteract($viewer, $object);
+ if (!$can_interact) {
+ return $this->buildLockedObjectResponse($object);
+ }
+
$config = $this->loadDefaultEditConfiguration($object);
if (!$config) {
return new Aphront404Response();
diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
--- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
+++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
@@ -22,6 +22,7 @@
private $noPermission;
private $fullWidth;
private $infoView;
+ private $isLocked;
private $currentVersion;
private $versionedDraft;
@@ -149,11 +150,20 @@
return $this->noPermission;
}
+ public function setIsLocked($is_locked) {
+ $this->isLocked = $is_locked;
+ return $this;
+ }
+
+ public function getIsLocked() {
+ return $this->isLocked;
+ }
+
public function setTransactionTimeline(
PhabricatorApplicationTransactionView $timeline) {
$timeline->setQuoteTargetID($this->getCommentID());
- if ($this->getNoPermission()) {
+ if ($this->getNoPermission() || $this->getIsLocked()) {
$timeline->setShouldTerminate(true);
}
@@ -166,6 +176,15 @@
return null;
}
+ if ($this->getIsLocked()) {
+ return id(new PHUIInfoView())
+ ->setSeverity(PHUIInfoView::SEVERITY_WARNING)
+ ->setErrors(
+ array(
+ pht('This object has been locked.'),
+ ));
+ }
+
$user = $this->getUser();
if (!$user->isLoggedIn()) {
$uri = id(new PhutilURI('/login/'))

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 17, 2:41 AM (6 d, 23 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7707946
Default Alt Text
D17453.id41964.diff (12 KB)

Event Timeline