diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index 3a0682422b..daa115c9ec 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -3475,7 +3475,7 @@ celerity_register_resource_map(array(
),
'phabricator-slowvote-css' =>
array(
- 'uri' => '/res/d1c2e05a/rsrc/css/application/slowvote/slowvote.css',
+ 'uri' => '/res/11373549/rsrc/css/application/slowvote/slowvote.css',
'type' => 'css',
'requires' =>
array(
@@ -3702,7 +3702,7 @@ celerity_register_resource_map(array(
),
'phabricator-zindex-css' =>
array(
- 'uri' => '/res/99a9447b/rsrc/css/core/z-index.css',
+ 'uri' => '/res/a50437bf/rsrc/css/core/z-index.css',
'type' => 'css',
'requires' =>
array(
@@ -4149,7 +4149,7 @@ celerity_register_resource_map(array(
), array(
'packages' =>
array(
- 'c01cebae' =>
+ 'f32a863a' =>
array(
'name' => 'core.pkg.css',
'symbols' =>
@@ -4197,7 +4197,7 @@ celerity_register_resource_map(array(
40 => 'phabricator-property-list-view-css',
41 => 'phabricator-tag-view-css',
),
- 'uri' => '/res/pkg/c01cebae/core.pkg.css',
+ 'uri' => '/res/pkg/f32a863a/core.pkg.css',
'type' => 'css',
),
'75ccea43' =>
@@ -4391,16 +4391,16 @@ celerity_register_resource_map(array(
'reverse' =>
array(
'aphront-attached-file-view-css' => 'adc3c36d',
- 'aphront-dialog-view-css' => 'c01cebae',
- 'aphront-error-view-css' => 'c01cebae',
- 'aphront-form-view-css' => 'c01cebae',
- 'aphront-list-filter-view-css' => 'c01cebae',
- 'aphront-pager-view-css' => 'c01cebae',
- 'aphront-panel-view-css' => 'c01cebae',
- 'aphront-table-view-css' => 'c01cebae',
- 'aphront-tokenizer-control-css' => 'c01cebae',
- 'aphront-tooltip-css' => 'c01cebae',
- 'aphront-typeahead-control-css' => 'c01cebae',
+ 'aphront-dialog-view-css' => 'f32a863a',
+ 'aphront-error-view-css' => 'f32a863a',
+ 'aphront-form-view-css' => 'f32a863a',
+ 'aphront-list-filter-view-css' => 'f32a863a',
+ 'aphront-pager-view-css' => 'f32a863a',
+ 'aphront-panel-view-css' => 'f32a863a',
+ 'aphront-table-view-css' => 'f32a863a',
+ 'aphront-tokenizer-control-css' => 'f32a863a',
+ 'aphront-tooltip-css' => 'f32a863a',
+ 'aphront-typeahead-control-css' => 'f32a863a',
'differential-changeset-view-css' => 'dd27a69b',
'differential-core-view-css' => 'dd27a69b',
'differential-inline-comment-editor' => '504ca7d2',
@@ -4414,7 +4414,7 @@ celerity_register_resource_map(array(
'differential-table-of-contents-css' => 'dd27a69b',
'diffusion-commit-view-css' => 'c8ce2d88',
'diffusion-icons-css' => 'c8ce2d88',
- 'global-drag-and-drop-css' => 'c01cebae',
+ 'global-drag-and-drop-css' => 'f32a863a',
'inline-comment-summary-css' => 'dd27a69b',
'javelin-aphlict' => '75ccea43',
'javelin-behavior' => 'a9f14d76',
@@ -4488,55 +4488,55 @@ celerity_register_resource_map(array(
'javelin-util' => 'a9f14d76',
'javelin-vector' => 'a9f14d76',
'javelin-workflow' => 'a9f14d76',
- 'lightbox-attachment-css' => 'c01cebae',
+ 'lightbox-attachment-css' => 'f32a863a',
'maniphest-task-summary-css' => 'adc3c36d',
'maniphest-transaction-detail-css' => 'adc3c36d',
- 'phabricator-action-list-view-css' => 'c01cebae',
- 'phabricator-application-launch-view-css' => 'c01cebae',
+ 'phabricator-action-list-view-css' => 'f32a863a',
+ 'phabricator-application-launch-view-css' => 'f32a863a',
'phabricator-busy' => '75ccea43',
'phabricator-content-source-view-css' => 'dd27a69b',
- 'phabricator-core-css' => 'c01cebae',
- 'phabricator-crumbs-view-css' => 'c01cebae',
+ 'phabricator-core-css' => 'f32a863a',
+ 'phabricator-crumbs-view-css' => 'f32a863a',
'phabricator-drag-and-drop-file-upload' => '504ca7d2',
'phabricator-dropdown-menu' => '75ccea43',
'phabricator-file-upload' => '75ccea43',
- 'phabricator-filetree-view-css' => 'c01cebae',
- 'phabricator-flag-css' => 'c01cebae',
- 'phabricator-form-view-css' => 'c01cebae',
- 'phabricator-header-view-css' => 'c01cebae',
+ 'phabricator-filetree-view-css' => 'f32a863a',
+ 'phabricator-flag-css' => 'f32a863a',
+ 'phabricator-form-view-css' => 'f32a863a',
+ 'phabricator-header-view-css' => 'f32a863a',
'phabricator-hovercard' => '75ccea43',
- 'phabricator-jump-nav' => 'c01cebae',
+ 'phabricator-jump-nav' => 'f32a863a',
'phabricator-keyboard-shortcut' => '75ccea43',
'phabricator-keyboard-shortcut-manager' => '75ccea43',
- 'phabricator-main-menu-view' => 'c01cebae',
+ 'phabricator-main-menu-view' => 'f32a863a',
'phabricator-menu-item' => '75ccea43',
- 'phabricator-nav-view-css' => 'c01cebae',
+ 'phabricator-nav-view-css' => 'f32a863a',
'phabricator-notification' => '75ccea43',
- 'phabricator-notification-css' => 'c01cebae',
- 'phabricator-notification-menu-css' => 'c01cebae',
- 'phabricator-object-item-list-view-css' => 'c01cebae',
+ 'phabricator-notification-css' => 'f32a863a',
+ 'phabricator-notification-menu-css' => 'f32a863a',
+ 'phabricator-object-item-list-view-css' => 'f32a863a',
'phabricator-object-selector-css' => 'dd27a69b',
'phabricator-phtize' => '75ccea43',
'phabricator-prefab' => '75ccea43',
'phabricator-project-tag-css' => 'adc3c36d',
- 'phabricator-property-list-view-css' => 'c01cebae',
- 'phabricator-remarkup-css' => 'c01cebae',
+ 'phabricator-property-list-view-css' => 'f32a863a',
+ 'phabricator-remarkup-css' => 'f32a863a',
'phabricator-shaped-request' => '504ca7d2',
- 'phabricator-side-menu-view-css' => 'c01cebae',
- 'phabricator-standard-page-view' => 'c01cebae',
- 'phabricator-tag-view-css' => 'c01cebae',
+ 'phabricator-side-menu-view-css' => 'f32a863a',
+ 'phabricator-standard-page-view' => 'f32a863a',
+ 'phabricator-tag-view-css' => 'f32a863a',
'phabricator-textareautils' => '75ccea43',
'phabricator-tooltip' => '75ccea43',
- 'phabricator-transaction-view-css' => 'c01cebae',
- 'phabricator-zindex-css' => 'c01cebae',
- 'phui-button-css' => 'c01cebae',
- 'phui-form-css' => 'c01cebae',
- 'phui-icon-view-css' => 'c01cebae',
- 'phui-spacing-css' => 'c01cebae',
- 'sprite-apps-large-css' => 'c01cebae',
- 'sprite-gradient-css' => 'c01cebae',
- 'sprite-icons-css' => 'c01cebae',
- 'sprite-menu-css' => 'c01cebae',
- 'syntax-highlighting-css' => 'c01cebae',
+ 'phabricator-transaction-view-css' => 'f32a863a',
+ 'phabricator-zindex-css' => 'f32a863a',
+ 'phui-button-css' => 'f32a863a',
+ 'phui-form-css' => 'f32a863a',
+ 'phui-icon-view-css' => 'f32a863a',
+ 'phui-spacing-css' => 'f32a863a',
+ 'sprite-apps-large-css' => 'f32a863a',
+ 'sprite-gradient-css' => 'f32a863a',
+ 'sprite-icons-css' => 'f32a863a',
+ 'sprite-menu-css' => 'f32a863a',
+ 'syntax-highlighting-css' => 'f32a863a',
),
));
diff --git a/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php b/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php
index a7be2d3a02..75c7740dcf 100644
--- a/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php
+++ b/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php
@@ -51,7 +51,7 @@ final class PhabricatorSlowvoteEditController
if ($request->isFormPost()) {
$v_question = $request->getStr('question');
$v_description = $request->getStr('description');
- $v_responses = $request->getInt('responses');
+ $v_responses = (int)$request->getInt('responses');
$v_shuffle = (int)$request->getBool('shuffle');
if ($is_new) {
diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php
index 8d8980be18..a61577c22b 100644
--- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php
+++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php
@@ -13,116 +13,40 @@ final class PhabricatorSlowvotePollController
}
public function processRequest() {
-
$request = $this->getRequest();
$user = $request->getUser();
- $viewer_phid = $user->getPHID();
$poll = id(new PhabricatorSlowvoteQuery())
->setViewer($user)
->withIDs(array($this->id))
->needOptions(true)
->needChoices(true)
+ ->needViewerChoices(true)
->executeOne();
if (!$poll) {
return new Aphront404Response();
}
- $options = $poll->getOptions();
- $choices = $poll->getChoices();
-
- $choices_by_option = mgroup($choices, 'getOptionID');
- $choices_by_user = mgroup($choices, 'getAuthorPHID');
- $viewer_choices = idx($choices_by_user, $viewer_phid, array());
+ $poll_view = id(new SlowvoteEmbedView())
+ ->setHeadless(true)
+ ->setUser($user)
+ ->setPoll($poll);
if ($request->isAjax()) {
- $embed = id(new SlowvoteEmbedView())
- ->setPoll($poll)
- ->setOptions($options)
- ->setViewerChoices($viewer_choices);
-
return id(new AphrontAjaxResponse())
- ->setContent(array(
- 'pollID' => $poll->getID(),
- 'contentHTML' => $embed->render()));
+ ->setContent(
+ array(
+ 'pollID' => $poll->getID(),
+ 'contentHTML' => $poll_view->render(),
+ ));
}
- require_celerity_resource('phabricator-slowvote-css');
-
- $phids = array_merge(
- mpull($choices, 'getAuthorPHID'),
- array(
- $poll->getAuthorPHID(),
- ));
-
- $query = new PhabricatorObjectHandleData($phids);
- $query->setViewer($user);
- $handles = $query->loadHandles();
- $objects = $query->loadObjects();
-
- if ($poll->getShuffle()) {
- shuffle($options);
- }
-
- $option_markup = array();
- foreach ($options as $option) {
- $option_markup[] = $this->renderPollOption(
- $poll,
- $viewer_choices,
- $option);
- }
-
- switch ($poll->getMethod()) {
- case PhabricatorSlowvotePoll::METHOD_PLURALITY:
- $choice_ids = array();
- foreach ($choices_by_user as $user_phid => $user_choices) {
- $choice_ids[$user_phid] = head($user_choices)->getOptionID();
- }
- break;
- case PhabricatorSlowvotePoll::METHOD_APPROVAL:
- break;
- default:
- throw new Exception("Unknown poll method!");
- }
-
- $result_markup = $this->renderResultMarkup(
- $poll,
- $options,
- $choices,
- $viewer_choices,
- $choices_by_option,
- $handles,
- $objects);
-
- if ($viewer_choices) {
- $instructions =
- pht('Your vote has been recorded... but there is still ample time to '.
- 'rethink your position. Have you thoroughly considered all possible '.
- 'eventualities?');
- } else {
- $instructions =
- pht('This is a weighty matter indeed. Consider your choices with the '.
- 'greatest of care.');
- }
-
- $form = id(new AphrontFormView())
- ->setUser($user)
- ->setFlexible(true)
- ->setAction(sprintf('/vote/%d/', $poll->getID()))
- ->appendChild(hsprintf(
- '
%s
',
- $instructions))
- ->appendChild(
- id(new AphrontFormMarkupControl())
- ->setLabel(pht('Vote'))
- ->setValue($option_markup))
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->setValue(pht('Engage in Deliberations')));
-
$header = id(new PhabricatorHeaderView())
->setHeader($poll->getQuestion());
+ $xaction_header = id(new PhabricatorHeaderView())
+ ->setHeader(pht('Ongoing Deliberations'));
+
$actions = $this->buildActionView($poll);
$properties = $this->buildPropertyView($poll);
@@ -131,15 +55,6 @@ final class PhabricatorSlowvotePollController
id(new PhabricatorCrumbView())
->setName('V'.$poll->getID()));
- $panel = new AphrontPanelView();
- $panel->setWidth(AphrontPanelView::WIDTH_WIDE);
- $panel->appendChild($result_markup);
-
- $content = array(
- $form,
- hsprintf('
'),
- $panel);
-
$xactions = $this->buildTransactions($poll);
$add_comment = $this->buildCommentForm($poll);
@@ -149,7 +64,13 @@ final class PhabricatorSlowvotePollController
$header,
$actions,
$properties,
- $content,
+ phutil_tag(
+ 'div',
+ array(
+ 'class' => 'ml',
+ ),
+ $poll_view),
+ $xaction_header,
$xactions,
$add_comment,
),
@@ -161,208 +82,6 @@ final class PhabricatorSlowvotePollController
));
}
-
- private function renderPollOption(
- PhabricatorSlowvotePoll $poll,
- array $viewer_choices,
- PhabricatorSlowvoteOption $option) {
- assert_instances_of($viewer_choices, 'PhabricatorSlowvoteChoice');
-
- $id = $option->getID();
- switch ($poll->getMethod()) {
- case PhabricatorSlowvotePoll::METHOD_PLURALITY:
-
- // Render a radio button.
-
- $selected_option = head($viewer_choices);
- if ($selected_option) {
- $selected = $selected_option->getOptionID();
- } else {
- $selected = null;
- }
-
- if ($selected == $id) {
- $checked = "checked";
- } else {
- $checked = null;
- }
-
- $input = phutil_tag(
- 'input',
- array(
- 'type' => 'radio',
- 'name' => 'vote[]',
- 'value' => $id,
- 'checked' => $checked,
- ));
- break;
- case PhabricatorSlowvotePoll::METHOD_APPROVAL:
-
- // Render a check box.
-
- $checked = null;
- foreach ($viewer_choices as $choice) {
- if ($choice->getOptionID() == $id) {
- $checked = 'checked';
- break;
- }
- }
-
- $input = phutil_tag(
- 'input',
- array(
- 'type' => 'checkbox',
- 'name' => 'vote[]',
- 'checked' => $checked,
- 'value' => $id,
- ));
- break;
- default:
- throw new Exception("Unknown poll method!");
- }
-
- if ($checked) {
- $checked_class = 'phabricator-slowvote-checked';
- } else {
- $checked_class = null;
- }
-
- return phutil_tag(
- 'label',
- array(
- 'class' => 'phabricator-slowvote-label '.$checked_class,
- ),
- array($input, $option->getName()));
- }
-
- private function renderVoteCount(
- PhabricatorSlowvotePoll $poll,
- array $choices,
- array $chosen) {
- assert_instances_of($choices, 'PhabricatorSlowvoteChoice');
- assert_instances_of($chosen, 'PhabricatorSlowvoteChoice');
-
- switch ($poll->getMethod()) {
- case PhabricatorSlowvotePoll::METHOD_PLURALITY:
- $out_of_total = count($choices);
- break;
- case PhabricatorSlowvotePoll::METHOD_APPROVAL:
- // Count unique respondents for approval votes.
- $out_of_total = count(mpull($choices, null, 'getAuthorPHID'));
- break;
- default:
- throw new Exception("Unknown poll method!");
- }
-
- return sprintf(
- '%d / %d (%d%%)',
- number_format(count($chosen)),
- number_format($out_of_total),
- $out_of_total
- ? round(100 * count($chosen) / $out_of_total)
- : 0);
- }
-
- private function renderResultMarkup(
- PhabricatorSlowvotePoll $poll,
- array $options,
- array $choices,
- array $viewer_choices,
- array $choices_by_option,
- array $handles,
- array $objects) {
- assert_instances_of($options, 'PhabricatorSlowvoteOption');
- assert_instances_of($choices, 'PhabricatorSlowvoteChoice');
- assert_instances_of($viewer_choices, 'PhabricatorSlowvoteChoice');
- assert_instances_of($handles, 'PhabricatorObjectHandle');
- assert_instances_of($objects, 'PhabricatorLiskDAO');
-
- $viewer_phid = $this->getRequest()->getUser()->getPHID();
-
- $can_see_responses = false;
- $need_vote = false;
- switch ($poll->getResponseVisibility()) {
- case PhabricatorSlowvotePoll::RESPONSES_VISIBLE:
- $can_see_responses = true;
- break;
- case PhabricatorSlowvotePoll::RESPONSES_VOTERS:
- $can_see_responses = (bool)$viewer_choices;
- $need_vote = true;
- break;
- case PhabricatorSlowvotePoll::RESPONSES_OWNER:
- $can_see_responses = ($viewer_phid == $poll->getAuthorPHID());
- break;
- }
-
- $result_markup = id(new AphrontFormLayoutView())
- ->appendChild(phutil_tag('h1', array(), pht('Ongoing Deliberation')));
-
- if (!$can_see_responses) {
- if ($need_vote) {
- $reason = pht("You must vote to see the results.");
- } else {
- $reason = pht("The results are not public.");
- }
- $result_markup
- ->appendChild(hsprintf(
- '%s
',
- $reason));
- return $result_markup;
- }
-
- foreach ($options as $option) {
- $id = $option->getID();
-
- $chosen = idx($choices_by_option, $id, array());
- $users = array_select_keys($handles, mpull($chosen, 'getAuthorPHID'));
- if ($users) {
- $user_markup = array();
- foreach ($users as $handle) {
- $object = idx($objects, $handle->getPHID());
- if (!$object) {
- continue;
- }
-
- $profile_image = $handle->getImageURI();
-
- $user_markup[] = phutil_tag(
- 'a',
- array(
- 'href' => $handle->getURI(),
- 'class' => 'phabricator-slowvote-facepile',
- ),
- phutil_tag(
- 'img',
- array(
- 'src' => $profile_image,
- )));
- }
- } else {
- $user_markup = pht('This option has failed to appeal to anyone.');
- }
-
- $vote_count = $this->renderVoteCount(
- $poll,
- $choices,
- $chosen);
-
- $result_markup->appendChild(hsprintf(
- ''.
- '
%s
'.
- '
%s
'.
- '
'.
- '%s'.
- '
'.
- '
'.
- '
',
- $vote_count,
- $option->getName(),
- phutil_tag('div', array(), $user_markup)));
- }
-
- return $result_markup;
- }
-
private function buildActionView(PhabricatorSlowvotePoll $poll) {
$viewer = $this->getRequest()->getUser();
diff --git a/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php b/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php
index c23a1c5770..44925de7bb 100644
--- a/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php
+++ b/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php
@@ -26,8 +26,14 @@ final class PhabricatorSlowvoteEditor
switch ($xaction->getTransactionType()) {
case PhabricatorSlowvoteTransaction::TYPE_RESPONSES:
+ if ($old === null) {
+ return true;
+ }
return ((int)$old !== (int)$new);
case PhabricatorSlowvoteTransaction::TYPE_SHUFFLE:
+ if ($old === null) {
+ return true;
+ }
return ((bool)$old !== (bool)$new);
}
diff --git a/src/applications/slowvote/remarkup/SlowvoteRemarkupRule.php b/src/applications/slowvote/remarkup/SlowvoteRemarkupRule.php
index c4db192ee7..ad734e3cde 100644
--- a/src/applications/slowvote/remarkup/SlowvoteRemarkupRule.php
+++ b/src/applications/slowvote/remarkup/SlowvoteRemarkupRule.php
@@ -25,16 +25,11 @@ final class SlowvoteRemarkupRule
protected function renderObjectEmbed($object, $handle, $options) {
$viewer = $this->getEngine()->getConfig('viewer');
- $options = $object->getOptions();
- $choices = $object->getChoices();
- $viewer_choices = $object->getViewerChoices($viewer);
-
$embed = id(new SlowvoteEmbedView())
- ->setPoll($object)
- ->setOptions($options)
- ->setViewerChoices($viewer_choices);
+ ->setUser($viewer)
+ ->setPoll($object);
- return $embed->render();
+ return $embed;
}
}
diff --git a/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php b/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php
index 4772ea7c5b..e8d61448e5 100644
--- a/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php
+++ b/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php
@@ -74,7 +74,7 @@ final class PhabricatorSlowvotePoll extends PhabricatorSlowvoteDAO
}
public function attachViewerChoices(PhabricatorUser $viewer, array $choices) {
- assert_instances_of($choices, 'PhabricatorSlowvoteOption');
+ assert_instances_of($choices, 'PhabricatorSlowvoteChoice');
$this->viewerChoices[$viewer->getPHID()] = $choices;
return $this;
}
diff --git a/src/applications/slowvote/view/SlowvoteEmbedView.php b/src/applications/slowvote/view/SlowvoteEmbedView.php
index 626026de70..cbda276f07 100644
--- a/src/applications/slowvote/view/SlowvoteEmbedView.php
+++ b/src/applications/slowvote/view/SlowvoteEmbedView.php
@@ -6,123 +6,349 @@
final class SlowvoteEmbedView extends AphrontView {
private $poll;
- private $options;
- private $viewerChoices;
+ private $handles;
+ private $headless;
+
+ public function setHeadless($headless) {
+ $this->headless = $headless;
+ return $this;
+ }
public function setPoll(PhabricatorSlowvotePoll $poll) {
$this->poll = $poll;
return $this;
}
- public function setOptions(array $options) {
- $this->options = $options;
- return $this;
- }
-
- public function setViewerChoices(array $viewer_choices) {
- $this->viewerChoices = $viewer_choices;
- return $this;
+ public function getPoll() {
+ return $this->poll;
}
public function render() {
-
if (!$this->poll) {
throw new Exception("Call setPoll() before render()!");
}
- if (!$this->options) {
- throw new Exception("Call setOptions() before render()!");
- }
+ $poll = $this->poll;
- if ($this->poll->getShuffle()) {
- shuffle($this->options);
+ $phids = array();
+ foreach ($poll->getChoices() as $choice) {
+ $phids[] = $choice->getAuthorPHID();
+ }
+ $phids[] = $poll->getAuthorPHID();
+
+ $this->handles = id(new PhabricatorObjectHandleData($phids))
+ ->setViewer($this->getUser())
+ ->loadHandles();
+
+ $options = $poll->getOptions();
+
+ if ($poll->getShuffle()) {
+ shuffle($options);
}
require_celerity_resource('phabricator-slowvote-css');
require_celerity_resource('javelin-behavior-slowvote-embed');
$config = array(
- 'pollID' => $this->poll->getID());
+ 'pollID' => $poll->getID());
Javelin::initBehavior('slowvote-embed', $config);
- $user_choices = array();
- if (!empty($this->viewerChoices)) {
- $user_choices = mpull($this->viewerChoices, null, 'getOptionID');
- }
+ $user_choices = $poll->getViewerChoices($this->getUser());
+ $user_choices = mpull($user_choices, 'getOptionID', 'getOptionID');
- $options = array();
- $ribbon_colors = array('#DF0101', '#DF7401', '#D7DF01', '#74DF00',
- '#01DF01', '#01DF74', '#01DFD7', '#0174DF', '#0101DF', '#5F04B4',
- '#B404AE');
- shuffle($ribbon_colors);
-
- foreach ($this->options as $option) {
- $classes = 'phabricator-slowvote-embed-option-text';
-
- $selected = '';
-
-
- if (idx($user_choices, $option->getID(), false)) {
- $classes .= ' phabricator-slowvote-embed-option-selected';
- $selected = '@';
- }
-
- $is_selected = javelin_tag(
- 'div',
- array(
- 'class' => 'phabricator-slowvote-embed-option-vote',
- 'sigil' => 'slowvote-embed-vote'
- ),
- $selected);
-
- $option_text = javelin_tag(
- 'div',
- array(
- 'class' => $classes,
- 'sigil' => 'slowvote-option',
- 'meta' => array(
- 'optionID' => $option->getID()
- )
- ),
- array($is_selected, $option->getName()));
-
- $options[] = phutil_tag(
- 'div',
- array(
- 'class' => 'phabricator-slowvote-embed-option',
- 'style' =>
- sprintf('border-left: 7px solid %s;', array_shift($ribbon_colors))
- ),
- array($option_text));
+ $out = array();
+ foreach ($options as $option) {
+ $is_selected = isset($user_choices[$option->getID()]);
+ $out[] = $this->renderLabel($option, $is_selected);
}
$link_to_slowvote = phutil_tag(
'a',
array(
- 'href' => '/V'.$this->poll->getID()
+ 'href' => '/V'.$poll->getID()
),
- $this->poll->getQuestion());
+ $poll->getQuestion());
- $header = phutil_tag(
- 'div',
- array(),
- array('V'.$this->poll->getID().': ', $link_to_slowvote));
+ 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)));
- $body = phutil_tag(
+ $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);
+ }
+
+ $header = array(
+ $header,
+ $description);
+ }
+
+ $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.');
+ }
+ } 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.');
+ }
+
+ $hint = phutil_tag(
+ 'span',
+ array(
+ 'class' => 'slowvote-hint',
+ ),
+ $quip);
+
+ $submit = phutil_tag(
'div',
- array(),
- $options);
+ array(
+ 'class' => 'slowvote-footer',
+ ),
+ phutil_tag(
+ 'div',
+ array(
+ 'class' => 'slowvote-footer-content',
+ ),
+ array(
+ $hint,
+ phutil_tag(
+ 'button',
+ array(
+ ),
+ pht('Engage in Deliberations')),
+ )));
+
+ $body = phabricator_form(
+ $this->getUser(),
+ array(
+ 'action' => '/vote/'.$poll->getID().'/',
+ 'method' => 'POST',
+ 'class' => 'slowvote-body',
+ ),
+ array(
+ phutil_tag(
+ 'div',
+ array(
+ 'class' => 'slowvote-body-content',
+ ),
+ $out),
+ $submit,
+ ));
return javelin_tag(
'div',
array(
- 'class' => 'phabricator-slowvote-embed',
+ 'class' => 'slowvote-embed',
'sigil' => 'slowvote-embed',
'meta' => array(
- 'pollID' => $this->poll->getID()
+ 'pollID' => $poll->getID()
)
),
array($header, $body));
}
+ 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',
+ );
+
+ return phutil_tag(
+ 'input',
+ array(
+ 'type' => idx($types, $this->getPoll()->getMethod()),
+ 'name' => 'vote[]',
+ 'value' => $option->getID(),
+ 'checked' => ($selected ? 'checked' : null),
+ ));
+ }
+
+ 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());
+ }
+ }
+
}
diff --git a/webroot/rsrc/css/application/slowvote/slowvote.css b/webroot/rsrc/css/application/slowvote/slowvote.css
index 84470e973f..d82b770440 100644
--- a/webroot/rsrc/css/application/slowvote/slowvote.css
+++ b/webroot/rsrc/css/application/slowvote/slowvote.css
@@ -2,90 +2,124 @@
* @provides phabricator-slowvote-css
*/
-.phabricator-slowvote-comments {
- width: 100%;
+.slowvote-embed {
+ margin: 24px 12px;
+ background: #ffffff;
+ border-color: #888888;
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 4px;
+ box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.20),
+ inset 0 0 2px rgba(0, 0, 0, 0.10);
}
-.phabricator-slowvote-comments th {
- width: 150px;
- text-align: right;
- padding: 6px 4px 6px;
- white-space: nowrap;
+.slowvote-header {
+ font-weight: bold;
+ line-height: 16px;
+ border-bottom: 1px solid #bbbbbb;
}
-.phabricator-slowvote-comments td {
- vertical-align: top;
- padding: 6px 2px;
- border-bottom: 1px solid #d0d0d0;
-}
-
-.phabricator-slowvote-datestamp {
- font-size: 9px;
- font-family: "Verdana";
+.slowvote-description {
color: #666666;
- margin-top: 1px;
+ padding: 8px;
+ border-bottom: 1px solid #bbbbbb;
}
-.phabricator-slowvote-hr {
- border: none;
- height: 1px;
- position: relative;
- background: #c0c0c0;
+.slowvote-header-content {
+ padding: 8px;
}
-.phabricator-slowvote-count {
- float: right;
- font-size: 13px;
- font-weight: bold;
+.slowvote-body {
}
-.phabricator-slowvote-label {
+.slowvote-body-content {
+ padding: 4px 16px;
+}
+
+.slowvote-option-label {
+ border: 1px solid #666666;
display: block;
- width: 100%;
- font-size: 14px;
- font-weight: bold;
- color: #222222;
- text-align: left;
- margin: 0px 0px 6px;
- padding: 6px 4px;
- background: #cccccc;
- border-bottom: 1px solid #aaaaaa;
- cursor: pointer;
-}
-
-.aphront-form-input .phabricator-slowvote-label input {
- display: inline;
- width: auto;
- margin-right: 12px;
-}
-
-.phabricator-slowvote-facepile {
- width: 50px;
- height: 50px;
- overflow: hidden;
position: relative;
- float: left;
- margin: 0px 4px 6px 0px;
+ padding: 8px 4px;
+ cursor: pointer;
+ font-weight: bold;
+ overflow: hidden;
+ background-color: {$lightblue};
}
-.phabricator-slowvote-embed {
- width: 400px;
- background: #f7f7f7;
- border: 1px solid #dbdbdb;
- padding: 5px;
+.slowvote-bar {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ overflow: hidden;
+ background-color: {$blue};
}
-.phabricator-slowvote-embed-option {
- margin: 1px;
- background: #F0FFFF;
+.slowvote-control-offset {
+ white-space: nowrap;
+ position: absolute;
+ left: 32px;
+ top: 8px;
+ width: 100%;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
}
-.phabricator-slowvote-embed-option-vote {
+.slowvote-bar .slowvote-control-offset {
+ color: #ffffff;
+ text-shadow: 0 1px 0 #000000;
+}
+
+.slowvote-option-label-group {
+ margin: 8px 0 16px;
+}
+
+.slowvote-option-label input[type="radio"],
+.slowvote-option-label input[type="checkbox"] {
+ margin: 0 12px 0 8px;
+ font-weight: bold;
+}
+
+.slowvote-above-the-bar {
+ position: relative;
+}
+
+.slowvote-status {
+ text-align: right;
+ color: #333333;
+ font-weight: normal;
+ padding: 2px 0;
+ line-height: 15px;
+ text-align: right;
+ font-size: 11px;
+}
+
+.slowvote-voter {
display: inline-block;
- width: 1em;
+ width: 25px;
+ height: 25px;
+ background: #f3f3f3;
+ background-size: 25px 25px;
}
-.phabricator-slowvote-embed-option-text {
- border: 1px solid #dbdbdb;
- border-left: 0px;
+.slowvote-footer {
+ border-top-width: 1px;
+ border-top-style: solid;
+ border-top-color: #bbbbbb;
+ position: relative;
+}
+
+.slowvote-footer-content {
+ padding: 8px;
+ overflow: hidden;
+}
+
+.slowvote-footer-content .slowvote-hint {
+ line-height: 24px;
+ color: #888888;
+}
+
+.slowvote-footer-content button {
+ float: right;
}
diff --git a/webroot/rsrc/css/core/z-index.css b/webroot/rsrc/css/core/z-index.css
index 685e810f9b..56a650bec5 100644
--- a/webroot/rsrc/css/core/z-index.css
+++ b/webroot/rsrc/css/core/z-index.css
@@ -22,6 +22,14 @@
z-index: 2;
}
+.slowvote-bar {
+ z-index: 2;
+}
+
+.slowvote-above-the-bar {
+ z-index: 3;
+}
+
.phabricator-timeline-icon-fill {
z-index: 3;
}