@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 232 lines 6.7 kB view raw
1<?php 2 3final class PhabricatorProjectTriggerViewController 4 extends PhabricatorProjectTriggerController { 5 6 public function shouldAllowPublic() { 7 return true; 8 } 9 10 public function handleRequest(AphrontRequest $request) { 11 $request = $this->getRequest(); 12 $viewer = $request->getViewer(); 13 14 $id = $request->getURIData('id'); 15 16 $trigger = id(new PhabricatorProjectTriggerQuery()) 17 ->setViewer($viewer) 18 ->withIDs(array($id)) 19 ->executeOne(); 20 if (!$trigger) { 21 return new Aphront404Response(); 22 } 23 $trigger->setViewer($viewer); 24 25 $rules_view = $this->newRulesView($trigger); 26 $columns_view = $this->newColumnsView($trigger); 27 28 $title = $trigger->getObjectName(); 29 30 $header = id(new PHUIHeaderView()) 31 ->setHeader($trigger->getDisplayName()); 32 33 $timeline = $this->buildTransactionTimeline( 34 $trigger, 35 new PhabricatorProjectTriggerTransactionQuery()); 36 $timeline->setShouldTerminate(true); 37 38 $curtain = $this->newCurtain($trigger); 39 40 $column_view = id(new PHUITwoColumnView()) 41 ->setHeader($header) 42 ->setCurtain($curtain) 43 ->setMainColumn( 44 array( 45 $rules_view, 46 $columns_view, 47 $timeline, 48 )); 49 50 $crumbs = $this->buildApplicationCrumbs() 51 ->addTextCrumb($trigger->getObjectName()) 52 ->setBorder(true); 53 54 return $this->newPage() 55 ->setTitle($title) 56 ->setCrumbs($crumbs) 57 ->appendChild($column_view); 58 } 59 60 private function newColumnsView(PhabricatorProjectTrigger $trigger) { 61 $viewer = $this->getViewer(); 62 63 // NOTE: When showing columns which use this trigger, we want to represent 64 // all columns the trigger is used by: even columns the user can't see. 65 66 // If we hide columns the viewer can't see, they might think that the 67 // trigger isn't widely used and is safe to edit, when it may actually 68 // be in use on workboards they don't have access to. 69 70 // Query the columns with the omnipotent viewer first, then pull out their 71 // PHIDs and throw the actual objects away. Re-query with the real viewer 72 // so we load only the columns they can actually see, but have a list of 73 // all the impacted column PHIDs. 74 75 // (We're also exposing the status of columns the user might not be able 76 // to see. This technically violates policy, but the trigger usage table 77 // hints at it anyway and it seems unlikely to ever have any security 78 // impact, but is useful in assessing whether a trigger is really in use 79 // or not.) 80 81 $omnipotent_viewer = PhabricatorUser::getOmnipotentUser(); 82 $all_columns = id(new PhabricatorProjectColumnQuery()) 83 ->setViewer($omnipotent_viewer) 84 ->withTriggerPHIDs(array($trigger->getPHID())) 85 ->execute(); 86 $column_map = mpull($all_columns, 'getStatus', 'getPHID'); 87 88 if ($column_map) { 89 $visible_columns = id(new PhabricatorProjectColumnQuery()) 90 ->setViewer($viewer) 91 ->withPHIDs(array_keys($column_map)) 92 ->execute(); 93 $visible_columns = mpull($visible_columns, null, 'getPHID'); 94 } else { 95 $visible_columns = array(); 96 } 97 98 $rows = array(); 99 foreach ($column_map as $column_phid => $column_status) { 100 $column = idx($visible_columns, $column_phid); 101 102 if ($column) { 103 $project = $column->getProject(); 104 105 $project_name = phutil_tag( 106 'a', 107 array( 108 'href' => $project->getURI(), 109 ), 110 $project->getDisplayName()); 111 112 $column_name = phutil_tag( 113 'a', 114 array( 115 'href' => $column->getWorkboardURI(), 116 ), 117 $column->getDisplayName()); 118 } else { 119 $project_name = null; 120 $column_name = phutil_tag('em', array(), pht('Restricted Column')); 121 } 122 123 if ($column_status == PhabricatorProjectColumn::STATUS_ACTIVE) { 124 $status_icon = id(new PHUIIconView()) 125 ->setIcon('fa-columns', 'blue') 126 ->setTooltip(pht('Active Column')); 127 } else { 128 $status_icon = id(new PHUIIconView()) 129 ->setIcon('fa-eye-slash', 'grey') 130 ->setTooltip(pht('Hidden Column')); 131 } 132 133 $rows[] = array( 134 $status_icon, 135 $project_name, 136 $column_name, 137 ); 138 } 139 140 $table_view = id(new AphrontTableView($rows)) 141 ->setNoDataString(pht('This trigger is not used by any columns.')) 142 ->setHeaders( 143 array( 144 null, 145 pht('Project'), 146 pht('Column'), 147 )) 148 ->setColumnClasses( 149 array( 150 null, 151 null, 152 'wide pri', 153 )); 154 155 $header_view = id(new PHUIHeaderView()) 156 ->setHeader(pht('Used by Columns')); 157 158 return id(new PHUIObjectBoxView()) 159 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 160 ->setHeader($header_view) 161 ->setTable($table_view); 162 } 163 164 private function newRulesView(PhabricatorProjectTrigger $trigger) { 165 $viewer = $this->getViewer(); 166 $rules = $trigger->getTriggerRules(); 167 168 $rows = array(); 169 foreach ($rules as $rule) { 170 $value = $rule->getRecord()->getValue(); 171 172 $rows[] = array( 173 $rule->getRuleViewIcon($value), 174 $rule->getRuleViewLabel(), 175 $rule->getRuleViewDescription($value), 176 ); 177 } 178 179 $table_view = id(new AphrontTableView($rows)) 180 ->setNoDataString(pht('This trigger has no rules.')) 181 ->setHeaders( 182 array( 183 null, 184 pht('Rule'), 185 pht('Action'), 186 )) 187 ->setColumnClasses( 188 array( 189 null, 190 'pri', 191 'wide', 192 )); 193 194 $header_view = id(new PHUIHeaderView()) 195 ->setHeader(pht('Trigger Rules')) 196 ->setSubheader( 197 pht( 198 'When a card is dropped into a column that uses this trigger, '. 199 'these actions will be taken.')); 200 201 return id(new PHUIObjectBoxView()) 202 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 203 ->setHeader($header_view) 204 ->setTable($table_view); 205 } 206 private function newCurtain(PhabricatorProjectTrigger $trigger) { 207 $viewer = $this->getViewer(); 208 209 $can_edit = PhabricatorPolicyFilter::hasCapability( 210 $viewer, 211 $trigger, 212 PhabricatorPolicyCapability::CAN_EDIT); 213 214 $curtain = $this->newCurtainView($trigger); 215 216 $edit_uri = $this->getApplicationURI( 217 urisprintf( 218 'trigger/edit/%d/', 219 $trigger->getID())); 220 221 $curtain->addAction( 222 id(new PhabricatorActionView()) 223 ->setName(pht('Edit Trigger')) 224 ->setIcon('fa-pencil') 225 ->setHref($edit_uri) 226 ->setDisabled(!$can_edit) 227 ->setWorkflow(!$can_edit)); 228 229 return $curtain; 230 } 231 232}