Skip to content

Commit 76dfeb9

Browse files
author
epriestley
committed
Allow "Custom" policies to be selected in the policy control
Summary: Ref T603. When a user selects "Custom", we pop open the rules dialog and let them create a new rule or edit the existing rule. Test Plan: Set some objects to have custom policies. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T603 Differential Revision: https://secure.phabricator.com/D7300
1 parent 3a4c08d commit 76dfeb9

File tree

5 files changed

+135
-15
lines changed

5 files changed

+135
-15
lines changed

src/__celerity_resource_map__.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2295,7 +2295,7 @@
22952295
),
22962296
'javelin-behavior-policy-control' =>
22972297
array(
2298-
'uri' => '/res/f43ba427/rsrc/js/application/policy/behavior-policy-control.js',
2298+
'uri' => '/res/ce9f54c8/rsrc/js/application/policy/behavior-policy-control.js',
22992299
'type' => 'js',
23002300
'requires' =>
23012301
array(
@@ -2304,6 +2304,7 @@
23042304
2 => 'javelin-util',
23052305
3 => 'phabricator-dropdown-menu',
23062306
4 => 'phabricator-menu-item',
2307+
5 => 'javelin-workflow',
23072308
),
23082309
'disk' => '/rsrc/js/application/policy/behavior-policy-control.js',
23092310
),

src/applications/meta/controller/PhabricatorApplicationEditController.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,16 @@ public function processRequest() {
4747
}
4848

4949
if (empty($policies[$new])) {
50-
// Can't set the policy to something invalid.
51-
continue;
50+
// Not a standard policy, check for a custom policy.
51+
$policy = id(new PhabricatorPolicyQuery())
52+
->setViewer($user)
53+
->withPHIDs(array($new))
54+
->executeOne();
55+
if (!$policy) {
56+
// Not a custom policy either. Can't set the policy to something
57+
// invalid, so skip this.
58+
continue;
59+
}
5260
}
5361

5462
if ($new == PhabricatorPolicies::POLICY_PUBLIC) {

src/applications/policy/rule/PhabricatorPolicyRuleProjects.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public function getRuleDescription() {
1010
}
1111

1212
public function willApplyRules(PhabricatorUser $viewer, array $values) {
13-
$values = array_unique(array_filter($values));
13+
$values = array_unique(array_filter(array_mergev($values)));
1414
if (!$values) {
1515
return;
1616
}

src/view/form/control/AphrontFormPolicyControl.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,48 @@ protected function getOptions() {
5454
'icon' => $policy->getIcon(),
5555
);
5656
}
57+
58+
// If we were passed several custom policy options, throw away the ones
59+
// which aren't the value for this capability. For example, an object might
60+
// have a custom view pollicy and a custom edit policy. When we render
61+
// the selector for "Can View", we don't want to show the "Can Edit"
62+
// custom policy -- if we did, the menu would look like this:
63+
//
64+
// Custom
65+
// Custom Policy
66+
// Custom Policy
67+
//
68+
// ...where one is the "view" custom policy, and one is the "edit" custom
69+
// policy.
70+
71+
$type_custom = PhabricatorPolicyType::TYPE_CUSTOM;
72+
if (!empty($options[$type_custom])) {
73+
$options[$type_custom] = array_select_keys(
74+
$options[$type_custom],
75+
array($this->getValue()));
76+
}
77+
78+
// If there aren't any custom policies, add a placeholder policy so we
79+
// render a menu item. This allows the user to switch to a custom policy.
80+
81+
if (empty($options[$type_custom])) {
82+
$placeholder = new PhabricatorPolicy();
83+
$placeholder->setName(pht('Custom Policy...'));
84+
$options[$type_custom][$this->getCustomPolicyPlaceholder()] = array(
85+
'name' => $placeholder->getName(),
86+
'full' => $placeholder->getName(),
87+
'icon' => $placeholder->getIcon(),
88+
);
89+
}
90+
91+
$options = array_select_keys(
92+
$options,
93+
array(
94+
PhabricatorPolicyType::TYPE_GLOBAL,
95+
PhabricatorPolicyType::TYPE_CUSTOM,
96+
PhabricatorPolicyType::TYPE_PROJECT,
97+
));
98+
5799
return $options;
58100
}
59101

