label. */ public static function themes() { $themes = array( 'default' => __('Default', '3task-polls'), 'minimal' => __('Minimal', '3task-polls'), 'rounded' => __('Rounded', '3task-polls'), 'square' => __('Square', '3task-polls'), 'pill' => __('Pill', '3task-polls'), ); /** * Filters the poll designs. * * @param array $themes Key => label. */ return (array) apply_filters('tpoll_themes', $themes); } /** * Effective display options of a poll: shortcode attribute, then poll setting, then global default. * * @param TPoll_Poll $poll Poll. * @param array $atts Shortcode attributes. * @return array */ public static function display_options($poll, $atts = array()) { $global = get_option('tpoll_settings', array()); $show_results = !empty($atts['show_results']) ? $atts['show_results'] : $poll->get_setting('show_results', $global['show_results'] ?? 'after_vote'); if (!in_array($show_results, array('after_vote', 'always', 'never'), true)) { $show_results = 'after_vote'; } if (isset($atts['show_voters']) && $atts['show_voters'] !== '') { $show_voters = ($atts['show_voters'] === 'yes'); } else { $show_voters = (bool) $poll->get_setting('show_voters_count', $global['show_voters_count'] ?? true); } $theme = !empty($atts['theme']) ? $atts['theme'] : $poll->get_setting('theme', $global['theme'] ?? 'default'); if (!array_key_exists($theme, self::themes())) { $theme = 'default'; } $dark_mode = !empty($atts['dark_mode']) ? $atts['dark_mode'] : $poll->get_setting('dark_mode', $global['dark_mode'] ?? 'no'); if (!in_array($dark_mode, array('auto', 'yes', 'no'), true)) { $dark_mode = 'no'; } // The former stored defaults (#4F46E5, #E5E7EB) count as "no custom color". $global_bar = (string) sanitize_hex_color($global['results_bar_color'] ?? ''); $global_bar = strtoupper($global_bar) === '#4F46E5' ? '' : $global_bar; $bar_color = sanitize_hex_color($poll->get_style('bar_color', '')) ?: $global_bar; $bar_bg = (string) sanitize_hex_color($global['results_bar_bg'] ?? ''); $bar_bg = strtoupper($bar_bg) === '#E5E7EB' ? '' : $bar_bg; $animation = ($global['animation'] ?? 'grow') === 'none' ? 'none' : 'grow'; return compact('show_results', 'show_voters', 'theme', 'dark_mode', 'bar_color', 'bar_bg', 'animation'); } /** * Whether results may be shown to the current visitor. * * @param TPoll_Poll $poll Poll. * @param string $show_results after_vote, always or never. * @param bool $has_voted Visitor has voted. */ public static function results_visible($poll, $show_results, $has_voted) { if ($show_results === 'never') { $visible = false; } elseif ($show_results === 'always' || $poll->is_closed()) { $visible = true; } else { $visible = (bool) $has_voted; } /** * Filters whether the results may be shown to the current visitor. * * @param bool $visible Result of the poll setting. * @param TPoll_Poll $poll Poll. * @param string $show_results after_vote, always or never. * @param bool $has_voted Visitor has voted. */ return (bool) apply_filters('tpoll_results_visible', $visible, $poll, $show_results, (bool) $has_voted); } /** * Render shortcode */ public function render($atts) { $atts = shortcode_atts(array( 'id' => 0, 'show_results' => '', // after_vote, always, never (empty = use poll setting) 'show_voters' => '', // yes, no (empty = use poll setting) 'theme' => '', // (empty = use poll setting) 'dark_mode' => '', // auto, yes, no (empty = use poll setting) 'context' => 0 // post ID, only for polls counted per post ), $atts, 'tpoll'); $poll_id = absint($atts['id']); if (!$poll_id) { return ''; } $poll = TPoll_Poll::get($poll_id); if (!$poll) { return ''; } if ($poll->status === 'draft' && !current_user_can('edit_tpoll')) { return ''; } $this->context = TPoll_Public::context_for($poll, $atts['context']); wp_enqueue_style('tpoll-frontend'); wp_enqueue_script('tpoll-frontend'); $opt = self::display_options($poll, $atts); /** * Fires when a poll is rendered, so add-ons can load their assets. * * @param TPoll_Poll $poll Poll. * @param array $opt Display options. */ do_action('tpoll_enqueue_assets', $poll, $opt); $restriction = $poll->get_setting('vote_restriction', 'ip'); $is_closed = $poll->is_closed(); $is_upcoming = !$is_closed && $poll->is_upcoming(); $needs_login = ($restriction === 'user' && !is_user_logged_in()); $show_form = !$is_closed && !$is_upcoming && $poll->status !== 'draft'; $show_results_now = self::results_visible($poll, $opt['show_results'], false); $classes = array( 'tpoll-poll', 'tpoll-type-' . $poll->type, 'tpoll-theme-' . $opt['theme'], ); if ($opt['dark_mode'] === 'yes') { $classes[] = 'tpoll-dark'; } elseif ($opt['dark_mode'] === 'auto') { $classes[] = 'tpoll-dark-auto'; } if ($opt['animation'] === 'none') { $classes[] = 'tpoll-no-animation'; } $style = ''; if ($opt['bar_color']) { $style .= '--tpoll-bar:' . $opt['bar_color'] . ';'; } // Only a custom track color is set inline, so the dark mode keeps its own track color. if ($opt['bar_bg']) { $style .= '--tpoll-bar-bg:' . $opt['bar_bg'] . ';'; } $question_id = 'tpoll-q-' . $poll_id; ob_start(); ?>
context) : ?>data-context="context); ?>" style=""> question)) : ?>
question); ?>
start_date)) { printf( /* translators: %s: date and time the poll opens */ esc_html__('This poll opens on %s', '3task-polls'), esc_html(mysql2date(get_option('date_format') . ' ' . get_option('time_format'), $poll->start_date)) ); } else { esc_html_e('This poll is not open yet.', '3task-polls'); } ?>
render_poll_form($poll, $question_id, $show_results_now); ?>
type === 'reactions' && $show_form) { $show_results_now = false; } ?>
> context), $opt['show_voters']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped inside results_html(). } ?>
get_answers(); /** * Filters whether the answers are shuffled (e.g. never for a versus poll). * * @param bool $shuffle Poll setting "randomize answers". * @param TPoll_Poll $poll Poll. */ if (apply_filters('tpoll_shuffle_answers', (bool) $poll->get_setting('randomize_answers', false), $poll)) { shuffle($answers); } ?>
type) { case 'single': case 'quiz': $this->render_single_choice($poll, $answers, $question_id); break; case 'multiple': $this->render_multiple_choice($poll, $answers, $question_id); break; case 'rating': $this->render_rating($poll, $answers, $question_id); break; case 'voting': $this->render_voting($poll, $answers, $question_id, $show_counts); break; case 'reactions': $this->render_reactions($poll, $answers, $question_id, $show_counts); break; default: /** * Renders the answer area of an add-on poll type. * * @param TPoll_Poll $poll Poll. * @param array $answers Answers (already shuffled if enabled). * @param string $question_id ID of the question element, for aria-labelledby. */ do_action('tpoll_render_form_' . $poll->type, $poll, $answers, $question_id); break; } /** * Fires after the answers, inside the vote form. Fields named tpoll_extra[...] * are sent along with the vote. * * @param TPoll_Poll $poll Poll. */ do_action('tpoll_form_after_answers', $poll); /** * Filters the types that show the vote button. * * @param string[] $types Default: single, multiple. */ $submit_types = (array) apply_filters('tpoll_submit_button_types', array('single', 'multiple')); ?> type, $submit_types, true)) : ?>
get_setting('max_choices', 0); ?>
data-max-choices="">

