mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-30 02:32:42 +01:00
(stable) Promote 2019 Week 37
This commit is contained in:
commit
80d17ad780
45 changed files with 917 additions and 378 deletions
|
@ -9,7 +9,7 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => '3c8a0668',
|
||||
'conpherence.pkg.js' => '020aebcf',
|
||||
'core.pkg.css' => '5a4a5010',
|
||||
'core.pkg.css' => 'c69171e6',
|
||||
'core.pkg.js' => '73a06a9f',
|
||||
'differential.pkg.css' => '8d8360fb',
|
||||
'differential.pkg.js' => '0b037a4f',
|
||||
|
@ -155,7 +155,7 @@ return array(
|
|||
'rsrc/css/phui/phui-form-view.css' => '01b796c0',
|
||||
'rsrc/css/phui/phui-form.css' => '159e2d9c',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
|
||||
'rsrc/css/phui/phui-header-view.css' => '285c9139',
|
||||
'rsrc/css/phui/phui-header-view.css' => 'b500eeea',
|
||||
'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0',
|
||||
'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec',
|
||||
'rsrc/css/phui/phui-icon.css' => '4cbc684a',
|
||||
|
@ -168,6 +168,7 @@ return array(
|
|||
'rsrc/css/phui/phui-object-box.css' => 'f434b6be',
|
||||
'rsrc/css/phui/phui-pager.css' => 'd022c7ad',
|
||||
'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8',
|
||||
'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64',
|
||||
'rsrc/css/phui/phui-property-list-view.css' => 'cad62236',
|
||||
'rsrc/css/phui/phui-remarkup-preview.css' => '91767007',
|
||||
'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370',
|
||||
|
@ -178,7 +179,7 @@ return array(
|
|||
'rsrc/css/phui/phui-two-column-view.css' => '01e6991e',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => '9e9eb0df',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6',
|
||||
'rsrc/css/phui/workboards/phui-workpanel.css' => '3ae89b20',
|
||||
'rsrc/css/sprite-login.css' => '18b368a6',
|
||||
'rsrc/css/sprite-tokens.css' => 'f1896dc5',
|
||||
|
@ -396,7 +397,7 @@ return array(
|
|||
'rsrc/js/application/files/behavior-icon-composer.js' => '38a6cedb',
|
||||
'rsrc/js/application/files/behavior-launch-icon-composer.js' => 'a17b84f1',
|
||||
'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => 'b347a301',
|
||||
'rsrc/js/application/herald/HeraldRuleEditor.js' => '27daef73',
|
||||
'rsrc/js/application/herald/HeraldRuleEditor.js' => '2633bef7',
|
||||
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
|
||||
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
|
||||
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688',
|
||||
|
@ -570,7 +571,7 @@ return array(
|
|||
'global-drag-and-drop-css' => '1d2713a4',
|
||||
'harbormaster-css' => '8dfe16b2',
|
||||
'herald-css' => '648d39e2',
|
||||
'herald-rule-editor' => '27daef73',
|
||||
'herald-rule-editor' => '2633bef7',
|
||||
'herald-test-css' => 'e004176f',
|
||||
'inline-comment-summary-css' => '81eb368d',
|
||||
'javelin-aphlict' => '022516b4',
|
||||
|
@ -842,7 +843,7 @@ return array(
|
|||
'phui-form-css' => '159e2d9c',
|
||||
'phui-form-view-css' => '01b796c0',
|
||||
'phui-head-thing-view-css' => 'd7f293df',
|
||||
'phui-header-view-css' => '285c9139',
|
||||
'phui-header-view-css' => 'b500eeea',
|
||||
'phui-hovercard' => '074f0783',
|
||||
'phui-hovercard-view-css' => '6ca90fa0',
|
||||
'phui-icon-set-selector-css' => '7aa5f3ec',
|
||||
|
@ -863,6 +864,7 @@ return array(
|
|||
'phui-oi-simple-ui-css' => '6a30fa46',
|
||||
'phui-pager-css' => 'd022c7ad',
|
||||
'phui-pinboard-view-css' => '1f08f5d8',
|
||||
'phui-policy-section-view-css' => '139fdc64',
|
||||
'phui-property-list-view-css' => 'cad62236',
|
||||
'phui-remarkup-preview-css' => '91767007',
|
||||
'phui-segment-bar-view-css' => '5166b370',
|
||||
|
@ -874,7 +876,7 @@ return array(
|
|||
'phui-two-column-view-css' => '01e6991e',
|
||||
'phui-workboard-color-css' => 'e86de308',
|
||||
'phui-workboard-view-css' => '74fc9d98',
|
||||
'phui-workcard-view-css' => '9e9eb0df',
|
||||
'phui-workcard-view-css' => '913441b6',
|
||||
'phui-workpanel-view-css' => '3ae89b20',
|
||||
'phuix-action-list-view' => 'c68f183f',
|
||||
'phuix-action-view' => 'aaa08f3b',
|
||||
|
@ -1115,7 +1117,7 @@ return array(
|
|||
'javelin-json',
|
||||
'phabricator-draggable-list',
|
||||
),
|
||||
'27daef73' => array(
|
||||
'2633bef7' => array(
|
||||
'multirow-row-manager',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
|
3
resources/sql/autopatches/20190909.herald.01.rebuild.php
Normal file
3
resources/sql/autopatches/20190909.herald.01.rebuild.php
Normal file
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery('HeraldRuleQuery');
|
|
@ -3217,6 +3217,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineSettingsPanel' => 'applications/settings/panel/PhabricatorEditEngineSettingsPanel.php',
|
||||
'PhabricatorEditEngineStaticCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php',
|
||||
'PhabricatorEditEngineSubtype' => 'applications/transactions/editengine/PhabricatorEditEngineSubtype.php',
|
||||
'PhabricatorEditEngineSubtypeHeraldField' => 'applications/transactions/herald/PhabricatorEditEngineSubtypeHeraldField.php',
|
||||
'PhabricatorEditEngineSubtypeInterface' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeInterface.php',
|
||||
'PhabricatorEditEngineSubtypeMap' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeMap.php',
|
||||
'PhabricatorEditEngineSubtypeTestCase' => 'applications/transactions/editengine/__tests__/PhabricatorEditEngineSubtypeTestCase.php',
|
||||
|
@ -3438,8 +3439,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagDeleteController' => 'applications/flag/controller/PhabricatorFlagDeleteController.php',
|
||||
'PhabricatorFlagDestructionEngineExtension' => 'applications/flag/engineextension/PhabricatorFlagDestructionEngineExtension.php',
|
||||
'PhabricatorFlagEditController' => 'applications/flag/controller/PhabricatorFlagEditController.php',
|
||||
'PhabricatorFlagHeraldAction' => 'applications/flag/herald/PhabricatorFlagHeraldAction.php',
|
||||
'PhabricatorFlagListController' => 'applications/flag/controller/PhabricatorFlagListController.php',
|
||||
'PhabricatorFlagQuery' => 'applications/flag/query/PhabricatorFlagQuery.php',
|
||||
'PhabricatorFlagRemoveFlagHeraldAction' => 'applications/flag/herald/PhabricatorFlagRemoveFlagHeraldAction.php',
|
||||
'PhabricatorFlagSearchEngine' => 'applications/flag/query/PhabricatorFlagSearchEngine.php',
|
||||
'PhabricatorFlagSelectControl' => 'applications/flag/view/PhabricatorFlagSelectControl.php',
|
||||
'PhabricatorFlaggableInterface' => 'applications/flag/interface/PhabricatorFlaggableInterface.php',
|
||||
|
@ -4193,8 +4196,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyManagementWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementWorkflow.php',
|
||||
'PhabricatorPolicyPHIDTypePolicy' => 'applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php',
|
||||
'PhabricatorPolicyQuery' => 'applications/policy/query/PhabricatorPolicyQuery.php',
|
||||
'PhabricatorPolicyRef' => 'applications/policy/view/PhabricatorPolicyRef.php',
|
||||
'PhabricatorPolicyRequestExceptionHandler' => 'aphront/handler/PhabricatorPolicyRequestExceptionHandler.php',
|
||||
'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php',
|
||||
'PhabricatorPolicyRulesView' => 'applications/policy/view/PhabricatorPolicyRulesView.php',
|
||||
'PhabricatorPolicySearchEngineExtension' => 'applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php',
|
||||
'PhabricatorPolicyStrengthConstants' => 'applications/policy/constants/PhabricatorPolicyStrengthConstants.php',
|
||||
'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php',
|
||||
|
@ -9546,6 +9551,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorEditEngineStaticCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditEngineSubtype' => 'Phobject',
|
||||
'PhabricatorEditEngineSubtypeHeraldField' => 'HeraldField',
|
||||
'PhabricatorEditEngineSubtypeMap' => 'Phobject',
|
||||
'PhabricatorEditEngineSubtypeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorEditEngineSubtypeTransaction' => 'PhabricatorEditEngineTransactionType',
|
||||
|
@ -9799,7 +9805,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorFlagAddFlagHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorFlagAddFlagHeraldAction' => 'PhabricatorFlagHeraldAction',
|
||||
'PhabricatorFlagColor' => 'PhabricatorFlagConstants',
|
||||
'PhabricatorFlagConstants' => 'Phobject',
|
||||
'PhabricatorFlagController' => 'PhabricatorController',
|
||||
|
@ -9807,8 +9813,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagDeleteController' => 'PhabricatorFlagController',
|
||||
'PhabricatorFlagDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
||||
'PhabricatorFlagEditController' => 'PhabricatorFlagController',
|
||||
'PhabricatorFlagHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorFlagListController' => 'PhabricatorFlagController',
|
||||
'PhabricatorFlagQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorFlagRemoveFlagHeraldAction' => 'PhabricatorFlagHeraldAction',
|
||||
'PhabricatorFlagSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorFlagSelectControl' => 'AphrontFormControl',
|
||||
'PhabricatorFlaggableInterface' => 'PhabricatorPHIDInterface',
|
||||
|
@ -10671,8 +10679,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorPolicyPHIDTypePolicy' => 'PhabricatorPHIDType',
|
||||
'PhabricatorPolicyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorPolicyRef' => 'Phobject',
|
||||
'PhabricatorPolicyRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||
'PhabricatorPolicyRule' => 'Phobject',
|
||||
'PhabricatorPolicyRulesView' => 'AphrontView',
|
||||
'PhabricatorPolicySearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||
'PhabricatorPolicyStrengthConstants' => 'PhabricatorPolicyConstants',
|
||||
'PhabricatorPolicyTestCase' => 'PhabricatorTestCase',
|
||||
|
|
|
@ -50,30 +50,47 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
|||
if (!in_array('STRICT_ALL_TABLES', $modes)) {
|
||||
$summary = pht(
|
||||
'MySQL is not in strict mode (on host "%s"), but using strict mode '.
|
||||
'is strongly encouraged.',
|
||||
'is recommended.',
|
||||
$host_name);
|
||||
|
||||
$message = pht(
|
||||
"On database host \"%s\", the global %s is not set to %s. ".
|
||||
"It is strongly encouraged that you enable this mode when running ".
|
||||
"Phabricator.\n\n".
|
||||
"By default MySQL will silently ignore some types of errors, which ".
|
||||
"can cause data loss and raise security concerns. Enabling strict ".
|
||||
"mode makes MySQL raise an explicit error instead, and prevents this ".
|
||||
"entire class of problems from doing any damage.\n\n".
|
||||
"You can find more information about this mode (and how to configure ".
|
||||
"it) in the MySQL manual. Usually, it is sufficient to add this to ".
|
||||
"your %s file (in the %s section) and then restart %s:\n\n".
|
||||
"%s\n".
|
||||
"(Note that if you run other applications against the same database, ".
|
||||
"they may not work in strict mode. Be careful about enabling it in ".
|
||||
"these cases.)",
|
||||
'On database host "%s", the global "sql_mode" setting does not '.
|
||||
'include the "STRICT_ALL_TABLES" mode. Enabling this mode is '.
|
||||
'recommended to generally improve how MySQL handles certain errors.'.
|
||||
"\n\n".
|
||||
'Without this mode enabled, MySQL will silently ignore some error '.
|
||||
'conditions, including inserts which attempt to store more data in '.
|
||||
'a column than actually fits. This behavior is usually undesirable '.
|
||||
'and can lead to data corruption (by truncating multibyte characters '.
|
||||
'in the middle), data loss (by discarding the data which does not '.
|
||||
'fit into the column), or security concerns (for example, by '.
|
||||
'truncating keys or credentials).'.
|
||||
"\n\n".
|
||||
'Phabricator is developed and tested in "STRICT_ALL_TABLES" mode so '.
|
||||
'you should normally never encounter these situations, but may run '.
|
||||
'into them if you interact with the database directly, run '.
|
||||
'third-party code, develop extensions, or just encounter a bug in '.
|
||||
'the software.'.
|
||||
"\n\n".
|
||||
'Enabling "STRICT_ALL_TABLES" makes MySQL raise an explicit error '.
|
||||
'if one of these unusual situations does occur. This is a safer '.
|
||||
'behavior and prevents these situations from causing secret, subtle, '.
|
||||
'and potentially serious issues later on.'.
|
||||
"\n\n".
|
||||
'You can find more information about this mode (and how to configure '.
|
||||
'it) in the MySQL manual. Usually, it is sufficient to add this to '.
|
||||
'your "my.cnf" file (in the "[mysqld]" section) and then '.
|
||||
'restart "mysqld":'.
|
||||
"\n\n".
|
||||
'%s'.
|
||||
"\n".
|
||||
'Note that if you run other applications against the same database, '.
|
||||
'they may not work in strict mode.'.
|
||||
"\n\n".
|
||||
'If you can not or do not want to enable "STRICT_ALL_TABLES", you '.
|
||||
'can safely ignore this warning. Phabricator will work correctly '.
|
||||
'with this mode enabled or disabled.',
|
||||
$host_name,
|
||||
phutil_tag('tt', array(), 'sql_mode'),
|
||||
phutil_tag('tt', array(), 'STRICT_ALL_TABLES'),
|
||||
phutil_tag('tt', array(), 'my.cnf'),
|
||||
phutil_tag('tt', array(), '[mysqld]'),
|
||||
phutil_tag('tt', array(), 'mysqld'),
|
||||
phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES'));
|
||||
|
||||
$this->newIssue('sql_mode.strict')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFlagAddFlagHeraldAction extends HeraldAction {
|
||||
final class PhabricatorFlagAddFlagHeraldAction
|
||||
extends PhabricatorFlagHeraldAction {
|
||||
|
||||
const ACTIONCONST = 'flag';
|
||||
|
||||
|
@ -11,18 +12,6 @@ final class PhabricatorFlagAddFlagHeraldAction extends HeraldAction {
|
|||
return pht('Mark with flag');
|
||||
}
|
||||
|
||||
public function getActionGroupKey() {
|
||||
return HeraldSupportActionGroup::ACTIONGROUPKEY;
|
||||
}
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorFlaggableInterface);
|
||||
}
|
||||
|
||||
public function supportsRuleType($rule_type) {
|
||||
return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||
}
|
||||
|
||||
public function applyEffect($object, HeraldEffect $effect) {
|
||||
$phid = $this->getAdapter()->getPHID();
|
||||
$rule = $effect->getRule();
|
||||
|
|
18
src/applications/flag/herald/PhabricatorFlagHeraldAction.php
Normal file
18
src/applications/flag/herald/PhabricatorFlagHeraldAction.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorFlagHeraldAction
|
||||
extends HeraldAction {
|
||||
|
||||
public function getActionGroupKey() {
|
||||
return HeraldSupportActionGroup::ACTIONGROUPKEY;
|
||||
}
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorFlaggableInterface);
|
||||
}
|
||||
|
||||
public function supportsRuleType($rule_type) {
|
||||
return ($rule_type === HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFlagRemoveFlagHeraldAction
|
||||
extends PhabricatorFlagHeraldAction {
|
||||
|
||||
const ACTIONCONST = 'unflag';
|
||||
|
||||
const DO_UNFLAG = 'do.unflag';
|
||||
const DO_IGNORE_UNFLAG = 'do.ignore-unflag';
|
||||
|
||||
public function getHeraldActionName() {
|
||||
return pht('Remove flag');
|
||||
}
|
||||
|
||||
public function applyEffect($object, HeraldEffect $effect) {
|
||||
$phid = $this->getAdapter()->getPHID();
|
||||
$rule = $effect->getRule();
|
||||
$author = $rule->getAuthor();
|
||||
|
||||
$flag = PhabricatorFlagQuery::loadUserFlag($author, $phid);
|
||||
if (!$flag) {
|
||||
$this->logEffect(self::DO_IGNORE_UNFLAG, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($flag->getColor() !== $effect->getTarget()) {
|
||||
$this->logEffect(self::DO_IGNORE_UNFLAG, $flag->getColor());
|
||||
return;
|
||||
}
|
||||
|
||||
$flag->delete();
|
||||
|
||||
$this->logEffect(self::DO_UNFLAG, $flag->getColor());
|
||||
}
|
||||
|
||||
public function getHeraldActionValueType() {
|
||||
return id(new HeraldSelectFieldValue())
|
||||
->setKey('flag.color')
|
||||
->setOptions(PhabricatorFlagColor::getColorNameMap())
|
||||
->setDefault(PhabricatorFlagColor::COLOR_BLUE);
|
||||
}
|
||||
|
||||
protected function getActionEffectMap() {
|
||||
return array(
|
||||
self::DO_IGNORE_UNFLAG => array(
|
||||
'icon' => 'fa-times',
|
||||
'color' => 'grey',
|
||||
'name' => pht('Did Not Remove Flag'),
|
||||
),
|
||||
self::DO_UNFLAG => array(
|
||||
'icon' => 'fa-flag',
|
||||
'name' => pht('Removed Flag'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderActionDescription($value) {
|
||||
$color = PhabricatorFlagColor::getColorName($value);
|
||||
return pht('Remove %s flag.', $color);
|
||||
}
|
||||
|
||||
protected function renderActionEffectDescription($type, $data) {
|
||||
switch ($type) {
|
||||
case self::DO_IGNORE_UNFLAG:
|
||||
if (!$data) {
|
||||
return pht('Not marked with any flag.');
|
||||
} else {
|
||||
return pht(
|
||||
'Marked with flag of the wrong color ("%s").',
|
||||
PhabricatorFlagColor::getColorName($data));
|
||||
}
|
||||
case self::DO_UNFLAG:
|
||||
return pht(
|
||||
'Removed "%s" flag.',
|
||||
PhabricatorFlagColor::getColorName($data));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -96,4 +96,8 @@ final class HarbormasterRunBuildPlansHeraldAction
|
|||
return $record->getTarget();
|
||||
}
|
||||
|
||||
public function isActionAvailable() {
|
||||
return id(new PhabricatorHarbormasterApplication())->isInstalled();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -405,4 +405,8 @@ abstract class HeraldAction extends Phobject {
|
|||
return array();
|
||||
}
|
||||
|
||||
public function isActionAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -373,6 +373,16 @@ abstract class HeraldAdapter extends Phobject {
|
|||
return $field->getFieldGroupKey();
|
||||
}
|
||||
|
||||
public function isFieldAvailable($field_key) {
|
||||
$field = $this->getFieldImplementation($field_key);
|
||||
|
||||
if (!$field) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $field->isFieldAvailable();
|
||||
}
|
||||
|
||||
|
||||
/* -( Conditions )--------------------------------------------------------- */
|
||||
|
||||
|
@ -765,6 +775,16 @@ abstract class HeraldAdapter extends Phobject {
|
|||
return $action->getActionGroupKey();
|
||||
}
|
||||
|
||||
public function isActionAvailable($action_key) {
|
||||
$action = $this->getActionImplementation($action_key);
|
||||
|
||||
if (!$action) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $action->isActionAvailable();
|
||||
}
|
||||
|
||||
public function getActions($rule_type) {
|
||||
$actions = array();
|
||||
foreach ($this->getActionsForRuleType($rule_type) as $key => $action) {
|
||||
|
|
|
@ -404,8 +404,8 @@ final class HeraldRuleController extends HeraldController {
|
|||
HeraldAdapter $adapter) {
|
||||
|
||||
$all_rules = $this->loadRulesThisRuleMayDependUpon($rule);
|
||||
$all_rules = mpull($all_rules, 'getName', 'getPHID');
|
||||
asort($all_rules);
|
||||
$all_rules = msortv($all_rules, 'getEditorSortVector');
|
||||
$all_rules = mpull($all_rules, 'getEditorDisplayName', 'getPHID');
|
||||
|
||||
$all_fields = $adapter->getFieldNameMap();
|
||||
$all_conditions = $adapter->getConditionNameMap();
|
||||
|
@ -674,15 +674,6 @@ final class HeraldRuleController extends HeraldController {
|
|||
->execute();
|
||||
}
|
||||
|
||||
// mark disabled rules as disabled since they are not useful as such;
|
||||
// don't filter though to keep edit cases sane / expected
|
||||
foreach ($all_rules as $current_rule) {
|
||||
if ($current_rule->getIsDisabled()) {
|
||||
$current_rule->makeEphemeral();
|
||||
$current_rule->setName($rule->getName().' '.pht('(Disabled)'));
|
||||
}
|
||||
}
|
||||
|
||||
// A rule can not depend upon itself.
|
||||
unset($all_rules[$rule->getID()]);
|
||||
|
||||
|
@ -693,7 +684,10 @@ final class HeraldRuleController extends HeraldController {
|
|||
$group_map = array();
|
||||
foreach ($field_map as $field_key => $field_name) {
|
||||
$group_key = $adapter->getFieldGroupKey($field_key);
|
||||
$group_map[$group_key][$field_key] = $field_name;
|
||||
$group_map[$group_key][$field_key] = array(
|
||||
'name' => $field_name,
|
||||
'available' => $adapter->isFieldAvailable($field_key),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getGroups(
|
||||
|
@ -705,7 +699,10 @@ final class HeraldRuleController extends HeraldController {
|
|||
$group_map = array();
|
||||
foreach ($action_map as $action_key => $action_name) {
|
||||
$group_key = $adapter->getActionGroupKey($action_key);
|
||||
$group_map[$group_key][$action_key] = $action_name;
|
||||
$group_map[$group_key][$action_key] = array(
|
||||
'name' => $action_name,
|
||||
'available' => $adapter->isActionAvailable($action_key),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getGroups(
|
||||
|
|
|
@ -37,6 +37,20 @@ final class HeraldRuleIndexEngineExtension
|
|||
|
||||
$phids = array();
|
||||
|
||||
$fields = HeraldField::getAllFields();
|
||||
foreach ($rule->getConditions() as $condition_record) {
|
||||
$field = idx($fields, $condition_record->getFieldName());
|
||||
|
||||
if (!$field) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$affected_phids = $field->getPHIDsAffectedByCondition($condition_record);
|
||||
foreach ($affected_phids as $phid) {
|
||||
$phids[] = $phid;
|
||||
}
|
||||
}
|
||||
|
||||
$actions = HeraldAction::getAllActions();
|
||||
foreach ($rule->getActions() as $action_record) {
|
||||
$action = idx($actions, $action_record->getAction());
|
||||
|
|
|
@ -176,6 +176,29 @@ abstract class HeraldField extends Phobject {
|
|||
return $value_type->renderEditorValue($value);
|
||||
}
|
||||
|
||||
public function getPHIDsAffectedByCondition(HeraldCondition $condition) {
|
||||
try {
|
||||
$standard_type = $this->getHeraldFieldStandardType();
|
||||
} catch (PhutilMethodNotImplementedException $ex) {
|
||||
$standard_type = null;
|
||||
}
|
||||
|
||||
switch ($standard_type) {
|
||||
case self::STANDARD_PHID:
|
||||
case self::STANDARD_PHID_NULLABLE:
|
||||
case self::STANDARD_PHID_LIST:
|
||||
$phids = $condition->getValue();
|
||||
|
||||
if (!is_array($phids)) {
|
||||
$phids = array();
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
final public function setAdapter(HeraldAdapter $adapter) {
|
||||
$this->adapter = $adapter;
|
||||
return $this;
|
||||
|
@ -218,4 +241,8 @@ abstract class HeraldField extends Phobject {
|
|||
return false;
|
||||
}
|
||||
|
||||
public function isFieldAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -259,6 +259,22 @@ final class HeraldRule extends HeraldDAO
|
|||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
public function getEditorSortVector() {
|
||||
return id(new PhutilSortVector())
|
||||
->addInt($this->getIsDisabled() ? 1 : 0)
|
||||
->addString($this->getName());
|
||||
}
|
||||
|
||||
public function getEditorDisplayName() {
|
||||
$name = pht('%s %s', $this->getMonogram(), $this->getName());
|
||||
|
||||
if ($this->getIsDisabled()) {
|
||||
$name = pht('%s (Disabled)', $name);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
/* -( Repetition Policies )------------------------------------------------ */
|
||||
|
||||
|
|
|
@ -130,4 +130,9 @@ final class LegalpadRequireSignatureHeraldAction
|
|||
'Require document signatures: %s.',
|
||||
$this->renderHandleList($value));
|
||||
}
|
||||
|
||||
public function isActionAvailable() {
|
||||
return id(new PhabricatorLegalpadApplication())->isInstalled();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -344,6 +344,8 @@ dictionary with these keys:
|
|||
- `children` //Optional map.// Configure options shown to the user when
|
||||
they "Create Subtask". See below.
|
||||
- `fields` //Optional map.// Configure field behaviors. See below.
|
||||
- `mutations` //Optional list.// Configure which subtypes this subtype
|
||||
can easily be converted to by using the "Change Subtype" action. See below.
|
||||
|
||||
Each subtype must have a unique key, and you must define a subtype with
|
||||
the key "%s", which is used as a default subtype.
|
||||
|
@ -404,7 +406,7 @@ The `fields` key can configure the behavior of custom fields on specific
|
|||
task subtypes. For example:
|
||||
|
||||
```
|
||||
{
|
||||
{
|
||||
...
|
||||
"fields": {
|
||||
"custom.some-field": {
|
||||
|
@ -412,7 +414,7 @@ task subtypes. For example:
|
|||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each field supports these options:
|
||||
|
@ -421,6 +423,31 @@ Each field supports these options:
|
|||
subtypes.
|
||||
- `name` //Optional string.// Custom name of this field for the subtype.
|
||||
|
||||
|
||||
The `mutations` key allows you to control the behavior of the "Change Subtype"
|
||||
action above the comment area. By default, this action allows users to change
|
||||
the task subtype into any other subtype.
|
||||
|
||||
If you'd prefer to make it more difficult to change subtypes or offer only a
|
||||
subset of subtypes, you can specify the list of subtypes that "Change Subtypes"
|
||||
offers. For example, if you have several similar subtypes and want to allow
|
||||
tasks to be converted between them but not easily converted to other types,
|
||||
you can make the "Change Subtypes" control show only these options like this:
|
||||
|
||||
```
|
||||
{
|
||||
...
|
||||
"mutations": ["bug", "issue", "defect"]
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
If you specify an empty list, the "Change Subtypes" action will be completely
|
||||
hidden.
|
||||
|
||||
This mutation list is advisory and only configures the UI. Tasks may still be
|
||||
converted across subtypes freely by using the Bulk Editor or API.
|
||||
|
||||
EOTEXT
|
||||
,
|
||||
$subtype_default_key));
|
||||
|
|
|
@ -564,7 +564,8 @@ final class ManiphestTask extends ManiphestDAO
|
|||
|
||||
public function newEditEngineSubtypeMap() {
|
||||
$config = PhabricatorEnv::getEnvConfig('maniphest.subtypes');
|
||||
return PhabricatorEditEngineSubtype::newSubtypeMap($config);
|
||||
return PhabricatorEditEngineSubtype::newSubtypeMap($config)
|
||||
->setDatasource(new ManiphestTaskSubtypeDatasource());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,17 @@ final class PhabricatorDatasourceURIEngineExtension
|
|||
->setProtocol(null)
|
||||
->setPort(null);
|
||||
|
||||
return phutil_string_cast($uri);
|
||||
$uri = phutil_string_cast($uri);
|
||||
|
||||
// See T13412. If the URI was in the form "http://dev.example.com" with
|
||||
// no trailing slash, there may be no path. Redirecting to the empty
|
||||
// string is considered an error by safety checks during redirection,
|
||||
// so treat this like the user entered the URI with a trailing slash.
|
||||
if (!strlen($uri)) {
|
||||
$uri = '/';
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -52,28 +52,22 @@ final class PhabricatorApplicationPolicyChangeTransaction
|
|||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old = $this->renderApplicationPolicy($this->getOldValue());
|
||||
$new = $this->renderApplicationPolicy($this->getNewValue());
|
||||
|
||||
return pht(
|
||||
'%s changed the "%s" policy from "%s" to "%s".',
|
||||
'%s changed the %s policy from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderCapability(),
|
||||
$old,
|
||||
$new);
|
||||
$this->renderOldPolicy(),
|
||||
$this->renderNewPolicy());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
$old = $this->renderApplicationPolicy($this->getOldValue());
|
||||
$new = $this->renderApplicationPolicy($this->getNewValue());
|
||||
|
||||
return pht(
|
||||
'%s changed the "%s" policy for application %s from "%s" to "%s".',
|
||||
'%s changed the %s policy for application %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderCapability(),
|
||||
$this->renderObject(),
|
||||
$old,
|
||||
$new);
|
||||
$this->renderOldPolicy(),
|
||||
$this->renderNewPolicy());
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
|
@ -165,38 +159,11 @@ final class PhabricatorApplicationPolicyChangeTransaction
|
|||
return $errors;
|
||||
}
|
||||
|
||||
private function renderApplicationPolicy($name) {
|
||||
$policies = $this->getAllPolicies();
|
||||
if (empty($policies[$name])) {
|
||||
// Not a standard policy, check for a custom policy.
|
||||
$policy = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(array($name))
|
||||
->executeOne();
|
||||
$policies[$name] = $policy;
|
||||
}
|
||||
|
||||
$policy = idx($policies, $name);
|
||||
return $this->renderValue($policy->getFullName());
|
||||
}
|
||||
|
||||
private function getAllPolicies() {
|
||||
if (!$this->policies) {
|
||||
$viewer = $this->getViewer();
|
||||
$application = $this->getObject();
|
||||
$this->policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($application)
|
||||
->execute();
|
||||
}
|
||||
|
||||
return $this->policies;
|
||||
}
|
||||
|
||||
private function renderCapability() {
|
||||
$application = $this->getObject();
|
||||
$capability = $this->getCapabilityName();
|
||||
return $application->getCapabilityLabel($capability);
|
||||
$label = $application->getCapabilityLabel($capability);
|
||||
return $this->renderValue($label);
|
||||
}
|
||||
|
||||
private function getCapabilityName() {
|
||||
|
|
|
@ -190,14 +190,6 @@ final class PassphraseCredentialViewController extends PassphraseController {
|
|||
pht('Credential Type'),
|
||||
$type->getCredentialTypeName());
|
||||
|
||||
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
|
||||
$viewer,
|
||||
$credential);
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Editable By'),
|
||||
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
|
||||
|
||||
if ($type->shouldRequireUsername()) {
|
||||
$properties->addProperty(
|
||||
pht('Username'),
|
||||
|
|
|
@ -143,14 +143,6 @@ final class PhameBlogManageController extends PhameBlogController {
|
|||
),
|
||||
$feed_uri));
|
||||
|
||||
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
|
||||
$viewer,
|
||||
$blog);
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Editable By'),
|
||||
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
|
||||
|
||||
$engine = id(new PhabricatorMarkupEngine())
|
||||
->setViewer($viewer)
|
||||
->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION)
|
||||
|
|
|
@ -293,37 +293,6 @@ final class PhrictionDocumentController
|
|||
} else {
|
||||
throw new Exception(pht("Unknown document status '%s'!", $doc_status));
|
||||
}
|
||||
|
||||
$move_notice = null;
|
||||
if ($current_status == PhrictionChangeType::CHANGE_MOVE_HERE) {
|
||||
$from_doc_id = $content->getChangeRef();
|
||||
|
||||
$slug_uri = null;
|
||||
|
||||
// If the old document exists and is visible, provide a link to it.
|
||||
$from_docs = id(new PhrictionDocumentQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($from_doc_id))
|
||||
->execute();
|
||||
if ($from_docs) {
|
||||
$from_doc = head($from_docs);
|
||||
$slug_uri = PhrictionDocument::getSlugURI($from_doc->getSlug());
|
||||
}
|
||||
|
||||
$move_notice = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
|
||||
|
||||
if ($slug_uri) {
|
||||
$move_notice->appendChild(
|
||||
pht(
|
||||
'This document was moved from %s.',
|
||||
phutil_tag('a', array('href' => $slug_uri), $slug_uri)));
|
||||
} else {
|
||||
// Render this for consistency, even though it's a bit silly.
|
||||
$move_notice->appendChild(
|
||||
pht('This document was moved from elsewhere.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$children = $this->renderDocumentChildren($slug);
|
||||
|
|
|
@ -49,7 +49,7 @@ final class PhrictionDocumentMoveToTransaction
|
|||
$new = $this->getNewValue();
|
||||
|
||||
return pht(
|
||||
'%s moved this document from %s',
|
||||
'%s moved this document from %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderHandle($new['phid']));
|
||||
}
|
||||
|
|
|
@ -318,7 +318,7 @@ final class PhabricatorPolicyExplainController
|
|||
->setViewer($viewer)
|
||||
->setIcon($handle->getIcon().' bluegrey')
|
||||
->setHeader(pht('Object Policy'))
|
||||
->appendList(
|
||||
->appendParagraph(
|
||||
array(
|
||||
array(
|
||||
phutil_tag('strong', array(), pht('%s:', $capability_name)),
|
||||
|
@ -337,6 +337,13 @@ final class PhabricatorPolicyExplainController
|
|||
$policy->getPHID()),
|
||||
));
|
||||
|
||||
if ($policy->isCustomPolicy()) {
|
||||
$rules_view = id(new PhabricatorPolicyRulesView())
|
||||
->setViewer($viewer)
|
||||
->setPolicy($policy);
|
||||
$object_section->appendRulesView($rules_view);
|
||||
}
|
||||
|
||||
$strength = $this->getStrengthInformation($object, $policy, $capability);
|
||||
if ($strength) {
|
||||
$object_section->appendHint($strength);
|
||||
|
|
|
@ -602,12 +602,13 @@ final class PhabricatorPolicyFilter extends Phobject {
|
|||
PhabricatorPolicyInterface $object,
|
||||
$policy,
|
||||
$capability) {
|
||||
$viewer = $this->viewer;
|
||||
|
||||
if (!$this->raisePolicyExceptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->viewer->isOmnipotent()) {
|
||||
if ($viewer->isOmnipotent()) {
|
||||
// Never raise policy exceptions for the omnipotent viewer. Although we
|
||||
// will never normally issue a policy rejection for the omnipotent
|
||||
// viewer, we can end up here when queries blanket reject objects that
|
||||
|
@ -634,9 +635,60 @@ final class PhabricatorPolicyFilter extends Phobject {
|
|||
$capability);
|
||||
}
|
||||
|
||||
$more = PhabricatorPolicy::getPolicyExplanation($this->viewer, $policy);
|
||||
$more = (array)$more;
|
||||
$more = array_filter($more);
|
||||
// See T13411. If you receive a policy exception because you can't view
|
||||
// an object, we also want to avoid disclosing too many details about the
|
||||
// actual policy (for example, the names of projects in the policy).
|
||||
|
||||
// If you failed a "CAN_VIEW" check, or failed some other check and don't
|
||||
// have "CAN_VIEW" on the object, we give you an "opaque" explanation.
|
||||
// Otherwise, we give you a more detailed explanation.
|
||||
|
||||
$view_capability = PhabricatorPolicyCapability::CAN_VIEW;
|
||||
if ($capability === $view_capability) {
|
||||
$show_details = false;
|
||||
} else {
|
||||
$show_details = self::hasCapability(
|
||||
$viewer,
|
||||
$object,
|
||||
$view_capability);
|
||||
}
|
||||
|
||||
// TODO: This is a bit clumsy. We're producing HTML and text versions of
|
||||
// this message, but can't render the full policy rules in text today.
|
||||
// Users almost never get a text-only version of this exception anyway.
|
||||
|
||||
$head = null;
|
||||
$more = null;
|
||||
|
||||
if ($show_details) {
|
||||
$head = PhabricatorPolicy::getPolicyExplanation($viewer, $policy);
|
||||
|
||||
$policy_type = PhabricatorPolicyPHIDTypePolicy::TYPECONST;
|
||||
$is_custom = (phid_get_type($policy) === $policy_type);
|
||||
if ($is_custom) {
|
||||
$policy_map = PhabricatorPolicyQuery::loadPolicies(
|
||||
$viewer,
|
||||
$object);
|
||||
if (isset($policy_map[$capability])) {
|
||||
require_celerity_resource('phui-policy-section-view-css');
|
||||
|
||||
$more = id(new PhabricatorPolicyRulesView())
|
||||
->setViewer($viewer)
|
||||
->setPolicy($policy_map[$capability]);
|
||||
|
||||
$more = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phui-policy-section-view-rules',
|
||||
),
|
||||
$more);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$head = PhabricatorPolicy::getOpaquePolicyExplanation($viewer, $policy);
|
||||
}
|
||||
|
||||
$head = (array)$head;
|
||||
|
||||
$exceptions = PhabricatorPolicy::getSpecialRules(
|
||||
$object,
|
||||
|
@ -644,7 +696,10 @@ final class PhabricatorPolicyFilter extends Phobject {
|
|||
$capability,
|
||||
true);
|
||||
|
||||
$details = array_filter(array_merge($more, $exceptions));
|
||||
$text_details = array_filter(array_merge($head, $exceptions));
|
||||
$text_details = implode(' ', $text_details);
|
||||
|
||||
$html_details = array($head, $more, $exceptions);
|
||||
|
||||
$access_denied = $this->renderAccessDenied($object);
|
||||
|
||||
|
@ -653,7 +708,7 @@ final class PhabricatorPolicyFilter extends Phobject {
|
|||
$access_denied,
|
||||
$capability_name,
|
||||
$rejection,
|
||||
implode(' ', $details));
|
||||
$text_details);
|
||||
|
||||
$exception = id(new PhabricatorPolicyException($full_message))
|
||||
->setTitle($access_denied)
|
||||
|
@ -661,7 +716,7 @@ final class PhabricatorPolicyFilter extends Phobject {
|
|||
->setRejection($rejection)
|
||||
->setCapability($capability)
|
||||
->setCapabilityName($capability_name)
|
||||
->setMoreInfo($details);
|
||||
->setMoreInfo($html_details);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
|
|
@ -60,8 +60,10 @@ final class PhabricatorPolicyManagementShowWorkflow
|
|||
|
||||
$console->writeOut("__%s__\n\n", pht('CAPABILITIES'));
|
||||
foreach ($policies as $capability => $policy) {
|
||||
$ref = $policy->newRef($viewer);
|
||||
|
||||
$console->writeOut(" **%s**\n", $capability);
|
||||
$console->writeOut(" %s\n", $policy->renderDescription());
|
||||
$console->writeOut(" %s\n", $ref->getPolicyDisplayName());
|
||||
$console->writeOut(" %s\n",
|
||||
PhabricatorPolicy::getPolicyExplanation($viewer, $policy->getPHID()));
|
||||
$console->writeOut("\n");
|
||||
|
|
|
@ -43,13 +43,13 @@ final class PhabricatorPolicyQuery
|
|||
|
||||
public static function renderPolicyDescriptions(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorPolicyInterface $object,
|
||||
$icon = false) {
|
||||
PhabricatorPolicyInterface $object) {
|
||||
|
||||
$policies = self::loadPolicies($viewer, $object);
|
||||
|
||||
foreach ($policies as $capability => $policy) {
|
||||
$policies[$capability] = $policy->renderDescription($icon);
|
||||
$policies[$capability] = $policy->newRef($viewer)
|
||||
->newCapabilityLink($object, $capability);
|
||||
}
|
||||
|
||||
return $policies;
|
||||
|
|
|
@ -85,8 +85,10 @@ final class PhabricatorPolicy
|
|||
$phid_type = phid_get_type($policy_identifier);
|
||||
switch ($phid_type) {
|
||||
case PhabricatorProjectProjectPHIDType::TYPECONST:
|
||||
$policy->setType(PhabricatorPolicyType::TYPE_PROJECT);
|
||||
$policy->setName($handle->getName());
|
||||
$policy
|
||||
->setType(PhabricatorPolicyType::TYPE_PROJECT)
|
||||
->setName($handle->getName())
|
||||
->setIcon($handle->getIcon());
|
||||
break;
|
||||
case PhabricatorPeopleUserPHIDType::TYPECONST:
|
||||
$policy->setType(PhabricatorPolicyType::TYPE_USER);
|
||||
|
@ -218,6 +220,25 @@ final class PhabricatorPolicy
|
|||
PhabricatorUser $viewer,
|
||||
$policy) {
|
||||
|
||||
$type = phid_get_type($policy);
|
||||
if ($type === PhabricatorProjectProjectPHIDType::TYPECONST) {
|
||||
$handle = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($policy))
|
||||
->executeOne();
|
||||
|
||||
return pht(
|
||||
'Members of the project "%s" can take this action.',
|
||||
$handle->getFullName());
|
||||
}
|
||||
|
||||
return self::getOpaquePolicyExplanation($viewer, $policy);
|
||||
}
|
||||
|
||||
public static function getOpaquePolicyExplanation(
|
||||
PhabricatorUser $viewer,
|
||||
$policy) {
|
||||
|
||||
$rule = PhabricatorPolicyQuery::getObjectPolicyRule($policy);
|
||||
if ($rule) {
|
||||
return $rule->getPolicyExplanation();
|
||||
|
@ -243,7 +264,9 @@ final class PhabricatorPolicy
|
|||
$type = phid_get_type($policy);
|
||||
if ($type == PhabricatorProjectProjectPHIDType::TYPECONST) {
|
||||
return pht(
|
||||
'Members of the project "%s" can take this action.',
|
||||
'Members of a particular project can take this action. (You '.
|
||||
'can not see this object, so the name of this project is '.
|
||||
'restricted.)',
|
||||
$handle->getFullName());
|
||||
} else if ($type == PhabricatorPeopleUserPHIDType::TYPECONST) {
|
||||
return pht(
|
||||
|
@ -274,45 +297,22 @@ final class PhabricatorPolicy
|
|||
}
|
||||
}
|
||||
|
||||
public function renderDescription($icon = false) {
|
||||
$img = null;
|
||||
if ($icon) {
|
||||
$img = id(new PHUIIconView())
|
||||
->setIcon($this->getIcon());
|
||||
public function newRef(PhabricatorUser $viewer) {
|
||||
return id(new PhabricatorPolicyRef())
|
||||
->setViewer($viewer)
|
||||
->setPolicy($this);
|
||||
}
|
||||
|
||||
if ($this->getHref()) {
|
||||
$desc = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $this->getHref(),
|
||||
'class' => 'policy-link',
|
||||
'sigil' => $this->getWorkflow() ? 'workflow' : null,
|
||||
),
|
||||
array(
|
||||
$img,
|
||||
$this->getName(),
|
||||
));
|
||||
} else {
|
||||
if ($img) {
|
||||
$desc = array($img, $this->getName());
|
||||
} else {
|
||||
$desc = $this->getName();
|
||||
}
|
||||
public function isProjectPolicy() {
|
||||
return ($this->getType() === PhabricatorPolicyType::TYPE_PROJECT);
|
||||
}
|
||||
|
||||
switch ($this->getType()) {
|
||||
case PhabricatorPolicyType::TYPE_PROJECT:
|
||||
return pht('%s (Project)', $desc);
|
||||
case PhabricatorPolicyType::TYPE_CUSTOM:
|
||||
return $desc;
|
||||
case PhabricatorPolicyType::TYPE_MASKED:
|
||||
return pht(
|
||||
'%s (You do not have permission to view policy details.)',
|
||||
$desc);
|
||||
default:
|
||||
return $desc;
|
||||
public function isCustomPolicy() {
|
||||
return ($this->getType() === PhabricatorPolicyType::TYPE_CUSTOM);
|
||||
}
|
||||
|
||||
public function isMaskedPolicy() {
|
||||
return ($this->getType() === PhabricatorPolicyType::TYPE_MASKED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -93,6 +93,16 @@ final class PHUIPolicySectionView
|
|||
return $this->appendChild(phutil_tag('p', array(), $content));
|
||||
}
|
||||
|
||||
public function appendRulesView(PhabricatorPolicyRulesView $rules_view) {
|
||||
return $this->appendChild(
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phui-policy-section-view-rules',
|
||||
),
|
||||
$rules_view));
|
||||
}
|
||||
|
||||
protected function getTagAttributes() {
|
||||
return array(
|
||||
'class' => 'phui-policy-section-view',
|
||||
|
@ -100,7 +110,7 @@ final class PHUIPolicySectionView
|
|||
}
|
||||
|
||||
protected function getTagContent() {
|
||||
require_celerity_resource('phui-header-view-css');
|
||||
require_celerity_resource('phui-policy-section-view-css');
|
||||
|
||||
$icon_view = null;
|
||||
$icon = $this->getIcon();
|
||||
|
|
99
src/applications/policy/view/PhabricatorPolicyRef.php
Normal file
99
src/applications/policy/view/PhabricatorPolicyRef.php
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPolicyRef
|
||||
extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
private $policy;
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
public function setPolicy(PhabricatorPolicy $policy) {
|
||||
$this->policy = $policy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPolicy() {
|
||||
return $this->policy;
|
||||
}
|
||||
|
||||
public function getPolicyDisplayName() {
|
||||
$policy = $this->getPolicy();
|
||||
return $policy->getFullName();
|
||||
}
|
||||
|
||||
public function newTransactionLink(
|
||||
$mode,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
$policy = $this->getPolicy();
|
||||
|
||||
if ($policy->isCustomPolicy()) {
|
||||
$uri = urisprintf(
|
||||
'/transactions/%s/%s/',
|
||||
$mode,
|
||||
$xaction->getPHID());
|
||||
$workflow = true;
|
||||
} else {
|
||||
$uri = $policy->getHref();
|
||||
$workflow = false;
|
||||
}
|
||||
|
||||
return $this->newLink($uri, $workflow);
|
||||
}
|
||||
|
||||
public function newCapabilityLink($object, $capability) {
|
||||
$policy = $this->getPolicy();
|
||||
|
||||
$uri = urisprintf(
|
||||
'/policy/explain/%s/%s/',
|
||||
$object->getPHID(),
|
||||
$capability);
|
||||
|
||||
return $this->newLink($uri, true);
|
||||
}
|
||||
|
||||
private function newLink($uri, $workflow) {
|
||||
$policy = $this->getPolicy();
|
||||
$name = $policy->getName();
|
||||
|
||||
if ($uri !== null) {
|
||||
$name = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $uri,
|
||||
'sigil' => ($workflow ? 'workflow' : null),
|
||||
),
|
||||
$name);
|
||||
}
|
||||
|
||||
$hint = $this->getPolicyTypeHint();
|
||||
if ($hint !== null) {
|
||||
$name = pht('%s (%s)', $name, $hint);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
private function getPolicyTypeHint() {
|
||||
$policy = $this->getPolicy();
|
||||
|
||||
if ($policy->isProjectPolicy()) {
|
||||
return pht('Project');
|
||||
}
|
||||
|
||||
if ($policy->isMaskedPolicy()) {
|
||||
return pht('You do not have permission to view policy details.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
84
src/applications/policy/view/PhabricatorPolicyRulesView.php
Normal file
84
src/applications/policy/view/PhabricatorPolicyRulesView.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPolicyRulesView
|
||||
extends AphrontView {
|
||||
|
||||
private $policy;
|
||||
|
||||
public function setPolicy(PhabricatorPolicy $policy) {
|
||||
$this->policy = $policy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPolicy() {
|
||||
return $this->policy;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$policy = $this->getPolicy();
|
||||
|
||||
require_celerity_resource('policy-transaction-detail-css');
|
||||
|
||||
$rule_objects = array();
|
||||
foreach ($policy->getCustomRuleClasses() as $class) {
|
||||
$rule_objects[$class] = newv($class, array());
|
||||
}
|
||||
|
||||
$policy = clone $policy;
|
||||
$policy->attachRuleObjects($rule_objects);
|
||||
|
||||
$details = array();
|
||||
$details[] = phutil_tag(
|
||||
'p',
|
||||
array(
|
||||
'class' => 'policy-transaction-detail-intro',
|
||||
),
|
||||
pht('These rules are processed in order:'));
|
||||
|
||||
foreach ($policy->getRules() as $index => $rule) {
|
||||
$rule_object = $rule_objects[$rule['rule']];
|
||||
if ($rule['action'] == 'allow') {
|
||||
$icon = 'fa-check-circle green';
|
||||
} else {
|
||||
$icon = 'fa-minus-circle red';
|
||||
}
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon($icon)
|
||||
->setText(
|
||||
ucfirst($rule['action']).' '.$rule_object->getRuleDescription());
|
||||
|
||||
$handle_phids = $rule_object->getRequiredHandlePHIDsForSummary(
|
||||
$rule['value']);
|
||||
if ($handle_phids) {
|
||||
$value = $this->getViewer()
|
||||
->renderHandleList($handle_phids)
|
||||
->setAsInline(true);
|
||||
} else {
|
||||
$value = $rule['value'];
|
||||
}
|
||||
|
||||
$details[] = phutil_tag('div',
|
||||
array(
|
||||
'class' => 'policy-transaction-detail-row',
|
||||
),
|
||||
array(
|
||||
$icon,
|
||||
$value,
|
||||
));
|
||||
}
|
||||
|
||||
$details[] = phutil_tag(
|
||||
'p',
|
||||
array(
|
||||
'class' => 'policy-transaction-detail-end',
|
||||
),
|
||||
pht(
|
||||
'If no rules match, %s all other users.',
|
||||
phutil_tag('b',
|
||||
array(),
|
||||
$policy->getDefaultAction())));
|
||||
|
||||
return $details;
|
||||
}
|
||||
|
||||
}
|
|
@ -904,7 +904,8 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
|||
|
||||
public function newEditEngineSubtypeMap() {
|
||||
$config = PhabricatorEnv::getEnvConfig('projects.subtypes');
|
||||
return PhabricatorEditEngineSubtype::newSubtypeMap($config);
|
||||
return PhabricatorEditEngineSubtype::newSubtypeMap($config)
|
||||
->setDatasource(new PhabricatorProjectSubtypeDatasource());
|
||||
}
|
||||
|
||||
public function newSubtypeObject() {
|
||||
|
|
|
@ -80,14 +80,6 @@ final class PhabricatorSpacesViewController
|
|||
? pht('Yes')
|
||||
: pht('No'));
|
||||
|
||||
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
|
||||
$viewer,
|
||||
$space);
|
||||
|
||||
$list->addProperty(
|
||||
pht('Editable By'),
|
||||
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
|
||||
|
||||
$description = $space->getDescription();
|
||||
if (strlen($description)) {
|
||||
$description = new PHUIRemarkupView($viewer, $description);
|
||||
|
|
|
@ -33,6 +33,7 @@ final class PhabricatorApplicationTransactionValueController
|
|||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
case PhabricatorTransactions::TYPE_JOIN_POLICY:
|
||||
case PhabricatorRepositoryPushPolicyTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorApplicationPolicyChangeTransaction::TRANSACTIONTYPE:
|
||||
break;
|
||||
default:
|
||||
return new Aphront404Response();
|
||||
|
@ -57,89 +58,16 @@ final class PhabricatorApplicationTransactionValueController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$rule_objects = array();
|
||||
foreach ($policy->getCustomRuleClasses() as $class) {
|
||||
$rule_objects[$class] = newv($class, array());
|
||||
}
|
||||
$policy->attachRuleObjects($rule_objects);
|
||||
$rules_view = id(new PhabricatorPolicyRulesView())
|
||||
->setViewer($viewer)
|
||||
->setPolicy($policy);
|
||||
|
||||
$this->requireResource('policy-transaction-detail-css');
|
||||
$cancel_uri = $this->guessCancelURI($viewer, $xaction);
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle($policy->getFullName())
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->appendChild($this->renderPolicyDetails($policy, $rule_objects))
|
||||
->appendChild($rules_view)
|
||||
->addCancelButton($cancel_uri, pht('Close'));
|
||||
}
|
||||
|
||||
private function extractPHIDs(
|
||||
PhabricatorPolicy $policy,
|
||||
array $rule_objects) {
|
||||
|
||||
$phids = array();
|
||||
foreach ($policy->getRules() as $rule) {
|
||||
$rule_object = $rule_objects[$rule['rule']];
|
||||
$phids[] =
|
||||
$rule_object->getRequiredHandlePHIDsForSummary($rule['value']);
|
||||
}
|
||||
return array_filter(array_mergev($phids));
|
||||
}
|
||||
|
||||
private function renderPolicyDetails(
|
||||
PhabricatorPolicy $policy,
|
||||
array $rule_objects) {
|
||||
$details = array();
|
||||
$details[] = phutil_tag(
|
||||
'p',
|
||||
array(
|
||||
'class' => 'policy-transaction-detail-intro',
|
||||
),
|
||||
pht('These rules are processed in order:'));
|
||||
|
||||
foreach ($policy->getRules() as $index => $rule) {
|
||||
$rule_object = $rule_objects[$rule['rule']];
|
||||
if ($rule['action'] == 'allow') {
|
||||
$icon = 'fa-check-circle green';
|
||||
} else {
|
||||
$icon = 'fa-minus-circle red';
|
||||
}
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon($icon)
|
||||
->setText(
|
||||
ucfirst($rule['action']).' '.$rule_object->getRuleDescription());
|
||||
|
||||
$handle_phids = $rule_object->getRequiredHandlePHIDsForSummary(
|
||||
$rule['value']);
|
||||
if ($handle_phids) {
|
||||
$value = $this->getViewer()
|
||||
->renderHandleList($handle_phids)
|
||||
->setAsInline(true);
|
||||
} else {
|
||||
$value = $rule['value'];
|
||||
}
|
||||
|
||||
$details[] = phutil_tag('div',
|
||||
array(
|
||||
'class' => 'policy-transaction-detail-row',
|
||||
),
|
||||
array(
|
||||
$icon,
|
||||
$value,
|
||||
));
|
||||
}
|
||||
|
||||
$details[] = phutil_tag(
|
||||
'p',
|
||||
array(
|
||||
'class' => 'policy-transaction-detail-end',
|
||||
),
|
||||
pht(
|
||||
'If no rules match, %s all other users.',
|
||||
phutil_tag('b',
|
||||
array(),
|
||||
$policy->getDefaultAction())));
|
||||
return $details;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ final class PhabricatorEditEngineSubtype
|
|||
private $childSubtypes = array();
|
||||
private $childIdentifiers = array();
|
||||
private $fieldConfiguration = array();
|
||||
private $mutations;
|
||||
|
||||
public function setKey($key) {
|
||||
$this->key = $key;
|
||||
|
@ -78,6 +79,15 @@ final class PhabricatorEditEngineSubtype
|
|||
return $this->childIdentifiers;
|
||||
}
|
||||
|
||||
public function setMutations($mutations) {
|
||||
$this->mutations = $mutations;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMutations() {
|
||||
return $this->mutations;
|
||||
}
|
||||
|
||||
public function hasTagView() {
|
||||
return (bool)strlen($this->getTagText());
|
||||
}
|
||||
|
@ -152,6 +162,7 @@ final class PhabricatorEditEngineSubtype
|
|||
'icon' => 'optional string',
|
||||
'children' => 'optional map<string, wild>',
|
||||
'fields' => 'optional map<string, wild>',
|
||||
'mutations' => 'optional list<string>',
|
||||
));
|
||||
|
||||
$key = $value['key'];
|
||||
|
@ -217,6 +228,28 @@ final class PhabricatorEditEngineSubtype
|
|||
'with key "%s". This subtype is required and must be defined.',
|
||||
self::SUBTYPE_DEFAULT));
|
||||
}
|
||||
|
||||
foreach ($config as $value) {
|
||||
$key = idx($value, 'key');
|
||||
|
||||
$mutations = idx($value, 'mutations');
|
||||
if (!$mutations) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($mutations as $mutation) {
|
||||
if (!isset($map[$mutation])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Subtype configuration is invalid: subtype with key "%s" '.
|
||||
'specifies that it can mutate into subtype "%s", but that is '.
|
||||
'not a valid subtype.',
|
||||
$key,
|
||||
$mutation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function newSubtypeMap(array $config) {
|
||||
|
@ -267,6 +300,8 @@ final class PhabricatorEditEngineSubtype
|
|||
}
|
||||
}
|
||||
|
||||
$subtype->setMutations(idx($entry, 'mutations'));
|
||||
|
||||
$map[$key] = $subtype;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ final class PhabricatorEditEngineSubtypeMap
|
|||
extends Phobject {
|
||||
|
||||
private $subtypes;
|
||||
private $datasource;
|
||||
|
||||
public function __construct(array $subtypes) {
|
||||
assert_instances_of($subtypes, 'PhabricatorEditEngineSubtype');
|
||||
|
@ -39,6 +40,57 @@ final class PhabricatorEditEngineSubtypeMap
|
|||
return $this->subtypes[$subtype_key];
|
||||
}
|
||||
|
||||
public function setDatasource(PhabricatorTypeaheadDatasource $datasource) {
|
||||
$this->datasource = $datasource;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newDatasource() {
|
||||
if (!$this->datasource) {
|
||||
throw new PhutilInvalidStateException('setDatasource');
|
||||
}
|
||||
|
||||
return clone($this->datasource);
|
||||
}
|
||||
|
||||
public function getMutationMap($source_key) {
|
||||
return mpull($this->getMutations($source_key), 'getName');
|
||||
}
|
||||
|
||||
public function getMutations($source_key) {
|
||||
$mutations = $this->subtypes;
|
||||
|
||||
$subtype = idx($this->subtypes, $source_key);
|
||||
if ($subtype) {
|
||||
$map = $subtype->getMutations();
|
||||
if ($map !== null) {
|
||||
$map = array_fuse($map);
|
||||
foreach ($mutations as $key => $mutation) {
|
||||
if ($key === $source_key) {
|
||||
// This is the current subtype, so we always want to show it.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($map[$key])) {
|
||||
// This is an allowed mutation, so keep it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Discard other subtypes as mutation options.
|
||||
unset($mutations[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the only available mutation is the current subtype, treat this like
|
||||
// no mutations are available.
|
||||
if (array_keys($mutations) === array($source_key)) {
|
||||
$mutations = array();
|
||||
}
|
||||
|
||||
return $mutations;
|
||||
}
|
||||
|
||||
public function getCreateFormsForSubtype(
|
||||
PhabricatorEditEngine $edit_engine,
|
||||
PhabricatorEditEngineSubtypeInterface $object) {
|
||||
|
|
|
@ -29,9 +29,17 @@ final class PhabricatorSubtypeEditEngineExtension
|
|||
PhabricatorApplicationTransactionInterface $object) {
|
||||
|
||||
$subtype_type = PhabricatorTransactions::TYPE_SUBTYPE;
|
||||
$subtype_value = $object->getEditEngineSubtype();
|
||||
|
||||
$map = $object->newEditEngineSubtypeMap();
|
||||
|
||||
if ($object->getID()) {
|
||||
$options = $map->getMutationMap($subtype_value);
|
||||
} else {
|
||||
// NOTE: This is a crude proxy for "are we in the bulk edit workflow".
|
||||
// We want to allow any mutation.
|
||||
$options = $map->getDisplayMap();
|
||||
}
|
||||
|
||||
$subtype_field = id(new PhabricatorSelectEditField())
|
||||
->setKey(self::EDITKEY)
|
||||
|
@ -40,12 +48,12 @@ final class PhabricatorSubtypeEditEngineExtension
|
|||
->setTransactionType($subtype_type)
|
||||
->setConduitDescription(pht('Change the object subtype.'))
|
||||
->setConduitTypeDescription(pht('New object subtype key.'))
|
||||
->setValue($object->getEditEngineSubtype())
|
||||
->setValue($subtype_value)
|
||||
->setOptions($options);
|
||||
|
||||
// If subtypes are configured, enable changing them from the bulk editor
|
||||
// and comment action stack.
|
||||
if ($map->getCount() > 1) {
|
||||
// If subtypes are configured, enable changing them from the bulk editor.
|
||||
// Bulk editor
|
||||
if ($options) {
|
||||
$subtype_field
|
||||
->setBulkEditLabel(pht('Change subtype to'))
|
||||
->setCommentActionLabel(pht('Change Subtype'))
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEditEngineSubtypeHeraldField
|
||||
extends HeraldField {
|
||||
|
||||
const FIELDCONST = 'subtype';
|
||||
|
||||
public function getHeraldFieldName() {
|
||||
return pht('Subtype');
|
||||
}
|
||||
|
||||
public function getFieldGroupKey() {
|
||||
return HeraldSupportFieldGroup::FIELDGROUPKEY;
|
||||
}
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof PhabricatorEditEngineSubtypeInterface);
|
||||
}
|
||||
|
||||
public function getHeraldFieldValue($object) {
|
||||
return $object->getEditEngineSubtype();
|
||||
}
|
||||
|
||||
protected function getHeraldFieldStandardType() {
|
||||
return self::STANDARD_PHID;
|
||||
}
|
||||
|
||||
protected function getDatasource() {
|
||||
$object = $this->getAdapter()->getObject();
|
||||
$map = $object->newEditEngineSubtypeMap();
|
||||
return $map->newDatasource();
|
||||
}
|
||||
|
||||
protected function getDatasourceValueMap() {
|
||||
$object = $this->getAdapter()->getObject();
|
||||
$map = $object->newEditEngineSubtypeMap();
|
||||
|
||||
$result = array();
|
||||
foreach ($map->getSubtypes() as $subtype) {
|
||||
$result[$subtype->getKey()] = $subtype->getName();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function isFieldAvailable() {
|
||||
$object = $this->getAdapter()->getObject();
|
||||
$map = $object->newEditEngineSubtypeMap();
|
||||
return ($map->getCount() > 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -445,19 +445,15 @@ abstract class PhabricatorApplicationTransaction
|
|||
$policy = PhabricatorPolicy::newFromPolicyAndHandle(
|
||||
$phid,
|
||||
$this->getHandleIfExists($phid));
|
||||
|
||||
$ref = $policy->newRef($this->getViewer());
|
||||
|
||||
if ($this->renderingTarget == self::TARGET_HTML) {
|
||||
switch ($policy->getType()) {
|
||||
case PhabricatorPolicyType::TYPE_CUSTOM:
|
||||
$policy->setHref('/transactions/'.$state.'/'.$this->getPHID().'/');
|
||||
$policy->setWorkflow(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
$output = $policy->renderDescription();
|
||||
$output = $ref->newTransactionLink($state, $this);
|
||||
} else {
|
||||
$output = hsprintf('%s', $policy->getFullName());
|
||||
$output = $ref->getPolicyDisplayName();
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
@ -775,6 +771,13 @@ abstract class PhabricatorApplicationTransaction
|
|||
case PhabricatorTransactions::TYPE_TOKEN:
|
||||
case PhabricatorTransactions::TYPE_MFA:
|
||||
return true;
|
||||
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||
// See T8952. When an application (usually Herald) modifies
|
||||
// subscribers, this tends to be very uninteresting.
|
||||
if ($this->isApplicationAuthor()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$edge_type = $this->getMetadataValue('edge:type');
|
||||
switch ($edge_type) {
|
||||
|
@ -1387,12 +1390,6 @@ abstract class PhabricatorApplicationTransaction
|
|||
return 25;
|
||||
}
|
||||
|
||||
if ($this->isApplicationAuthor()) {
|
||||
// When applications (most often: Herald) change subscriptions it
|
||||
// is very uninteresting.
|
||||
return 1;
|
||||
}
|
||||
|
||||
// In other cases, subscriptions are more interesting than comments
|
||||
// (which are shown anyway) but less interesting than any other type of
|
||||
// transaction.
|
||||
|
|
|
@ -215,17 +215,16 @@ abstract class PhabricatorModularTransactionType
|
|||
$phid,
|
||||
$handles[$phid]);
|
||||
|
||||
$ref = $policy->newRef($viewer);
|
||||
|
||||
if ($this->isTextMode()) {
|
||||
return $this->renderValue($policy->getFullName());
|
||||
}
|
||||
|
||||
$name = $ref->getPolicyDisplayName();
|
||||
} else {
|
||||
$storage = $this->getStorage();
|
||||
if ($policy->getType() == PhabricatorPolicyType::TYPE_CUSTOM) {
|
||||
$policy->setHref('/transactions/'.$mode.'/'.$storage->getPHID().'/');
|
||||
$policy->setWorkflow(true);
|
||||
$name = $ref->newTransactionLink($mode, $storage);
|
||||
}
|
||||
|
||||
return $this->renderValue($policy->renderDescription());
|
||||
return $this->renderValue($name);
|
||||
}
|
||||
|
||||
final protected function renderHandleList(array $phids) {
|
||||
|
|
|
@ -63,16 +63,23 @@ For detailed help on managing and stripping MFA, see the instructions in
|
|||
Unlocking Objects
|
||||
=================
|
||||
|
||||
If you aren't sure who owns an object, or no user account has access to an
|
||||
object, you can directly change object policies from the CLI:
|
||||
If you aren't sure who owns an object, you can inspect the policies from the
|
||||
CLI:
|
||||
|
||||
```
|
||||
$ ./bin/policy show <object>
|
||||
```
|
||||
|
||||
To identify the object you want to examine, you can specify an object
|
||||
name (like `T123`) or a PHID as the `<object>` parameter.
|
||||
|
||||
If examining the policy isn't helpful, or no user account has access to an
|
||||
object, you can then directly change object policies from the CLI:
|
||||
|
||||
```
|
||||
$ ./bin/policy unlock <object> [--view ...] [--edit ...] [--owner ...]
|
||||
```
|
||||
|
||||
To identify the object you want to unlock, you can specify an object name (like
|
||||
`T123`) or a PHID as the `<object>` parameter.
|
||||
|
||||
Use the `--view` and `--edit` flags (and, for some objects, the `--owner`
|
||||
flag) to specify new policies for the object.
|
||||
|
||||
|
|
|
@ -354,45 +354,3 @@ body .phui-header-shell.phui-bleed-header
|
|||
.phui-header-view .phui-tag-indigo a {
|
||||
color: {$sh-indigotext};
|
||||
}
|
||||
|
||||
.phui-policy-section-view {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-header {
|
||||
background: {$bluebackground};
|
||||
border-bottom: 1px solid {$lightblueborder};
|
||||
padding: 4px 8px;
|
||||
color: {$darkbluetext};
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-header-text {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-header .phui-icon-view {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-link .phui-icon-view {
|
||||
color: {$bluetext};
|
||||
}
|
||||
|
||||
.phui-policy-section-view-hint {
|
||||
color: {$greytext};
|
||||
background: {$lightbluebackground};
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-body {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-inactive-rule {
|
||||
color: {$greytext};
|
||||
}
|
||||
|
|
62
webroot/rsrc/css/phui/phui-policy-section-view.css
Normal file
62
webroot/rsrc/css/phui/phui-policy-section-view.css
Normal file
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* @provides phui-policy-section-view-css
|
||||
*/
|
||||
|
||||
.phui-policy-section-view {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-header {
|
||||
background: {$bluebackground};
|
||||
border-bottom: 1px solid {$lightblueborder};
|
||||
padding: 4px 8px;
|
||||
color: {$darkbluetext};
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-header-text {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-header .phui-icon-view {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-link .phui-icon-view {
|
||||
color: {$bluetext};
|
||||
}
|
||||
|
||||
.phui-policy-section-view-hint {
|
||||
color: {$greytext};
|
||||
background: {$lightbluebackground};
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-body {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.phui-policy-section-view-inactive-rule {
|
||||
color: {$greytext};
|
||||
}
|
||||
|
||||
.phui-policy-section-view-rules {
|
||||
margin: 8px 0;
|
||||
padding: 8px;
|
||||
background: {$lightbluebackground};
|
||||
border: 1px solid {$lightblueborder};
|
||||
}
|
||||
|
||||
.phui-policy-section-view .phui-policy-section-view-body ul {
|
||||
margin: 8px 0;
|
||||
padding: 0 16px 0 24px;
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
.phui-policy-section-view .phui-policy-section-view-body p + p {
|
||||
margin-top: 8px;
|
||||
}
|
|
@ -36,6 +36,10 @@
|
|||
|
||||
.phui-workcard .phui-oi-link {
|
||||
white-space: normal;
|
||||
|
||||
/* See T13413. This works around a Chrome 77 rendering engine freeze. */
|
||||
word-wrap: normal;
|
||||
|
||||
font-weight: normal;
|
||||
color: {$blacktext};
|
||||
margin-left: 2px;
|
||||
|
|
|
@ -350,8 +350,10 @@ JX.install('HeraldRuleEditor', {
|
|||
sigil: 'field-select'
|
||||
};
|
||||
|
||||
var field_select = this._renderGroupSelect(groups, attrs);
|
||||
field_select.value = this._config.conditions[row_id][0];
|
||||
var field_select = this._renderGroupSelect(
|
||||
groups,
|
||||
attrs,
|
||||
this._config.conditions[row_id][0]);
|
||||
|
||||
var field_cell = JX.$N('td', {sigil: 'field-cell'}, field_select);
|
||||
|
||||
|
@ -367,18 +369,38 @@ JX.install('HeraldRuleEditor', {
|
|||
}
|
||||
},
|
||||
|
||||
_renderGroupSelect: function(groups, attrs) {
|
||||
_renderGroupSelect: function(groups, attrs, value) {
|
||||
var optgroups = [];
|
||||
for (var ii = 0; ii < groups.length; ii++) {
|
||||
var group = groups[ii];
|
||||
var options = [];
|
||||
for (var k in group.options) {
|
||||
options.push(JX.$N('option', {value: k}, group.options[k]));
|
||||
var option = group.options[k];
|
||||
|
||||
var name = option.name;
|
||||
var available = option.available;
|
||||
|
||||
// See T7961. If the option is not marked as "available", we only
|
||||
// include it in the dropdown if the dropdown already has it as a
|
||||
// value. We want to hide options provided by applications which are
|
||||
// not installed, but do not want to break existing rules.
|
||||
|
||||
if (available || (k === value)) {
|
||||
options.push(JX.$N('option', {value: k}, name));
|
||||
}
|
||||
}
|
||||
if (options.length) {
|
||||
optgroups.push(JX.$N('optgroup', {label: group.label}, options));
|
||||
}
|
||||
}
|
||||
|
||||
return JX.$N('select', attrs, optgroups);
|
||||
var select = JX.$N('select', attrs, optgroups);
|
||||
|
||||
if (value !== undefined) {
|
||||
select.value = value;
|
||||
}
|
||||
|
||||
return select;
|
||||
},
|
||||
|
||||
_newAction : function(data) {
|
||||
|
@ -402,8 +424,10 @@ JX.install('HeraldRuleEditor', {
|
|||
sigil: 'action-select'
|
||||
};
|
||||
|
||||
var action_select = this._renderGroupSelect(groups, attrs);
|
||||
action_select.value = action[0];
|
||||
var action_select = this._renderGroupSelect(
|
||||
groups,
|
||||
attrs,
|
||||
action[0]);
|
||||
|
||||
var action_cell = JX.$N('td', {sigil: 'action-cell'}, action_select);
|
||||
|
||||
|
|
Loading…
Reference in a new issue