2013-04-16 17:18:52 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @group slowvote
|
|
|
|
*/
|
|
|
|
final class SlowvoteEmbedView extends AphrontView {
|
|
|
|
|
|
|
|
private $poll;
|
2013-07-15 22:18:50 +02:00
|
|
|
private $handles;
|
|
|
|
private $headless;
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
public function setHeadless($headless) {
|
|
|
|
$this->headless = $headless;
|
2013-04-16 17:18:52 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
public function setPoll(PhabricatorSlowvotePoll $poll) {
|
|
|
|
$this->poll = $poll;
|
2013-04-16 17:18:52 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
public function getPoll() {
|
|
|
|
return $this->poll;
|
2013-04-16 17:18:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function render() {
|
|
|
|
if (!$this->poll) {
|
|
|
|
throw new Exception("Call setPoll() before render()!");
|
|
|
|
}
|
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$poll = $this->poll;
|
|
|
|
|
|
|
|
$phids = array();
|
|
|
|
foreach ($poll->getChoices() as $choice) {
|
|
|
|
$phids[] = $choice->getAuthorPHID();
|
2013-04-16 17:18:52 +02:00
|
|
|
}
|
2013-07-15 22:18:50 +02:00
|
|
|
$phids[] = $poll->getAuthorPHID();
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-09-11 21:27:28 +02:00
|
|
|
$this->handles = id(new PhabricatorHandleQuery())
|
2013-07-15 22:18:50 +02:00
|
|
|
->setViewer($this->getUser())
|
2013-09-11 21:27:28 +02:00
|
|
|
->withPHIDs($phids)
|
|
|
|
->execute();
|
2013-07-15 22:18:50 +02:00
|
|
|
|
|
|
|
$options = $poll->getOptions();
|
|
|
|
|
|
|
|
if ($poll->getShuffle()) {
|
|
|
|
shuffle($options);
|
2013-04-16 17:18:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
require_celerity_resource('phabricator-slowvote-css');
|
2013-05-04 00:48:59 +02:00
|
|
|
require_celerity_resource('javelin-behavior-slowvote-embed');
|
|
|
|
|
|
|
|
$config = array(
|
2013-07-15 22:18:50 +02:00
|
|
|
'pollID' => $poll->getID());
|
2013-05-04 00:48:59 +02:00
|
|
|
Javelin::initBehavior('slowvote-embed', $config);
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$user_choices = $poll->getViewerChoices($this->getUser());
|
|
|
|
$user_choices = mpull($user_choices, 'getOptionID', 'getOptionID');
|
|
|
|
|
|
|
|
$out = array();
|
|
|
|
foreach ($options as $option) {
|
|
|
|
$is_selected = isset($user_choices[$option->getID()]);
|
|
|
|
$out[] = $this->renderLabel($option, $is_selected);
|
2013-04-16 17:18:52 +02:00
|
|
|
}
|
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$link_to_slowvote = phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/V'.$poll->getID()
|
|
|
|
),
|
|
|
|
$poll->getQuestion());
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
if ($this->headless) {
|
|
|
|
$header = null;
|
|
|
|
} else {
|
|
|
|
$header = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-header',
|
|
|
|
),
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-header-content',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
'V'.$poll->getID(),
|
|
|
|
' ',
|
|
|
|
$link_to_slowvote)));
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$description = null;
|
|
|
|
if ($poll->getDescription()) {
|
|
|
|
$description = PhabricatorMarkupEngine::renderOneObject(
|
|
|
|
id(new PhabricatorMarkupOneOff())->setContent(
|
|
|
|
$poll->getDescription()),
|
|
|
|
'default',
|
|
|
|
$this->getUser());
|
|
|
|
$description = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-description',
|
|
|
|
),
|
|
|
|
$description);
|
|
|
|
}
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$header = array(
|
|
|
|
$header,
|
|
|
|
$description);
|
|
|
|
}
|
2013-05-04 00:48:59 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$vis = $poll->getResponseVisibility();
|
|
|
|
if ($this->areResultsVisible()) {
|
|
|
|
if ($vis == PhabricatorSlowvotePoll::RESPONSES_OWNER) {
|
|
|
|
$quip = pht('Only you can see the results.');
|
|
|
|
} else {
|
|
|
|
$quip = pht('Voting improves cardiovascular endurance.');
|
2013-04-16 17:18:52 +02:00
|
|
|
}
|
2013-07-15 22:18:50 +02:00
|
|
|
} else if ($vis == PhabricatorSlowvotePoll::RESPONSES_VOTERS) {
|
|
|
|
$quip = pht('You must vote to see the results.');
|
|
|
|
} else if ($vis == PhabricatorSlowvotePoll::RESPONSES_OWNER) {
|
|
|
|
$quip = pht('Only the author can see the results.');
|
|
|
|
}
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$hint = phutil_tag(
|
|
|
|
'span',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-hint',
|
|
|
|
),
|
|
|
|
$quip);
|
2013-05-04 00:48:59 +02:00
|
|
|
|
2014-04-24 21:00:31 +02:00
|
|
|
if ($poll->getIsClosed()) {
|
|
|
|
$submit = null;
|
|
|
|
} else {
|
|
|
|
$submit = phutil_tag(
|
2013-05-04 00:48:59 +02:00
|
|
|
'div',
|
|
|
|
array(
|
2014-04-24 21:00:31 +02:00
|
|
|
'class' => 'slowvote-footer',
|
2013-05-04 00:48:59 +02:00
|
|
|
),
|
2014-04-24 21:00:31 +02:00
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-footer-content',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
$hint,
|
|
|
|
phutil_tag(
|
|
|
|
'button',
|
|
|
|
array(
|
|
|
|
),
|
|
|
|
pht('Engage in Deliberations')),
|
|
|
|
)));
|
|
|
|
}
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
$body = phabricator_form(
|
|
|
|
$this->getUser(),
|
2013-04-16 17:18:52 +02:00
|
|
|
array(
|
2013-07-15 22:18:50 +02:00
|
|
|
'action' => '/vote/'.$poll->getID().'/',
|
|
|
|
'method' => 'POST',
|
|
|
|
'class' => 'slowvote-body',
|
2013-04-16 17:18:52 +02:00
|
|
|
),
|
2013-07-15 22:18:50 +02:00
|
|
|
array(
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-body-content',
|
|
|
|
),
|
|
|
|
$out),
|
|
|
|
$submit,
|
|
|
|
));
|
2013-04-16 17:18:52 +02:00
|
|
|
|
2013-05-04 00:48:59 +02:00
|
|
|
return javelin_tag(
|
2013-04-16 17:18:52 +02:00
|
|
|
'div',
|
|
|
|
array(
|
2013-07-15 22:18:50 +02:00
|
|
|
'class' => 'slowvote-embed',
|
2013-05-04 00:48:59 +02:00
|
|
|
'sigil' => 'slowvote-embed',
|
|
|
|
'meta' => array(
|
2013-07-15 22:18:50 +02:00
|
|
|
'pollID' => $poll->getID()
|
2013-05-04 00:48:59 +02:00
|
|
|
)
|
2013-04-16 17:18:52 +02:00
|
|
|
),
|
|
|
|
array($header, $body));
|
|
|
|
}
|
2013-05-04 00:48:59 +02:00
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
private function renderLabel(PhabricatorSlowvoteOption $option, $selected) {
|
|
|
|
$classes = array();
|
|
|
|
$classes[] = 'slowvote-option-label';
|
|
|
|
|
|
|
|
$status = $this->renderStatus($option);
|
|
|
|
$voters = $this->renderVoters($option);
|
|
|
|
|
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-option-label-group',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
phutil_tag(
|
|
|
|
'label',
|
|
|
|
array(
|
|
|
|
'class' => implode(' ', $classes),
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-control-offset',
|
|
|
|
),
|
|
|
|
$option->getName()),
|
|
|
|
$this->renderBar($option),
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-above-the-bar',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
$this->renderControl($option, $selected),
|
|
|
|
)),
|
|
|
|
)),
|
|
|
|
$status,
|
|
|
|
$voters,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderBar(PhabricatorSlowvoteOption $option) {
|
|
|
|
if (!$this->areResultsVisible()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$poll = $this->getPoll();
|
|
|
|
|
|
|
|
$choices = mgroup($poll->getChoices(), 'getOptionID');
|
|
|
|
$choices = count(idx($choices, $option->getID(), array()));
|
|
|
|
$count = count(mgroup($poll->getChoices(), 'getAuthorPHID'));
|
|
|
|
|
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-bar',
|
|
|
|
'style' => sprintf(
|
|
|
|
'width: %.1f%%;',
|
|
|
|
$count ? 100 * ($choices / $count) : 0),
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-control-offset',
|
|
|
|
),
|
|
|
|
$option->getName()),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderControl(PhabricatorSlowvoteOption $option, $selected) {
|
|
|
|
$types = array(
|
|
|
|
PhabricatorSlowvotePoll::METHOD_PLURALITY => 'radio',
|
|
|
|
PhabricatorSlowvotePoll::METHOD_APPROVAL => 'checkbox',
|
|
|
|
);
|
|
|
|
|
2014-04-24 21:00:31 +02:00
|
|
|
$closed = $this->getPoll()->getIsClosed();
|
|
|
|
|
2013-07-15 22:18:50 +02:00
|
|
|
return phutil_tag(
|
|
|
|
'input',
|
|
|
|
array(
|
|
|
|
'type' => idx($types, $this->getPoll()->getMethod()),
|
|
|
|
'name' => 'vote[]',
|
|
|
|
'value' => $option->getID(),
|
|
|
|
'checked' => ($selected ? 'checked' : null),
|
2014-04-24 21:00:31 +02:00
|
|
|
'disabled' => ($closed ? 'disabled' : null),
|
2013-07-15 22:18:50 +02:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderVoters(PhabricatorSlowvoteOption $option) {
|
|
|
|
if (!$this->areResultsVisible()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$poll = $this->getPoll();
|
|
|
|
|
|
|
|
$choices = mgroup($poll->getChoices(), 'getOptionID');
|
|
|
|
$choices = idx($choices, $option->getID(), array());
|
|
|
|
|
|
|
|
if (!$choices) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$handles = $this->handles;
|
|
|
|
$authors = mpull($choices, 'getAuthorPHID', 'getAuthorPHID');
|
|
|
|
|
|
|
|
$viewer_phid = $this->getUser()->getPHID();
|
|
|
|
|
|
|
|
// Put the viewer first if they've voted for this option.
|
|
|
|
$authors = array_select_keys($authors, array($viewer_phid))
|
|
|
|
+ $authors;
|
|
|
|
|
|
|
|
$voters = array();
|
|
|
|
foreach ($authors as $author_phid) {
|
|
|
|
$handle = $handles[$author_phid];
|
|
|
|
|
|
|
|
$voters[] = javelin_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-voter',
|
|
|
|
'style' => 'background-image: url('.$handle->getImageURI().')',
|
|
|
|
'sigil' => 'has-tooltip',
|
|
|
|
'meta' => array(
|
|
|
|
'tip' => $handle->getName(),
|
|
|
|
),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-voters',
|
|
|
|
),
|
|
|
|
$voters);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderStatus(PhabricatorSlowvoteOption $option) {
|
|
|
|
if (!$this->areResultsVisible()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$poll = $this->getPoll();
|
|
|
|
|
|
|
|
$choices = mgroup($poll->getChoices(), 'getOptionID');
|
|
|
|
$choices = count(idx($choices, $option->getID(), array()));
|
|
|
|
$count = count(mgroup($poll->getChoices(), 'getAuthorPHID'));
|
|
|
|
|
|
|
|
$percent = sprintf('%d%%', $count ? 100 * $choices / $count : 0);
|
|
|
|
|
|
|
|
switch ($poll->getMethod()) {
|
|
|
|
case PhabricatorSlowvotePoll::METHOD_PLURALITY:
|
|
|
|
$status = pht('%s (%d / %d)', $percent, $choices, $count);
|
|
|
|
break;
|
|
|
|
case PhabricatorSlowvotePoll::METHOD_APPROVAL:
|
|
|
|
$status = pht('%s Approval (%d / %d)', $percent, $choices, $count);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'slowvote-status',
|
|
|
|
),
|
|
|
|
$status);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function areResultsVisible() {
|
|
|
|
$poll = $this->getPoll();
|
|
|
|
|
|
|
|
$vis = $poll->getResponseVisibility();
|
|
|
|
if ($vis == PhabricatorSlowvotePoll::RESPONSES_VISIBLE) {
|
|
|
|
return true;
|
|
|
|
} else if ($vis == PhabricatorSlowvotePoll::RESPONSES_OWNER) {
|
|
|
|
return ($poll->getAuthorPHID() == $this->getUser()->getPHID());
|
|
|
|
} else {
|
|
|
|
$choices = mgroup($poll->getChoices(), 'getAuthorPHID');
|
|
|
|
return (bool)idx($choices, $this->getUser()->getPHID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-16 17:18:52 +02:00
|
|
|
}
|