id, $poll, $this->context); if ($results) { $up = (string) $results['up']; $down = (string) $results['down']; } } $answer = isset($answers[0]) ? $answers[0] : null; ?>
id, $poll, $this->context); foreach ((array) ($results['answers'] ?? array()) as $row) { $counts[(int) $row['id']] = (int) $row['votes']; } } ?>
0) { $parts .= ''; $offset -= $pct; } $legend .= '
  • ' . esc_html($answer['text']) . ' ' . esc_html(number_format_i18n($pct, 1)) . ' % ' /* translators: %d: number of votes */ . esc_html(sprintf(_n('%d vote', '%d votes', (int) $answer['votes'], '3task-polls'), (int) $answer['votes'])) . '
  • '; $i++; } return '
    '; } /** * Results HTML, shared by the shortcode and the AJAX responses. * * @param TPoll_Poll|null $poll Poll. * @param array|null $results Result of TPoll_Vote::get_results(). * @param bool $show_voters Show the voter count. * @return string */ public static function results_html($poll, $results, $show_voters) { if (!$poll || !$results) { return ''; } /** * Lets add-on types render their own results. Return a non-empty string to replace the default output. * * @param string $html Empty string. * @param TPoll_Poll $poll Poll. * @param array $results Results. * @param bool $show_voters Show the voter count. */ $custom = (string) apply_filters('tpoll_results_html', '', $poll, $results, $show_voters); if ($custom !== '') { return $custom; } ob_start(); ?>
    type === 'voting') : ?>
    type === 'reactions') : ?>
    type === 'single' && $poll->get_setting('result_chart', 'bars') === 'donut') : ?> type === 'rating') : ?>
     %
    type === 'multiple') : ?>