mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
Port the "Slowvote" application
Summary: Port slowvote. This has some style/layout roughness but gets us most of the way there. I'll followup to fix some of the markup issues. Test Plan: Created and voted in several different kinds of poll. Reviewed By: codeblock Reviewers: codeblock, tomo, jungejason, aran, tuomaspelkonen Commenters: aran, jungejason CC: aran, codeblock, jungejason, epriestley Differential Revision: 613
This commit is contained in:
parent
cce786a89f
commit
57097c2874
25 changed files with 1248 additions and 11 deletions
50
resources/sql/patches/056.slowvote.sql
Normal file
50
resources/sql/patches/056.slowvote.sql
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
CREATE DATABASE phabricator_slowvote;
|
||||||
|
|
||||||
|
CREATE TABLE phabricator_slowvote.slowvote_poll (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
question VARCHAR(255) NOT NULL,
|
||||||
|
phid VARCHAR(64) BINARY NOT NULL,
|
||||||
|
UNIQUE KEY (phid),
|
||||||
|
authorPHID VARCHAR(64) BINARY NOT NULL,
|
||||||
|
responseVisibility INT UNSIGNED NOT NULL,
|
||||||
|
shuffle INT UNSIGNED NOT NULL,
|
||||||
|
method INT UNSIGNED NOT NULL,
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
dateModified INT UNSIGNED NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE phabricator_slowvote.slowvote_option (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
pollID INT UNSIGNED NOT NULL,
|
||||||
|
KEY (pollID),
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
dateModified INT UNSIGNED NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE phabricator_slowvote.slowvote_comment (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
pollID INT UNSIGNED NOT NULL,
|
||||||
|
UNIQUE KEY (pollID, authorPHID),
|
||||||
|
authorPHID VARCHAR(64) BINARY NOT NULL,
|
||||||
|
commentText LONGBLOB NOT NULL,
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
dateModified INT UNSIGNED NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE phabricator_slowvote.slowvote_choice (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
pollID INT UNSIGNED NOT NULL,
|
||||||
|
KEY (pollID),
|
||||||
|
optionID INT UNSIGNED NOT NULL,
|
||||||
|
authorPHID VARCHAR(64) BINARY NOT NULL,
|
||||||
|
KEY (authorPHID),
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
dateModified INT UNSIGNED NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO phabricator_directory.directory_item
|
||||||
|
(name, description, href, categoryID, sequence, dateCreated, dateModified)
|
||||||
|
VALUES
|
||||||
|
("Slowvote", "Design by committee.", "/vote/", 5, 250,
|
||||||
|
UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
|
@ -283,17 +283,6 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/css/application/herald/herald-test.css',
|
'disk' => '/rsrc/css/application/herald/herald-test.css',
|
||||||
),
|
),
|
||||||
0 =>
|
|
||||||
array(
|
|
||||||
'uri' => '/res/1da00bfe/rsrc/js/javelin/lib/__tests__/URI.js',
|
|
||||||
'type' => 'js',
|
|
||||||
'requires' =>
|
|
||||||
array(
|
|
||||||
0 => 'javelin-uri',
|
|
||||||
1 => 'javelin-php-serializer',
|
|
||||||
),
|
|
||||||
'disk' => '/rsrc/js/javelin/lib/__tests__/URI.js',
|
|
||||||
),
|
|
||||||
'javelin-behavior' =>
|
'javelin-behavior' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/b28adfa1/rsrc/js/javelin/lib/behavior.js',
|
'uri' => '/res/b28adfa1/rsrc/js/javelin/lib/behavior.js',
|
||||||
|
@ -487,6 +476,17 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/differential/behavior-show-all-comments.js',
|
'disk' => '/rsrc/js/application/differential/behavior-show-all-comments.js',
|
||||||
),
|
),
|
||||||
|
0 =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/1da00bfe/rsrc/js/javelin/lib/__tests__/URI.js',
|
||||||
|
'type' => 'js',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-uri',
|
||||||
|
1 => 'javelin-php-serializer',
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/js/javelin/lib/__tests__/URI.js',
|
||||||
|
),
|
||||||
'javelin-behavior-differential-show-more' =>
|
'javelin-behavior-differential-show-more' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/a766c717/rsrc/js/application/differential/behavior-show-more.js',
|
'uri' => '/res/a766c717/rsrc/js/application/differential/behavior-show-more.js',
|
||||||
|
@ -1099,6 +1099,15 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/core/ShapedRequest.js',
|
'disk' => '/rsrc/js/application/core/ShapedRequest.js',
|
||||||
),
|
),
|
||||||
|
'phabricator-slowvote-css' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/2caefc66/rsrc/css/application/slowvote/slowvote.css',
|
||||||
|
'type' => 'css',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/css/application/slowvote/slowvote.css',
|
||||||
|
),
|
||||||
'phabricator-standard-page-view' =>
|
'phabricator-standard-page-view' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/e8238633/rsrc/css/application/base/standard-page-view.css',
|
'uri' => '/res/e8238633/rsrc/css/application/base/standard-page-view.css',
|
||||||
|
|
|
@ -521,6 +521,15 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSearchSelectController' => 'applications/search/controller/select',
|
'PhabricatorSearchSelectController' => 'applications/search/controller/select',
|
||||||
'PhabricatorSearchUserIndexer' => 'applications/search/index/indexer/user',
|
'PhabricatorSearchUserIndexer' => 'applications/search/index/indexer/user',
|
||||||
'PhabricatorSetup' => 'infrastructure/setup',
|
'PhabricatorSetup' => 'infrastructure/setup',
|
||||||
|
'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/choice',
|
||||||
|
'PhabricatorSlowvoteComment' => 'applications/slowvote/storage/comment',
|
||||||
|
'PhabricatorSlowvoteController' => 'applications/slowvote/controller/base',
|
||||||
|
'PhabricatorSlowvoteCreateController' => 'applications/slowvote/controller/create',
|
||||||
|
'PhabricatorSlowvoteDAO' => 'applications/slowvote/storage/base',
|
||||||
|
'PhabricatorSlowvoteListController' => 'applications/slowvote/controller/list',
|
||||||
|
'PhabricatorSlowvoteOption' => 'applications/slowvote/storage/option',
|
||||||
|
'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/poll',
|
||||||
|
'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/poll',
|
||||||
'PhabricatorStandardPageView' => 'view/page/standard',
|
'PhabricatorStandardPageView' => 'view/page/standard',
|
||||||
'PhabricatorStatusController' => 'applications/status/base',
|
'PhabricatorStatusController' => 'applications/status/base',
|
||||||
'PhabricatorSyntaxHighlighter' => 'applications/markup/syntax',
|
'PhabricatorSyntaxHighlighter' => 'applications/markup/syntax',
|
||||||
|
@ -1002,6 +1011,15 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSearchResultView' => 'AphrontView',
|
'PhabricatorSearchResultView' => 'AphrontView',
|
||||||
'PhabricatorSearchSelectController' => 'PhabricatorSearchController',
|
'PhabricatorSearchSelectController' => 'PhabricatorSearchController',
|
||||||
'PhabricatorSearchUserIndexer' => 'PhabricatorSearchDocumentIndexer',
|
'PhabricatorSearchUserIndexer' => 'PhabricatorSearchDocumentIndexer',
|
||||||
|
'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO',
|
||||||
|
'PhabricatorSlowvoteComment' => 'PhabricatorSlowvoteDAO',
|
||||||
|
'PhabricatorSlowvoteController' => 'PhabricatorController',
|
||||||
|
'PhabricatorSlowvoteCreateController' => 'PhabricatorSlowvoteController',
|
||||||
|
'PhabricatorSlowvoteDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'PhabricatorSlowvoteListController' => 'PhabricatorSlowvoteController',
|
||||||
|
'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO',
|
||||||
|
'PhabricatorSlowvotePoll' => 'PhabricatorSlowvoteDAO',
|
||||||
|
'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
|
||||||
'PhabricatorStandardPageView' => 'AphrontPageView',
|
'PhabricatorStandardPageView' => 'AphrontPageView',
|
||||||
'PhabricatorStatusController' => 'PhabricatorController',
|
'PhabricatorStatusController' => 'PhabricatorController',
|
||||||
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
|
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
|
||||||
|
|
|
@ -334,6 +334,12 @@ class AphrontDefaultApplicationConfiguration
|
||||||
'/feed/' => array(
|
'/feed/' => array(
|
||||||
'$' => 'PhabricatorFeedStreamController',
|
'$' => 'PhabricatorFeedStreamController',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'/V(?P<id>\d+)$' => 'PhabricatorSlowvotePollController',
|
||||||
|
'/vote/' => array(
|
||||||
|
'(?:view/(?P<view>\w+)/)?$' => 'PhabricatorSlowvoteListController',
|
||||||
|
'create/' => 'PhabricatorSlowvoteCreateController',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ final class PhabricatorPHIDConstants {
|
||||||
const PHID_TYPE_OPKG = 'OPKG';
|
const PHID_TYPE_OPKG = 'OPKG';
|
||||||
const PHID_TYPE_PSTE = 'PSTE';
|
const PHID_TYPE_PSTE = 'PSTE';
|
||||||
const PHID_TYPE_STRY = 'STRY';
|
const PHID_TYPE_STRY = 'STRY';
|
||||||
|
const PHID_TYPE_POLL = 'POLL';
|
||||||
|
|
||||||
public static function getTypes() {
|
public static function getTypes() {
|
||||||
return array(
|
return array(
|
||||||
|
@ -47,6 +48,7 @@ final class PhabricatorPHIDConstants {
|
||||||
self::PHID_TYPE_PSTE,
|
self::PHID_TYPE_PSTE,
|
||||||
self::PHID_TYPE_OPKG,
|
self::PHID_TYPE_OPKG,
|
||||||
self::PHID_TYPE_STRY,
|
self::PHID_TYPE_STRY,
|
||||||
|
self::PHID_TYPE_POLL,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class PhabricatorSlowvoteController extends PhabricatorController {
|
||||||
|
|
||||||
|
public function buildStandardPageResponse($view, array $data) {
|
||||||
|
$page = $this->buildStandardPageView();
|
||||||
|
|
||||||
|
$page->setApplicationName('Slowvote');
|
||||||
|
$page->setBaseURI('/vote/');
|
||||||
|
$page->setTitle(idx($data, 'title'));
|
||||||
|
$page->setGlyph("\xE2\x9C\x94");
|
||||||
|
|
||||||
|
$doc_href = PhabricatorEnv::getDoclink('articles/Slowvote_User_Guide.html');
|
||||||
|
$page->setTabs(
|
||||||
|
array(
|
||||||
|
'help' => array(
|
||||||
|
'name' => 'Help',
|
||||||
|
'href' => $doc_href,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
null);
|
||||||
|
|
||||||
|
$page->appendChild($view);
|
||||||
|
|
||||||
|
$response = new AphrontWebpageResponse();
|
||||||
|
return $response->setContent($page->render());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/applications/slowvote/controller/base/__init__.php
Normal file
16
src/applications/slowvote/controller/base/__init__.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/webpage');
|
||||||
|
phutil_require_module('phabricator', 'applications/base/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvoteController.php');
|
|
@ -0,0 +1,160 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvoteCreateController
|
||||||
|
extends PhabricatorSlowvoteController {
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
|
||||||
|
$poll = new PhabricatorSlowvotePoll();
|
||||||
|
$poll->setAuthorPHID($user->getPHID());
|
||||||
|
|
||||||
|
$e_question = true;
|
||||||
|
$e_response = true;
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
$responses = $request->getArr('response');
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$poll->setQuestion($request->getStr('question'));
|
||||||
|
$poll->setResponseVisibility($request->getInt('response_visibility'));
|
||||||
|
$poll->setShuffle($request->getBool('shuffle', false));
|
||||||
|
$poll->setMethod($request->getInt('method'));
|
||||||
|
|
||||||
|
if (!strlen($poll->getQuestion())) {
|
||||||
|
$e_question = 'Required';
|
||||||
|
$errors[] = 'You must ask a poll question.';
|
||||||
|
} else {
|
||||||
|
$e_question = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responses = array_filter($responses);
|
||||||
|
if (empty($responses)) {
|
||||||
|
$errors[] = 'You must offer at least one response.';
|
||||||
|
$e_response = 'Required';
|
||||||
|
} else {
|
||||||
|
$e_response = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
$poll->save();
|
||||||
|
|
||||||
|
foreach ($responses as $response) {
|
||||||
|
$option = new PhabricatorSlowvoteOption();
|
||||||
|
$option->setName($response);
|
||||||
|
$option->setPollID($poll->getID());
|
||||||
|
$option->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI('/V'.$poll->getID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$error_view = null;
|
||||||
|
if ($errors) {
|
||||||
|
$error_view = new AphrontErrorView();
|
||||||
|
$error_view->setTitle('Form Errors');
|
||||||
|
$error_view->setErrors($errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setUser($user)
|
||||||
|
->appendChild(
|
||||||
|
'<p class="aphront-form-instructions">Resolve issues and build '.
|
||||||
|
'consensus through protracted deliberation.</p>')
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextControl())
|
||||||
|
->setLabel('Question')
|
||||||
|
->setName('question')
|
||||||
|
->setValue($poll->getQuestion())
|
||||||
|
->setError($e_question));
|
||||||
|
|
||||||
|
for ($ii = 0; $ii < 10; $ii++) {
|
||||||
|
$n = ($ii + 1);
|
||||||
|
$response = id(new AphrontFormTextControl())
|
||||||
|
->setLabel("Response {$n}")
|
||||||
|
->setName('response[]')
|
||||||
|
->setValue(idx($responses, $ii, ''));
|
||||||
|
|
||||||
|
if ($ii == 0) {
|
||||||
|
$response->setError($e_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form->appendChild($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
$poll_type_options = array(
|
||||||
|
PhabricatorSlowvotePoll::METHOD_PLURALITY => 'Plurality (Single Choice)',
|
||||||
|
PhabricatorSlowvotePoll::METHOD_APPROVAL => 'Approval (Multiple Choice)',
|
||||||
|
);
|
||||||
|
|
||||||
|
$response_type_options = array(
|
||||||
|
PhabricatorSlowvotePoll::RESPONSES_VISIBLE
|
||||||
|
=> 'Allow anyone to see the responses',
|
||||||
|
PhabricatorSlowvotePoll::RESPONSES_VOTERS
|
||||||
|
=> 'Require a vote to see the responses',
|
||||||
|
PhabricatorSlowvotePoll::RESPONSES_OWNER
|
||||||
|
=> 'Only I can see the responses',
|
||||||
|
);
|
||||||
|
|
||||||
|
$form
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel('Vote Type')
|
||||||
|
->setName('method')
|
||||||
|
->setValue($poll->getMethod())
|
||||||
|
->setOptions($poll_type_options))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel('Responses')
|
||||||
|
->setName('response_visibility')
|
||||||
|
->setValue($poll->getResponseVisibility())
|
||||||
|
->setOptions($response_type_options))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormCheckboxControl())
|
||||||
|
->setLabel('Shuffle')
|
||||||
|
->addCheckbox(
|
||||||
|
'shuffle',
|
||||||
|
1,
|
||||||
|
'Show choices in random order',
|
||||||
|
$poll->getShuffle()))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->setValue('Create Slowvote')
|
||||||
|
->addCancelButton('/vote/'));
|
||||||
|
|
||||||
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||||
|
$panel->setHeader('Create Slowvote');
|
||||||
|
$panel->appendChild($form);
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
array(
|
||||||
|
$error_view,
|
||||||
|
$panel,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => 'Create Slowvote',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/applications/slowvote/controller/create/__init__.php
Normal file
24
src/applications/slowvote/controller/create/__init__.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/option');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/poll');
|
||||||
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/checkbox');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/select');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/text');
|
||||||
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvoteCreateController.php');
|
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvoteListController
|
||||||
|
extends PhabricatorSlowvoteController {
|
||||||
|
|
||||||
|
private $view;
|
||||||
|
|
||||||
|
const VIEW_ALL = 'all';
|
||||||
|
const VIEW_CREATED = 'created';
|
||||||
|
const VIEW_VOTED = 'voted';
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->view = idx($data, 'view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
|
||||||
|
$filters = array(
|
||||||
|
self::VIEW_ALL => 'All Slowvotes',
|
||||||
|
self::VIEW_CREATED => 'Created',
|
||||||
|
self::VIEW_VOTED => 'Voted In',
|
||||||
|
);
|
||||||
|
|
||||||
|
$view = isset($filters[$this->view])
|
||||||
|
? $this->view
|
||||||
|
: self::VIEW_ALL;
|
||||||
|
|
||||||
|
$side_nav = new AphrontSideNavView();
|
||||||
|
foreach ($filters as $key => $name) {
|
||||||
|
$side_nav->addNavItem(
|
||||||
|
phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => '/vote/view/'.$key.'/',
|
||||||
|
'class' => ($view == $key) ? 'aphront-side-nav-selected' : null,
|
||||||
|
),
|
||||||
|
phutil_escape_html($name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$poll = new PhabricatorSlowvotePoll();
|
||||||
|
$conn = $poll->establishConnection('r');
|
||||||
|
|
||||||
|
$pager = new AphrontPagerView();
|
||||||
|
$pager->setOffset($request->getInt('page'));
|
||||||
|
$pager->setURI($request->getRequestURI(), 'page');
|
||||||
|
$offset = $pager->getOffset();
|
||||||
|
$limit = $pager->getPageSize() + 1;
|
||||||
|
|
||||||
|
switch ($view) {
|
||||||
|
case self::VIEW_ALL:
|
||||||
|
$data = queryfx_all(
|
||||||
|
$conn,
|
||||||
|
'SELECT * FROM %T ORDER BY id DESC LIMIT %d, %d',
|
||||||
|
$poll->getTableName(),
|
||||||
|
$offset,
|
||||||
|
$limit);
|
||||||
|
break;
|
||||||
|
case self::VIEW_CREATED:
|
||||||
|
$data = queryfx_all(
|
||||||
|
$conn,
|
||||||
|
'SELECT * FROM %T WHERE authorPHID = %s ORDER BY id DESC
|
||||||
|
LIMIT %d, %d',
|
||||||
|
$poll->getTableName(),
|
||||||
|
$user->getPHID(),
|
||||||
|
$offset,
|
||||||
|
$limit);
|
||||||
|
break;
|
||||||
|
case self::VIEW_VOTED:
|
||||||
|
$choice = new PhabricatorSlowvoteChoice();
|
||||||
|
$data = queryfx_all(
|
||||||
|
$conn,
|
||||||
|
'SELECT p.* FROM %T p JOIN %T o
|
||||||
|
ON o.pollID = p.id
|
||||||
|
WHERE o.authorPHID = %s
|
||||||
|
GROUP BY p.id
|
||||||
|
ORDER BY p.id DESC
|
||||||
|
LIMIT %d, %d',
|
||||||
|
$poll->getTableName(),
|
||||||
|
$choice->getTableName(),
|
||||||
|
$user->getPHID(),
|
||||||
|
$offset,
|
||||||
|
$limit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $pager->sliceResults($data);
|
||||||
|
$polls = $poll->loadAllFromArray($data);
|
||||||
|
|
||||||
|
$phids = mpull($polls, 'getAuthorPHID');
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ($polls as $poll) {
|
||||||
|
$rows[] = array(
|
||||||
|
$handles[$poll->getAuthorPHID()]->renderLink(),
|
||||||
|
phutil_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => '/V'.$poll->getID(),
|
||||||
|
),
|
||||||
|
phutil_escape_html('V'.$poll->getID().' '.$poll->getQuestion())),
|
||||||
|
phabricator_date($poll->getDateCreated(), $user),
|
||||||
|
phabricator_time($poll->getDateCreated(), $user),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = new AphrontTableView($rows);
|
||||||
|
$table->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'',
|
||||||
|
'pri wide',
|
||||||
|
'',
|
||||||
|
'right',
|
||||||
|
));
|
||||||
|
$table->setHeaders(
|
||||||
|
array(
|
||||||
|
'Author',
|
||||||
|
'Poll',
|
||||||
|
'Date',
|
||||||
|
'Time',
|
||||||
|
));
|
||||||
|
|
||||||
|
$headers = array(
|
||||||
|
self::VIEW_ALL
|
||||||
|
=> 'Slowvotes Not Yet Consumed by the Ravages of Time',
|
||||||
|
self::VIEW_CREATED
|
||||||
|
=> 'Slowvotes Birthed from Your Noblest of Great Minds',
|
||||||
|
self::VIEW_VOTED
|
||||||
|
=> 'Slowvotes Within Which You Express Your Mighty Opinion',
|
||||||
|
);
|
||||||
|
|
||||||
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setHeader(idx($headers, $view));
|
||||||
|
$panel->setCreateButton('Create Slowvote', '/vote/create/');
|
||||||
|
$panel->appendChild($table);
|
||||||
|
$panel->appendChild($pager);
|
||||||
|
|
||||||
|
$side_nav->appendChild($panel);
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
$side_nav,
|
||||||
|
array(
|
||||||
|
'title' => 'Slowvotes',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/applications/slowvote/controller/list/__init__.php
Normal file
24
src/applications/slowvote/controller/list/__init__.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/choice');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/poll');
|
||||||
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
|
phutil_require_module('phabricator', 'view/control/pager');
|
||||||
|
phutil_require_module('phabricator', 'view/control/table');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/sidenav');
|
||||||
|
phutil_require_module('phabricator', 'view/utils');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvoteListController.php');
|
|
@ -0,0 +1,390 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvotePollController
|
||||||
|
extends PhabricatorSlowvoteController {
|
||||||
|
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->id = $data['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
$viewer_phid = $user->getPHID();
|
||||||
|
|
||||||
|
$poll = id(new PhabricatorSlowvotePoll())->load($this->id);
|
||||||
|
if (!$poll) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = id(new PhabricatorSlowvoteOption())->loadAllWhere(
|
||||||
|
'pollID = %d',
|
||||||
|
$poll->getID());
|
||||||
|
$choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere(
|
||||||
|
'pollID = %d',
|
||||||
|
$poll->getID());
|
||||||
|
$comments = id(new PhabricatorSlowvoteComment())->loadAllWhere(
|
||||||
|
'pollID = %d',
|
||||||
|
$poll->getID());
|
||||||
|
|
||||||
|
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!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$choices_by_option = mgroup($choices, 'getOptionID');
|
||||||
|
$comments_by_user = mpull($comments, null, 'getAuthorPHID');
|
||||||
|
$choices_by_user = mgroup($choices, 'getAuthorPHID');
|
||||||
|
$viewer_choices = idx($choices_by_user, $viewer_phid, array());
|
||||||
|
$viewer_comment = idx($comments_by_user, $viewer_phid, null);
|
||||||
|
|
||||||
|
$comment_text = null;
|
||||||
|
if ($viewer_comment) {
|
||||||
|
$comment_text = $viewer_comment->getCommentText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$comment = idx($comments_by_user, $viewer_phid, null);
|
||||||
|
if ($comment) {
|
||||||
|
$comment->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment_text = $request->getStr('comments');
|
||||||
|
if (strlen($comment_text)) {
|
||||||
|
id(new PhabricatorSlowvoteComment())
|
||||||
|
->setAuthorPHID($viewer_phid)
|
||||||
|
->setPollID($poll->getID())
|
||||||
|
->setCommentText($comment_text)
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$votes = $request->getArr('vote');
|
||||||
|
|
||||||
|
switch ($poll->getMethod()) {
|
||||||
|
case PhabricatorSlowvotePoll::METHOD_PLURALITY:
|
||||||
|
// Enforce only one vote.
|
||||||
|
$votes = array_slice($votes, 0, 1);
|
||||||
|
break;
|
||||||
|
case PhabricatorSlowvotePoll::METHOD_APPROVAL:
|
||||||
|
// No filtering.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Unknown poll method!");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($viewer_choices as $viewer_choice) {
|
||||||
|
$viewer_choice->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($votes as $vote) {
|
||||||
|
id(new PhabricatorSlowvoteChoice())
|
||||||
|
->setAuthorPHID($viewer_phid)
|
||||||
|
->setPollID($poll->getID())
|
||||||
|
->setOptionID($vote)
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI('/V'.$poll->getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
require_celerity_resource('phabricator-slowvote-css');
|
||||||
|
|
||||||
|
$phids = array_merge(
|
||||||
|
mpull($choices, 'getAuthorPHID'),
|
||||||
|
mpull($comments, 'getAuthorPHID'),
|
||||||
|
array(
|
||||||
|
$poll->getAuthorPHID(),
|
||||||
|
));
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||||
|
|
||||||
|
if ($poll->getShuffle()) {
|
||||||
|
shuffle($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
$option_markup = array();
|
||||||
|
foreach ($options as $option) {
|
||||||
|
$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_render_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_render_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$option_markup[] = phutil_render_tag(
|
||||||
|
'label',
|
||||||
|
array(
|
||||||
|
'class' => 'phabricator-slowvote-label '.$checked_class,
|
||||||
|
),
|
||||||
|
$input.phutil_escape_html($option->getName()));
|
||||||
|
}
|
||||||
|
$option_markup = implode("\n", $option_markup);
|
||||||
|
|
||||||
|
$comments_by_option = array();
|
||||||
|
switch ($poll->getMethod()) {
|
||||||
|
case PhabricatorSlowvotePoll::METHOD_PLURALITY:
|
||||||
|
$choice_ids = array();
|
||||||
|
foreach ($choices_by_user as $user_phid => $choices) {
|
||||||
|
$choice_ids[$user_phid] = head($choices)->getOptionID();
|
||||||
|
}
|
||||||
|
foreach ($comments as $comment) {
|
||||||
|
$choice = idx($choice_ids, $comment->getAuthorPHID());
|
||||||
|
if ($choice) {
|
||||||
|
$comments_by_option[$choice][] = $comment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PhabricatorSlowvotePoll::METHOD_APPROVAL:
|
||||||
|
// All comments are grouped in approval voting.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Unknown poll method!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$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('<h1>Ongoing Deliberation</h1>');
|
||||||
|
|
||||||
|
if (!$can_see_responses) {
|
||||||
|
if ($need_vote) {
|
||||||
|
$reason = "You must vote to see the results.";
|
||||||
|
} else {
|
||||||
|
$reason = "The results are not public.";
|
||||||
|
}
|
||||||
|
$result_markup
|
||||||
|
->appendChild(
|
||||||
|
'<p class="aphront-form-instructions"><em>'.$reason.'</em></p>');
|
||||||
|
} else {
|
||||||
|
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) {
|
||||||
|
$user_markup[] = $handle->renderLink();
|
||||||
|
}
|
||||||
|
$user_markup = implode('', $user_markup);
|
||||||
|
} else {
|
||||||
|
$user_markup = 'This option has failed to appeal to anyone.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment_markup = $this->renderComments(
|
||||||
|
idx($comments_by_option, $id, array()),
|
||||||
|
$handles);
|
||||||
|
|
||||||
|
$display = sprintf(
|
||||||
|
'%d / %d (%d%%)',
|
||||||
|
number_format(count($chosen)),
|
||||||
|
number_format($out_of_total),
|
||||||
|
$out_of_total
|
||||||
|
? round(100 * count($chosen) / $out_of_total)
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
$result_markup->appendChild(
|
||||||
|
'<div>'.
|
||||||
|
'<div class="phabricator-slowvote-count">'.
|
||||||
|
$display.
|
||||||
|
'</div>'.
|
||||||
|
'<h1>'.phutil_escape_html($option->getName()).'</h1>'.
|
||||||
|
'<hr class="phabricator-slowvote-hr" />'.
|
||||||
|
$user_markup.
|
||||||
|
'<hr class="phabricator-slowvote-hr" />'.
|
||||||
|
$comment_markup.
|
||||||
|
'</div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($poll->getMethod() == PhabricatorSlowvotePoll::METHOD_APPROVAL &&
|
||||||
|
$comments) {
|
||||||
|
$comment_markup = $this->renderComments(
|
||||||
|
$comments,
|
||||||
|
$handles);
|
||||||
|
$result_markup->appendChild(
|
||||||
|
'<h1>Motions Proposed for Consideration</h1>');
|
||||||
|
$result_markup->appendChild($comment_markup);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($viewer_choices) {
|
||||||
|
$instructions =
|
||||||
|
'Your vote has been recorded... but there is still ample time to '.
|
||||||
|
'rethink your position. Have you thoroughly considered all possible '.
|
||||||
|
'eventualities?';
|
||||||
|
} else {
|
||||||
|
$instructions =
|
||||||
|
'This is a weighty matter indeed. Consider your choices with the '.
|
||||||
|
'greatest of care.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setUser($user)
|
||||||
|
->appendChild(
|
||||||
|
'<p class="aphront-form-instructions">'.$instructions.'</p>')
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormMarkupControl())
|
||||||
|
->setLabel('Vote')
|
||||||
|
->setValue($option_markup))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormTextAreaControl())
|
||||||
|
->setLabel('Comments')
|
||||||
|
->setHeight(AphrontFormTextAreaControl::HEIGHT_SHORT)
|
||||||
|
->setName('comments')
|
||||||
|
->setValue($comment_text))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->setValue('Cautiously Engage in Deliberations'));
|
||||||
|
|
||||||
|
|
||||||
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setHeader(phutil_escape_html($poll->getQuestion()));
|
||||||
|
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
|
||||||
|
|
||||||
|
$panel->appendChild($form);
|
||||||
|
$panel->appendChild('<br /><br />');
|
||||||
|
$panel->appendChild($result_markup);
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
$panel,
|
||||||
|
array(
|
||||||
|
'title' => 'V'.$poll->getID().' '.$poll->getQuestion(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderComments(array $comments, array $handles) {
|
||||||
|
$viewer = $this->getRequest()->getUser();
|
||||||
|
|
||||||
|
$factory = new DifferentialMarkupEngineFactory();
|
||||||
|
$engine = $factory->newDifferentialCommentMarkupEngine();
|
||||||
|
|
||||||
|
$comment_markup = array();
|
||||||
|
foreach ($comments as $comment) {
|
||||||
|
$handle = $handles[$comment->getAuthorPHID()];
|
||||||
|
|
||||||
|
$markup = $engine->markupText($comment->getCommentText());
|
||||||
|
|
||||||
|
require_celerity_resource('phabricator-remarkup-css');
|
||||||
|
|
||||||
|
$comment_markup[] =
|
||||||
|
'<tr>'.
|
||||||
|
'<th>'.
|
||||||
|
$handle->renderLink().
|
||||||
|
'<div class="phabricator-slowvote-datestamp">'.
|
||||||
|
phabricator_datetime($comment->getDateCreated(), $viewer).
|
||||||
|
'</div>'.
|
||||||
|
'<td>'.
|
||||||
|
'<div class="phabricator-remarkup">'.
|
||||||
|
$markup.
|
||||||
|
'</div>'.
|
||||||
|
'</td>'.
|
||||||
|
'</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($comment_markup) {
|
||||||
|
$comment_markup = phutil_render_tag(
|
||||||
|
'table',
|
||||||
|
array(
|
||||||
|
'class' => 'phabricator-slowvote-comments',
|
||||||
|
),
|
||||||
|
implode("\n", $comment_markup));
|
||||||
|
} else {
|
||||||
|
$comment_markup = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $comment_markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
src/applications/slowvote/controller/poll/__init__.php
Normal file
31
src/applications/slowvote/controller/poll/__init__.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/differential/parser/markup');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/choice');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/comment');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/option');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/poll');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/markup');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/textarea');
|
||||||
|
phutil_require_module('phabricator', 'view/form/layout');
|
||||||
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
|
phutil_require_module('phabricator', 'view/utils');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvotePollController.php');
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvoteDAO extends PhabricatorLiskDAO {
|
||||||
|
|
||||||
|
public function getApplicationName() {
|
||||||
|
return 'slowvote';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/applications/slowvote/storage/base/__init__.php
Normal file
12
src/applications/slowvote/storage/base/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/base/storage/lisk');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvoteDAO.php');
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvoteChoice extends PhabricatorSlowvoteDAO {
|
||||||
|
|
||||||
|
protected $pollID;
|
||||||
|
protected $optionID;
|
||||||
|
protected $authorPHID;
|
||||||
|
|
||||||
|
}
|
12
src/applications/slowvote/storage/choice/__init__.php
Normal file
12
src/applications/slowvote/storage/choice/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/base');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvoteChoice.php');
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvoteComment extends PhabricatorSlowvoteDAO {
|
||||||
|
|
||||||
|
protected $pollID;
|
||||||
|
protected $authorPHID;
|
||||||
|
protected $commentText;
|
||||||
|
|
||||||
|
}
|
12
src/applications/slowvote/storage/comment/__init__.php
Normal file
12
src/applications/slowvote/storage/comment/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/base');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvoteComment.php');
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvoteOption extends PhabricatorSlowvoteDAO {
|
||||||
|
|
||||||
|
protected $pollID;
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
12
src/applications/slowvote/storage/option/__init__.php
Normal file
12
src/applications/slowvote/storage/option/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/base');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvoteOption.php');
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSlowvotePoll extends PhabricatorSlowvoteDAO {
|
||||||
|
|
||||||
|
const RESPONSES_VISIBLE = 0;
|
||||||
|
const RESPONSES_VOTERS = 1;
|
||||||
|
const RESPONSES_OWNER = 2;
|
||||||
|
|
||||||
|
const METHOD_PLURALITY = 0;
|
||||||
|
const METHOD_APPROVAL = 1;
|
||||||
|
|
||||||
|
protected $question;
|
||||||
|
protected $phid;
|
||||||
|
protected $authorPHID;
|
||||||
|
protected $responseVisibility;
|
||||||
|
protected $shuffle;
|
||||||
|
protected $method;
|
||||||
|
|
||||||
|
public function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePHID() {
|
||||||
|
return PhabricatorPHID::generateNewPHID(
|
||||||
|
PhabricatorPHIDConstants::PHID_TYPE_POLL);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
src/applications/slowvote/storage/poll/__init__.php
Normal file
14
src/applications/slowvote/storage/poll/__init__.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/constants');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/storage/phid');
|
||||||
|
phutil_require_module('phabricator', 'applications/slowvote/storage/base');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorSlowvotePoll.php');
|
27
src/docs/userguide/slowvote.diviner
Normal file
27
src/docs/userguide/slowvote.diviner
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
@title Slowvote User Guide
|
||||||
|
@group userguide
|
||||||
|
|
||||||
|
Detailed dispute resolution manual.
|
||||||
|
|
||||||
|
= Overview =
|
||||||
|
|
||||||
|
Slowvote is a simple polling application for asking questions like "Where should
|
||||||
|
we order pizza from?", "Are dogs better than cats?" and "What is the optimal
|
||||||
|
strategic direction for our company?". You can use it to quickly get feedback
|
||||||
|
from people in a lightweight, structured way.
|
||||||
|
|
||||||
|
Slowvote is based on a Facebook application called Quickvote, which was pretty
|
||||||
|
good when it was originally written but went through a couple of poorly executed
|
||||||
|
rewrites and ended up being extremely slow and glitchy, often taking more than
|
||||||
|
10 seconds to load. I originally designed and implemented the entire Slowvote
|
||||||
|
application while waiting for a Quikvote to load.
|
||||||
|
|
||||||
|
= Plurality vs Approval =
|
||||||
|
|
||||||
|
When you create a poll, you can choose **plurality** or **approval** voting.
|
||||||
|
|
||||||
|
In plurality voting, users choose one option. The winning option is the one with
|
||||||
|
the most votes.
|
||||||
|
|
||||||
|
In approval voting, users choose all the options they approve of. The winning
|
||||||
|
option is the one the largest number of people accept.
|
60
webroot/rsrc/css/application/slowvote/slowvote.css
Normal file
60
webroot/rsrc/css/application/slowvote/slowvote.css
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* @provides phabricator-slowvote-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.phabricator-slowvote-comments {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phabricator-slowvote-comments th {
|
||||||
|
width: 150px;
|
||||||
|
text-align: right;
|
||||||
|
padding: 6px 4px 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phabricator-slowvote-comments td {
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 6px 2px;
|
||||||
|
border-bottom: 1px solid #d0d0d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phabricator-slowvote-datestamp {
|
||||||
|
font-size: 9px;
|
||||||
|
font-family: "Verdana";
|
||||||
|
color: #666666;
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phabricator-slowvote-hr {
|
||||||
|
border: none;
|
||||||
|
height: 1px;
|
||||||
|
position: relative;
|
||||||
|
background: #c0c0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phabricator-slowvote-count {
|
||||||
|
float: right;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phabricator-slowvote-label {
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Reference in a new issue