__('Unlimited voting', 'acf-onyx-poll'), 2 => __('Per device', 'acf-onyx-poll'),
30 => __('Every 30 minutes', 'acf-onyx-poll'), 60 => __('Every 1 hour', 'acf-onyx-poll'),
120 => __('Every 2 hours', 'acf-onyx-poll'), 180 => __('Every 3 hours', 'acf-onyx-poll'),
240 => __('Every 4 hours', 'acf-onyx-poll'), 300 => __('Every 5 hours', 'acf-onyx-poll'),
720 => __('Every 12 hours', 'acf-onyx-poll'), 1440 => __('Every 24 hours', 'acf-onyx-poll'));
}
private function select($name, $label, $value, $choices, $description = '') {
echo '' . esc_html($label) . ' ';
echo '';
foreach ($choices as $key => $text) {
echo '' . esc_html($text) . ' ';
}
echo ' ';
$this->description($name, $description);
echo ' ';
}
private function description($name, $description) {
if ($description) { echo '' . esc_html($description) . ' '; }
}
private function toggle($name, $label, $value, $description = '') {
echo '' . esc_html($label) . ' ';
echo ' ';
$this->description($name, $description);
echo ' ';
}
public function form($post) {
if ($post->post_type !== 'onyxpolls') { return; }
$poll = OnyxPollsStorage::get($post->ID);
if (!$poll) { return; }
wp_nonce_field('onyx_poll_save', 'onyx_poll_nonce');
$snapshot = wp_json_encode(array('version' => $poll['version'], 'answers' => $poll['answers']));
echo ' ';
echo ' ';
echo '
' . esc_html__('Answers', 'acf-onyx-poll') . ' *
';
echo '
' . esc_html__('Order', 'acf-onyx-poll') . ' ' . esc_html__('Image', 'acf-onyx-poll') . ' ' . esc_html__('Answer', 'acf-onyx-poll') . ' ' . esc_html__('Votes', 'acf-onyx-poll') . ' ' . esc_html__('Actions', 'acf-onyx-poll') . ' ';
$answers = $poll['answers'];
while (count($answers) < 2) { $answers[] = array('id' => 0, 'answer' => '', 'image_id' => 0, 'votes' => 0); }
foreach ($answers as $i => $answer) { $this->row($answer, $i); }
echo '
' . esc_html__('Add row', 'acf-onyx-poll') . '
';
echo '
';
$this->row(array('id' => 0, 'answer' => '', 'image_id' => 0, 'votes' => 0), '__index__');
echo ' ';
$this->select('limit_vote', __('Limit voting', 'acf-onyx-poll'), $poll['limit_vote'], self::limits(), __('Controls repeat votes per browser session using server-side records. Unlimited voting removes the poll interval; each session can submit up to 10 voting requests per minute.', 'acf-onyx-poll'));
$this->select('results', __('Results', 'acf-onyx-poll'), $poll['results'], array(1 => __('Show percentages', 'acf-onyx-poll'), 2 => __('Show vote counts', 'acf-onyx-poll'), 3 => __('Show both', 'acf-onyx-poll')), __('Display answer totals as percentages, numbers or both.', 'acf-onyx-poll'));
$end = $poll['end'] ? substr(str_replace(' ', 'T', $poll['end']), 0, 16) : '';
/* translators: %s: Site timezone name or UTC offset. */
$end_help = sprintf(__('Voting closes at this time in the site timezone (%s), even if cron is delayed.', 'acf-onyx-poll'), wp_timezone_string());
echo '' . esc_html__('Poll end date', 'acf-onyx-poll') . ' ' . esc_html($end_help) . ' ';
echo '' . esc_html__('Total votes', 'acf-onyx-poll') . ' ' . esc_html__('Calculated by adding the votes for all answers.', 'acf-onyx-poll') . ' ';
$this->toggle('images', __('Enable images', 'acf-onyx-poll'), $poll['images'], __('Show an image with each answer. Every answer needs an image when enabled.', 'acf-onyx-poll'));
$this->toggle('modal', __('Poll modal', 'acf-onyx-poll'), $poll['modal'], __('Allow this poll in the site modal. The latest active poll with this option is used.', 'acf-onyx-poll'));
$this->toggle('show_results', __('Show results', 'acf-onyx-poll'), $poll['show_results'], __('Let visitors view results before the deadline. Expired polls always show results.', 'acf-onyx-poll'));
$this->toggle('expired', __('Expired', 'acf-onyx-poll'), $poll['expired'], __('Close voting manually. To reopen, disable this option and set a future deadline.', 'acf-onyx-poll'));
echo '
';
}
private function row($answer, $i) {
$name = 'onyx_poll[answers][' . $i . ']';
$image_label = $answer['image_id'] ? __('Change image', 'acf-onyx-poll') : __('Select image', 'acf-onyx-poll');
echo '' . esc_html(is_numeric($i) ? $i + 1 : '') . ' ';
echo '';
if ($answer['image_id']) { echo wp_get_attachment_image($answer['image_id'], 'thumbnail'); }
echo '
';
echo ' ';
echo ' ';
echo '';
$actions = array('up' => array(__('Move up', 'acf-onyx-poll'), 'arrow-up-alt2'),
'down' => array(__('Move down', 'acf-onyx-poll'), 'arrow-down-alt2'),
'remove' => array(__('Remove answer', 'acf-onyx-poll'), 'trash'));
foreach ($actions as $action => $button) {
echo ' ';
}
echo '
';
}
public function assets() {
$screen = get_current_screen();
if (!$screen || $screen->post_type !== 'onyxpolls') { return; }
wp_enqueue_media();
wp_enqueue_script('onyx-poll-admin', plugins_url('assets/js/admin.js', ACF_ONYX_POLL_FILE), array('jquery-ui-sortable'), OnyxPolls::get_asset_vars('assets/js/admin.js')->ver, true);
wp_localize_script('onyx-poll-admin', 'onyxPollAdmin', array('imageTitle' => __('Select image', 'acf-onyx-poll'), 'imageButton' => __('Use image', 'acf-onyx-poll'), 'imageChoose' => __('Select image', 'acf-onyx-poll'), 'imageChange' => __('Change image', 'acf-onyx-poll')));
}
public function save($id, $post) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($id) || !isset($_POST['onyx_poll_nonce'])) { return; }
if (!current_user_can('edit_post', $id) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['onyx_poll_nonce'])), 'onyx_poll_save')) { return; }
$json = base64_decode(isset($_POST['onyx_poll_snapshot']) && is_string($_POST['onyx_poll_snapshot']) ? wp_unslash($_POST['onyx_poll_snapshot']) : '', true);
$signature = isset($_POST['onyx_poll_signature']) ? sanitize_text_field(wp_unslash($_POST['onyx_poll_signature'])) : '';
$snapshot = $json ? json_decode($json, true) : null;
if (!$snapshot || !isset($snapshot['version'], $snapshot['answers']) || !hash_equals(hash_hmac('sha256', $id . ':' . $json, wp_salt('nonce')), $signature)) {
$this->error = __('Invalid poll parameters.', 'acf-onyx-poll'); return;
}
$input = isset($_POST['onyx_poll']) ? wp_unslash($_POST['onyx_poll']) : array();
$validated = self::validate($input);
if (is_wp_error($validated)) { $this->error = $validated->get_error_message(); return; }
$result = OnyxPollsStorage::save($id, $validated['poll'], $validated['answers'], $snapshot);
if (is_wp_error($result)) { $this->error = $result->get_error_message(); }
}
public static function validate($input) {
$invalid = new WP_Error('onyx_poll_invalid', __('Complete all required poll fields. At least two answers are required.', 'acf-onyx-poll'));
if (!is_array($input) || !isset($input['answers']) || !is_array($input['answers']) || count($input['answers']) < 2) { return $invalid; }
$limit = isset($input['limit_vote']) && is_scalar($input['limit_vote']) ? (int) $input['limit_vote'] : 0;
$results = isset($input['results']) && is_scalar($input['results']) ? (int) $input['results'] : 0;
$end = isset($input['end']) && is_string($input['end']) ? $input['end'] : '';
$date = DateTime::createFromFormat('!Y-m-d\TH:i', $end, wp_timezone());
if (!isset(self::limits()[$limit]) || !in_array($results, array(1, 2, 3), true) || !$date || $date->format('Y-m-d\TH:i') !== $end) { return $invalid; }
$data = array('limit_vote' => $limit, 'results' => $results, 'end' => $date->format('Y-m-d H:i:s'));
foreach (array('images', 'modal', 'show_results', 'expired') as $key) { $data[$key] = empty($input[$key]) ? 0 : 1; }
$answers = array();
foreach ($input['answers'] as $answer) {
if (!is_array($answer) || !isset($answer['answer'], $answer['votes']) || !is_string($answer['answer']) || !is_scalar($answer['votes']) || !preg_match('/^\d+$/', (string) $answer['votes'])) { return $invalid; }
$text = sanitize_text_field($answer['answer']);
$image = isset($answer['image_id']) && is_scalar($answer['image_id']) ? absint($answer['image_id']) : 0;
if ($text === '' || ($data['images'] && (!$image || !wp_attachment_is_image($image)))) { return $invalid; }
$answers[] = array('id' => isset($answer['id']) && is_scalar($answer['id']) ? absint($answer['id']) : 0,
'answer' => $text, 'image_id' => $image, 'votes' => (int) $answer['votes']);
}
return array('poll' => $data, 'answers' => $answers);
}
public function redirect($location) {
return $this->error ? add_query_arg('onyx_poll_error', $this->error, $location) : $location;
}
public function notice() {
$screen = get_current_screen();
if ($screen && $screen->post_type === 'onyxpolls' && !empty($_GET['onyx_poll_error'])) {
echo '' . esc_html(sanitize_text_field(wp_unslash($_GET['onyx_poll_error']))) . '
';
}
if (get_option(OnyxPollsStorage::VERSION) !== OnyxPollsStorage::DB_VERSION || !get_option(OnyxPollsStorage::MIGRATION)) {
echo '' . esc_html__('Poll data migration is pending. It will resume automatically; existing data is preserved.', 'acf-onyx-poll') . '
';
}
}
public function menu() {
add_submenu_page('edit.php?post_type=onyxpolls', __('Poll settings', 'acf-onyx-poll'), __('Settings', 'acf-onyx-poll'), 'manage_options', 'onyx-poll-settings', array($this, 'settings_form'));
}
public function settings_form() {
if (!current_user_can('manage_options')) { return; }
$settings = OnyxPollsStorage::settings();
echo '' . esc_html__('Poll settings', 'acf-onyx-poll') . ' ';
if (isset($_GET['updated'])) { echo '
' . esc_html__('Settings saved.', 'acf-onyx-poll') . '
'; }
echo '
';
require_once __DIR__ . '/poll-settings-help.php';
OnyxPollsSettingsHelp::render();
echo '
';
}
public function save_settings() {
if (!current_user_can('manage_options')) { wp_die(esc_html__('You are not allowed to change these settings.', 'acf-onyx-poll')); }
check_admin_referer('onyx_poll_settings');
$input = isset($_POST['onyx_poll']) ? wp_unslash($_POST['onyx_poll']) : array();
if (!is_array($input)) { wp_die(esc_html__('Invalid poll parameters.', 'acf-onyx-poll')); }
$settings = OnyxPollsStorage::settings();
$settings['modal_time'] = isset($input['modal_time']) && is_scalar($input['modal_time']) && in_array((int) $input['modal_time'], array(1, 2, 3, 4, 5, 12, 24), true) ? (int) $input['modal_time'] : '';
$settings['css'] = empty($input['css']) ? 0 : 1;
$settings['image_size'] = isset($input['image_size']) && is_string($input['image_size']) && isset(wp_get_registered_image_subsizes()[$input['image_size']]) ? $input['image_size'] : 'thumbnail';
if (isset($input['delete_on_uninstall'])) {
if (!current_user_can('manage_options')) { wp_die(esc_html__('You are not allowed to change these settings.', 'acf-onyx-poll')); }
if (!empty($input['delete_on_uninstall']) && empty($_POST['onyx_delete_confirm'])) { wp_die(esc_html__('Confirm that you understand the permanent deletion warning before saving.', 'acf-onyx-poll')); }
$settings['delete_on_uninstall'] = empty($input['delete_on_uninstall']) ? 0 : 1;
}
update_option(OnyxPollsStorage::SETTINGS, $settings, false);
wp_safe_redirect(admin_url('edit.php?post_type=onyxpolls&page=onyx-poll-settings&updated=1'));
exit;
}
}
if (is_admin()) { new OnyxPollsAdmin(); }