@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 353 lines 11 kB view raw
1<?php 2 3final class HeraldNewController extends HeraldController { 4 5 public function handleRequest(AphrontRequest $request) { 6 $viewer = $this->getViewer(); 7 8 $adapter_type_map = HeraldAdapter::getEnabledAdapterMap($viewer); 9 $adapter_type = $request->getStr('adapter'); 10 11 if (!$adapter_type || !isset($adapter_type_map[$adapter_type])) { 12 $title = pht('Create Herald Rule'); 13 $content = $this->newAdapterMenu($title); 14 } else { 15 $adapter = HeraldAdapter::getAdapterForContentType($adapter_type); 16 17 $rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap(); 18 $rule_type = $request->getStr('type'); 19 20 if (!$rule_type || !isset($rule_type_map[$rule_type])) { 21 $title = pht( 22 'Create Herald Rule: %s', 23 $adapter->getAdapterContentName()); 24 25 $content = $this->newTypeMenu($adapter, $title); 26 } else { 27 if ($rule_type !== HeraldRuleTypeConfig::RULE_TYPE_OBJECT) { 28 $target_phid = null; 29 $target_okay = true; 30 } else { 31 $object_name = $request->getStr('objectName'); 32 $target_okay = false; 33 34 $errors = array(); 35 $e_object = null; 36 37 if ($request->isFormPost()) { 38 if (strlen($object_name)) { 39 $target_object = id(new PhabricatorObjectQuery()) 40 ->setViewer($viewer) 41 ->withNames(array($object_name)) 42 ->executeOne(); 43 if ($target_object) { 44 $can_edit = PhabricatorPolicyFilter::hasCapability( 45 $viewer, 46 $target_object, 47 PhabricatorPolicyCapability::CAN_EDIT); 48 if (!$can_edit) { 49 $errors[] = pht( 50 'You can not create a rule for that object, because you '. 51 'do not have permission to edit it. You can only create '. 52 'rules for objects you can edit.'); 53 $e_object = pht('Not Editable'); 54 } else { 55 if (!$adapter->canTriggerOnObject($target_object)) { 56 $errors[] = pht( 57 'This object is not of an allowed type for the rule. '. 58 'Rules can only trigger on certain objects.'); 59 $e_object = pht('Invalid'); 60 } else { 61 $target_phid = $target_object->getPHID(); 62 } 63 } 64 } else { 65 $errors[] = pht('No object exists by that name.'); 66 $e_object = pht('Invalid'); 67 } 68 } else { 69 $errors[] = pht( 70 'You must choose an object to associate this rule with.'); 71 $e_object = pht('Required'); 72 } 73 74 $target_okay = !$errors; 75 } 76 } 77 78 if (!$target_okay) { 79 $title = pht('Choose Object'); 80 $content = $this->newTargetForm( 81 $adapter, 82 $rule_type, 83 $object_name, 84 $errors, 85 $e_object, 86 $title); 87 } else { 88 $params = array( 89 'content_type' => $adapter_type, 90 'rule_type' => $rule_type, 91 'targetPHID' => $target_phid, 92 ); 93 94 $edit_uri = $this->getApplicationURI('edit/'); 95 $edit_uri = new PhutilURI($edit_uri, $params); 96 97 return id(new AphrontRedirectResponse()) 98 ->setURI($edit_uri); 99 } 100 } 101 } 102 103 $crumbs = $this 104 ->buildApplicationCrumbs() 105 ->addTextCrumb(pht('Create Rule')) 106 ->setBorder(true); 107 108 $view = id(new PHUITwoColumnView()) 109 ->setFooter($content); 110 111 return $this->newPage() 112 ->setTitle($title) 113 ->setCrumbs($crumbs) 114 ->appendChild($view); 115 } 116 117 private function newAdapterMenu($title) { 118 $viewer = $this->getViewer(); 119 120 $types = HeraldAdapter::getEnabledAdapterMap($viewer); 121 122 foreach ($types as $key => $type) { 123 $types[$key] = HeraldAdapter::getAdapterForContentType($key); 124 } 125 126 $types = msort($types, 'getAdapterContentName'); 127 128 $base_uri = $this->getApplicationURI('create/'); 129 130 $menu = id(new PHUIObjectItemListView()) 131 ->setViewer($viewer) 132 ->setBig(true); 133 134 foreach ($types as $key => $adapter) { 135 $adapter_uri = id(new PhutilURI($base_uri)) 136 ->replaceQueryParam('adapter', $key); 137 138 $description = $adapter->getAdapterContentDescription(); 139 $description = phutil_escape_html_newlines($description); 140 141 $item = id(new PHUIObjectItemView()) 142 ->setHeader($adapter->getAdapterContentName()) 143 ->setImageIcon($adapter->getAdapterContentIcon()) 144 ->addAttribute($description) 145 ->setHref($adapter_uri) 146 ->setClickable(true); 147 148 $menu->addItem($item); 149 } 150 151 $box = id(new PHUIObjectBoxView()) 152 ->setHeaderText($title) 153 ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) 154 ->setObjectList($menu); 155 156 return id(new PHUILauncherView()) 157 ->appendChild($box); 158 } 159 160 private function newTypeMenu(HeraldAdapter $adapter, $title) { 161 $viewer = $this->getViewer(); 162 163 $global_capability = HeraldManageGlobalRulesCapability::CAPABILITY; 164 $can_global = $this->hasApplicationCapability($global_capability); 165 166 if ($can_global) { 167 $global_note = pht( 168 'You have permission to create and manage global rules.'); 169 } else { 170 $global_note = pht( 171 'You do not have permission to create or manage global rules.'); 172 } 173 $global_note = phutil_tag('em', array(), $global_note); 174 175 $specs = array( 176 HeraldRuleTypeConfig::RULE_TYPE_PERSONAL => array( 177 'name' => pht('Personal Rule'), 178 'icon' => 'fa-user', 179 'help' => pht( 180 'Personal rules notify you about events. You own them, but they can '. 181 'only affect you. Personal rules only trigger for objects you have '. 182 'permission to see.'), 183 'enabled' => true, 184 ), 185 HeraldRuleTypeConfig::RULE_TYPE_OBJECT => array( 186 'name' => pht('Object Rule'), 187 'icon' => 'fa-cube', 188 'help' => pht( 189 'Object rules notify anyone about events. They are bound to an '. 190 'object (like a repository) and can only act on that object. You '. 191 'must be able to edit an object to create object rules for it. '. 192 'Other users who can edit the object can edit its rules.'), 193 'enabled' => true, 194 ), 195 HeraldRuleTypeConfig::RULE_TYPE_GLOBAL => array( 196 'name' => pht('Global Rule'), 197 'icon' => 'fa-globe', 198 'help' => array( 199 pht( 200 'Global rules notify anyone about events. Global rules can '. 201 'bypass access control policies and act on any object.'), 202 $global_note, 203 ), 204 'enabled' => $can_global, 205 ), 206 ); 207 208 $adapter_type = $adapter->getAdapterContentType(); 209 210 $base_uri = new PhutilURI($this->getApplicationURI('create/')); 211 212 $adapter_uri = id(clone $base_uri) 213 ->replaceQueryParam('adapter', $adapter_type); 214 215 $menu = id(new PHUIObjectItemListView()) 216 ->setUser($viewer) 217 ->setBig(true); 218 219 foreach ($specs as $rule_type => $spec) { 220 $type_uri = id(clone $adapter_uri) 221 ->replaceQueryParam('type', $rule_type); 222 223 $name = $spec['name']; 224 $icon = $spec['icon']; 225 226 $description = $spec['help']; 227 $description = (array)$description; 228 229 $enabled = $spec['enabled']; 230 if ($enabled) { 231 $enabled = $adapter->supportsRuleType($rule_type); 232 if (!$enabled) { 233 $description[] = phutil_tag( 234 'em', 235 array(), 236 pht( 237 'This rule type is not supported by the selected '. 238 'content type.')); 239 } 240 } 241 242 $description = phutil_implode_html( 243 array( 244 phutil_tag('br'), 245 phutil_tag('br'), 246 ), 247 $description); 248 249 $item = id(new PHUIObjectItemView()) 250 ->setHeader($name) 251 ->setImageIcon($icon) 252 ->addAttribute($description); 253 254 if ($enabled) { 255 $item 256 ->setHref($type_uri) 257 ->setClickable(true); 258 } else { 259 $item->setDisabled(true); 260 } 261 262 $menu->addItem($item); 263 } 264 265 $box = id(new PHUIObjectBoxView()) 266 ->setHeaderText($title) 267 ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) 268 ->setObjectList($menu); 269 270 $box->newTailButton() 271 ->setText(pht('Back to Content Types')) 272 ->setIcon('fa-chevron-left') 273 ->setHref($base_uri); 274 275 return id(new PHUILauncherView()) 276 ->appendChild($box); 277 } 278 279 280 private function newTargetForm( 281 HeraldAdapter $adapter, 282 $rule_type, 283 $object_name, 284 $errors, 285 $e_object, 286 $title) { 287 288 $viewer = $this->getViewer(); 289 $content_type = $adapter->getAdapterContentType(); 290 $rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap(); 291 292 $params = array( 293 'adapter' => $content_type, 294 'type' => $rule_type, 295 ); 296 297 $form = id(new AphrontFormView()) 298 ->setViewer($viewer) 299 ->appendChild( 300 id(new AphrontFormStaticControl()) 301 ->setLabel(pht('Rule for')) 302 ->setValue( 303 phutil_tag( 304 'strong', 305 array(), 306 $adapter->getAdapterContentName()))) 307 ->appendChild( 308 id(new AphrontFormStaticControl()) 309 ->setLabel(pht('Rule Type')) 310 ->setValue( 311 phutil_tag( 312 'strong', 313 array(), 314 idx($rule_type_map, $rule_type)))) 315 ->appendRemarkupInstructions( 316 pht( 317 'Choose the object this rule will act on (for example, enter '. 318 '`rX` to act on the `rX` repository, or `#project` to act on '. 319 'a project).')) 320 ->appendRemarkupInstructions( 321 $adapter->explainValidTriggerObjects()) 322 ->appendChild( 323 id(new AphrontFormTextControl()) 324 ->setName('objectName') 325 ->setError($e_object) 326 ->setValue($object_name) 327 ->setLabel(pht('Object'))); 328 329 foreach ($params as $key => $value) { 330 $form->addHiddenInput($key, $value); 331 } 332 333 $cancel_params = $params; 334 unset($cancel_params['type']); 335 336 $cancel_uri = $this->getApplicationURI('create/'); 337 $cancel_uri = new PhutilURI($cancel_uri, $cancel_params); 338 339 $form->appendChild( 340 id(new AphrontFormSubmitControl()) 341 ->setValue(pht('Continue')) 342 ->addCancelButton($cancel_uri, pht('Back'))); 343 344 $form_box = id(new PHUIObjectBoxView()) 345 ->setHeaderText($title) 346 ->setFormErrors($errors) 347 ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) 348 ->setForm($form); 349 350 return $form_box; 351 } 352 353}