@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
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}