mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 12:52:42 +01:00
Support Spaces transactions
Summary: Ref T8424. This adds crude integration with Paste's edit/view workflows: you can change the space a Paste appears in, see transactions, and get a policy callout. Lots of rough edges and non-obviousness but it pretty much works. Test Plan: - Created and updated Pastes. - Moved them between spaces, saw policy effects. - Read transactions. - Looked at feed. - Faked query to return no spaces, saw control and other stuff vanish. - Faked query to return no spaces, created pastes. - Tried to submit bad values and got errors. Reviewers: chad, btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T8424 Differential Revision: https://secure.phabricator.com/D13159
This commit is contained in:
parent
5deaeec668
commit
ef90007a21
13 changed files with 306 additions and 79 deletions
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => 'e2460e8f',
|
||||
'core.pkg.css' => 'd7ecac6d',
|
||||
'core.pkg.js' => '3bbe23c6',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '02273347',
|
||||
|
@ -136,7 +136,7 @@ return array(
|
|||
'rsrc/css/phui/phui-fontkit.css' => 'dd8ddf27',
|
||||
'rsrc/css/phui/phui-form-view.css' => '808329f2',
|
||||
'rsrc/css/phui/phui-form.css' => '25876baf',
|
||||
'rsrc/css/phui/phui-header-view.css' => '75aaf372',
|
||||
'rsrc/css/phui/phui-header-view.css' => '2dd74fe0',
|
||||
'rsrc/css/phui/phui-icon.css' => 'bc766998',
|
||||
'rsrc/css/phui/phui-image-mask.css' => '5a8b09c8',
|
||||
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
|
||||
|
@ -779,7 +779,7 @@ return array(
|
|||
'phui-fontkit-css' => 'dd8ddf27',
|
||||
'phui-form-css' => '25876baf',
|
||||
'phui-form-view-css' => '808329f2',
|
||||
'phui-header-view-css' => '75aaf372',
|
||||
'phui-header-view-css' => '2dd74fe0',
|
||||
'phui-icon-view-css' => 'bc766998',
|
||||
'phui-image-mask-css' => '5a8b09c8',
|
||||
'phui-info-panel-css' => '27ea50a1',
|
||||
|
|
|
@ -2560,6 +2560,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSpacesCapabilityCreateSpaces' => 'applications/spaces/capability/PhabricatorSpacesCapabilityCreateSpaces.php',
|
||||
'PhabricatorSpacesCapabilityDefaultEdit' => 'applications/spaces/capability/PhabricatorSpacesCapabilityDefaultEdit.php',
|
||||
'PhabricatorSpacesCapabilityDefaultView' => 'applications/spaces/capability/PhabricatorSpacesCapabilityDefaultView.php',
|
||||
'PhabricatorSpacesControl' => 'applications/spaces/view/PhabricatorSpacesControl.php',
|
||||
'PhabricatorSpacesController' => 'applications/spaces/controller/PhabricatorSpacesController.php',
|
||||
'PhabricatorSpacesDAO' => 'applications/spaces/storage/PhabricatorSpacesDAO.php',
|
||||
'PhabricatorSpacesEditController' => 'applications/spaces/controller/PhabricatorSpacesEditController.php',
|
||||
|
@ -6041,6 +6042,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSpacesCapabilityCreateSpaces' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorSpacesCapabilityDefaultEdit' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorSpacesCapabilityDefaultView' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorSpacesControl' => 'AphrontFormControl',
|
||||
'PhabricatorSpacesController' => 'PhabricatorController',
|
||||
'PhabricatorSpacesDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorSpacesEditController' => 'PhabricatorSpacesController',
|
||||
|
|
|
@ -57,13 +57,12 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
}
|
||||
}
|
||||
|
||||
$text = null;
|
||||
$e_text = true;
|
||||
$errors = array();
|
||||
$v_space = $paste->getSpacePHID();
|
||||
if ($is_create && $parent) {
|
||||
$v_title = pht('Fork of %s', $parent->getFullName());
|
||||
$v_language = $parent->getLanguage();
|
||||
$v_text = $parent->getRawContent();
|
||||
$v_space = $parent->getSpacePHID();
|
||||
} else {
|
||||
$v_title = $paste->getTitle();
|
||||
$v_language = $paste->getLanguage();
|
||||
|
@ -81,27 +80,21 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
$v_projects = array_reverse($v_projects);
|
||||
}
|
||||
|
||||
$validation_exception = null;
|
||||
if ($request->isFormPost()) {
|
||||
$xactions = array();
|
||||
|
||||
$v_text = $request->getStr('text');
|
||||
if (!strlen($v_text)) {
|
||||
$e_text = pht('Required');
|
||||
$errors[] = pht('The paste may not be blank.');
|
||||
} else {
|
||||
$e_text = null;
|
||||
}
|
||||
|
||||
$v_title = $request->getStr('title');
|
||||
$v_language = $request->getStr('language');
|
||||
$v_view_policy = $request->getStr('can_view');
|
||||
$v_edit_policy = $request->getStr('can_edit');
|
||||
$v_projects = $request->getArr('projects');
|
||||
$v_space = $request->getStr('spacePHID');
|
||||
|
||||
// NOTE: The author is the only editor and can always view the paste,
|
||||
// so it's impossible for them to choose an invalid policy.
|
||||
|
||||
if (!$errors) {
|
||||
if ($is_create || ($v_text !== $paste->getRawContent())) {
|
||||
$file = PhabricatorPasteEditor::initializeFileForPaste(
|
||||
$user,
|
||||
|
@ -125,6 +118,9 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
||||
->setNewValue($v_edit_policy);
|
||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
|
||||
->setNewValue($v_space);
|
||||
|
||||
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||
|
@ -136,13 +132,12 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
->setActor($user)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
try {
|
||||
$xactions = $editor->applyTransactions($paste, $xactions);
|
||||
return id(new AphrontRedirectResponse())->setURI($paste->getURI());
|
||||
} else {
|
||||
// make sure we update policy so its correctly populated to what
|
||||
// the user chose
|
||||
$paste->setViewPolicy($v_view_policy);
|
||||
$paste->setEditPolicy($v_edit_policy);
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,12 +167,19 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
->setObject($paste)
|
||||
->execute();
|
||||
|
||||
$form->appendControl(
|
||||
id(new PhabricatorSpacesControl())
|
||||
->setObject($paste)
|
||||
->setValue($v_space)
|
||||
->setName('spacePHID'));
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setUser($user)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
|
||||
->setPolicyObject($paste)
|
||||
->setPolicies($policies)
|
||||
->setValue($v_view_policy)
|
||||
->setName('can_view'));
|
||||
|
||||
$form->appendChild(
|
||||
|
@ -186,6 +188,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicyObject($paste)
|
||||
->setPolicies($policies)
|
||||
->setValue($v_edit_policy)
|
||||
->setName('can_edit'));
|
||||
|
||||
$form->appendControl(
|
||||
|
@ -199,7 +202,6 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel(pht('Text'))
|
||||
->setError($e_text)
|
||||
->setValue($v_text)
|
||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||
->setCustomClass('PhabricatorMonospaced')
|
||||
|
@ -222,9 +224,12 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($title)
|
||||
->setFormErrors($errors)
|
||||
->setForm($form);
|
||||
|
||||
if ($validation_exception) {
|
||||
$form_box->setValidationException($validation_exception);
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
|
||||
if (!$is_create) {
|
||||
$crumbs->addTextCrumb('P'.$paste->getID(), '/P'.$paste->getID());
|
||||
|
|
|
@ -42,7 +42,11 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
|||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/P'.$this->getID();
|
||||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
public function getMonogram() {
|
||||
return 'P'.$this->getID();
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
|
|
|
@ -751,6 +751,20 @@ final class PhabricatorUser
|
|||
$email->getUserPHID());
|
||||
}
|
||||
|
||||
public function getDefaultSpacePHID() {
|
||||
// TODO: We might let the user switch which space they're "in" later on;
|
||||
// for now just use the global space if one exists.
|
||||
|
||||
$spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($this);
|
||||
foreach ($spaces as $space) {
|
||||
if ($space->getIsDefaultNamespace()) {
|
||||
return $space->getPHID();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grant a user a source of authority, to let them bypass policy checks they
|
||||
|
|
|
@ -15,7 +15,7 @@ final class PhabricatorSpacesApplication extends PhabricatorApplication {
|
|||
}
|
||||
|
||||
public function getFontIcon() {
|
||||
return 'fa-compass';
|
||||
return 'fa-th-large';
|
||||
}
|
||||
|
||||
public function getTitleGlyph() {
|
||||
|
|
|
@ -28,7 +28,10 @@ final class PhabricatorSpacesNamespacePHIDType
|
|||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$namespace = $objects[$phid];
|
||||
$monogram = $namespace->getMonogram();
|
||||
|
||||
$handle->setName($namespace->getNamespaceName());
|
||||
$handle->setURI('/'.$monogram);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
49
src/applications/spaces/view/PhabricatorSpacesControl.php
Normal file
49
src/applications/spaces/view/PhabricatorSpacesControl.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSpacesControl extends AphrontFormControl {
|
||||
|
||||
private $object;
|
||||
|
||||
protected function shouldRender() {
|
||||
// Render this control only if some Spaces exist.
|
||||
return PhabricatorSpacesNamespaceQuery::getAllSpaces();
|
||||
}
|
||||
|
||||
public function setObject(PhabricatorSpacesInterface $object) {
|
||||
$this->object = $object;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getCustomControlClass() {
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function getOptions() {
|
||||
$viewer = $this->getUser();
|
||||
$viewer_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($viewer);
|
||||
|
||||
$map = mpull($viewer_spaces, 'getNamespaceName', 'getPHID');
|
||||
asort($map);
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
public function renderInput() {
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$this->setLabel(pht('Space'));
|
||||
|
||||
$value = $this->getValue();
|
||||
if ($value === null) {
|
||||
$value = $viewer->getDefaultSpacePHID();
|
||||
}
|
||||
|
||||
return AphrontFormSelectControl::renderSelectTag(
|
||||
$value,
|
||||
$this->getOptions(),
|
||||
array(
|
||||
'name' => $this->getName(),
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,7 @@ final class PhabricatorTransactions {
|
|||
const TYPE_BUILDABLE = 'harbormaster:buildable';
|
||||
const TYPE_TOKEN = 'token:give';
|
||||
const TYPE_INLINESTATE = 'core:inlinestate';
|
||||
const TYPE_SPACE = 'core:space';
|
||||
|
||||
const COLOR_RED = 'red';
|
||||
const COLOR_ORANGE = 'orange';
|
||||
|
|
|
@ -264,6 +264,10 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
$types[] = PhabricatorTransactions::TYPE_EDGE;
|
||||
}
|
||||
|
||||
if ($this->object instanceof PhabricatorSpacesInterface) {
|
||||
$types[] = PhabricatorTransactions::TYPE_SPACE;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
|
@ -292,6 +296,21 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return $object->getEditPolicy();
|
||||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
return $object->getJoinPolicy();
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
$space_phid = $object->getSpacePHID();
|
||||
if ($space_phid === null) {
|
||||
if ($this->getIsNewObject()) {
|
||||
// In this case, just return `null` so we know this is the initial
|
||||
// transaction and it should be hidden.
|
||||
return null;
|
||||
}
|
||||
|
||||
$default_space = PhabricatorSpacesNamespaceQuery::getDefaultSpace();
|
||||
if ($default_space) {
|
||||
$space_phid = $default_space->getPHID();
|
||||
}
|
||||
}
|
||||
return $space_phid;
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$edge_type = $xaction->getMetadataValue('edge:type');
|
||||
if (!$edge_type) {
|
||||
|
@ -337,7 +356,16 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
case PhabricatorTransactions::TYPE_BUILDABLE:
|
||||
case PhabricatorTransactions::TYPE_TOKEN:
|
||||
case PhabricatorTransactions::TYPE_INLINESTATE:
|
||||
return $xaction->getNewValue();
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
$space_phid = $xaction->getNewValue();
|
||||
if (!strlen($space_phid)) {
|
||||
// If an install has no Spaces, we might end up with the empty string
|
||||
// here instead of a strict `null`. Just make this work like callers
|
||||
// might reasonably expect.
|
||||
return null;
|
||||
} else {
|
||||
return $space_phid;
|
||||
}
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return $this->getEdgeTransactionNewValue($xaction);
|
||||
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||
|
@ -437,6 +465,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||
case PhabricatorTransactions::TYPE_INLINESTATE:
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
return $this->applyBuiltinInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
@ -485,6 +514,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
case PhabricatorTransactions::TYPE_INLINESTATE:
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
return $this->applyBuiltinExternalTransaction($object, $xaction);
|
||||
}
|
||||
|
@ -537,6 +567,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
$object->setJoinPolicy($xaction->getNewValue());
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
$object->setSpacePHID($xaction->getNewValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1190,18 +1223,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
PhabricatorPolicyCapability::CAN_VIEW);
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$actor,
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$actor,
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$actor,
|
||||
$object,
|
||||
|
@ -1882,6 +1906,12 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
$type,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
$errors[] = $this->validateSpaceTransactions(
|
||||
$object,
|
||||
$xactions,
|
||||
$type);
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||
$groups = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
|
@ -1968,6 +1998,52 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return $errors;
|
||||
}
|
||||
|
||||
|
||||
private function validateSpaceTransactions(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions,
|
||||
$transaction_type) {
|
||||
$errors = array();
|
||||
|
||||
$all_spaces = PhabricatorSpacesNamespaceQuery::getAllSpaces();
|
||||
$viewer_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces(
|
||||
$this->getActor());
|
||||
foreach ($xactions as $xaction) {
|
||||
$space_phid = $xaction->getNewValue();
|
||||
|
||||
if ($space_phid === null) {
|
||||
if (!$all_spaces) {
|
||||
// The install doesn't have any spaces, so this is fine.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The install has some spaces, so every object needs to be put
|
||||
// in a valid space.
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$transaction_type,
|
||||
pht('Invalid'),
|
||||
pht('You must choose a space for this object.'),
|
||||
$xaction);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the PHID isn't `null`, it needs to be a valid space that the
|
||||
// viewer can see.
|
||||
if (empty($viewer_spaces[$space_phid])) {
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$transaction_type,
|
||||
pht('Invalid'),
|
||||
pht(
|
||||
'You can not shift this object in the selected space, because '.
|
||||
'the space does not exist or you do not have access to it.'),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
|
||||
protected function adjustObjectForPolicyChecks(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
|
|
@ -250,6 +250,14 @@ abstract class PhabricatorApplicationTransaction
|
|||
$phids[] = array($new);
|
||||
}
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
if ($old) {
|
||||
$phids[] = array($old);
|
||||
}
|
||||
if ($new) {
|
||||
$phids[] = array($new);
|
||||
}
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_TOKEN:
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_BUILDABLE:
|
||||
|
@ -369,6 +377,8 @@ abstract class PhabricatorApplicationTransaction
|
|||
return 'fa-wrench';
|
||||
case PhabricatorTransactions::TYPE_TOKEN:
|
||||
return 'fa-trophy';
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
return 'fa-th-large';
|
||||
}
|
||||
|
||||
return 'fa-pencil';
|
||||
|
@ -438,6 +448,7 @@ abstract class PhabricatorApplicationTransaction
|
|||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
if ($this->getOldValue() === null) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -597,6 +608,8 @@ abstract class PhabricatorApplicationTransaction
|
|||
return pht(
|
||||
'All users are already subscribed to this %s.',
|
||||
$this->getApplicationObjectTypeName());
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
return pht('This object is already in that space.');
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return pht('Edges already exist; transaction has no effect.');
|
||||
}
|
||||
|
@ -636,6 +649,12 @@ abstract class PhabricatorApplicationTransaction
|
|||
$this->getApplicationObjectTypeName(),
|
||||
$this->renderPolicyName($old, 'old'),
|
||||
$this->renderPolicyName($new, 'new'));
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
return pht(
|
||||
'%s shifted this object from the %s space to the %s space.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($old),
|
||||
$this->renderHandleLink($new));
|
||||
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||
$add = array_diff($new, $old);
|
||||
$rem = array_diff($old, $new);
|
||||
|
@ -821,6 +840,13 @@ abstract class PhabricatorApplicationTransaction
|
|||
'%s updated subscribers of %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
case PhabricatorTransactions::TYPE_SPACE:
|
||||
return pht(
|
||||
'%s shifted %s from the %s space to the %s space.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid),
|
||||
$this->renderHandleLink($old),
|
||||
$this->renderHandleLink($new));
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$new = ipull($new, 'dst');
|
||||
$old = ipull($old, 'dst');
|
||||
|
|
|
@ -174,6 +174,20 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
|
||||
$header = array();
|
||||
|
||||
$header[] = $this->renderObjectSpaceInformation();
|
||||
|
||||
if ($this->objectName) {
|
||||
$header[] = array(
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/'.$this->objectName,
|
||||
),
|
||||
$this->objectName),
|
||||
' ',
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->actionLinks) {
|
||||
$actions = array();
|
||||
foreach ($this->actionLinks as $button) {
|
||||
|
@ -200,18 +214,6 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
}
|
||||
$header[] = $this->header;
|
||||
|
||||
if ($this->objectName) {
|
||||
array_unshift(
|
||||
$header,
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/'.$this->objectName,
|
||||
),
|
||||
$this->objectName),
|
||||
' ');
|
||||
}
|
||||
|
||||
if ($this->tags) {
|
||||
$header[] = ' ';
|
||||
$header[] = phutil_tag(
|
||||
|
@ -268,9 +270,9 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
}
|
||||
|
||||
private function renderPolicyProperty(PhabricatorPolicyInterface $object) {
|
||||
$policies = PhabricatorPolicyQuery::loadPolicies(
|
||||
$this->getUser(),
|
||||
$object);
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$policies = PhabricatorPolicyQuery::loadPolicies($viewer, $object);
|
||||
|
||||
$view_capability = PhabricatorPolicyCapability::CAN_VIEW;
|
||||
$policy = idx($policies, $view_capability);
|
||||
|
@ -294,4 +296,41 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
|
||||
return array($icon, $link);
|
||||
}
|
||||
|
||||
private function renderObjectSpaceInformation() {
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$object = $this->policyObject;
|
||||
if (!$object) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!($object instanceof PhabricatorSpacesInterface)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$space_phid = $object->getSpacePHID();
|
||||
if ($space_phid === null) {
|
||||
$default_space = PhabricatorSpacesNamespaceQuery::getDefaultSpace();
|
||||
if ($default_space) {
|
||||
$space_phid = $default_space->getPHID();
|
||||
}
|
||||
}
|
||||
|
||||
if ($space_phid === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'spaces-name',
|
||||
),
|
||||
array(
|
||||
$viewer->renderHandle($space_phid),
|
||||
' | ',
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -140,3 +140,11 @@ body.device-phone .phui-header-view {
|
|||
.device .phui-header-action-links .phui-mobile-menu {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.spaces-name {
|
||||
color: {$lightbluetext};
|
||||
}
|
||||
|
||||
.spaces-name .phui-handle {
|
||||
color: #000;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue