public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView())->setViewer($viewer)->setActionList($this->newActions()); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions($viewer, $repository); $view_parts = array(); if (PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) { $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($repository); $view_parts[] = $viewer->renderHandle($space_phid); } $view_parts[] = $descriptions[PhabricatorPolicyCapability::CAN_VIEW]; $view->addProperty(pht('Visible To'), phutil_implode_html(" · ", $view_parts)); $view->addProperty(pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); $pushable = $repository->isHosted() ? $descriptions[DiffusionPushCapability::CAPABILITY] : phutil_tag('em', array(), pht('Not a Hosted Repository')); $view->addProperty(pht('Pushable By'), $pushable); return $this->newBox(pht('Policies'), $view); }
private function appendSpaceInformation(AphrontDialogView $dialog, PhabricatorPolicyInterface $object, PhabricatorPolicy $policy, $capability) { $viewer = $this->getViewer(); if (!$object instanceof PhabricatorSpacesInterface) { return; } if (!PhabricatorSpacesNamespaceQuery::getSpacesExist($viewer)) { return; } // NOTE: We're intentionally letting users through here, even if they only // have access to one space. The intent is to help users in "space jail" // understand who objects they create are visible to: $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($object); $handles = $viewer->loadHandles(array($space_phid)); $doc_href = PhabricatorEnv::getDoclink('Spaces User Guide'); $dialog->appendParagraph(array(pht('This object is in %s, and can only be seen or edited by users with ' . 'access to view objects in the space.', $handles[$space_phid]->renderLink()), ' ', phutil_tag('strong', array(), phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Learn More'))))); $spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($viewer); $space = idx($spaces, $space_phid); if (!$space) { return; } $space_policies = PhabricatorPolicyQuery::loadPolicies($viewer, $space); $space_policy = idx($space_policies, PhabricatorPolicyCapability::CAN_VIEW); if (!$space_policy) { return; } $space_explanation = PhabricatorPolicy::getPolicyExplanation($viewer, $space_policy->getPHID()); $items = array(); $items[] = $space_explanation; foreach ($items as $key => $item) { $items[$key] = phutil_tag('li', array(), $item); } $dialog->appendParagraph(pht('Users who can see objects in this space:')); $dialog->appendChild(phutil_tag('ul', array(), $items)); $view_capability = PhabricatorPolicyCapability::CAN_VIEW; if ($capability == $view_capability) { $stronger = $space_policy->isStrongerThan($policy); if ($stronger) { $dialog->appendParagraph(pht('The space this object is in has a more restrictive view ' . 'policy ("%s") than the object does ("%s"), so the space\'s ' . 'view policy is shown as a hint instead of the object policy.', $space_policy->getShortName(), $policy->getShortName())); } } $dialog->appendParagraph(pht('After a user passes space policy checks, they must still pass ' . 'object policy checks.')); }
public function render() { $object = $this->getObject(); $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($object); if (!$space_phid) { return null; } // If the viewer can't see spaces, pretend they don't exist. $viewer = $this->getUser(); if (!PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) { return null; } // If this is the default space, don't show a space label. $default = PhabricatorSpacesNamespaceQuery::getDefaultSpace(); if ($default) { if ($default->getPHID() == $space_phid) { return null; } } return phutil_tag('span', array('class' => 'spaces-name'), array($viewer->renderHandle($space_phid)->setUseShortName(true), ' | ')); }
protected final function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) { $viewer = $this->getActor(); $object = $this->getMailReceiver(); $app_email = $this->getApplicationEmail(); $is_new = !$object->getID(); // If this is a new object which implements the Spaces interface and was // created by sending mail to an ApplicationEmail address, put the object // in the same Space the address is in. if ($is_new) { if ($object instanceof PhabricatorSpacesInterface) { if ($app_email) { $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($app_email); $object->setSpacePHID($space_phid); } } } $body_data = $mail->parseBody(); $body = $body_data['body']; $body = $this->enhanceBodyWithAttachments($body, $mail->getAttachments()); $xactions = $this->didReceiveMail($mail, $body); // If this object is subscribable, subscribe all the users who were // recipients on the message. if ($object instanceof PhabricatorSubscribableInterface) { $subscriber_phids = $mail->loadAllRecipientPHIDs(); if ($subscriber_phids) { $xactions[] = $this->newTransaction()->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)->setNewValue(array('+' => $subscriber_phids)); } } $command_xactions = $this->processMailCommands($mail, $body_data['commands']); foreach ($command_xactions as $xaction) { $xactions[] = $xaction; } if ($this->shouldCreateCommentFromMailBody()) { $comment = $this->newTransaction()->getApplicationTransactionCommentObject()->setContent($body); $xactions[] = $this->newTransaction()->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)->attachComment($comment); } $target = $object->getApplicationTransactionObject(); $this->newEditor($mail)->setContinueOnNoEffect(true)->applyTransactions($target, $xactions); }
public function buildCustomEditFields(PhabricatorEditEngine $engine, PhabricatorApplicationTransactionInterface $object) { $viewer = $engine->getViewer(); $editor = $object->getApplicationTransactionEditor(); $types = $editor->getTransactionTypesForObject($object); $types = array_fuse($types); $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($object)->execute(); $map = array(PhabricatorTransactions::TYPE_VIEW_POLICY => array('key' => 'policy.view', 'aliases' => array('view'), 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 'label' => pht('View Policy'), 'description' => pht('Controls who can view the object.'), 'description.conduit' => pht('Change the view policy of the object.'), 'edit' => 'view'), PhabricatorTransactions::TYPE_EDIT_POLICY => array('key' => 'policy.edit', 'aliases' => array('edit'), 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 'label' => pht('Edit Policy'), 'description' => pht('Controls who can edit the object.'), 'description.conduit' => pht('Change the edit policy of the object.'), 'edit' => 'edit'), PhabricatorTransactions::TYPE_JOIN_POLICY => array('key' => 'policy.join', 'aliases' => array('join'), 'capability' => PhabricatorPolicyCapability::CAN_JOIN, 'label' => pht('Join Policy'), 'description' => pht('Controls who can join the object.'), 'description.conduit' => pht('Change the join policy of the object.'), 'edit' => 'join')); $fields = array(); foreach ($map as $type => $spec) { if (empty($types[$type])) { continue; } $capability = $spec['capability']; $key = $spec['key']; $aliases = $spec['aliases']; $label = $spec['label']; $description = $spec['description']; $conduit_description = $spec['description.conduit']; $edit = $spec['edit']; $policy_field = id(new PhabricatorPolicyEditField())->setKey($key)->setLabel($label)->setAliases($aliases)->setIsCopyable(true)->setCapability($capability)->setPolicies($policies)->setTransactionType($type)->setEditTypeKey($edit)->setDescription($description)->setConduitDescription($conduit_description)->setConduitTypeDescription(pht('New policy PHID or constant.'))->setValue($object->getPolicy($capability)); $fields[] = $policy_field; if ($object instanceof PhabricatorSpacesInterface) { if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { $type_space = PhabricatorTransactions::TYPE_SPACE; if (isset($types[$type_space])) { $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($object); $space_field = id(new PhabricatorSpaceEditField())->setKey('spacePHID')->setLabel(pht('Space'))->setEditTypeKey('space')->setIsCopyable(true)->setIsLockable(false)->setIsReorderable(false)->setAliases(array('space', 'policy.space'))->setTransactionType($type_space)->setDescription(pht('Select a space for the object.'))->setConduitDescription(pht('Shift the object between spaces.'))->setConduitTypeDescription(pht('New space PHID.'))->setValue($space_phid); $fields[] = $space_field; $space_field->setPolicyField($policy_field); $policy_field->setSpaceField($space_field); } } } } return $fields; }
private function buildSpaceSection(PhabricatorPolicyInterface $object, PhabricatorPolicy $policy, $capability) { $viewer = $this->getViewer(); if (!$object instanceof PhabricatorSpacesInterface) { return null; } if (!PhabricatorSpacesNamespaceQuery::getSpacesExist($viewer)) { return null; } $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($object); $spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($viewer); $space = idx($spaces, $space_phid); if (!$space) { return null; } $space_policies = PhabricatorPolicyQuery::loadPolicies($viewer, $space); $space_policy = idx($space_policies, PhabricatorPolicyCapability::CAN_VIEW); if (!$space_policy) { return null; } $doc_href = PhabricatorEnv::getDoclink('Spaces User Guide'); $capability_name = $this->getCapabilityName($capability); $space_section = id(new PHUIPolicySectionView())->setViewer($viewer)->setIcon('fa-th-large bluegrey')->setHeader(pht('Space'))->setDocumentationLink(pht('Spaces Documentation'), $doc_href)->appendList(array(array(phutil_tag('strong', array(), pht('Space:')), ' ', $viewer->renderHandle($space_phid)->setAsTag(true)), array(phutil_tag('strong', array(), pht('%s:', $capability_name)), ' ', $space_policy->getShortName())))->appendParagraph(pht('This object is in %s and can only be seen or edited by users ' . 'with access to view objects in the space.', $viewer->renderHandle($space_phid))); $space_explanation = PhabricatorPolicy::getPolicyExplanation($viewer, $space_policy->getPHID()); $items = array(); $items[] = $space_explanation; $space_section->appendParagraph(pht('Users who can see objects in this space:'))->appendList($items); $view_capability = PhabricatorPolicyCapability::CAN_VIEW; if ($capability == $view_capability) { $stronger = $space_policy->isStrongerThan($policy); if ($stronger) { $space_section->appendHint(pht('The space this object is in has a more restrictive view ' . 'policy ("%s") than the object does ("%s"), so the space\'s ' . 'view policy is shown as a hint instead of the object policy.', $space_policy->getShortName(), $policy->getShortName())); } } $space_section->appendHint(pht('After a user passes space policy checks, they must still pass ' . 'object policy checks.')); return $space_section; }
private function renderPolicyProperty(PhabricatorPolicyInterface $object) { $viewer = $this->getUser(); $policies = PhabricatorPolicyQuery::loadPolicies($viewer, $object); $view_capability = PhabricatorPolicyCapability::CAN_VIEW; $policy = idx($policies, $view_capability); if (!$policy) { return null; } // If an object is in a Space with a strictly stronger (more restrictive) // policy, we show the more restrictive policy. This better aligns the // UI hint with the actual behavior. // NOTE: We'll do this even if the viewer has access to only one space, and // show them information about the existence of spaces if they click // through. $use_space_policy = false; if ($object instanceof PhabricatorSpacesInterface) { $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($object); $spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($viewer); $space = idx($spaces, $space_phid); if ($space) { $space_policies = PhabricatorPolicyQuery::loadPolicies($viewer, $space); $space_policy = idx($space_policies, $view_capability); if ($space_policy) { if ($space_policy->isStrongerThan($policy)) { $policy = $space_policy; $use_space_policy = true; } } } } $container_classes = array(); $container_classes[] = 'policy-header-callout'; $phid = $object->getPHID(); // If we're going to show the object policy, try to determine if the object // policy differs from the default policy. If it does, we'll call it out // as changed. if (!$use_space_policy) { $default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject($viewer, $object, $view_capability); if ($default_policy) { if ($default_policy->getPHID() != $policy->getPHID()) { $container_classes[] = 'policy-adjusted'; if ($default_policy->isStrongerThan($policy)) { // The policy has strictly been weakened. For example, the // default might be "All Users" and the current policy is "Public". $container_classes[] = 'policy-adjusted-weaker'; } else { if ($policy->isStrongerThan($default_policy)) { // The policy has strictly been strengthened, and is now more // restrictive than the default. For example, "All Users" has // been replaced with "No One". $container_classes[] = 'policy-adjusted-stronger'; } else { // The policy has been adjusted but not strictly strengthened // or weakened. For example, "Members of X" has been replaced with // "Members of Y". $container_classes[] = 'policy-adjusted-different'; } } } } } $icon = id(new PHUIIconView())->setIcon($policy->getIcon() . ' bluegrey'); $link = javelin_tag('a', array('class' => 'policy-link', 'href' => '/policy/explain/' . $phid . '/' . $view_capability . '/', 'sigil' => 'workflow'), $policy->getShortName()); return phutil_tag('span', array('class' => implode(' ', $container_classes)), array($icon, $link)); }
private function buildEmailTable($is_edit, $highlight) { $viewer = $this->getViewer(); $application = $this->getApplication(); $uri = new PhutilURI($this->getPanelURI()); $emails = id(new PhabricatorMetaMTAApplicationEmailQuery())->setViewer($viewer)->withApplicationPHIDs(array($application->getPHID()))->execute(); $rowc = array(); $rows = array(); foreach ($emails as $email) { $button_edit = javelin_tag('a', array('class' => 'button small grey', 'href' => $uri->alter('edit', $email->getID()), 'sigil' => 'workflow'), pht('Edit')); $button_remove = javelin_tag('a', array('class' => 'button small grey', 'href' => $uri->alter('delete', $email->getID()), 'sigil' => 'workflow'), pht('Delete')); if ($highlight == $email->getID()) { $rowc[] = 'highlighted'; } else { $rowc[] = null; } $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($email); if ($space_phid) { $email_space = $viewer->renderHandle($space_phid); } else { $email_space = null; } $rows[] = array($email_space, $email->getAddress(), $button_edit, $button_remove); } $table = id(new AphrontTableView($rows))->setNoDataString(pht('No application emails created yet.')); $table->setHeaders(array(pht('Space'), pht('Email'), pht('Edit'), pht('Delete'))); $table->setColumnClasses(array('', 'wide', 'action', 'action')); $table->setRowClasses($rowc); $table->setColumnVisibility(array(PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer), true, $is_edit, $is_edit)); return $table; }
public function getHeraldField($field_name) { switch ($field_name) { case self::FIELD_RULE: return null; case self::FIELD_CONTENT_SOURCE: return $this->getContentSource()->getSource(); case self::FIELD_ALWAYS: return true; case self::FIELD_IS_NEW_OBJECT: return $this->getIsNewObject(); case self::FIELD_CC: $object = $this->getObject(); if (!$object instanceof PhabricatorSubscribableInterface) { throw new Exception(pht('Adapter object (of class "%s") does not implement interface ' . '"%s", so the subscribers field value can not be determined.', get_class($object), 'PhabricatorSubscribableInterface')); } $phid = $object->getPHID(); return PhabricatorSubscribersQuery::loadSubscribersForPHID($phid); case self::FIELD_APPLICATION_EMAIL: $value = array(); // while there is only one match by implementation, we do set // comparisons on phids, so return an array with just the phid if ($this->getApplicationEmail()) { $value[] = $this->getApplicationEmail()->getPHID(); } return $value; case self::FIELD_SPACE: $object = $this->getObject(); if (!$object instanceof PhabricatorSpacesInterface) { throw new Exception(pht('Adapter object (of class "%s") does not implement interface ' . '"%s", so the Space field value can not be determined.', get_class($object), 'PhabricatorSpacesInterface')); } return PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($object); default: if ($this->isHeraldCustomKey($field_name)) { return $this->getCustomFieldValue($field_name); } throw new Exception(pht("Unknown field '%s'!", $field_name)); } }
public function getHeraldFieldValue($object) { return PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($object); }
private function buildTransactions($actions, ManiphestTask $task) { $value_map = array(); $type_map = array('add_comment' => PhabricatorTransactions::TYPE_COMMENT, 'assign' => ManiphestTransaction::TYPE_OWNER, 'status' => ManiphestTransaction::TYPE_STATUS, 'priority' => ManiphestTransaction::TYPE_PRIORITY, 'add_project' => PhabricatorTransactions::TYPE_EDGE, 'remove_project' => PhabricatorTransactions::TYPE_EDGE, 'add_ccs' => PhabricatorTransactions::TYPE_SUBSCRIBERS, 'remove_ccs' => PhabricatorTransactions::TYPE_SUBSCRIBERS, 'space' => PhabricatorTransactions::TYPE_SPACE); $edge_edit_types = array('add_project' => true, 'remove_project' => true, 'add_ccs' => true, 'remove_ccs' => true); $xactions = array(); foreach ($actions as $action) { if (empty($type_map[$action['action']])) { throw new Exception(pht("Unknown batch edit action '%s'!", $action)); } $type = $type_map[$action['action']]; // Figure out the current value, possibly after modifications by other // batch actions of the same type. For example, if the user chooses to // "Add Comment" twice, we should add both comments. More notably, if the // user chooses "Remove Project..." and also "Add Project...", we should // avoid restoring the removed project in the second transaction. if (array_key_exists($type, $value_map)) { $current = $value_map[$type]; } else { switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: $current = null; break; case ManiphestTransaction::TYPE_OWNER: $current = $task->getOwnerPHID(); break; case ManiphestTransaction::TYPE_STATUS: $current = $task->getStatus(); break; case ManiphestTransaction::TYPE_PRIORITY: $current = $task->getPriority(); break; case PhabricatorTransactions::TYPE_EDGE: $current = $task->getProjectPHIDs(); break; case PhabricatorTransactions::TYPE_SUBSCRIBERS: $current = $task->getSubscriberPHIDs(); break; case PhabricatorTransactions::TYPE_SPACE: $current = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID($task); break; } } // Check if the value is meaningful / provided, and normalize it if // necessary. This discards, e.g., empty comments and empty owner // changes. $value = $action['value']; switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: if (!strlen($value)) { continue 2; } break; case PhabricatorTransactions::TYPE_SPACE: if (empty($value)) { continue 2; } $value = head($value); break; case ManiphestTransaction::TYPE_OWNER: if (empty($value)) { continue 2; } $value = head($value); $no_owner = PhabricatorPeopleNoOwnerDatasource::FUNCTION_TOKEN; if ($value === $no_owner) { $value = null; } break; case PhabricatorTransactions::TYPE_EDGE: if (empty($value)) { continue 2; } break; case PhabricatorTransactions::TYPE_SUBSCRIBERS: if (empty($value)) { continue 2; } break; } // If the edit doesn't change anything, go to the next action. This // check is only valid for changes like "owner", "status", etc, not // for edge edits, because we should still apply an edit like // "Remove Projects: A, B" to a task with projects "A, B". if (empty($edge_edit_types[$action['action']])) { if ($value == $current) { continue; } } // Apply the value change; for most edits this is just replacement, but // some need to merge the current and edited values (add/remove project). switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: if (strlen($current)) { $value = $current . "\n\n" . $value; } break; case PhabricatorTransactions::TYPE_EDGE: $is_remove = $action['action'] == 'remove_project'; $current = array_fill_keys($current, true); $value = array_fill_keys($value, true); $new = $current; $did_something = false; if ($is_remove) { foreach ($value as $phid => $ignored) { if (isset($new[$phid])) { unset($new[$phid]); $did_something = true; } } } else { foreach ($value as $phid => $ignored) { if (empty($new[$phid])) { $new[$phid] = true; $did_something = true; } } } if (!$did_something) { continue 2; } $value = array_keys($new); break; case PhabricatorTransactions::TYPE_SUBSCRIBERS: $is_remove = $action['action'] == 'remove_ccs'; $current = array_fill_keys($current, true); $new = array(); $did_something = false; if ($is_remove) { foreach ($value as $phid) { if (isset($current[$phid])) { $new[$phid] = true; $did_something = true; } } if ($new) { $value = array('-' => array_keys($new)); } } else { $new = array(); foreach ($value as $phid) { $new[$phid] = true; $did_something = true; } if ($new) { $value = array('+' => array_keys($new)); } } if (!$did_something) { continue 2; } break; } $value_map[$type] = $value; } $template = new ManiphestTransaction(); foreach ($value_map as $type => $value) { $xaction = clone $template; $xaction->setTransactionType($type); switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: $xaction->attachComment(id(new ManiphestTransactionComment())->setContent($value)); break; case PhabricatorTransactions::TYPE_EDGE: $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $xaction->setMetadataValue('edge:type', $project_type)->setNewValue(array('=' => array_fuse($value))); break; default: $xaction->setNewValue($value); break; } $xactions[] = $xaction; } return $xactions; }