summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/poll
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/poll')
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll-bar--block.tpl.php26
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll-bar.tpl.php26
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll-results--block.tpl.php28
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll-results.tpl.php30
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll-rtl.css10
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll-vote.tpl.php31
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll.css51
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll.install215
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll.module1019
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll.pages.inc97
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll.test872
-rw-r--r--kolab.org/www/drupal-7.26/modules/poll/poll.tokens.inc107
13 files changed, 2525 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll-bar--block.tpl.php b/kolab.org/www/drupal-7.26/modules/poll/poll-bar--block.tpl.php
new file mode 100644
index 0000000..3b91afc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll-bar--block.tpl.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display the bar for a single choice in a
+ * poll.
+ *
+ * Variables available:
+ * - $title: The title of the poll.
+ * - $votes: The number of votes for this choice
+ * - $total_votes: The number of votes for this choice
+ * - $percentage: The percentage of votes for this choice.
+ * - $vote: The choice number of the current user's vote.
+ * - $voted: Set to TRUE if the user voted for this choice.
+ *
+ * @see template_preprocess_poll_bar()
+ */
+?>
+
+<div class="text"><?php print $title; ?></div>
+<div class="bar">
+ <div style="width: <?php print $percentage; ?>%;" class="foreground"></div>
+</div>
+<div class="percent">
+ <?php print $percentage; ?>%
+</div>
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll-bar.tpl.php b/kolab.org/www/drupal-7.26/modules/poll/poll-bar.tpl.php
new file mode 100644
index 0000000..9426ff5
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll-bar.tpl.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display the bar for a single choice in a
+ * poll.
+ *
+ * Variables available:
+ * - $title: The title of the poll.
+ * - $votes: The number of votes for this choice
+ * - $total_votes: The number of votes for this choice
+ * - $percentage: The percentage of votes for this choice.
+ * - $vote: The choice number of the current user's vote.
+ * - $voted: Set to TRUE if the user voted for this choice.
+ *
+ * @see template_preprocess_poll_bar()
+ */
+?>
+
+<div class="text"><?php print $title; ?></div>
+<div class="bar">
+ <div style="width: <?php print $percentage; ?>%;" class="foreground"></div>
+</div>
+<div class="percent">
+ <?php print $percentage; ?>% (<?php print format_plural($votes, '1 vote', '@count votes'); ?>)
+</div>
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll-results--block.tpl.php b/kolab.org/www/drupal-7.26/modules/poll/poll-results--block.tpl.php
new file mode 100644
index 0000000..f8387f5
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll-results--block.tpl.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * @file
+ * Default theme implementation to display the poll results in a block.
+ *
+ * Variables available:
+ * - $title: The title of the poll.
+ * - $results: The results of the poll.
+ * - $votes: The total results in the poll.
+ * - $links: Links in the poll.
+ * - $nid: The nid of the poll
+ * - $cancel_form: A form to cancel the user's vote, if allowed.
+ * - $raw_links: The raw array of links. Should be run through theme('links')
+ * if used.
+ * - $vote: The choice number of the current user's vote.
+ *
+ * @see template_preprocess_poll_results()
+ */
+?>
+
+<div class="poll">
+ <div class="title"><?php print $title ?></div>
+ <?php print $results ?>
+ <div class="total">
+ <?php print t('Total votes: @votes', array('@votes' => $votes)); ?>
+ </div>
+</div>
+<div class="links"><?php print $links; ?></div>
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll-results.tpl.php b/kolab.org/www/drupal-7.26/modules/poll/poll-results.tpl.php
new file mode 100644
index 0000000..678bc2b
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll-results.tpl.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display the poll results in a block.
+ *
+ * Variables available:
+ * - $title: The title of the poll.
+ * - $results: The results of the poll.
+ * - $votes: The total results in the poll.
+ * - $links: Links in the poll.
+ * - $nid: The nid of the poll
+ * - $cancel_form: A form to cancel the user's vote, if allowed.
+ * - $raw_links: The raw array of links.
+ * - $vote: The choice number of the current user's vote.
+ *
+ * @see template_preprocess_poll_results()
+ *
+ * @ingroup themeable
+ */
+?>
+<div class="poll">
+ <?php print $results; ?>
+ <div class="total">
+ <?php print t('Total votes: @votes', array('@votes' => $votes)); ?>
+ </div>
+ <?php if (!empty($cancel_form)): ?>
+ <?php print $cancel_form; ?>
+ <?php endif; ?>
+</div>
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll-rtl.css b/kolab.org/www/drupal-7.26/modules/poll/poll-rtl.css
new file mode 100644
index 0000000..14d42e6
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll-rtl.css
@@ -0,0 +1,10 @@
+
+.poll .bar .foreground {
+ float: right;
+}
+.poll .percent {
+ text-align: left;
+}
+.poll .vote-form .choices {
+ text-align: right;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll-vote.tpl.php b/kolab.org/www/drupal-7.26/modules/poll/poll-vote.tpl.php
new file mode 100644
index 0000000..699a26d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll-vote.tpl.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display voting form for a poll.
+ *
+ * - $choice: The radio buttons for the choices in the poll.
+ * - $title: The title of the poll.
+ * - $block: True if this is being displayed as a block.
+ * - $vote: The vote button
+ * - $rest: Anything else in the form that may have been added via
+ * form_alter hooks.
+ *
+ * @see template_preprocess_poll_vote()
+ *
+ * @ingroup themeable
+ */
+?>
+<div class="poll">
+ <div class="vote-form">
+ <div class="choices">
+ <?php if ($block): ?>
+ <div class="title"><?php print $title; ?></div>
+ <?php endif; ?>
+ <?php print $choice; ?>
+ </div>
+ <?php print $vote; ?>
+ </div>
+ <?php // This is the 'rest' of the form, in case items have been added. ?>
+ <?php print $rest ?>
+</div>
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll.css b/kolab.org/www/drupal-7.26/modules/poll/poll.css
new file mode 100644
index 0000000..8b04e38
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll.css
@@ -0,0 +1,51 @@
+
+.poll {
+ overflow: hidden;
+}
+.poll .bar {
+ height: 1em;
+ margin: 1px 0;
+ background-color: #ddd;
+}
+.poll .bar .foreground {
+ background-color: #000;
+ height: 1em;
+ float: left; /* LTR */
+}
+.poll .links {
+ text-align: center;
+}
+.poll .percent {
+ text-align: right; /* LTR */
+}
+.poll .total {
+ text-align: center;
+}
+.poll .vote-form {
+ text-align: center;
+}
+.poll .vote-form .choices {
+ text-align: left; /* LTR */
+ margin: 0 auto;
+ display: table;
+}
+.poll .vote-form .choices .title {
+ font-weight: bold;
+}
+.node-form #edit-poll-more {
+ margin: 0;
+}
+.node-form #poll-choice-table .form-text {
+ display: inline;
+ width: auto;
+}
+.node-form #poll-choice-table td.choice-flag {
+ white-space: nowrap;
+ width: 4em;
+}
+td.poll-chtext {
+ width: 80%;
+}
+td.poll-chvotes .form-text {
+ width: 85%;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll.info b/kolab.org/www/drupal-7.26/modules/poll/poll.info
new file mode 100644
index 0000000..312b4fb
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll.info
@@ -0,0 +1,13 @@
+name = Poll
+description = Allows your site to capture votes on different topics in the form of multiple choice questions.
+package = Core
+version = VERSION
+core = 7.x
+files[] = poll.test
+stylesheets[all][] = poll.css
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll.install b/kolab.org/www/drupal-7.26/modules/poll/poll.install
new file mode 100644
index 0000000..8c58025
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll.install
@@ -0,0 +1,215 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the poll module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function poll_schema() {
+ $schema['poll'] = array(
+ 'description' => 'Stores poll-specific information for poll nodes.',
+ 'fields' => array(
+ 'nid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => "The poll's {node}.nid.",
+ ),
+ 'runtime' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The number of seconds past {node}.created during which the poll is open.',
+ ),
+ 'active' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Boolean indicating whether or not the poll is open.',
+ ),
+ ),
+ 'primary key' => array('nid'),
+ 'foreign keys' => array(
+ 'poll_node' => array(
+ 'table' => 'node',
+ 'columns' => array('nid' => 'nid'),
+ ),
+ ),
+ );
+
+ $schema['poll_choice'] = array(
+ 'description' => 'Stores information about all choices for all {poll}s.',
+ 'fields' => array(
+ 'chid' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'Unique identifier for a poll choice.',
+ ),
+ 'nid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The {node}.nid this choice belongs to.',
+ ),
+ 'chtext' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The text for this choice.',
+ 'translatable' => TRUE,
+ ),
+ 'chvotes' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The total number of votes this choice has received by all users.',
+ ),
+ 'weight' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The sort order of this choice among all choices for the same node.',
+ ),
+ ),
+ 'indexes' => array(
+ 'nid' => array('nid'),
+ ),
+ 'primary key' => array('chid'),
+ 'foreign keys' => array(
+ 'choice_node' => array(
+ 'table' => 'node',
+ 'columns' => array('nid' => 'nid'),
+ ),
+ ),
+ );
+
+ $schema['poll_vote'] = array(
+ 'description' => 'Stores per-{users} votes for each {poll}.',
+ 'fields' => array(
+ 'chid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => "The {users}'s vote for this poll.",
+ ),
+ 'nid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'The {poll} node this vote is for.',
+ ),
+ 'uid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The {users}.uid this vote is from unless the voter was anonymous.',
+ ),
+ 'hostname' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The IP address this vote is from unless the voter was logged in.',
+ ),
+ 'timestamp' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The timestamp of the vote creation.',
+ ),
+ ),
+ 'primary key' => array('nid', 'uid', 'hostname'),
+ 'foreign keys' => array(
+ 'poll_node' => array(
+ 'table' => 'node',
+ 'columns' => array('nid' => 'nid'),
+ ),
+ 'voter' => array(
+ 'table' => 'users',
+ 'columns' => array('uid' => 'uid'),
+ ),
+ ),
+ 'indexes' => array(
+ 'chid' => array('chid'),
+ 'hostname' => array('hostname'),
+ 'uid' => array('uid'),
+ ),
+ );
+
+ return $schema;
+}
+
+/**
+ * Use the poll_choice primary key to record votes in poll_votes rather than
+ * the choice order. Rename chorder to weight.
+ *
+ * Rename {poll_choices} table to {poll_choice} and {poll_votes} to {poll_vote}.
+ */
+function poll_update_7001() {
+ // Add chid column and convert existing votes.
+ db_add_field('poll_votes', 'chid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
+ db_add_index('poll_votes', 'chid', array('chid'));
+ db_update('poll_votes')
+ ->expression('chid', Database::getConnection()->prefixTables('COALESCE((SELECT chid FROM {poll_choices} c WHERE {poll_votes}.chorder = c.chorder AND {poll_votes}.nid = c.nid), 0)'))
+ ->execute();
+ // Delete invalid votes.
+ db_delete('poll_votes')->condition('chid', 0)->execute();
+ // Remove old chorder column.
+ db_drop_field('poll_votes', 'chorder');
+
+ // Change the chorder column to weight in poll_choices.
+ db_change_field('poll_choices', 'chorder', 'weight', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'tiny'));
+
+ db_rename_table('poll_votes', 'poll_vote');
+ db_rename_table('poll_choices', 'poll_choice');
+}
+
+/**
+ * Add timestamp field to {poll_vote}.
+ */
+function poll_update_7002() {
+ $field = array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ );
+ db_add_field('poll_vote', 'timestamp', $field);
+}
+
+/**
+ * Change the weight column to normal int.
+ */
+function poll_update_7003() {
+ db_change_field('poll_choice', 'weight', 'weight', array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The sort order of this choice among all choices for the same node.',
+ ));
+}
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Update the database to match the schema.
+ */
+function poll_update_7004() {
+ // Remove field default.
+ db_change_field('poll_vote', 'chid', 'chid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE));
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll.module b/kolab.org/www/drupal-7.26/modules/poll/poll.module
new file mode 100644
index 0000000..70eb65d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll.module
@@ -0,0 +1,1019 @@
+<?php
+
+/**
+ * @file
+ * Enables your site to capture votes on different topics in the form of multiple
+ * choice questions.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function poll_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#poll':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Poll module can be used to create simple surveys or questionnaires that display cumulative results. A poll is a good way to receive feedback from site users and community members. For more information, see the online handbook entry for the <a href="@poll">Poll module</a>.', array('@poll' => 'http://drupal.org/documentation/modules/poll/')) . '</p>';
+ $output .= '<h3>' . t('Uses') . '</h3>';
+ $output .= '<dl>';
+ $output .= '<dt>' . t('Creating a poll') . '</dt>';
+ $output .= '<dd>' . t('Users can create a poll by clicking on Poll on the <a href="@add-content">Add new content</a> page, and entering the question being posed, the answer choices, and beginning vote counts for each choice. The status (closed or active) and duration (length of time the poll remains active for new votes) can also be specified.', array('@add-content' => url('node/add'))) . '</dd>';
+ $output .= '<dt>' . t('Viewing polls') . '</dt>';
+ $output .= '<dd>' . t('You can visit the <a href="@poll">Polls</a> page to view all current polls, or alternately enable the <em>Most recent poll</em> block on the <a href="@blocks">Blocks administration page</a>. To vote in or view the results of a specific poll, you can click on the poll itself.', array('@poll' => url('poll'), '@blocks' => url('admin/structure/block'))) . '</dd>';
+ $output .= '</dl>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function poll_theme() {
+ $theme_hooks = array(
+ 'poll_vote' => array(
+ 'template' => 'poll-vote',
+ 'render element' => 'form',
+ ),
+ 'poll_choices' => array(
+ 'render element' => 'form',
+ ),
+ 'poll_results' => array(
+ 'template' => 'poll-results',
+ 'variables' => array('raw_title' => NULL, 'results' => NULL, 'votes' => NULL, 'raw_links' => NULL, 'block' => NULL, 'nid' => NULL, 'vote' => NULL),
+ ),
+ 'poll_bar' => array(
+ 'template' => 'poll-bar',
+ 'variables' => array('title' => NULL, 'votes' => NULL, 'total_votes' => NULL, 'vote' => NULL, 'block' => NULL),
+ ),
+ );
+ // The theme system automatically discovers the theme's functions and
+ // templates that implement more targeted "suggestions" of generic theme
+ // hooks. But suggestions implemented by a module must be explicitly
+ // registered.
+ $theme_hooks += array(
+ 'poll_results__block' => array(
+ 'template' => 'poll-results--block',
+ 'variables' => $theme_hooks['poll_results']['variables'],
+ ),
+ 'poll_bar__block' => array(
+ 'template' => 'poll-bar--block',
+ 'variables' => $theme_hooks['poll_bar']['variables'],
+ ),
+ );
+ return $theme_hooks;
+}
+
+/**
+ * Implements hook_permission().
+ */
+function poll_permission() {
+ $perms = array(
+ 'vote on polls' => array(
+ 'title' => t('Vote on polls'),
+ ),
+ 'cancel own vote' => array(
+ 'title' => t('Cancel and change own votes'),
+ ),
+ 'inspect all votes' => array(
+ 'title' => t('View details for all votes'),
+ ),
+ );
+
+ return $perms;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function poll_menu() {
+ $items['poll'] = array(
+ 'title' => 'Polls',
+ 'page callback' => 'poll_page',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_SUGGESTED_ITEM,
+ 'file' => 'poll.pages.inc',
+ );
+
+ $items['node/%node/votes'] = array(
+ 'title' => 'Votes',
+ 'page callback' => 'poll_votes',
+ 'page arguments' => array(1),
+ 'access callback' => '_poll_menu_access',
+ 'access arguments' => array(1, 'inspect all votes', FALSE),
+ 'weight' => 3,
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => 'poll.pages.inc',
+ );
+
+ $items['node/%node/results'] = array(
+ 'title' => 'Results',
+ 'page callback' => 'poll_results',
+ 'page arguments' => array(1),
+ 'access callback' => '_poll_menu_access',
+ 'access arguments' => array(1, 'access content', TRUE),
+ 'weight' => 3,
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => 'poll.pages.inc',
+ );
+
+ return $items;
+}
+
+/**
+ * Callback function to see if a node is acceptable for poll menu items.
+ */
+function _poll_menu_access($node, $perm, $inspect_allowvotes) {
+ return user_access($perm) && ($node->type == 'poll') && ($node->allowvotes || !$inspect_allowvotes);
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function poll_block_info() {
+ $blocks['recent']['info'] = t('Most recent poll');
+ $blocks['recent']['properties']['administrative'] = TRUE;
+ return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ *
+ * Generates a block containing the latest poll.
+ */
+function poll_block_view($delta = '') {
+ if (user_access('access content')) {
+ // Retrieve the latest poll.
+ $select = db_select('node', 'n');
+ $select->join('poll', 'p', 'p.nid = n.nid');
+ $select->fields('n', array('nid'))
+ ->condition('n.status', 1)
+ ->condition('p.active', 1)
+ ->orderBy('n.created', 'DESC')
+ ->range(0, 1)
+ ->addTag('node_access');
+
+ $record = $select->execute()->fetchObject();
+ if ($record) {
+ $poll = node_load($record->nid);
+ if ($poll->nid) {
+ $poll = poll_block_latest_poll_view($poll);
+ $block['subject'] = t('Poll');
+ $block['content'] = $poll->content;
+ return $block;
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_cron().
+ *
+ * Closes polls that have exceeded their allowed runtime.
+ */
+function poll_cron() {
+ $nids = db_query('SELECT p.nid FROM {poll} p INNER JOIN {node} n ON p.nid = n.nid WHERE (n.created + p.runtime) < :request_time AND p.active = :active AND p.runtime <> :runtime', array(':request_time' => REQUEST_TIME, ':active' => 1, ':runtime' => 0))->fetchCol();
+ if (!empty($nids)) {
+ db_update('poll')
+ ->fields(array('active' => 0))
+ ->condition('nid', $nids, 'IN')
+ ->execute();
+ }
+}
+
+/**
+ * Implements hook_node_info().
+ */
+function poll_node_info() {
+ return array(
+ 'poll' => array(
+ 'name' => t('Poll'),
+ 'base' => 'poll',
+ 'description' => t('A <em>poll</em> is a question with a set of possible responses. A <em>poll</em>, once created, automatically provides a simple running count of the number of votes received for each response.'),
+ 'title_label' => t('Question'),
+ 'has_body' => FALSE,
+ )
+ );
+}
+
+/**
+ * Implements hook_field_extra_fields().
+ */
+function poll_field_extra_fields() {
+ $extra['node']['poll'] = array(
+ 'form' => array(
+ 'choice_wrapper' => array(
+ 'label' => t('Poll choices'),
+ 'description' => t('Poll choices'),
+ 'weight' => -4,
+ ),
+ 'settings' => array(
+ 'label' => t('Poll settings'),
+ 'description' => t('Poll module settings'),
+ 'weight' => -3,
+ ),
+ ),
+ 'display' => array(
+ 'poll_view_voting' => array(
+ 'label' => t('Poll vote'),
+ 'description' => t('Poll vote'),
+ 'weight' => 0,
+ ),
+ 'poll_view_results' => array(
+ 'label' => t('Poll results'),
+ 'description' => t('Poll results'),
+ 'weight' => 0,
+ ),
+ )
+ );
+
+ return $extra;
+}
+
+/**
+ * Implements hook_form().
+ */
+function poll_form($node, &$form_state) {
+ global $user;
+
+ $admin = user_access('bypass node access') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid);
+
+ $type = node_type_get_type($node);
+
+ // The submit handlers to add more poll choices require that this form is
+ // cached, regardless of whether Ajax is used.
+ $form_state['cache'] = TRUE;
+
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => check_plain($type->title_label),
+ '#required' => TRUE,
+ '#default_value' => $node->title,
+ '#weight' => -5,
+ );
+
+ if (isset($form_state['choice_count'])) {
+ $choice_count = $form_state['choice_count'];
+ }
+ else {
+ $choice_count = max(2, empty($node->choice) ? 2 : count($node->choice));
+ }
+
+ // Add a wrapper for the choices and more button.
+ $form['choice_wrapper'] = array(
+ '#tree' => FALSE,
+ '#weight' => -4,
+ '#prefix' => '<div class="clearfix" id="poll-choice-wrapper">',
+ '#suffix' => '</div>',
+ );
+
+ // Container for just the poll choices.
+ $form['choice_wrapper']['choice'] = array(
+ '#prefix' => '<div id="poll-choices">',
+ '#suffix' => '</div>',
+ '#theme' => 'poll_choices',
+ );
+
+ // Add the current choices to the form.
+ $delta = 0;
+ $weight = 0;
+ if (isset($node->choice)) {
+ $delta = count($node->choice);
+ foreach ($node->choice as $chid => $choice) {
+ $key = 'chid:' . $chid;
+ $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, $choice['chid'], $choice['chtext'], $choice['chvotes'], $choice['weight'], $choice_count);
+ $weight = max($choice['weight'], $weight);
+ }
+ }
+
+ // Add initial or additional choices.
+ $existing_delta = $delta;
+ for ($delta; $delta < $choice_count; $delta++) {
+ $key = 'new:' . ($delta - $existing_delta);
+ // Increase the weight of each new choice.
+ $weight++;
+ $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL, '', 0, $weight, $choice_count);
+ }
+
+ // We name our button 'poll_more' to avoid conflicts with other modules using
+ // Ajax-enabled buttons with the id 'more'.
+ $form['choice_wrapper']['poll_more'] = array(
+ '#type' => 'submit',
+ '#value' => t('More choices'),
+ '#attributes' => array(
+ 'title' => t("If the amount of boxes above isn't enough, click here to add more choices."),
+ ),
+ '#weight' => 1,
+ '#limit_validation_errors' => array(array('choice')),
+ '#submit' => array('poll_more_choices_submit'),
+ '#ajax' => array(
+ 'callback' => 'poll_choice_js',
+ 'wrapper' => 'poll-choices',
+ 'effect' => 'fade',
+ ),
+ );
+
+ // Poll attributes
+ $duration = array(
+ // 1-6 days.
+ 86400, 2 * 86400, 3 * 86400, 4 * 86400, 5 * 86400, 6 * 86400,
+ // 1-3 weeks (7 days).
+ 604800, 2 * 604800, 3 * 604800,
+ // 1-3,6,9 months (30 days).
+ 2592000, 2 * 2592000, 3 * 2592000, 6 * 2592000, 9 * 2592000,
+ // 1 year (365 days).
+ 31536000,
+ );
+ $duration = array(0 => t('Unlimited')) + drupal_map_assoc($duration, 'format_interval');
+ $active = array(0 => t('Closed'), 1 => t('Active'));
+
+ $form['settings'] = array(
+ '#type' => 'fieldset',
+ '#collapsible' => TRUE,
+ '#title' => t('Poll settings'),
+ '#weight' => -3,
+ '#access' => $admin,
+ );
+
+ $form['settings']['active'] = array(
+ '#type' => 'radios',
+ '#title' => t('Poll status'),
+ '#default_value' => isset($node->active) ? $node->active : 1,
+ '#options' => $active,
+ '#description' => t('When a poll is closed, visitors can no longer vote for it.'),
+ '#access' => $admin,
+ );
+ $form['settings']['runtime'] = array(
+ '#type' => 'select',
+ '#title' => t('Poll duration'),
+ '#default_value' => isset($node->runtime) ? $node->runtime : 0,
+ '#options' => $duration,
+ '#description' => t('After this period, the poll will be closed automatically.'),
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler to add more choices to a poll form.
+ *
+ * This handler is run regardless of whether JS is enabled or not. It makes
+ * changes to the form state. If the button was clicked with JS disabled, then
+ * the page is reloaded with the complete rebuilt form. If the button was
+ * clicked with JS enabled, then ajax_form_callback() calls poll_choice_js() to
+ * return just the changed part of the form.
+ */
+function poll_more_choices_submit($form, &$form_state) {
+ // If this is a Ajax POST, add 1, otherwise add 5 more choices to the form.
+ if ($form_state['values']['poll_more']) {
+ $n = $_GET['q'] == 'system/ajax' ? 1 : 5;
+ $form_state['choice_count'] = count($form_state['values']['choice']) + $n;
+ }
+ // Renumber the choices. This invalidates the corresponding key/value
+ // associations in $form_state['input'], so clear that out. This requires
+ // poll_form() to rebuild the choices with the values in
+ // $form_state['node']->choice, which it does.
+ $form_state['node']->choice = array_values($form_state['values']['choice']);
+ unset($form_state['input']['choice']);
+ $form_state['rebuild'] = TRUE;
+}
+
+function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight = 0, $size = 10) {
+ $form = array(
+ '#tree' => TRUE,
+ '#weight' => $weight,
+ );
+
+ // We'll manually set the #parents property of these fields so that
+ // their values appear in the $form_state['values']['choice'] array.
+ $form['chid'] = array(
+ '#type' => 'value',
+ '#value' => $chid,
+ '#parents' => array('choice', $key, 'chid'),
+ );
+
+ $form['chtext'] = array(
+ '#type' => 'textfield',
+ '#title' => $value !== '' ? t('Choice label') : t('New choice label'),
+ '#title_display' => 'invisible',
+ '#default_value' => $value,
+ '#parents' => array('choice', $key, 'chtext'),
+ );
+
+ $form['chvotes'] = array(
+ '#type' => 'textfield',
+ '#title' => $value !== '' ? t('Vote count for choice @label', array('@label' => $value)) : t('Vote count for new choice'),
+ '#title_display' => 'invisible',
+ '#default_value' => $votes,
+ '#size' => 5,
+ '#maxlength' => 7,
+ '#parents' => array('choice', $key, 'chvotes'),
+ '#access' => user_access('administer nodes'),
+ '#element_validate' => array('element_validate_integer'),
+ );
+
+ $form['weight'] = array(
+ '#type' => 'weight',
+ '#title' => $value !== '' ? t('Weight for choice @label', array('@label' => $value)) : t('Weight for new choice'),
+ '#title_display' => 'invisible',
+ '#default_value' => $weight,
+ '#delta' => $size,
+ '#parents' => array('choice', $key, 'weight'),
+ );
+
+ return $form;
+}
+
+/**
+ * Ajax callback in response to new choices being added to the form.
+ *
+ * This returns the new page content to replace the page content made obsolete
+ * by the form submission.
+ *
+ * @see poll_more_choices_submit()
+ */
+function poll_choice_js($form, $form_state) {
+ return $form['choice_wrapper']['choice'];
+}
+
+/**
+ * Form submit handler for node_form().
+ *
+ * Upon preview and final submission, we need to renumber poll choices and
+ * create a teaser output.
+ */
+function poll_node_form_submit(&$form, &$form_state) {
+ // Renumber choices.
+ $form_state['values']['choice'] = array_values($form_state['values']['choice']);
+ $form_state['values']['teaser'] = poll_teaser((object) $form_state['values']);
+}
+
+/**
+ * Implements hook_validate().
+ */
+function poll_validate($node, $form) {
+ if (isset($node->title)) {
+ // Check for at least two options and validate amount of votes.
+ $realchoices = 0;
+ foreach ($node->choice as $i => $choice) {
+ if ($choice['chtext'] != '') {
+ $realchoices++;
+ }
+ if (isset($choice['chvotes']) && $choice['chvotes'] < 0) {
+ form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
+ }
+ }
+
+ if ($realchoices < 2) {
+ form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
+ }
+ }
+}
+
+/**
+ * Implements hook_field_attach_prepare_translation_alter().
+ */
+function poll_field_attach_prepare_translation_alter(&$entity, $context) {
+ if ($context['entity_type'] == 'node' && $entity->type == 'poll') {
+ $entity->choice = $context['source_entity']->choice;
+ foreach ($entity->choice as $i => $options) {
+ $entity->choice[$i]['chvotes'] = 0;
+ }
+ }
+}
+
+/**
+ * Implements hook_load().
+ */
+function poll_load($nodes) {
+ global $user;
+ foreach ($nodes as $node) {
+ $poll = db_query("SELECT runtime, active FROM {poll} WHERE nid = :nid", array(':nid' => $node->nid))->fetchObject();
+
+ if (empty($poll)) {
+ $poll = new stdClass();
+ }
+
+ // Load the appropriate choices into the $poll object.
+ $poll->choice = db_select('poll_choice', 'c')
+ ->addTag('translatable')
+ ->fields('c', array('chid', 'chtext', 'chvotes', 'weight'))
+ ->condition('c.nid', $node->nid)
+ ->orderBy('weight')
+ ->execute()->fetchAllAssoc('chid', PDO::FETCH_ASSOC);
+
+ // Determine whether or not this user is allowed to vote.
+ $poll->allowvotes = FALSE;
+ if (user_access('vote on polls') && $poll->active) {
+ if ($user->uid) {
+ // If authenticated, find existing vote based on uid.
+ $poll->vote = db_query('SELECT chid FROM {poll_vote} WHERE nid = :nid AND uid = :uid', array(':nid' => $node->nid, ':uid' => $user->uid))->fetchField();
+ if (empty($poll->vote)) {
+ $poll->vote = -1;
+ $poll->allowvotes = TRUE;
+ }
+ }
+ elseif (!empty($_SESSION['poll_vote'][$node->nid])) {
+ // Otherwise the user is anonymous. Look for an existing vote in the
+ // user's session.
+ $poll->vote = $_SESSION['poll_vote'][$node->nid];
+ }
+ else {
+ // Finally, query the database for an existing vote based on anonymous
+ // user's hostname.
+ $poll->allowvotes = !db_query("SELECT 1 FROM {poll_vote} WHERE nid = :nid AND hostname = :hostname AND uid = 0", array(':nid' => $node->nid, ':hostname' => ip_address()))->fetchField();
+ }
+ }
+ foreach ($poll as $key => $value) {
+ $nodes[$node->nid]->$key = $value;
+ }
+ }
+}
+
+/**
+ * Implements hook_insert().
+ */
+function poll_insert($node) {
+ if (!user_access('administer nodes')) {
+ // Make sure all votes are 0 initially
+ foreach ($node->choice as $i => $choice) {
+ $node->choice[$i]['chvotes'] = 0;
+ }
+ $node->active = 1;
+ }
+
+ db_insert('poll')
+ ->fields(array(
+ 'nid' => $node->nid,
+ 'runtime' => $node->runtime,
+ 'active' => $node->active,
+ ))
+ ->execute();
+
+ foreach ($node->choice as $choice) {
+ if ($choice['chtext'] != '') {
+ db_insert('poll_choice')
+ ->fields(array(
+ 'nid' => $node->nid,
+ 'chtext' => $choice['chtext'],
+ 'chvotes' => $choice['chvotes'],
+ 'weight' => $choice['weight'],
+ ))
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_update().
+ */
+function poll_update($node) {
+ // Update poll settings.
+ db_update('poll')
+ ->fields(array(
+ 'runtime' => $node->runtime,
+ 'active' => $node->active,
+ ))
+ ->condition('nid', $node->nid)
+ ->execute();
+
+ // Poll choices with empty titles signifies removal. We remove all votes to
+ // the removed options, so people who voted on them can vote again.
+ foreach ($node->choice as $key => $choice) {
+ if (!empty($choice['chtext'])) {
+ db_merge('poll_choice')
+ ->key(array('chid' => $choice['chid']))
+ ->fields(array(
+ 'chtext' => $choice['chtext'],
+ 'chvotes' => (int) $choice['chvotes'],
+ 'weight' => $choice['weight'],
+ ))
+ ->insertFields(array(
+ 'nid' => $node->nid,
+ 'chtext' => $choice['chtext'],
+ 'chvotes' => (int) $choice['chvotes'],
+ 'weight' => $choice['weight'],
+ ))
+ ->execute();
+ }
+ else {
+ db_delete('poll_vote')
+ ->condition('nid', $node->nid)
+ ->condition('chid', $key)
+ ->execute();
+ db_delete('poll_choice')
+ ->condition('nid', $node->nid)
+ ->condition('chid', $choice['chid'])
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_delete().
+ */
+function poll_delete($node) {
+ db_delete('poll')
+ ->condition('nid', $node->nid)
+ ->execute();
+ db_delete('poll_choice')
+ ->condition('nid', $node->nid)
+ ->execute();
+ db_delete('poll_vote')
+ ->condition('nid', $node->nid)
+ ->execute();
+}
+
+/**
+ * Return content for 'latest poll' block.
+ *
+ * @param $node
+ * The node object to load.
+ */
+function poll_block_latest_poll_view($node) {
+ global $user;
+ $output = '';
+
+ // This is necessary for shared objects because PHP doesn't copy objects, but
+ // passes them by reference. So when the objects are cached it can result in
+ // the wrong output being displayed on subsequent calls. The cloning and
+ // unsetting of $node->content prevents the block output from being the same
+ // as the node output.
+ $node = clone $node;
+ unset($node->content);
+
+ // No 'read more' link.
+ $node->readmore = FALSE;
+ $node->teaser = '';
+
+ $links = array();
+ $links[] = array('title' => t('Older polls'), 'href' => 'poll', 'attributes' => array('title' => t('View the list of polls on this site.')));
+ if ($node->allowvotes) {
+ $links[] = array('title' => t('Results'), 'href' => 'node/' . $node->nid . '/results', 'attributes' => array('title' => t('View the current poll results.')));
+ }
+
+ $node->links = $links;
+
+ if (!empty($node->allowvotes)) {
+ $node->content['poll_view_voting'] = drupal_get_form('poll_view_voting', $node, TRUE);
+ $node->content['links'] = array(
+ '#theme' => 'links',
+ '#links' => $node->links,
+ '#weight' => 5,
+ );
+ }
+ else {
+ $node->content['poll_view_results'] = array('#markup' => poll_view_results($node, TRUE, TRUE));
+ }
+
+ return $node;
+}
+
+
+/**
+ * Implements hook_view().
+ */
+function poll_view($node, $view_mode) {
+ global $user;
+ $output = '';
+
+ if (!empty($node->allowvotes) && empty($node->show_results)) {
+ $node->content['poll_view_voting'] = drupal_get_form('poll_view_voting', $node);
+ }
+ else {
+ $node->content['poll_view_results'] = array('#markup' => poll_view_results($node, $view_mode));
+ }
+ return $node;
+}
+
+/**
+ * Creates a simple teaser that lists all the choices.
+ *
+ * This is primarily used for RSS.
+ */
+function poll_teaser($node) {
+ $teaser = NULL;
+ if (is_array($node->choice)) {
+ foreach ($node->choice as $k => $choice) {
+ if ($choice['chtext'] != '') {
+ $teaser .= '* ' . check_plain($choice['chtext']) . "\n";
+ }
+ }
+ }
+ return $teaser;
+}
+
+/**
+ * Generates the voting form for a poll.
+ *
+ * @ingroup forms
+ * @see poll_vote()
+ * @see phptemplate_preprocess_poll_vote()
+ */
+function poll_view_voting($form, &$form_state, $node, $block = FALSE) {
+ if ($node->choice) {
+ $list = array();
+ foreach ($node->choice as $i => $choice) {
+ $list[$i] = check_plain($choice['chtext']);
+ }
+ $form['choice'] = array(
+ '#type' => 'radios',
+ '#title' => t('Choices'),
+ '#title_display' => 'invisible',
+ '#default_value' => -1,
+ '#options' => $list,
+ );
+ }
+
+ $form['vote'] = array(
+ '#type' => 'submit',
+ '#value' => t('Vote'),
+ '#submit' => array('poll_vote'),
+ );
+
+ // Store the node so we can get to it in submit functions.
+ $form['#node'] = $node;
+ $form['#block'] = $block;
+
+ // Set form caching because we could have multiple of these forms on
+ // the same page, and we want to ensure the right one gets picked.
+ $form_state['cache'] = TRUE;
+
+ // Provide a more cleanly named voting form theme.
+ $form['#theme'] = 'poll_vote';
+ return $form;
+}
+
+/**
+ * Validation function for processing votes
+ */
+function poll_view_voting_validate($form, &$form_state) {
+ if ($form_state['values']['choice'] == -1) {
+ form_set_error( 'choice', t('Your vote could not be recorded because you did not select any of the choices.'));
+ }
+}
+
+/**
+ * Submit handler for processing a vote.
+ */
+function poll_vote($form, &$form_state) {
+ $node = $form['#node'];
+ $choice = $form_state['values']['choice'];
+
+ global $user;
+ db_insert('poll_vote')
+ ->fields(array(
+ 'nid' => $node->nid,
+ 'chid' => $choice,
+ 'uid' => $user->uid,
+ 'hostname' => ip_address(),
+ 'timestamp' => REQUEST_TIME,
+ ))
+ ->execute();
+
+ // Add one to the votes.
+ db_update('poll_choice')
+ ->expression('chvotes', 'chvotes + 1')
+ ->condition('chid', $choice)
+ ->execute();
+
+ cache_clear_all();
+
+ if (!$user->uid) {
+ // The vote is recorded so the user gets the result view instead of the
+ // voting form when viewing the poll. Saving a value in $_SESSION has the
+ // convenient side effect of preventing the user from hitting the page
+ // cache. When anonymous voting is allowed, the page cache should only
+ // contain the voting form, not the results.
+ $_SESSION['poll_vote'][$node->nid] = $choice;
+ }
+
+ drupal_set_message(t('Your vote was recorded.'));
+
+ // Return the user to whatever page they voted from.
+}
+
+/**
+ * Themes the voting form for a poll.
+ *
+ * Inputs: $form
+ */
+function template_preprocess_poll_vote(&$variables) {
+ $form = $variables['form'];
+ $variables['choice'] = drupal_render($form['choice']);
+ $variables['title'] = check_plain($form['#node']->title);
+ $variables['vote'] = drupal_render($form['vote']);
+ $variables['rest'] = drupal_render_children($form);
+ $variables['block'] = $form['#block'];
+ if ($variables['block']) {
+ $variables['theme_hook_suggestions'][] = 'poll_vote__block';
+ }
+}
+
+/**
+ * Generates a graphical representation of the results of a poll.
+ */
+function poll_view_results($node, $view_mode, $block = FALSE) {
+ // Make sure that choices are ordered by their weight.
+ uasort($node->choice, 'drupal_sort_weight');
+
+ // Count the votes and find the maximum.
+ $total_votes = 0;
+ $max_votes = 0;
+ foreach ($node->choice as $choice) {
+ if (isset($choice['chvotes'])) {
+ $total_votes += $choice['chvotes'];
+ $max_votes = max($max_votes, $choice['chvotes']);
+ }
+ }
+
+ $poll_results = '';
+ foreach ($node->choice as $i => $choice) {
+ if (!empty($choice['chtext'])) {
+ $chvotes = isset($choice['chvotes']) ? $choice['chvotes'] : NULL;
+ $poll_results .= theme('poll_bar', array('title' => $choice['chtext'], 'votes' => $chvotes, 'total_votes' => $total_votes, 'vote' => isset($node->vote) && $node->vote == $i, 'block' => $block));
+ }
+ }
+
+ return theme('poll_results', array('raw_title' => $node->title, 'results' => $poll_results, 'votes' => $total_votes, 'raw_links' => isset($node->links) ? $node->links : array(), 'block' => $block, 'nid' => $node->nid, 'vote' => isset($node->vote) ? $node->vote : NULL));
+}
+
+
+/**
+ * Returns HTML for an admin poll form for choices.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_poll_choices($variables) {
+ $form = $variables['form'];
+
+ drupal_add_tabledrag('poll-choice-table', 'order', 'sibling', 'poll-weight');
+
+ $is_admin= user_access('administer nodes');
+ $delta = 0;
+ $rows = array();
+ $headers = array('', t('Choice'));
+ if ($is_admin) {
+ $headers[] = t('Vote count');
+ }
+ $headers[] = t('Weight');
+
+ foreach (element_children($form) as $key) {
+ $delta++;
+ // Set special classes for drag and drop updating.
+ $form[$key]['weight']['#attributes']['class'] = array('poll-weight');
+
+ // Build the table row.
+ $row = array(
+ 'data' => array(
+ array('class' => array('choice-flag')),
+ drupal_render($form[$key]['chtext']),
+ ),
+ 'class' => array('draggable'),
+ );
+ if ($is_admin) {
+ $row['data'][] = drupal_render($form[$key]['chvotes']);
+ }
+ $row['data'][] = drupal_render($form[$key]['weight']);
+
+ // Add any additional classes set on the row.
+ if (!empty($form[$key]['#attributes']['class'])) {
+ $row['class'] = array_merge($row['class'], $form[$key]['#attributes']['class']);
+ }
+
+ $rows[] = $row;
+ }
+
+ $output = theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => array('id' => 'poll-choice-table')));
+ $output .= drupal_render_children($form);
+ return $output;
+}
+
+/**
+ * Preprocess the poll_results theme hook.
+ *
+ * Inputs: $raw_title, $results, $votes, $raw_links, $block, $nid, $vote. The
+ * $raw_* inputs to this are naturally unsafe; often safe versions are
+ * made to simply overwrite the raw version, but in this case it seems likely
+ * that the title and the links may be overridden by the theme layer, so they
+ * are left in with a different name for that purpose.
+ *
+ * @see poll-results.tpl.php
+ * @see poll-results--block.tpl.php
+ */
+function template_preprocess_poll_results(&$variables) {
+ $variables['links'] = theme('links__poll_results', array('links' => $variables['raw_links']));
+ if (isset($variables['vote']) && $variables['vote'] > -1 && user_access('cancel own vote')) {
+ $elements = drupal_get_form('poll_cancel_form', $variables['nid']);
+ $variables['cancel_form'] = drupal_render($elements);
+ }
+ $variables['title'] = check_plain($variables['raw_title']);
+
+ if ($variables['block']) {
+ $variables['theme_hook_suggestions'][] = 'poll_results__block';
+ }
+}
+
+/**
+ * Preprocess the poll_bar theme hook.
+ *
+ * Inputs: $title, $votes, $total_votes, $voted, $block
+ *
+ * @see poll-bar.tpl.php
+ * @see poll-bar--block.tpl.php
+ * @see theme_poll_bar()
+ */
+function template_preprocess_poll_bar(&$variables) {
+ if ($variables['block']) {
+ $variables['theme_hook_suggestions'][] = 'poll_bar__block';
+ }
+ $variables['title'] = check_plain($variables['title']);
+ $variables['percentage'] = round($variables['votes'] * 100 / max($variables['total_votes'], 1));
+}
+
+/**
+ * Builds the cancel form for a poll.
+ *
+ * @ingroup forms
+ * @see poll_cancel()
+ */
+function poll_cancel_form($form, &$form_state, $nid) {
+ $form_state['cache'] = TRUE;
+
+ // Store the nid so we can get to it in submit functions.
+ $form['#nid'] = $nid;
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Cancel your vote'),
+ '#submit' => array('poll_cancel')
+ );
+
+ return $form;
+}
+
+/**
+ * Submit callback for poll_cancel_form().
+ */
+function poll_cancel($form, &$form_state) {
+ global $user;
+ $node = node_load($form['#nid']);
+
+ db_delete('poll_vote')
+ ->condition('nid', $node->nid)
+ ->condition($user->uid ? 'uid' : 'hostname', $user->uid ? $user->uid : ip_address())
+ ->execute();
+
+ // Subtract from the votes.
+ db_update('poll_choice')
+ ->expression('chvotes', 'chvotes - 1')
+ ->condition('chid', $node->vote)
+ ->execute();
+
+ unset($_SESSION['poll_vote'][$node->nid]);
+
+ drupal_set_message(t('Your vote was cancelled.'));
+}
+
+/**
+ * Implements hook_user_cancel().
+ */
+function poll_user_cancel($edit, $account, $method) {
+ switch ($method) {
+ case 'user_cancel_reassign':
+ db_update('poll_vote')
+ ->fields(array('uid' => 0))
+ ->condition('uid', $account->uid)
+ ->execute();
+ break;
+ }
+}
+
+/**
+ * Implements hook_user_delete().
+ */
+function poll_user_delete($account) {
+ db_delete('poll_vote')
+ ->condition('uid', $account->uid)
+ ->execute();
+}
+
+/**
+ * Implements hook_rdf_mapping().
+ */
+function poll_rdf_mapping() {
+ return array(
+ array(
+ 'type' => 'node',
+ 'bundle' => 'poll',
+ 'mapping' => array(
+ 'rdftype' => array('sioc:Post', 'sioct:Poll'),
+ ),
+ ),
+ );
+}
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll.pages.inc b/kolab.org/www/drupal-7.26/modules/poll/poll.pages.inc
new file mode 100644
index 0000000..15f3ba7
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll.pages.inc
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @file
+ * User page callbacks for the poll module.
+ */
+
+/**
+ * Menu callback to provide a simple list of all polls available.
+ */
+function poll_page() {
+ $polls_per_page = 15;
+
+ $count_select = db_select('node', 'n');
+ $count_select->addExpression('COUNT(*)', 'expression');
+ $count_select->join('poll', 'p', 'p.nid = n.nid');
+ $count_select->condition('n.status', 1);
+
+ // List all polls.
+ $select = db_select('node', 'n');
+ $select->join('poll', 'p', 'p.nid = n.nid');
+ $select->join('poll_choice', 'c', 'c.nid = n.nid');
+ $select->addExpression('SUM(c.chvotes)', 'votes');
+ $select = $select->fields('n', array('nid', 'title', 'created'))
+ ->fields('p', array('active'))
+ ->condition('n.status', 1)
+ ->orderBy('n.created', 'DESC')
+ ->groupBy('n.nid')
+ ->groupBy('n.title')
+ ->groupBy('p.active')
+ ->groupBy('n.created')
+ ->extend('PagerDefault')
+ ->limit($polls_per_page)
+ ->addTag('node_access');
+ $select->setCountQuery($count_select);
+ $queried_nodes = $select->execute()
+ ->fetchAllAssoc('nid');
+
+ $output = '<ul>';
+ foreach ($queried_nodes as $node) {
+ $output .= '<li>' . l($node->title, "node/$node->nid") . ' - ' . format_plural($node->votes, '1 vote', '@count votes') . ' - ' . ($node->active ? t('open') : t('closed')) . '</li>';
+ }
+ $output .= '</ul>';
+ $output .= theme('pager');
+ return $output;
+}
+
+/**
+ * Callback for the 'votes' tab for polls you can see other votes on
+ */
+function poll_votes($node) {
+ $votes_per_page = 20;
+ drupal_set_title($node->title);
+
+ $header[] = array('data' => t('Visitor'), 'field' => 'u.name');
+ $header[] = array('data' => t('Vote'), 'field' => 'pc.chtext');
+ $header[] = array('data' => t('Timestamp'), 'field' => 'pv.timestamp', 'sort' => 'desc');
+
+ $select = db_select('poll_vote', 'pv')->extend('PagerDefault')->extend('TableSort');
+ $select->join('poll_choice', 'pc', 'pv.chid = pc.chid');
+ $select->join('users', 'u', 'pv.uid = u.uid');
+ $queried_votes = $select
+ ->addTag('translatable')
+ ->fields('pv', array('chid', 'uid', 'hostname', 'timestamp', 'nid'))
+ ->fields('pc', array('chtext'))
+ ->fields('u', array('name'))
+ ->condition('pv.nid', $node->nid)
+ ->limit($votes_per_page)
+ ->orderByHeader($header)
+ ->execute();
+
+ $rows = array();
+ foreach ($queried_votes as $vote) {
+ $rows[] = array(
+ $vote->name ? theme('username', array('account' => $vote)) : check_plain($vote->hostname),
+ check_plain($vote->chtext),
+ format_date($vote->timestamp),
+ );
+ }
+ $build['poll_votes_table'] = array(
+ '#theme' => 'table',
+ '#header' => $header,
+ '#rows' => $rows,
+ '#prefix' => t('This table lists all the recorded votes for this poll. If anonymous users are allowed to vote, they will be identified by the IP address of the computer they used when they voted.'),
+ );
+ $build['poll_votes_pager'] = array('#theme' => 'pager');
+ return $build;
+}
+
+/**
+ * Callback for the 'results' tab for polls you can vote on
+ */
+function poll_results($node) {
+ drupal_set_title($node->title);
+ $node->show_results = TRUE;
+ return node_show($node);
+}
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll.test b/kolab.org/www/drupal-7.26/modules/poll/poll.test
new file mode 100644
index 0000000..35eea22
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll.test
@@ -0,0 +1,872 @@
+<?php
+
+/**
+ * @file
+ * Tests for poll.module.
+ */
+
+class PollTestCase extends DrupalWebTestCase {
+
+ /**
+ * Creates a poll.
+ *
+ * @param string $title
+ * The title of the poll.
+ * @param array $choices
+ * A list of choice labels.
+ * @param boolean $preview
+ * (optional) Whether to test if the preview is working or not. Defaults to
+ * TRUE.
+ *
+ * @return
+ * The node id of the created poll, or FALSE on error.
+ */
+ function pollCreate($title, $choices, $preview = TRUE) {
+ $this->assertTrue(TRUE, 'Create a poll');
+
+ $admin_user = $this->drupalCreateUser(array('create poll content', 'administer nodes'));
+ $web_user = $this->drupalCreateUser(array('create poll content', 'access content', 'edit own poll content'));
+ $this->drupalLogin($admin_user);
+
+ // Get the form first to initialize the state of the internal browser.
+ $this->drupalGet('node/add/poll');
+
+ // Prepare a form with two choices.
+ list($edit, $index) = $this->_pollGenerateEdit($title, $choices);
+
+ // Verify that the vote count element only allows non-negative integers.
+ $edit['choice[new:1][chvotes]'] = -1;
+ $edit['choice[new:0][chvotes]'] = $this->randomString(7);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText(t('Negative values are not allowed.'));
+ $this->assertText(t('Vote count for new choice must be an integer.'));
+
+ // Repeat steps for initializing the state of the internal browser.
+ $this->drupalLogin($web_user);
+ $this->drupalGet('node/add/poll');
+ list($edit, $index) = $this->_pollGenerateEdit($title, $choices);
+
+ // Re-submit the form until all choices are filled in.
+ if (count($choices) > 2) {
+ while ($index < count($choices)) {
+ $this->drupalPost(NULL, $edit, t('More choices'));
+ $this->assertPollChoiceOrder($choices, $index);
+ list($edit, $index) = $this->_pollGenerateEdit($title, $choices, $index);
+ }
+ }
+
+ if ($preview) {
+ $this->drupalPost(NULL, $edit, t('Preview'));
+ $this->assertPollChoiceOrder($choices, $index, TRUE);
+ list($edit, $index) = $this->_pollGenerateEdit($title, $choices, $index);
+ }
+
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($title);
+ $this->assertText(t('@type @title has been created.', array('@type' => node_type_get_name('poll'), '@title' => $title)), 'Poll has been created.');
+ $this->assertTrue($node->nid, 'Poll has been found in the database.');
+
+ return isset($node->nid) ? $node->nid : FALSE;
+ }
+
+ /**
+ * Generates POST values for the poll node form, specifically poll choices.
+ *
+ * @param $title
+ * The title for the poll node.
+ * @param $choices
+ * An array containing poll choices, as generated by
+ * PollTestCase::_generateChoices().
+ * @param $index
+ * (optional) The amount/number of already submitted poll choices. Defaults
+ * to 0.
+ *
+ * @return
+ * An indexed array containing:
+ * - The generated POST values, suitable for
+ * DrupalWebTestCase::drupalPost().
+ * - The number of poll choices contained in 'edit', for potential re-usage
+ * in subsequent invocations of this function.
+ */
+ function _pollGenerateEdit($title, array $choices, $index = 0) {
+ $max_new_choices = ($index == 0 ? 2 : 5);
+ $already_submitted_choices = array_slice($choices, 0, $index);
+ $new_choices = array_values(array_slice($choices, $index, $max_new_choices));
+
+ $edit = array(
+ 'title' => $title,
+ );
+ foreach ($already_submitted_choices as $k => $text) {
+ $edit['choice[chid:' . $k . '][chtext]'] = $text;
+ }
+ foreach ($new_choices as $k => $text) {
+ $edit['choice[new:' . $k . '][chtext]'] = $text;
+ }
+ return array($edit, count($already_submitted_choices) + count($new_choices));
+ }
+
+ function _generateChoices($count = 7) {
+ $choices = array();
+ for ($i = 1; $i <= $count; $i++) {
+ $choices[] = $this->randomName();
+ }
+ return $choices;
+ }
+
+ /**
+ * Assert correct poll choice order in the node form after submission.
+ *
+ * Verifies both the order in the DOM and in the 'weight' form elements.
+ *
+ * @param $choices
+ * An array containing poll choices, as generated by
+ * PollTestCase::_generateChoices().
+ * @param $index
+ * (optional) The amount/number of already submitted poll choices. Defaults
+ * to 0.
+ * @param $preview
+ * (optional) Whether to also check the poll preview.
+ *
+ * @see PollTestCase::_pollGenerateEdit()
+ */
+ function assertPollChoiceOrder(array $choices, $index = 0, $preview = FALSE) {
+ $expected = array();
+ $weight = 0;
+ foreach ($choices as $id => $label) {
+ if ($id < $index) {
+ // The expected weight of each choice is higher than the previous one.
+ $weight++;
+ // Directly assert the weight form element value for this choice.
+ $this->assertFieldByName('choice[chid:' . $id . '][weight]', $weight, format_string('Found choice @id with weight @weight.', array(
+ '@id' => $id,
+ '@weight' => $weight,
+ )));
+ // Append to our (to be reversed) stack of labels.
+ $expected[$weight] = $label;
+ }
+ }
+ ksort($expected);
+
+ // Verify DOM order of poll choices (i.e., #weight of form elements).
+ $elements = $this->xpath('//input[starts-with(@name, :prefix) and contains(@name, :suffix)]', array(
+ ':prefix' => 'choice[chid:',
+ ':suffix' => '][chtext]',
+ ));
+ $expected_order = $expected;
+ foreach ($elements as $element) {
+ $next_label = array_shift($expected_order);
+ $this->assertEqual((string) $element['value'], $next_label);
+ }
+
+ // If requested, also verify DOM order in preview.
+ if ($preview) {
+ $elements = $this->xpath('//div[contains(@class, :teaser)]/descendant::div[@class=:text]', array(
+ ':teaser' => 'node-teaser',
+ ':text' => 'text',
+ ));
+ $expected_order = $expected;
+ foreach ($elements as $element) {
+ $next_label = array_shift($expected_order);
+ $this->assertEqual((string) $element, $next_label, format_string('Found choice @label in preview.', array(
+ '@label' => $next_label,
+ )));
+ }
+ }
+ }
+
+ function pollUpdate($nid, $title, $edit) {
+ // Edit the poll node.
+ $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
+ $this->assertText(t('@type @title has been updated.', array('@type' => node_type_get_name('poll'), '@title' => $title)), 'Poll has been updated.');
+ }
+}
+
+class PollCreateTestCase extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Poll create',
+ 'description' => 'Adds "more choices", previews and creates a poll.',
+ 'group' => 'Poll'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+ }
+
+ function testPollCreate() {
+ $title = $this->randomName();
+ $choices = $this->_generateChoices(7);
+ $poll_nid = $this->pollCreate($title, $choices, TRUE);
+
+ // Verify poll appears on 'poll' page.
+ $this->drupalGet('poll');
+ $this->assertText($title, 'Poll appears in poll list.');
+ $this->assertText('open', 'Poll is active.');
+
+ // Click on the poll title to go to node page.
+ $this->clickLink($title);
+ $this->assertText('Total votes: 0', 'Link to poll correct.');
+
+ // Now add a new option to make sure that when we update the node the
+ // option is displayed.
+ $node = node_load($poll_nid);
+
+ $new_option = $this->randomName();
+
+ $vote_count = '2000';
+ $node->choice[] = array(
+ 'chid' => '',
+ 'chtext' => $new_option,
+ 'chvotes' => (int) $vote_count,
+ 'weight' => 1000,
+ );
+
+ node_save($node);
+
+ $this->drupalGet('poll');
+ $this->clickLink($title);
+ $this->assertText($new_option, 'New option found.');
+
+ $option = $this->xpath('//div[@id="node-1"]//div[@class="poll"]//div[@class="text"]');
+ $this->assertEqual(end($option), $new_option, 'Last item is equal to new option.');
+
+ $votes = $this->xpath('//div[@id="node-1"]//div[@class="poll"]//div[@class="percent"]');
+ $this->assertTrue(strpos(end($votes), $vote_count) > 0, "Votes saved.");
+ }
+
+ function testPollClose() {
+ $content_user = $this->drupalCreateUser(array('create poll content', 'edit any poll content', 'access content'));
+ $vote_user = $this->drupalCreateUser(array('cancel own vote', 'inspect all votes', 'vote on polls', 'access content'));
+
+ // Create poll.
+ $title = $this->randomName();
+ $choices = $this->_generateChoices(7);
+ $poll_nid = $this->pollCreate($title, $choices, FALSE);
+
+ $this->drupalLogout();
+ $this->drupalLogin($content_user);
+
+ // Edit the poll node and close the poll.
+ $close_edit = array('active' => 0);
+ $this->pollUpdate($poll_nid, $title, $close_edit);
+
+ // Verify 'Vote' button no longer appears.
+ $this->drupalGet('node/' . $poll_nid);
+ $elements = $this->xpath('//input[@id="edit-vote"]');
+ $this->assertTrue(empty($elements), "Vote button doesn't appear.");
+
+ // Verify status on 'poll' page is 'closed'.
+ $this->drupalGet('poll');
+ $this->assertText($title, 'Poll appears in poll list.');
+ $this->assertText('closed', 'Poll is closed.');
+
+ // Edit the poll node and re-activate.
+ $open_edit = array('active' => 1);
+ $this->pollUpdate($poll_nid, $title, $open_edit);
+
+ // Vote on the poll.
+ $this->drupalLogout();
+ $this->drupalLogin($vote_user);
+ $vote_edit = array('choice' => '1');
+ $this->drupalPost('node/' . $poll_nid, $vote_edit, t('Vote'));
+ $this->assertText('Your vote was recorded.', 'Your vote was recorded.');
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(isset($elements[0]), "'Cancel your vote' button appears.");
+
+ // Edit the poll node and close the poll.
+ $this->drupalLogout();
+ $this->drupalLogin($content_user);
+ $close_edit = array('active' => 0);
+ $this->pollUpdate($poll_nid, $title, $close_edit);
+
+ // Verify 'Cancel your vote' button no longer appears.
+ $this->drupalGet('node/' . $poll_nid);
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(empty($elements), "'Cancel your vote' button no longer appears.");
+ }
+}
+
+class PollVoteTestCase extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Poll vote',
+ 'description' => 'Vote on a poll',
+ 'group' => 'Poll'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+ }
+
+ function tearDown() {
+ parent::tearDown();
+ }
+
+ function testPollVote() {
+ $title = $this->randomName();
+ $choices = $this->_generateChoices(7);
+ $poll_nid = $this->pollCreate($title, $choices, FALSE);
+ $this->drupalLogout();
+
+ $vote_user = $this->drupalCreateUser(array('cancel own vote', 'inspect all votes', 'vote on polls', 'access content'));
+ $restricted_vote_user = $this->drupalCreateUser(array('vote on polls', 'access content'));
+
+ $this->drupalLogin($vote_user);
+
+ // Record a vote for the first choice.
+ $edit = array(
+ 'choice' => '1',
+ );
+ $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+ $this->assertText('Your vote was recorded.', 'Your vote was recorded.');
+ $this->assertText('Total votes: 1', 'Vote count updated correctly.');
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(isset($elements[0]), "'Cancel your vote' button appears.");
+
+ $this->drupalGet("node/$poll_nid/votes");
+ $this->assertText(t('This table lists all the recorded votes for this poll. If anonymous users are allowed to vote, they will be identified by the IP address of the computer they used when they voted.'), 'Vote table text.');
+ $this->assertText($choices[0], 'Vote recorded');
+
+ // Ensure poll listing page has correct number of votes.
+ $this->drupalGet('poll');
+ $this->assertText($title, 'Poll appears in poll list.');
+ $this->assertText('1 vote', 'Poll has 1 vote.');
+
+ // Cancel a vote.
+ $this->drupalPost('node/' . $poll_nid, array(), t('Cancel your vote'));
+ $this->assertText('Your vote was cancelled.', 'Your vote was cancelled.');
+ $this->assertNoText('Cancel your vote', "Cancel vote button doesn't appear.");
+
+ $this->drupalGet("node/$poll_nid/votes");
+ $this->assertNoText($choices[0], 'Vote cancelled');
+
+ // Ensure poll listing page has correct number of votes.
+ $this->drupalGet('poll');
+ $this->assertText($title, 'Poll appears in poll list.');
+ $this->assertText('0 votes', 'Poll has 0 votes.');
+
+ // Log in as a user who can only vote on polls.
+ $this->drupalLogout();
+ $this->drupalLogin($restricted_vote_user);
+
+ // Vote on a poll.
+ $edit = array(
+ 'choice' => '1',
+ );
+ $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+ $this->assertText('Your vote was recorded.', 'Your vote was recorded.');
+ $this->assertText('Total votes: 1', 'Vote count updated correctly.');
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(empty($elements), "'Cancel your vote' button does not appear.");
+ }
+}
+
+class PollBlockTestCase extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Block availability',
+ 'description' => 'Check if the most recent poll block is available.',
+ 'group' => 'Poll',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+
+ // Create and login user
+ $admin_user = $this->drupalCreateUser(array('administer blocks'));
+ $this->drupalLogin($admin_user);
+ }
+
+ function testRecentBlock() {
+ // Set block title to confirm that the interface is available.
+ $this->drupalPost('admin/structure/block/manage/poll/recent/configure', array('title' => $this->randomName(8)), t('Save block'));
+ $this->assertText(t('The block configuration has been saved.'), 'Block configuration set.');
+
+ // Set the block to a region to confirm block is available.
+ $edit = array();
+ $edit['blocks[poll_recent][region]'] = 'footer';
+ $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+ $this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.');
+
+ // Create a poll which should appear in recent polls block.
+ $title = $this->randomName();
+ $choices = $this->_generateChoices(7);
+ $poll_nid = $this->pollCreate($title, $choices, TRUE);
+
+ // Verify poll appears in a block.
+ // View user page so we're not matching the poll node on front page.
+ $this->drupalGet('user');
+ // If a 'block' view not generated, this title would not appear even though
+ // the choices might.
+ $this->assertText($title, 'Poll appears in block.');
+
+ // Logout and login back in as a user who can vote.
+ $this->drupalLogout();
+ $vote_user = $this->drupalCreateUser(array('cancel own vote', 'inspect all votes', 'vote on polls', 'access content'));
+ $this->drupalLogin($vote_user);
+
+ // Verify we can vote via the block.
+ $edit = array(
+ 'choice' => '1',
+ );
+ $this->drupalPost('user/' . $vote_user->uid, $edit, t('Vote'));
+ $this->assertText('Your vote was recorded.', 'Your vote was recorded.');
+ $this->assertText('Total votes: 1', 'Vote count updated correctly.');
+ $this->assertText('Older polls', 'Link to older polls appears.');
+ $this->clickLink('Older polls');
+ $this->assertText('1 vote - open', 'Link to poll listing correct.');
+
+ // Close the poll and verify block doesn't appear.
+ $content_user = $this->drupalCreateUser(array('create poll content', 'edit any poll content', 'access content'));
+ $this->drupalLogout();
+ $this->drupalLogin($content_user);
+ $close_edit = array('active' => 0);
+ $this->pollUpdate($poll_nid, $title, $close_edit);
+ $this->drupalGet('user/' . $content_user->uid);
+ $this->assertNoText($title, 'Poll no longer appears in block.');
+ }
+}
+
+/**
+ * Test adding new choices.
+ */
+class PollJSAddChoice extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Poll add choice',
+ 'description' => 'Submits a POST request for an additional poll choice.',
+ 'group' => 'Poll'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+ }
+
+ /**
+ * Test adding a new choice.
+ */
+ function testAddChoice() {
+ $web_user = $this->drupalCreateUser(array('create poll content', 'access content'));
+ $this->drupalLogin($web_user);
+ $this->drupalGet('node/add/poll');
+ $edit = array(
+ "title" => $this->randomName(),
+ 'choice[new:0][chtext]' => $this->randomName(),
+ 'choice[new:1][chtext]' => $this->randomName(),
+ );
+
+ // Press 'add choice' button through Ajax, and place the expected HTML result
+ // as the tested content.
+ $commands = $this->drupalPostAJAX(NULL, $edit, array('op' => t('More choices')));
+ $this->content = $commands[1]['data'];
+
+ $this->assertFieldByName('choice[chid:0][chtext]', $edit['choice[new:0][chtext]'], format_string('Field !i found', array('!i' => 0)));
+ $this->assertFieldByName('choice[chid:1][chtext]', $edit['choice[new:1][chtext]'], format_string('Field !i found', array('!i' => 1)));
+ $this->assertFieldByName('choice[new:0][chtext]', '', format_string('Field !i found', array('!i' => 2)));
+ }
+}
+
+class PollVoteCheckHostname extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'User poll vote capability.',
+ 'description' => 'Check that users and anonymous users from specified ip-address can only vote once.',
+ 'group' => 'Poll'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+
+ // Create and login user.
+ $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'create poll content'));
+ $this->drupalLogin($this->admin_user);
+
+ // Allow anonymous users to vote on polls.
+ user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
+ 'access content' => TRUE,
+ 'vote on polls' => TRUE,
+ 'cancel own vote' => TRUE,
+ ));
+
+ // Enable page cache to verify that the result page is not saved in the
+ // cache when anonymous voting is allowed.
+ variable_set('cache', 1);
+
+ // Create poll.
+ $title = $this->randomName();
+ $choices = $this->_generateChoices(3);
+ $this->poll_nid = $this->pollCreate($title, $choices, FALSE);
+
+ $this->drupalLogout();
+
+ // Create web users.
+ $this->web_user1 = $this->drupalCreateUser(array('access content', 'vote on polls', 'cancel own vote'));
+ $this->web_user2 = $this->drupalCreateUser(array('access content', 'vote on polls'));
+ }
+
+ /**
+ * Check that anonymous users with same ip cannot vote on poll more than once
+ * unless user is logged in.
+ */
+ function testHostnamePollVote() {
+ // Login User1.
+ $this->drupalLogin($this->web_user1);
+
+ $edit = array(
+ 'choice' => '1',
+ );
+
+ // User1 vote on Poll.
+ $this->drupalPost('node/' . $this->poll_nid, $edit, t('Vote'));
+ $this->assertText(t('Your vote was recorded.'), format_string('%user vote was recorded.', array('%user' => $this->web_user1->name)));
+ $this->assertText(t('Total votes: @votes', array('@votes' => 1)), 'Vote count updated correctly.');
+
+ // Check to make sure User1 cannot vote again.
+ $this->drupalGet('node/' . $this->poll_nid);
+ $elements = $this->xpath('//input[@value="Vote"]');
+ $this->assertTrue(empty($elements), format_string("%user is not able to vote again.", array('%user' => $this->web_user1->name)));
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(!empty($elements), "'Cancel your vote' button appears.");
+
+ // Logout User1.
+ $this->drupalLogout();
+
+ // Fill the page cache by requesting the poll.
+ $this->drupalGet('node/' . $this->poll_nid);
+ $this->assertEqual($this->drupalGetHeader('x-drupal-cache'), 'MISS', 'Page was cacheable but was not in the cache.');
+ $this->drupalGet('node/' . $this->poll_nid);
+ $this->assertEqual($this->drupalGetHeader('x-drupal-cache'), 'HIT', 'Page was cached.');
+
+ // Anonymous user vote on Poll.
+ $this->drupalPost(NULL, $edit, t('Vote'));
+ $this->assertText(t('Your vote was recorded.'), 'Anonymous vote was recorded.');
+ $this->assertText(t('Total votes: @votes', array('@votes' => 2)), 'Vote count updated correctly.');
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(!empty($elements), "'Cancel your vote' button appears.");
+
+ // Check to make sure Anonymous user cannot vote again.
+ $this->drupalGet('node/' . $this->poll_nid);
+ $this->assertFalse($this->drupalGetHeader('x-drupal-cache'), 'Page was not cacheable.');
+ $elements = $this->xpath('//input[@value="Vote"]');
+ $this->assertTrue(empty($elements), "Anonymous is not able to vote again.");
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(!empty($elements), "'Cancel your vote' button appears.");
+
+ // Login User2.
+ $this->drupalLogin($this->web_user2);
+
+ // User2 vote on poll.
+ $this->drupalPost('node/' . $this->poll_nid, $edit, t('Vote'));
+ $this->assertText(t('Your vote was recorded.'), format_string('%user vote was recorded.', array('%user' => $this->web_user2->name)));
+ $this->assertText(t('Total votes: @votes', array('@votes' => 3)), 'Vote count updated correctly.');
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(empty($elements), "'Cancel your vote' button does not appear.");
+
+ // Logout User2.
+ $this->drupalLogout();
+
+ // Change host name for anonymous users.
+ db_update('poll_vote')
+ ->fields(array(
+ 'hostname' => '123.456.789.1',
+ ))
+ ->condition('hostname', '', '<>')
+ ->execute();
+
+ // Check to make sure Anonymous user can vote again with a new session after
+ // a hostname change.
+ $this->drupalGet('node/' . $this->poll_nid);
+ $this->assertEqual($this->drupalGetHeader('x-drupal-cache'), 'MISS', 'Page was cacheable but was not in the cache.');
+ $this->drupalPost(NULL, $edit, t('Vote'));
+ $this->assertText(t('Your vote was recorded.'), format_string('%user vote was recorded.', array('%user' => $this->web_user2->name)));
+ $this->assertText(t('Total votes: @votes', array('@votes' => 4)), 'Vote count updated correctly.');
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(!empty($elements), "'Cancel your vote' button appears.");
+
+ // Check to make sure Anonymous user cannot vote again with a new session,
+ // and that the vote from the previous session cannot be cancelledd.
+ $this->curlClose();
+ $this->drupalGet('node/' . $this->poll_nid);
+ $this->assertEqual($this->drupalGetHeader('x-drupal-cache'), 'MISS', 'Page was cacheable but was not in the cache.');
+ $elements = $this->xpath('//input[@value="Vote"]');
+ $this->assertTrue(empty($elements), 'Anonymous is not able to vote again.');
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(empty($elements), "'Cancel your vote' button does not appear.");
+
+ // Login User1.
+ $this->drupalLogin($this->web_user1);
+
+ // Check to make sure User1 still cannot vote even after hostname changed.
+ $this->drupalGet('node/' . $this->poll_nid);
+ $elements = $this->xpath('//input[@value="Vote"]');
+ $this->assertTrue(empty($elements), format_string("%user is not able to vote again.", array('%user' => $this->web_user1->name)));
+ $elements = $this->xpath('//input[@value="Cancel your vote"]');
+ $this->assertTrue(!empty($elements), "'Cancel your vote' button appears.");
+ }
+}
+
+/**
+ * Test poll token replacement in strings.
+ */
+class PollTokenReplaceTestCase extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Poll token replacement',
+ 'description' => 'Generates text using placeholders for dummy content to check poll token replacement.',
+ 'group' => 'Poll',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+ }
+
+ /**
+ * Creates a poll, then tests the tokens generated from it.
+ */
+ function testPollTokenReplacement() {
+ global $language;
+
+ // Craete a poll with three choices.
+ $title = $this->randomName();
+ $choices = $this->_generateChoices(3);
+ $poll_nid = $this->pollCreate($title, $choices, FALSE);
+ $this->drupalLogout();
+
+ // Create four users and have each of them vote.
+ $vote_user1 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+ $this->drupalLogin($vote_user1);
+ $edit = array(
+ 'choice' => '1',
+ );
+ $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+ $this->drupalLogout();
+
+ $vote_user2 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+ $this->drupalLogin($vote_user2);
+ $edit = array(
+ 'choice' => '1',
+ );
+ $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+ $this->drupalLogout();
+
+ $vote_user3 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+ $this->drupalLogin($vote_user3);
+ $edit = array(
+ 'choice' => '2',
+ );
+ $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+ $this->drupalLogout();
+
+ $vote_user4 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+ $this->drupalLogin($vote_user4);
+ $edit = array(
+ 'choice' => '3',
+ );
+ $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+ $this->drupalLogout();
+
+ $poll = node_load($poll_nid, NULL, TRUE);
+
+ // Generate and test sanitized tokens.
+ $tests = array();
+ $tests['[node:poll-votes]'] = 4;
+ $tests['[node:poll-winner]'] = filter_xss($poll->choice[1]['chtext']);
+ $tests['[node:poll-winner-votes]'] = 2;
+ $tests['[node:poll-winner-percent]'] = 50;
+ $tests['[node:poll-duration]'] = format_interval($poll->runtime, 1, $language->language);
+
+ // Test to make sure that we generated something for each token.
+ $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('node' => $poll), array('language' => $language));
+ $this->assertEqual($output, $expected, format_string('Sanitized poll token %token replaced.', array('%token' => $input)));
+ }
+
+ // Generate and test unsanitized tokens.
+ $tests['[node:poll-winner]'] = $poll->choice[1]['chtext'];
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('node' => $poll), array('language' => $language, 'sanitize' => FALSE));
+ $this->assertEqual($output, $expected, format_string('Unsanitized poll token %token replaced.', array('%token' => $input)));
+ }
+ }
+}
+
+class PollExpirationTestCase extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Poll expiration',
+ 'description' => 'Test the poll auto-expiration logic.',
+ 'group' => 'Poll',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+ }
+
+ function testAutoExpire() {
+ // Set up a poll.
+ $title = $this->randomName();
+ $choices = $this->_generateChoices(2);
+ $poll_nid = $this->pollCreate($title, $choices, FALSE);
+ $this->assertTrue($poll_nid, 'Poll for auto-expire test created.');
+
+ // Visit the poll edit page and verify that by default, expiration
+ // is set to unlimited.
+ $this->drupalGet("node/$poll_nid/edit");
+ $this->assertField('runtime', 'Poll expiration setting found.');
+ $elements = $this->xpath('//select[@id="edit-runtime"]/option[@selected="selected"]');
+ $this->assertTrue(isset($elements[0]['value']) && $elements[0]['value'] == 0, 'Poll expiration set to unlimited.');
+
+ // Set the expiration to one week.
+ $edit = array();
+ $poll_expiration = 604800; // One week.
+ $edit['runtime'] = $poll_expiration;
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw(t('Poll %title has been updated.', array('%title' => $title)), 'Poll expiration settings saved.');
+
+ // Make sure that the changed expiration settings is kept.
+ $this->drupalGet("node/$poll_nid/edit");
+ $elements = $this->xpath('//select[@id="edit-runtime"]/option[@selected="selected"]');
+ $this->assertTrue(isset($elements[0]['value']) && $elements[0]['value'] == $poll_expiration, 'Poll expiration set to unlimited.');
+
+ // Force a cron run. Since the expiration date has not yet been reached,
+ // the poll should remain active.
+ drupal_cron_run();
+ $this->drupalGet("node/$poll_nid/edit");
+ $elements = $this->xpath('//input[@id="edit-active-1"]');
+ $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), 'Poll is still active.');
+
+ // Test expiration. Since REQUEST_TIME is a constant and we don't
+ // want to keep SimpleTest waiting until the moment of expiration arrives,
+ // we forcibly change the expiration date in the database.
+ $created = db_query('SELECT created FROM {node} WHERE nid = :nid', array(':nid' => $poll_nid))->fetchField();
+ db_update('node')
+ ->fields(array('created' => $created - ($poll_expiration * 1.01)))
+ ->condition('nid', $poll_nid)
+ ->execute();
+
+ // Run cron and verify that the poll is now marked as "closed".
+ drupal_cron_run();
+ $this->drupalGet("node/$poll_nid/edit");
+ $elements = $this->xpath('//input[@id="edit-active-0"]');
+ $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), 'Poll has expired.');
+ }
+}
+
+class PollDeleteChoiceTestCase extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Poll choice deletion',
+ 'description' => 'Test the poll choice deletion logic.',
+ 'group' => 'Poll',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll');
+ }
+
+ function testChoiceRemoval() {
+ // Set up a poll with three choices.
+ $title = $this->randomName();
+ $choices = array('First choice', 'Second choice', 'Third choice');
+ $poll_nid = $this->pollCreate($title, $choices, FALSE);
+ $this->assertTrue($poll_nid, 'Poll for choice deletion logic test created.');
+
+ // Edit the poll, and try to delete first poll choice.
+ $this->drupalGet("node/$poll_nid/edit");
+ $edit['choice[chid:1][chtext]'] = '';
+ $this->drupalPost(NULL, $edit, t('Save'));
+
+ // Click on the poll title to go to node page.
+ $this->drupalGet('poll');
+ $this->clickLink($title);
+
+ // Check the first poll choice is deleted, while the others remain.
+ $this->assertNoText('First choice', 'First choice removed.');
+ $this->assertText('Second choice', 'Second choice remains.');
+ $this->assertText('Third choice', 'Third choice remains.');
+ }
+}
+
+/**
+ * Tests poll translation logic.
+ */
+class PollTranslateTestCase extends PollTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Poll translation',
+ 'description' => 'Test the poll translation logic.',
+ 'group' => 'Poll',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('poll', 'translation');
+ }
+
+ /**
+ * Tests poll creation and translation.
+ *
+ * Checks that the choice names get copied from the original poll and that
+ * the vote count values are set to 0.
+ */
+ function testPollTranslate() {
+ $admin_user = $this->drupalCreateUser(array('administer content types', 'administer languages', 'edit any poll content', 'create poll content', 'administer nodes', 'translate content'));
+
+ // Set up a poll with two choices.
+ $title = $this->randomName();
+ $choices = array($this->randomName(), $this->randomName());
+ $poll_nid = $this->pollCreate($title, $choices, FALSE);
+ $this->assertTrue($poll_nid, 'Poll for translation logic test created.');
+
+ $this->drupalLogout();
+ $this->drupalLogin($admin_user);
+
+ // Enable a second language.
+ $this->drupalGet('admin/config/regional/language');
+ $edit = array();
+ $edit['langcode'] = 'nl';
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+ $this->assertRaw(t('The language %language has been created and can now be used.', array('%language' => 'Dutch')), 'Language Dutch has been created.');
+
+ // Set "Poll" content type to use multilingual support with translation.
+ $this->drupalGet('admin/structure/types/manage/poll');
+ $edit = array();
+ $edit['language_content_type'] = 2;
+ $this->drupalPost('admin/structure/types/manage/poll', $edit, t('Save content type'));
+ $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Poll')), 'Poll content type has been updated.');
+
+ // Edit poll.
+ $this->drupalGet("node/$poll_nid/edit");
+ $edit = array();
+ // Set the poll's first choice count to 200.
+ $edit['choice[chid:1][chvotes]'] = 200;
+ // Set the language to Dutch.
+ $edit['language'] = 'nl';
+ $this->drupalPost(NULL, $edit, t('Save'));
+
+ // Translate the Dutch poll.
+ $this->drupalGet('node/add/poll', array('query' => array('translation' => $poll_nid, 'target' => 'en')));
+
+ $dutch_poll = node_load($poll_nid);
+
+ // Check that the vote count values didn't get copied from the Dutch poll
+ // and are set to 0.
+ $this->assertFieldByName('choice[chid:1][chvotes]', '0', ('Found choice with vote count 0'));
+ $this->assertFieldByName('choice[chid:2][chvotes]', '0', ('Found choice with vote count 0'));
+ // Check that the choice names got copied from the Dutch poll.
+ $this->assertFieldByName('choice[chid:1][chtext]', $dutch_poll->choice[1]['chtext'], format_string('Found choice with text @text', array('@text' => $dutch_poll->choice[1]['chtext'])));
+ $this->assertFieldByName('choice[chid:2][chtext]', $dutch_poll->choice[2]['chtext'], format_string('Found choice with text @text', array('@text' => $dutch_poll->choice[2]['chtext'])));
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/poll/poll.tokens.inc b/kolab.org/www/drupal-7.26/modules/poll/poll.tokens.inc
new file mode 100644
index 0000000..eda628b
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/poll/poll.tokens.inc
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Builds placeholder replacement tokens for values specific to Poll nodes.
+ */
+
+/**
+ * Implements hook_token_info().
+ */
+function poll_token_info() {
+ $node['poll-votes'] = array(
+ 'name' => t("Poll votes"),
+ 'description' => t("The number of votes that have been cast on a poll."),
+ );
+ $node['poll-winner'] = array(
+ 'name' => t("Poll winner"),
+ 'description' => t("The winning poll answer."),
+ );
+ $node['poll-winner-votes'] = array(
+ 'name' => t("Poll winner votes"),
+ 'description' => t("The number of votes received by the winning poll answer."),
+ );
+ $node['poll-winner-percent'] = array(
+ 'name' => t("Poll winner percent"),
+ 'description' => t("The percentage of votes received by the winning poll answer."),
+ );
+ $node['poll-duration'] = array(
+ 'name' => t("Poll duration"),
+ 'description' => t("The length of time the poll is set to run."),
+ );
+
+ return array(
+ 'tokens' => array('node' => $node),
+ );
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function poll_tokens($type, $tokens, array $data = array(), array $options = array()) {
+ $sanitize = !empty($options['sanitize']);
+ if (isset($options['language'])) {
+ $url_options['language'] = $options['language'];
+ $language_code = $options['language']->language;
+ }
+ else {
+ $language_code = NULL;
+ }
+
+ $replacements = array();
+
+ if ($type == 'node' && !empty($data['node']) && $data['node']->type == 'poll') {
+ $node = $data['node'];
+
+ $total_votes = 0;
+ $highest_votes = 0;
+ foreach ($node->choice as $choice) {
+ if ($choice['chvotes'] > $highest_votes) {
+ $winner = $choice;
+ $highest_votes = $choice['chvotes'];
+ }
+ $total_votes = $total_votes + $choice['chvotes'];
+ }
+ foreach ($tokens as $name => $original) {
+ switch ($name) {
+ case 'poll-votes':
+ $replacements[$original] = $total_votes;
+ break;
+
+ case 'poll-winner':
+ if (isset($winner)) {
+ $replacements[$original] = $sanitize ? filter_xss($winner['chtext']) : $winner['chtext'];
+ }
+ else {
+ $replacements[$original] = '';
+ }
+ break;
+
+ case 'poll-winner-votes':
+ if (isset($winner)) {
+ $replacements[$original] = $winner['chvotes'];
+ }
+ else {
+ $replacements[$original] = '';
+ }
+ break;
+
+ case 'poll-winner-percent':
+ if (isset($winner)) {
+ $percent = ($winner['chvotes'] / $total_votes) * 100;
+ $replacements[$original] = number_format($percent, 0);
+ }
+ else {
+ $replacements[$original] = '';
+ }
+ break;
+
+ case 'poll-duration':
+ $replacements[$original] = format_interval($node->runtime, 1, $language_code);
+ break;
+ }
+ }
+ }
+
+ return $replacements;
+}