@@ -121,6 +163,7 @@ protected function renderInput() {
121163
'icons' => $icons,
122164
'labels' => $labels,
123165
'value' => $this->getValue(),
166+
'customPlaceholder' => $this->getCustomPolicyPlaceholder(),
124167
));
125168

126169
$selected = $flat_options[$this->getValue()];
@@ -165,5 +208,8 @@ protected function renderInput() {
165208
));
166209
}
167210

211+
private function getCustomPolicyPlaceholder() {
212+
return 'custom:placeholder';
213+
}
168214

169215
}

webroot/rsrc/js/application/policy/behavior-policy-control.js

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* javelin-util
66
* phabricator-dropdown-menu
77
* phabricator-menu-item
8+
* javelin-workflow
89
* @javelin
910
*/
1011
JX.behavior('policy-control', function(config) {
@@ -23,25 +24,37 @@ JX.behavior('policy-control', function(config) {
2324
for (var ii = 0; ii < config.groups.length; ii++) {
2425
var group = config.groups[ii];
2526

26-
var header = new JX.PhabricatorMenuItem(config.labels[group]);
27+
var header = new JX.PhabricatorMenuItem(config.labels[group], JX.bag);
2728
header.setDisabled(true);
2829
menu.addItem(header);
2930

3031
for (var jj = 0; jj < config.order[group].length; jj++) {
3132
var phid = config.order[group][jj];
32-
var option = config.options[phid];
3333

34-
var render = [JX.$H(config.icons[option.icon]), option.name];
34+
var onselect;
35+
if (group == 'custom') {
36+
onselect = JX.bind(null, function(phid) {
37+
var uri = get_custom_uri(phid);
38+
39+
new JX.Workflow(uri)
40+
.setHandler(function(response) {
41+
if (!response.phid) {
42+
return;
43+
}
44+
45+
replace_policy(phid, response.phid, response.info);
46+
select_policy(response.phid);
47+
})
48+
.start();
49+
50+
}, phid);
51+
} else {
52+
onselect = JX.bind(null, select_policy, phid);
53+
}
3554

3655
var item = new JX.PhabricatorMenuItem(
37-
render,
38-
JX.bind(null, function(phid, render) {
39-
JX.DOM.setContent(
40-
JX.DOM.find(control, 'span', 'policy-label'),
41-
render);
42-
input.value = phid;
43-
value = phid;
44-
}, phid, render));
56+
render_option(phid, true),
57+
onselect);
4558

4659
if (phid == value) {
4760
item.setSelected(true);
@@ -53,4 +66,56 @@ JX.behavior('policy-control', function(config) {
5366

5467
});
5568

69+
70+
var select_policy = function(phid) {
71+
JX.DOM.setContent(
72+
JX.DOM.find(control, 'span', 'policy-label'),
73+
render_option(phid));
74+
75+
input.value = phid;
76+
value = phid;
77+
};
78+
79+
80+
var render_option = function(phid, with_title) {
81+
var option = config.options[phid];
82+
83+
var name = option.name;
84+
if (with_title && (option.full != option.name)) {
85+
name = JX.$N('span', {title: option.full}, name);
86+
}
87+
88+
return [JX.$H(config.icons[option.icon]), name];
89+
};
90+
91+
92+
/**
93+
* Get the workflow URI to create or edit a policy with a given PHID.
94+
*/
95+
var get_custom_uri = function(phid) {
96+
var uri = '/policy/edit/';
97+
if (phid != config.customPlaceholder) {
98+
uri += phid + '/';
99+
}
100+
return uri;
101+
};
102+
103+
104+
/**
105+
* Replace an existing policy option with a new one. Used to swap out custom
106+
* policies after the user edits them.
107+
*/
108+
var replace_policy = function(old_phid, new_phid, info) {
109+
config.options[new_phid] = info;
110+
for (var k in config.order) {
111+
for (var ii = 0; ii < config.order[k].length; ii++) {
112+
if (config.order[k][ii] == old_phid) {
113+
config.order[k][ii] = new_phid;
114+
return;
115+
}
116+
}
117+
}
118+
};
119+
120+
56121
});

0 commit comments

Comments
 (0)