@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 SlowvoteEmbedView extends AphrontView {
4
5 private $poll;
6 private $handles;
7
8 public function setPoll(PhabricatorSlowvotePoll $poll) {
9 $this->poll = $poll;
10 return $this;
11 }
12
13 public function getPoll() {
14 return $this->poll;
15 }
16
17 public function render() {
18 if (!$this->poll) {
19 throw new PhutilInvalidStateException('setPoll');
20 }
21
22 $poll = $this->poll;
23
24 $phids = array();
25 foreach ($poll->getChoices() as $choice) {
26 $phids[] = $choice->getAuthorPHID();
27 }
28 $phids[] = $poll->getAuthorPHID();
29
30 $this->handles = id(new PhabricatorHandleQuery())
31 ->setViewer($this->getUser())
32 ->withPHIDs($phids)
33 ->execute();
34
35 $options = $poll->getOptions();
36
37 if ($poll->getShuffle()) {
38 shuffle($options);
39 }
40
41 require_celerity_resource('phabricator-slowvote-css');
42
43 $user_choices = $poll->getViewerChoices($this->getUser());
44 $user_choices = mpull($user_choices, 'getOptionID', 'getOptionID');
45
46 $out = array();
47 foreach ($options as $option) {
48 $is_selected = isset($user_choices[$option->getID()]);
49 $out[] = $this->renderLabel($option, $is_selected);
50 }
51
52 $link_to_slowvote = phutil_tag(
53 'a',
54 array(
55 'href' => '/V'.$poll->getID(),
56 ),
57 $poll->getQuestion());
58
59 $header = id(new PHUIHeaderView())
60 ->setHeader($link_to_slowvote);
61
62 $description = $poll->getDescription();
63 if (strlen($description)) {
64 $description = new PHUIRemarkupView($this->getUser(), $description);
65 $description = phutil_tag(
66 'div',
67 array(
68 'class' => 'slowvote-description',
69 ),
70 $description);
71 }
72
73 $header = array(
74 $header,
75 $description,
76 );
77
78 $quip = pht('Voting improves cardiovascular endurance.');
79
80 $vis = $poll->getResponseVisibility();
81 if ($this->areResultsVisible()) {
82 if ($vis == SlowvotePollResponseVisibility::RESPONSES_OWNER) {
83 $quip = pht('Only you can see the results.');
84 }
85 } else if ($vis == SlowvotePollResponseVisibility::RESPONSES_VOTERS) {
86 $quip = pht('You must vote to see the results.');
87 } else if ($vis == SlowvotePollResponseVisibility::RESPONSES_OWNER) {
88 $quip = pht('Only the author can see the results.');
89 }
90
91 $hint = phutil_tag(
92 'span',
93 array(
94 'class' => 'slowvote-hint',
95 ),
96 $quip);
97
98 if ($poll->isClosed()) {
99 $submit = null;
100 } else {
101 $submit = phutil_tag(
102 'div',
103 array(
104 'class' => 'slowvote-footer',
105 ),
106 phutil_tag(
107 'div',
108 array(
109 'class' => 'slowvote-footer-content',
110 ),
111 array(
112 $hint,
113 phutil_tag(
114 'button',
115 array(
116 ),
117 pht('Engage in Deliberations')),
118 )));
119 }
120
121 $body = phabricator_form(
122 $this->getUser(),
123 array(
124 'action' => '/vote/'.$poll->getID().'/',
125 'method' => 'POST',
126 'class' => 'slowvote-body',
127 ),
128 array(
129 phutil_tag(
130 'div',
131 array(
132 'class' => 'slowvote-body-content',
133 ),
134 $out),
135 $submit,
136 ));
137
138 $embed = javelin_tag(
139 'div',
140 array(
141 'class' => 'slowvote-embed',
142 'sigil' => 'slowvote-embed',
143 'meta' => array(
144 'pollID' => $poll->getID(),
145 ),
146 ),
147 array($body));
148
149 return id(new PHUIObjectBoxView())
150 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
151 ->setHeader($header)
152 ->appendChild($embed)
153 ->addClass('slowvote-poll-view');
154 }
155
156 private function renderLabel(PhabricatorSlowvoteOption $option, $selected) {
157 $classes = array();
158 $classes[] = 'slowvote-option-label';
159
160 $status = $this->renderStatus($option);
161 $voters = $this->renderVoters($option);
162
163 return phutil_tag(
164 'div',
165 array(
166 'class' => 'slowvote-option-label-group',
167 ),
168 array(
169 phutil_tag(
170 'label',
171 array(
172 'class' => implode(' ', $classes),
173 ),
174 array(
175 phutil_tag(
176 'div',
177 array(
178 'class' => 'slowvote-control-offset',
179 ),
180 $option->getName()),
181 $this->renderBar($option),
182 phutil_tag(
183 'div',
184 array(
185 'class' => 'slowvote-above-the-bar',
186 ),
187 array(
188 $this->renderControl($option, $selected),
189 $status,
190 )),
191 )),
192 $voters,
193 ));
194 }
195
196 private function renderBar(PhabricatorSlowvoteOption $option) {
197 if (!$this->areResultsVisible()) {
198 return null;
199 }
200
201 $poll = $this->getPoll();
202
203 $choices = mgroup($poll->getChoices(), 'getOptionID');
204 $choices = count(idx($choices, $option->getID(), array()));
205 $count = count(mgroup($poll->getChoices(), 'getAuthorPHID'));
206
207 return phutil_tag(
208 'div',
209 array(
210 'class' => 'slowvote-bar',
211 'style' => sprintf(
212 'width: %.1f%%;',
213 $count ? 100 * ($choices / $count) : 0),
214 ),
215 array(
216 phutil_tag(
217 'div',
218 array(
219 'class' => 'slowvote-control-offset',
220 ),
221 $option->getName()),
222 ));
223 }
224
225 private function renderControl(PhabricatorSlowvoteOption $option, $selected) {
226 $types = array(
227 SlowvotePollVotingMethod::METHOD_PLURALITY => 'radio',
228 SlowvotePollVotingMethod::METHOD_APPROVAL => 'checkbox',
229 );
230
231 $closed = $this->getPoll()->isClosed();
232
233 return phutil_tag(
234 'input',
235 array(
236 'type' => idx($types, $this->getPoll()->getMethod()),
237 'name' => 'vote[]',
238 'value' => $option->getID(),
239 'checked' => ($selected ? 'checked' : null),
240 'disabled' => ($closed ? 'disabled' : null),
241 ));
242 }
243
244 private function renderVoters(PhabricatorSlowvoteOption $option) {
245 if (!$this->areResultsVisible()) {
246 return null;
247 }
248
249 $poll = $this->getPoll();
250
251 $choices = mgroup($poll->getChoices(), 'getOptionID');
252 $choices = idx($choices, $option->getID(), array());
253
254 if (!$choices) {
255 return null;
256 }
257
258 $handles = $this->handles;
259 $authors = mpull($choices, 'getAuthorPHID', 'getAuthorPHID');
260
261 $viewer_phid = $this->getUser()->getPHID();
262
263 // Put the viewer first if they've voted for this option.
264 $authors = array_select_keys($authors, array($viewer_phid))
265 + $authors;
266
267 $voters = array();
268 foreach ($authors as $author_phid) {
269 $handle = $handles[$author_phid];
270
271 $voters[] = javelin_tag(
272 'div',
273 array(
274 'class' => 'slowvote-voter',
275 'style' => 'background-image: url('.$handle->getImageURI().')',
276 'sigil' => 'has-tooltip',
277 'meta' => array(
278 'tip' => $handle->getName(),
279 ),
280 ));
281 }
282
283 return phutil_tag(
284 'div',
285 array(
286 'class' => 'slowvote-voters',
287 ),
288 $voters);
289 }
290
291 private function renderStatus(PhabricatorSlowvoteOption $option) {
292 if (!$this->areResultsVisible()) {
293 return null;
294 }
295
296 $poll = $this->getPoll();
297
298 $choices = mgroup($poll->getChoices(), 'getOptionID');
299 $choices = count(idx($choices, $option->getID(), array()));
300 $count = count(mgroup($poll->getChoices(), 'getAuthorPHID'));
301
302 $percent = sprintf('%d%%', $count ? 100 * $choices / $count : 0);
303
304 $method = $poll->getMethod();
305 switch ($method) {
306 case SlowvotePollVotingMethod::METHOD_PLURALITY:
307 $status = pht('%s (%d / %d)', $percent, $choices, $count);
308 break;
309 case SlowvotePollVotingMethod::METHOD_APPROVAL:
310 $status = pht('%s Approval (%d / %d)', $percent, $choices, $count);
311 break;
312 default:
313 $status = pht('Unknown ("%s")', $method);
314 break;
315 }
316
317 return phutil_tag(
318 'div',
319 array(
320 'class' => 'slowvote-status',
321 ),
322 $status);
323 }
324
325 private function areResultsVisible() {
326 $poll = $this->getPoll();
327
328 $visibility = $poll->getResponseVisibility();
329 if ($visibility == SlowvotePollResponseVisibility::RESPONSES_VISIBLE) {
330 return true;
331 }
332
333 $viewer = $this->getViewer();
334
335 if ($visibility == SlowvotePollResponseVisibility::RESPONSES_OWNER) {
336 return ($poll->getAuthorPHID() === $viewer->getPHID());
337 }
338
339 $choices = mgroup($poll->getChoices(), 'getAuthorPHID');
340 return (bool)idx($choices, $viewer->getPHID());
341 }
342
343}