3 final class PhabricatorPolicyEditController
4 extends PhabricatorPolicyController
{
6 public function handleRequest(AphrontRequest
$request) {
7 $viewer = $this->getViewer();
9 $object_phid = $request->getURIData('objectPHID');
11 $object = id(new PhabricatorObjectQuery())
13 ->withPHIDs(array($object_phid))
16 return new Aphront404Response();
19 $object_type = $request->getURIData('objectType');
21 $object_type = $request->getURIData('templateType');
24 $phid_types = PhabricatorPHIDType
::getAllInstalledTypes($viewer);
25 if (empty($phid_types[$object_type])) {
26 return new Aphront404Response();
28 $object = $phid_types[$object_type]->newObject();
30 return new Aphront404Response();
34 $phid = $request->getURIData('phid');
36 case AphrontFormPolicyControl
::getSelectProjectKey():
37 return $this->handleProjectRequest($request);
38 case AphrontFormPolicyControl
::getSelectCustomKey():
45 $action_options = array(
46 PhabricatorPolicy
::ACTION_ALLOW
=> pht('Allow'),
47 PhabricatorPolicy
::ACTION_DENY
=> pht('Deny'),
50 $rules = id(new PhutilClassMapQuery())
51 ->setAncestorClass('PhabricatorPolicyRule')
54 foreach ($rules as $key => $rule) {
55 if (!$rule->canApplyToObject($object)) {
60 $rules = msort($rules, 'getRuleOrder');
62 $default_rule = array(
63 'action' => head_key($action_options),
64 'rule' => head_key($rules),
69 $policies = id(new PhabricatorPolicyQuery())
71 ->withPHIDs(array($phid))
74 return new Aphront404Response();
76 $policy = head($policies);
78 $policy = id(new PhabricatorPolicy())
79 ->setRules(array($default_rule))
80 ->setDefaultAction(PhabricatorPolicy
::ACTION_DENY
);
83 $root_id = celerity_generate_unique_node_id();
85 $default_action = $policy->getDefaultAction();
86 $rule_data = $policy->getRules();
89 if ($request->isFormPost()) {
90 $data = $request->getStr('rules');
92 $data = phutil_json_decode($data);
93 } catch (PhutilJSONParserException
$ex) {
94 throw new PhutilProxyException(
95 pht('Failed to JSON decode rule data!'),
100 foreach ($data as $rule) {
101 $action = idx($rule, 'action');
107 throw new Exception(pht("Invalid action '%s'!", $action));
110 $rule_class = idx($rule, 'rule');
111 if (empty($rules[$rule_class])) {
112 throw new Exception(pht("Invalid rule class '%s'!", $rule_class));
115 $rule_obj = $rules[$rule_class];
117 $value = $rule_obj->getValueForStorage(idx($rule, 'value'));
119 $rule_data[] = array(
121 'rule' => $rule_class,
126 // Filter out nonsense rules, like a "users" rule without any users
127 // actually specified.
128 $valid_rules = array();
129 foreach ($rule_data as $rule) {
130 $rule_class = $rule['rule'];
131 if ($rules[$rule_class]->ruleHasEffect($rule['value'])) {
132 $valid_rules[] = $rule;
137 $errors[] = pht('None of these policy rules have any effect.');
140 // NOTE: Policies are immutable once created, and we always create a new
141 // policy here. If we didn't, we would need to lock this endpoint down,
142 // as users could otherwise just go edit the policies of objects with
146 $new_policy = new PhabricatorPolicy();
147 $new_policy->setRules($valid_rules);
148 $new_policy->setDefaultAction($request->getStr('default'));
152 'phid' => $new_policy->getPHID(),
154 'name' => $new_policy->getName(),
155 'full' => $new_policy->getName(),
156 'icon' => $new_policy->getIcon(),
160 return id(new AphrontAjaxResponse())->setContent($data);
164 // Convert rule values to display format (for example, expanding PHIDs
166 foreach ($rule_data as $key => $rule) {
167 $rule_data[$key]['value'] = $rules[$rule['rule']]->getValueForDisplay(
172 $default_select = AphrontFormSelectControl
::renderSelectTag(
180 $errors = id(new PHUIInfoView())
181 ->setErrors($errors);
184 $form = id(new PHUIFormLayoutView())
185 ->appendChild($errors)
195 id(new PHUIFormInsetView())
196 ->setTitle(pht('Rules'))
202 'class' => 'button button-green',
203 'sigil' => 'create-rule',
204 'mustcapture' => true,
207 ->setDescription(pht('These rules are processed in order.'))
208 ->setContent(javelin_tag(
212 'class' => 'policy-rules-table',
216 id(new AphrontFormMarkupControl())
217 ->setLabel(pht('If No Rules Match'))
219 '%s all other users.',
229 $rule_options = mpull($rules, 'getRuleDescription');
230 $type_map = mpull($rules, 'getValueControlType');
231 $templates = mpull($rules, 'getValueControlTemplate');
233 require_celerity_resource('policy-edit-css');
234 Javelin
::initBehavior(
235 'policy-rule-editor',
237 'rootID' => $root_id,
238 'actions' => $action_options,
239 'rules' => $rule_options,
240 'types' => $type_map,
241 'templates' => $templates,
242 'data' => $rule_data,
243 'defaultRule' => $default_rule,
246 $title = pht('Custom Policy');
248 $key = $request->getStr('capability');
250 $capability = PhabricatorPolicyCapability
::getCapabilityByKey($key);
251 $title = pht('Custom "%s" Policy', $capability->getCapabilityName());
254 $dialog = id(new AphrontDialogView())
255 ->setWidth(AphrontDialogView
::WIDTH_FULL
)
259 ->addSubmitButton(pht('Save Policy'))
260 ->addCancelButton('#');
262 return id(new AphrontDialogResponse())->setDialog($dialog);
265 private function handleProjectRequest(AphrontRequest
$request) {
266 $viewer = $this->getViewer();
271 if ($request->isFormPost()) {
272 $project_phids = $request->getArr('projectPHIDs');
273 $project_phid = head($project_phids);
275 $project = id(new PhabricatorObjectQuery())
277 ->withPHIDs(array($project_phid))
281 // Save this project as one of the user's most recently used projects,
282 // so we'll show it by default in future menus.
284 $favorites_key = PhabricatorPolicyFavoritesSetting
::SETTINGKEY
;
285 $favorites = $viewer->getUserSetting($favorites_key);
286 if (!is_array($favorites)) {
287 $favorites = array();
290 // Add this, or move it to the end of the list.
291 unset($favorites[$project_phid]);
292 $favorites[$project_phid] = true;
294 $preferences = PhabricatorUserPreferences
::loadUserPreferences($viewer);
296 $editor = id(new PhabricatorUserPreferencesEditor())
298 ->setContentSourceFromRequest($request)
299 ->setContinueOnNoEffect(true)
300 ->setContinueOnMissingFields(true);
303 $xactions[] = $preferences->newTransaction($favorites_key, $favorites);
304 $editor->applyTransactions($preferences, $xactions);
307 'phid' => $project->getPHID(),
309 'name' => $project->getName(),
310 'full' => $project->getName(),
311 'icon' => $project->getDisplayIconIcon(),
315 return id(new AphrontAjaxResponse())->setContent($data);
317 $errors[] = pht('You must choose a project.');
318 $e_project = pht('Required');
322 $project_datasource = id(new PhabricatorProjectDatasource())
328 $form = id(new AphrontFormView())
331 id(new AphrontFormTokenizerControl())
332 ->setLabel(pht('Members Of'))
333 ->setName('projectPHIDs')
335 ->setError($e_project)
336 ->setDatasource($project_datasource));
338 return $this->newDialog()
339 ->setWidth(AphrontDialogView
::WIDTH_FORM
)
341 ->setTitle(pht('Select Project'))
343 ->addSubmitButton(pht('Done'))
344 ->addCancelButton('#');