Server IP : 66.29.132.124 / Your IP : 3.23.92.50 Web Server : LiteSpeed System : Linux business141.web-hosting.com 4.18.0-553.lve.el8.x86_64 #1 SMP Mon May 27 15:27:34 UTC 2024 x86_64 User : wavevlvu ( 1524) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/wavevlvu/misswavenigeria.com/wp-content/plugins/totalpoll-lite/src/Poll/ |
Upload File : |
<?php namespace TotalPoll\Poll; use TotalPoll\Contracts\Poll\Model as ModelContract; use TotalPollVendors\TotalCore\Contracts\Form\Form; use TotalPollVendors\TotalCore\Contracts\Helpers\Renderable; use TotalPollVendors\TotalCore\Helpers\Arrays; use TotalPollVendors\TotalCore\Helpers\Misc; use TotalPollVendors\TotalCore\Helpers\Strings; use TotalPollVendors\TotalCore\Traits\Cookies; use TotalPollVendors\TotalCore\Traits\Metadata; use TotalSuite\Foundation\Database\Query\Expression; /** * Poll Model * * @package TotalPoll\Poll * @since 1.0.0 */ class Model implements Renderable, ModelContract { use Cookies; use Metadata; const WELCOME_SCREEN = 'welcome'; const VOTE_SCREEN = 'vote'; const RESULTS_SCREEN = 'results'; const THANKYOU_SCREEN = 'thankyou'; const WELCOME_ACTION = 'welcome'; const VIEW_ACTION = 'view'; const VOTE_ACTION = 'vote'; const RESULTS_ACTION = 'results'; const THANKYOU_ACTION = 'thankyou'; const SORT_BY_POSITION = 'position'; const SORT_BY_LABEL = 'label'; const SORT_BY_VOTES = 'votes'; const SORT_BY_RAND = 'random'; const SORT_ASC = 'asc'; const SORT_DESC = 'desc'; /** * Poll ID. * * @var int|null * @since 1.0.0 */ protected $id = null; /** * Poll UID. * * @var int|null * @since 4.1.3 */ protected $uid = null; /** * Poll attributes. * * @var array * @since 1.0.0 */ protected $attributes = []; /** * Poll settings. * * @var array|null * @since 1.0.0 */ protected $settings = null; /** * Poll seo attributes. * * @var array|null * @since 1.0.0 */ protected $seo = null; /** * Poll WordPress post. * * @var array|null|\WP_Post * @since 1.0.0 */ protected $pollPost = null; /** * Poll questions. * * @var array * @since 1.0.0 */ protected $questions = []; /** * Poll choices map (choice uid => question uid). * * @var array * @since 1.0.0 */ protected $choicesMap = []; /** * Poll total votes. * * @var int * @since 1.0.0 */ protected $totalVotes = 0; /** * Poll choices count. * * @var int * @since 1.0.0 */ protected $choicesCount = null; /** * Choice per page. * * @var int * @since 1.0.0 */ protected $choicesPerPage = 10; /** * Poll total pages. * * @var int|null * @since 1.0.0 */ protected $pagesCount = 0; /** * Poll current page. * * @var int|null * @since 1.0.0 */ protected $currentPage = 1; /** * Poll upload form. * * @var \TotalPollVendors\TotalCore\Contracts\Form\Form $form * @since 1.0.0 */ protected $form = null; /** * Poll current screen * * @var string * @since 1.0.0 */ protected $screen = self::WELCOME_SCREEN; /** * Poll current action * * @var string * @since 4.0.0 */ protected $action = self::VIEW_ACTION; /** * Sort choices by field. * * @var string * @since 1.0.0 */ protected $sortChoicesBy = self::SORT_BY_POSITION; /** * Sort choices direction. * * @var string * @since 1.0.0 */ protected $sortChoicesDirection = self::SORT_DESC; /** * Sort results by field. * * @var string * @since 1.0.0 */ protected $sortResultsBy = self::SORT_BY_POSITION; /** * Sort results direction. * * @var string * @since 1.0.0 */ protected $sortResultsDirection = self::SORT_DESC; /** * Limitations * * @var \TotalPollVendors\TotalCore\Contracts\Limitations\Bag * @since 1.0.0 */ protected $limitations; /** * Restrictions * * @var \TotalPollVendors\TotalCore\Contracts\Restrictions\Bag * @since 1.0.0 */ protected $restrictions; /** * Error. * * @var null|\WP_Error * @since 1.0.0 */ protected $error; /** * @var mixed */ protected static $originalExpressions; /** * Model constructor. * * @param $attributes array Poll attributes. * * @since 1.0.0 */ public function __construct( $attributes ) { /** * Filters the poll attributes. * * @param array $attributes Poll model attributes. * @param ModelContract $poll Poll model object. * * @return array * @since 4.0.0 */ $attributes = apply_filters( 'totalpoll/filters/poll/attributes', $attributes, $this ); $this->attributes = $attributes; $this->id = $attributes['id']; $this->action = (string) $attributes['action'] ?: $this->action; $this->pollPost = $attributes['post']; // Parse settings JSON. $this->settings = (array) json_decode( $this->pollPost->post_content, true ); /** * Filters the poll attributes. * * @param array $settings Poll settings. * @param ModelContract $poll Poll model object. * * @return array * @since 4.0.0 */ $this->settings = apply_filters( 'totalpoll/filters/poll/settings', $this->settings, $this ); // UID $this->uid = $this->getSettingsItem( 'uid' ); // Locale $locale = get_locale(); // Questions $questions = $this->getSettingsItem( 'questions', [] ); $receivedChoicesCookieName = $this->generateCookieName( 'received_' . $this->getId() . $this->getUid() ); $receivedChoicesCookieValue = (string) $this->getCookie( $receivedChoicesCookieName ); $receivedChoices = explode( '__', $receivedChoicesCookieValue ); foreach ( $questions as $questionIndex => $question ) : // Translation if ( ! empty( $question['translations'][ $locale ]['content'] ) ) : $question['content'] = $question['translations'][ $locale ]['content']; endif; // Move to questions array $this->questions[ $question['uid'] ] = $question; $this->questions[ $question['uid'] ]['index'] = $questionIndex; $this->questions[ $question['uid'] ]['votes'] = 0; $this->questions[ $question['uid'] ]['receivedVotes'] = 0; // Choices $choices = (array) $this->questions[ $question['uid'] ]['choices']; $this->questions[ $question['uid'] ]['choices'] = []; foreach ( $choices as $choiceIndex => $choice ) : // Translation if ( ! empty( $choice['translations'][ $locale ]['label'] ) ) : $choice['label'] = $choice['translations'][ $locale ]['label']; endif; // Add votes property $choice['votes'] = empty( $attributes['votes'][ $choice['uid'] ] ) ? 0 : $attributes['votes'][ $choice['uid'] ]; // Add to choices map $this->choicesMap[ $choice['uid'] ] = $question['uid']; $choice['index'] = $choiceIndex; $choice['questionUid'] = $question['uid']; $choice['receivedVotes'] = 0; if ( in_array( $choice['uid'], $receivedChoices ) ) { $choice['receivedVotes'] = 1; } // Alter visibility for users with cookie set to choice UID (Check CountVote command for more details) if ( $this->getCookie( $this->getPrefix( $choice['uid'] ) ) ) : $choice['visibility'] = true; endif; // Cumulative of votes for current question $this->questions[ $question['uid'] ]['votes'] += $choice['votes']; $this->totalVotes += $choice['votes']; $this->questions[ $question['uid'] ]['choices'][ $choice['uid'] ] = $choice; endforeach; // Calculate ranking of choices $questionChoices = $this->questions[ $question['uid'] ]['choices']; uasort( $questionChoices, [ $this, 'orderByVotes' ] ); $questionChoices = array_reverse( $questionChoices, true ); $rankedChoices = array_keys( $questionChoices ); foreach ( $rankedChoices as $index => $choiceUid ) : $this->questions[ $question['uid'] ]['choices'][ $choiceUid ]['rank'] = $index + 1; endforeach; endforeach; // Fields $fields = $this->getSettingsItem( 'fields', [] ); foreach ( $fields as &$field ) : // Translation if ( ! empty( $field['translations'][ $locale ]['label'] ) ) : $field['label'] = $field['translations'][ $locale ]['label']; endif; // Translation if ( ! empty( $field['translations'][ $locale ]['options'] ) ) : $field['options'] = $field['translations'][ $locale ]['options']; endif; endforeach; $this->setSettingsItem( 'fields', $fields ); // Current page $this->currentPage = empty( $attributes['currentPage'] ) ? $this->currentPage : (int) $attributes['currentPage']; // Choice per page $this->choicesPerPage = (int) $this->getSettingsItem( 'design.pagination.perPage' ) ?: 10; // Sort choices $this->sortChoicesBy = (string) $this->getSettingsItem( 'choices.sort.field', 'position' ); $this->sortChoicesDirection = (string) $this->getSettingsItem( 'choices.sort.direction', 'desc' ); // Sort results $this->sortResultsBy = (string) $this->getSettingsItem( 'results.sort.field', 'position' ); $this->sortResultsDirection = (string) $this->getSettingsItem( 'results.sort.direction', self::SORT_DESC ); /** * Filters the poll questions. * * @param array $questions Questions array. * @param Model $model Poll model. * * @return array * @since 4.0.1 */ $this->questions = apply_filters( 'totalpoll/filters/poll/questions', $this->questions, $this ); /** * Filters the poll total votes. * * @param int $votes Total votes. * @param Model $model Poll model. * * @return array * @since 4.0.1 */ $this->totalVotes = apply_filters( 'totalpoll/filters/poll/total-votes', $this->totalVotes, $this ); // Limitations $this->limitations = new \TotalPollVendors\TotalCore\Limitations\Bag(); // Period $periodArgs = $this->getSettingsItem( 'vote.limitations.period', [] ); if ( ! empty( $periodArgs['enabled'] ) ) : $this->limitations->add( 'period', new \TotalPoll\Limitations\Period( $periodArgs ) ); endif; // Membership $membershipArgs = $this->getSettingsItem( 'vote.limitations.membership', [] ); if ( ! empty( $membershipArgs['enabled'] ) ) : $this->limitations->add( 'membership', new \TotalPoll\Limitations\Membership( $membershipArgs ) ); endif; // Quota $quotaArgs = $this->getSettingsItem( 'vote.limitations.quota', [] ); if ( ! empty( $quotaArgs['enabled'] ) ) : $quotaArgs['currentValue'] = $this->totalVotes; $this->limitations->add( 'quota', new \TotalPoll\Limitations\Quota( $quotaArgs ) ); endif; // Region $regionArgs = $this->getSettingsItem( 'vote.limitations.region', [] ); if ( ! empty( $regionArgs['enabled'] ) ) : $regionArgs['ip'] = $attributes['ip']; $this->limitations->add( 'region', new \TotalPoll\Limitations\Region( $regionArgs ) ); endif; /** * Fires after limitations setup. * * @param ModelContract $poll Poll model object. * * @since 4.0.0 */ do_action( 'totalpoll/actions/poll/limitations', $this ); // Restrictions $this->restrictions = new \TotalPollVendors\TotalCore\Restrictions\Bag(); $frequencyArgs = $this->getSettingsItem( 'vote.frequency', [ 'timeout' => 3600 ] ); $frequencyArgs['uid'] = $this->getUid(); $frequencyArgs['poll'] = $this; $frequencyArgs['action'] = 'vote'; $frequencyArgs['fullCheck'] = TotalPoll()->option( 'performance.fullChecks.enabled' ); $frequencyArgs = apply_filters( 'totalpoll/filters/poll/vote/frequency', $frequencyArgs, $this ); if ( ! empty( $frequencyArgs['cookies']['enabled'] ) ) : $this->restrictions->add( 'cookies', new \TotalPoll\Restrictions\Cookies( $frequencyArgs ) ); endif; if ( ! empty( $frequencyArgs['ip']['enabled'] ) ) : $this->restrictions->add( 'ip', new \TotalPoll\Restrictions\IPAddress( $frequencyArgs ) ); endif; if ( ! empty( $frequencyArgs['user']['enabled'] ) ) : $this->restrictions->add( 'user', new \TotalPoll\Restrictions\LoggedInUser( $frequencyArgs ) ); endif; /** * Fires after restrictions setup. * * @param ModelContract $poll Poll model object. * @param array $frequencyArgs Frequency arguments. * * @since 4.0.0 */ do_action( 'totalpoll/actions/poll/restrictions', $this, $frequencyArgs ); // Screen if ( $this->hasVoted() || ! $this->isAcceptingVotes() ) : $this->screen = self::RESULTS_SCREEN; endif; /** * Fires after first screen setup. * * @param ModelContract $poll Poll model object. * * @since 4.0.0 */ do_action( 'totalpoll/actions/poll/screen', $this ); // Translation $this->setSettingsItem( 'seo.poll.title', $this->getSettingsItem( "seo.poll.translations.{$locale}.title", $this->getSettingsItem( 'seo.poll.title' ) ) ); $this->setSettingsItem( 'seo.poll.description', $this->getSettingsItem( "seo.poll.translations.{$locale}.description", $this->getSettingsItem( 'seo.poll.description' ) ) ); $this->setSettingsItem( 'content.welcome.content', $this->getSettingsItem( "content.welcome.translations.{$locale}.content", $this->getSettingsItem( 'content.welcome.content' ) ) ); $this->setSettingsItem( 'content.vote.above', $this->getSettingsItem( "content.vote.translations.{$locale}.above", $this->getSettingsItem( 'content.vote.above' ) ) ); $this->setSettingsItem( 'content.vote.below', $this->getSettingsItem( "content.vote.translations.{$locale}.below", $this->getSettingsItem( 'content.vote.below' ) ) ); $this->setSettingsItem( 'content.thankyou.content', $this->getSettingsItem( "content.thankyou.translations.{$locale}.content", $this->getSettingsItem( 'content.thankyou.content' ) ) ); $this->setSettingsItem( 'content.results.above', $this->getSettingsItem( "content.results.translations.{$locale}.above", $this->getSettingsItem( 'content.results.above' ) ) ); $this->setSettingsItem( 'content.results.below', $this->getSettingsItem( "content.results.translations.{$locale}.below", $this->getSettingsItem( 'content.results.below' ) ) ); $this->setSettingsItem( 'results.message', $this->getSettingsItem( "results.translations.{$locale}.message", $this->getSettingsItem( 'results.message' ) ) ); /** * Fires after poll model setup is completed. * * @param ModelContract $poll Poll model object. * * @since 4.0.0 */ do_action( 'totalpoll/actions/poll/setup', $this ); } /** * Get settings section or item. * * @param bool $section Settings section. * @param bool $args Path to setting. * * @return mixed|array|null * @since 1.0.0 */ public function getSettings( $section = false, $args = false ) { // Deep selection. if ( $args !== false && $section && isset( $this->settings[ $section ] ) ) : $paths = func_get_args(); unset( $paths[0] ); return Arrays::getDeep( $this->settings[ $section ], $paths ); endif; // Return specific settings section, otherwise, return all settings. if ( $section ) : return isset( $this->settings[ $section ] ) ? $this->settings[ $section ] : null; endif; return $this->settings; } /** * @param array $settings */ public function setSettings( array $settings = [] ) { $this->settings = $settings; } /** * Get settings item. * * @param bool $needle Settings name. * @param bool $default Default value. * * @return mixed|array|null * @since 1.0.0 */ public function getSettingsItem( $needle, $default = null ) { /** * Filters the poll settings item. * * @param array $settings Poll settings. * @param string $default Default value. * @param ModelContract $poll Poll model object. * * @return mixed * @since 4.0.0 */ return apply_filters( "totalpoll/filters/poll/settings-item/{$needle}", Arrays::getDotNotation( $this->settings, $needle, $default ), $this->settings, $default, $this ); } /** * Get poll post object. * * @return array|mixed|null|\WP_Post */ public function getPollPost() { return $this->pollPost; } /** * Get poll id. * * @return int * @since 1.0.0 */ public function getId() { return (int) $this->id; } /** * Get poll uid. * * @return int * @since 4.1.3 */ public function getUid() { return (string) $this->uid; } /** * Get poll title. * * @return string * @since 1.0.0 */ public function getTitle() { return $this->pollPost->post_title; } /** * Get seo attributes. * * @return array * @since 1.0.0 */ public function getSeoAttributes() { if ( $this->seo === null ) : $bindings = [ 'title' => $this->getTitle(), 'sitename' => get_bloginfo( 'name' ), ]; $this->seo = [ 'title' => Strings::template( $this->getSettingsItem( 'seo.poll.title' ), $bindings ), 'description' => Strings::template( $this->getSettingsItem( 'seo.poll.description' ), $bindings ), ]; endif; /** * Filters the poll seo attributes. * * @param array $seo SEO attributes. * @param ModelContract $poll Poll model object. * * @return array * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/seo', $this->seo, $this ); } /** * Get share attributes. * * @return array * @since 1.0.0 */ public function getShareAttributes() { $websites = array_filter( (array) TotalPoll()->option( 'share.websites', [] ) ); $websitesUrls = [ 'facebook' => 'https://www.facebook.com/sharer.php?u={{url}}', 'twitter' => 'https://twitter.com/intent/tweet?url={{url}}', 'pinterest' => 'https://pinterest.com/pin/create/bookmarklet/?url={{url}}', ]; $shareUrlArgs = [ 'utm_source' => '', 'utm_medium' => 'poll-share-button', 'utm_campaign' => $this->getTitle(), ]; foreach ( $websitesUrls as $service => $websitesUrl ) : $shareUrlArgs['utm_source'] = $service; $shareUrl = add_query_arg( $shareUrlArgs, $this->getUrl() ); $websitesUrls[ $service ] = Strings::template( $websitesUrl, [ 'url' => urlencode( $shareUrl ) ] ); endforeach; /** * Filters the poll sharing attributes. * * @param array $attributes Sharing attributes. * @param ModelContract $poll Poll model object. * * @return array * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/share', array_intersect_key( $websitesUrls, $websites ), $this ); } /** * Get poll thumbnail. * * @return false|string * @since 1.0.0 */ public function getThumbnail() { $thumbnail = wp_get_attachment_image_src( get_post_thumbnail_id( $this->id ), 'post-thumbnail' ); $thumbnail = empty( $thumbnail[0] ) ? TotalPoll()->env( 'url' ) . 'assets/dist/images/poll/no-preview.png' : $thumbnail[0]; /** * Filters the poll thumbnail. * * @param array $attributes Poll model attributes. * @param ModelContract $poll Poll model object. * * @return string * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/thumbnail', $thumbnail, $this ); } /** * Get time left to start. * * @return int|\DateInterval * @since 1.0.0 */ public function getTimeLeftToStart() { $startDate = $this->getSettingsItem( 'vote.limitations.period.start' ); $startDate = $startDate ? TotalPoll( 'datetime', [ $startDate ] ) : false; if ( $startDate && $startDate->getTimestamp() > current_time( 'timestamp' ) ) : $now = TotalPoll( 'datetime' ); return $startDate->diff( $now, true ); endif; return 0; } /** * Get time left to end. * * @return int|\DateInterval * @since 1.0.0 */ public function getTimeLeftToEnd() { $endDate = $this->getSettingsItem( 'vote.limitations.period.end' ); $endDate = $endDate ? TotalPoll( 'datetime', [ $endDate ] ) : false; if ( $endDate && $endDate->getTimestamp() > current_time( 'timestamp' ) ) : $now = TotalPoll( 'datetime' ); return $endDate->diff( $now, true ); endif; return 0; } /** * Get form. * * @return Form Form object * @since 1.0.0 */ public function getForm() { if ( $this->form === null ) : $this->form = TotalPoll( 'polls.form', [ $this ] ); endif; return $this->form; } /** * Get URL. * * @param array $args * * @return string * @since 1.0.0 */ public function getUrl( $args = [] ) { $base = $this->getPermalink(); if ( ! is_singular( TP_POLL_CPT_NAME ) ) { $base = wp_get_referer() ?: TotalPoll( 'http.request' )->request( 'totalpoll.referer' ) ?: $base; } $url = esc_url( add_query_arg( $args, $base ) ); /** * Filters the poll urls. * * @param string $url URL. * @param array $args Arguments. * @param ModelContract $poll Poll model object. * * @return string * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/url', $url, $args, $this ); } /** * Get action. * * @return string * @since 1.0.0 */ public function getAction() { return $this->action; } /** * Get AJAX url. * * @param array $args * * @return string */ public function getAjaxUrl( $args = [] ) { $args = Arrays::parse( $args, [ 'pollId' => $this->id, 'action' => $this->getAction(), 'screen' => $this->getScreen(), ] ); $base = admin_url( 'admin-ajax.php' ); $urlParameters = [ 'action' => 'totalpoll', 'totalpoll' => $args ]; $url = esc_url( add_query_arg( $urlParameters, $base ) ); /** * Filters the poll AJAX urls. * * @param string $url URL. * @param array $args Arguments. * @param ModelContract $poll Poll model object. * * @return string * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/ajax-url', $url, $args, $this ); } /** * Get poll permalink. * * @return false|string */ public function getPermalink() { return get_permalink( $this->pollPost ); } /** * Get poll total votes. * * @return int * @since 1.0.0 */ public function getTotalVotes() { return $this->totalVotes; } /** * @return string */ public function getTotalVotesNumber() { return number_format( $this->totalVotes ); } /** * @return string */ public function getTotalVotesWithLabel() { return sprintf( _n( '%s Vote', '%s Votes', $this->totalVotes, 'totalpoll' ), number_format( $this->totalVotes ) ); } /** * @param null $orderBy * @param string $direction * * @return array */ public function getQuestions( $orderBy = null, $direction = self::SORT_ASC ) { if ( $orderBy ) : foreach ( $this->questions as &$question ) : uasort( $question['choices'], [ $this, 'orderBy' . ucfirst( $orderBy ) ] ); if ( $direction && strtolower( $direction ) === self::SORT_DESC ) : $question['choices'] = array_reverse( $question['choices'], true ); endif; endforeach; endif; return $this->questions; } /** * @return array */ public function getQuestionsForVote() { /** * Filters the poll questions (for vote). * * @param array $questions Questions array. * @param Model $model Poll model. * * @return array * @since 4.0.1 */ return apply_filters( 'totalpoll/filters/poll/questions-for-vote', $this->getQuestions( $this->sortChoicesBy, $this->sortChoicesDirection ), $this ); } /** * @return array */ public function getQuestionsForResults() { /** * Filters the poll questions (for results). * * @param array $questions Questions array. * @param Model $model Poll model. * * @return array * @since 4.0.1 */ return apply_filters( 'totalpoll/filters/poll/questions-for-results', $this->getQuestions( $this->sortResultsBy, $this->sortResultsDirection ), $this ); } /** * @return int */ public function getQuestionsCount() { return count( $this->questions ); } /** * @return int */ public function getChoicesCount() { return count( $this->choicesMap ); } /** * @param $questionUid * * @return int */ public function getQuestionChoicesCount( $questionUid ) { return empty( $this->questions[ $questionUid ] ) ? 0 : count( $this->questions[ $questionUid ]['choices'] ); } /** * @param $questionUid * * @return array|mixed|null */ public function getQuestion( $questionUid ) { return empty( $this->questions[ $questionUid ] ) ? null : $this->questions[ $questionUid ]; } /** * @param $questionUid * * @return array */ public function getQuestionChoices( $questionUid ) { return empty( $this->questions[ $questionUid ] ) ? [] : $this->questions[ $questionUid ]['choices']; } /** * @param $questionUid * * @return int */ public function getQuestionVotes( $questionUid ) { return empty( $this->questions[ $questionUid ] ) ? 0 : $this->questions[ $questionUid ]['votes']; } /** * @param $questionUid * * @return string */ public function getQuestionVotesWithLabel( $questionUid ) { $votes = $this->getQuestionVotes( $questionUid ); return sprintf( _n( '%s Vote', '%s Votes', $votes, 'totalpoll' ), number_format( $votes ) ); } /** * @param $choiceUid * * @return array|mixed|null */ public function getQuestionUidByChoiceUid( $choiceUid ) { return empty( $this->choicesMap[ $choiceUid ] ) ? null : $this->choicesMap[ $choiceUid ]; } /** * @param $choiceUid * * @return array|null */ public function getChoice( $choiceUid ) { return empty( $this->choicesMap[ $choiceUid ] ) ? null : $this->questions[ $this->choicesMap[ $choiceUid ] ]['choices'][ $choiceUid ]; } /** * @param $choiceUid * * @return int|null */ public function getChoiceVotes( $choiceUid ) { if ( ! isset( $this->choicesMap[ $choiceUid ] ) ) : return null; endif; return $this->questions[ $this->choicesMap[ $choiceUid ] ]['choices'][ $choiceUid ]['votes']; } /** * @param $choiceUid * * @return null|string */ public function getChoiceVotesNumber( $choiceUid ) { if ( ! isset( $this->choicesMap[ $choiceUid ] ) ) : return null; endif; $votes = $this->questions[ $this->choicesMap[ $choiceUid ] ]['choices'][ $choiceUid ]['votes']; return number_format( $votes ); } /** * @param $choiceUid * * @return null|string */ public function getChoiceVotesWithLabel( $choiceUid ) { if ( ! isset( $this->choicesMap[ $choiceUid ] ) ) : return null; endif; $votes = $this->questions[ $this->choicesMap[ $choiceUid ] ]['choices'][ $choiceUid ]['votes']; return sprintf( _n( '%s Vote', '%s Votes', $votes, 'totalpoll' ), number_format( $votes ) ); } /** * @param $choiceUid * * @return null|string */ public function getChoiceVotesPercentage( $choiceUid ) { if ( ! isset( $this->choicesMap[ $choiceUid ] ) ) : return null; endif; $votes = $this->questions[ $this->choicesMap[ $choiceUid ] ]['choices'][ $choiceUid ]['votes']; $totalVotes = $this->questions[ $this->choicesMap[ $choiceUid ] ]['votes']; $percentage = $totalVotes ? number_format( ( $votes / $totalVotes ) * 100, 2 ) : '0.00'; return apply_filters( 'totalpoll/filters/poll/choice/percentage', $percentage, $votes, $totalVotes, $choiceUid, $this ); } /** * @param $choiceUid * * @return string */ public function getChoiceVotesPercentageWithLabel( $choiceUid ) { return $this->getChoiceVotesPercentage( $choiceUid ) . '%'; } /** * @param $choiceUid * * @return string */ public function getChoiceVotesFormatted( $choiceUid ) { $format = apply_filters( 'totalpoll/filters/poll/choice/votes/format', $this->getSettingsItem( 'results.format', '{{votesWithLabel}}' ), $choiceUid, $this ); $vars = apply_filters( 'totalpoll/filters/poll/choice/votes/args', [ 'votes' => $this->getChoiceVotesNumber( $choiceUid ), 'votesPercentage' => $this->getChoiceVotesPercentageWithLabel( $choiceUid ), 'votesWithLabel' => $this->getChoiceVotesWithLabel( $choiceUid ), 'votesTotal' => $this->getQuestionVotes( $this->getQuestionUidByChoiceUid( $choiceUid ) ), 'votesTotalWithLabel' => $this->getQuestionVotesWithLabel( $this->getQuestionUidByChoiceUid( $choiceUid ) ), ], $choiceUid, $this ); return Strings::template( $format, $vars ); } /** * @return array */ public function getChoices() { $choices = []; foreach ( $this->choicesMap as $choiceUid => $questionUid ) : $choices[ $choiceUid ] = $this->questions[ $questionUid ]['choices'][ $choiceUid ]; endforeach; return $choices; } /** * @param array $args * * @return array */ public function getChoicesRows( $args = [] ) { $perRow = $this->getSettingsItem( 'design.layout.columns', 4 ); $choices = $this->getChoices(); return $perRow === 0 ? [ $choices ] : array_chunk( $choices, $perRow, true ); } /** * @return float|int|string */ public function getColumnWidth() { $perRow = $this->getSettingsItem( 'design.layout.columns', 4 ); return 100 / $perRow; } /** * @return null|\WP_Error */ public function getError() { return $this->error; } /** * @return null|string */ public function getErrorMessage() { return is_wp_error( $this->error ) ? $this->error->get_error_message() : null; } /** * @return \TotalPollVendors\TotalCore\Contracts\Limitations\Bag|\TotalPollVendors\TotalCore\Limitations\Bag */ public function getLimitations() { return $this->limitations; } /** * @return \TotalPollVendors\TotalCore\Contracts\Restrictions\Bag|\TotalPollVendors\TotalCore\Restrictions\Bag */ public function getRestrictions() { return $this->restrictions; } /** * @return array */ public function getFields() { $fields = $this->getSettingsItem( 'fields', [] ); foreach ( $fields as $fieldIndex => $field ) : $fields[ $field['uid'] ] = [ 'type' => $field['type'], 'label' => $field['label'], 'name' => $field['name'], 'defaultValue' => $field['defaultValue'], 'options' => $field['options'], 'required' => ! empty( $field['validations']['filled']['enabled'] ), ]; unset( $fields[ $fieldIndex ] ); endforeach; /** * Filters the poll custom fields. * * @param array $fields Poll custom fields. * @param ModelContract $poll Poll model object. * * @return array * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/fields', $fields, $this ); } /** * Get prefix. * * @param string $append * * @return string * @since 1.0.0 */ public function getPrefix( $append = '' ) { /** * Filters poll prefix. * * @param string $prefix Poll prefix. * @param string $append Appended value. * @param ModelContract $poll Poll model object. * * @return array * @since 4.0.3 */ return apply_filters( 'totalpoll/filters/poll/prefix', "tp_{$this->id}_{$append}", $append, $this ); } /** * Get current screen. * * @return string Current screen. * @since 1.0.0 */ public function getScreen() { return $this->screen; } /** * @return string */ public function getHeader() { $header = ''; if ( $this->isVoteScreen() ) : $header .= $this->getAboveVoteContent(); elseif ( $this->isResultsScreen() ) : $header .= $this->getAboveResultsContent(); endif; return do_shortcode( $header ); } /** * @return string */ public function getFooter() { $footer = ''; if ( $this->isVoteScreen() ) : $footer .= $this->getBelowVoteContent(); elseif ( $this->isResultsScreen() ) : $footer .= $this->getBelowResultsContent(); endif; return do_shortcode( $footer ); } /** * @return string */ public function getWelcomeContent() { return $this->getSettingsItem( 'content.welcome.content' ); } /** * @return string */ public function getThankyouContent() { return $this->getSettingsItem( 'content.thankyou.content' ); } /** * @return string */ public function getAboveVoteContent() { return $this->getSettingsItem( 'content.vote.above' ); } /** * @return string */ public function getBelowVoteContent() { return $this->getSettingsItem( 'content.vote.below' ); } /** * @return string */ public function getAboveResultsContent() { return $this->getSettingsItem( 'content.results.above' ); } /** * @return string */ public function getBelowResultsContent() { return $this->getSettingsItem( 'content.results.below' ); } /** * @return string */ public function getHiddenResultsContent() { return apply_filters( 'totalpoll/filters/poll/results-hidden/message', $this->hasVoted() ? $this->getSettingsItem( 'results.message' ) : '', $this ); } /** * @return string */ public function getTemplateId() { return $this->getSettingsItem( 'design.template', 'basic-template' ); } /** * @return string */ public function getPresetUid() { return $this->getSettingsItem( 'presetUid', md5( $this->getId() ) ); } /** * Edit link in WordPress dashboard. * * @return string */ public function getAdminEditLink() { return admin_url( "post.php?post={$this->getId()}&action=edit" ); } /** * @param string $format * * @return string|void */ public function getAdminExportAsLink( $format = 'csv' ) { $url = sprintf( 'admin-ajax.php?action=%s&poll=%d&format=%s', 'totalpoll_insights_download', $this->getId(), strtolower( (string) $format ) ); return admin_url( $url ); } /** * Set/Override settings item value. * * @param $needle * @param $value * * @return void */ public function setSettingsItem( $needle, $value ) { $this->settings = Arrays::setDotNotation( $this->settings, $needle, $value ); } /** * Set current screen. * * @param $screen string Screen name. * * @return $this * @since 1.0.0 */ public function setScreen( $screen ) { $this->screen = esc_html( (string) $screen ); return $this; } /** * Set action. * * @param string $action */ public function setAction( $action ) { $this->action = sanitize_text_field( (string) $action ); } /** * Get received choices. * * @return array */ public function getReceivedChoices() { $choices = []; foreach ( $this->choicesMap as $choiceId => $questionUid ) : $choice = $this->questions[ $questionUid ]['choices'][ $choiceId ]; if ( ! empty( $choice['receivedVotes'] ) ) : $choices[ $choiceId ] = $choice; endif; endforeach; return $choices; } public function getReceivedQuestions() { $questions = []; foreach ( $this->choicesMap as $choiceId => $questionUid ) : $choice = $this->questions[ $questionUid ]['choices'][ $choiceId ]; if ( ! empty( $choice['receivedVotes'] ) ) : $questions[ $questionUid ] = true; endif; endforeach; return array_keys( $questions ); } /** * Set form. * * @param Form $form * * @return Form Form object * @since 1.0.0 */ public function setForm( Form $form ) { return $this->form = $form; } /** * Set an error. * * @param \WP_Error $error * * @since 1.0.0 */ public function setError( $error ) { $this->error = $error; } /** * @return bool */ public function hasError() { return ! empty( $this->error ); } /** * @return bool */ public function hasVoted() { /** * Filters whether current user has voted before. * * @param bool $hasVoted Whether current user has voted or not. * @param ModelContract $poll Poll model object. * * @return array * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/has-voted', is_wp_error( $this->getRestrictions() ->check() ) || $this->getRestrictions() ->isApplied() || did_action( 'totalpoll/actions/after/poll/command/vote' ), $this ); } /** * @return bool */ public function hasWelcomeContent() { return ! empty( $this->settings['content']['welcome']['content'] ); } /** * @return bool */ public function hasThankyouContent() { return ! empty( $this->settings['content']['thankyou']['content'] ); } /** * @return bool */ public function hasAboveVoteContent() { return ! empty( $this->settings['content']['vote']['above'] ); } /** * @return bool */ public function hasBelowVoteContent() { return ! empty( $this->settings['content']['vote']['below'] ); } /** * @return bool */ public function hasAboveResultsContent() { return ! empty( $this->settings['content']['results']['above'] ); } /** * @return bool */ public function hasBelowResultsContent() { return ! empty( $this->settings['content']['results']['below'] ); } /** * Is poll accepting votes. * * @return bool */ public function isAcceptingVotes() { $limited = $this->getLimitations() ->check(); if ( is_wp_error( $limited ) ) : $this->error = $limited; else : $restricted = $this->getRestrictions() ->check(); if ( is_wp_error( $restricted ) ) : $this->error = $restricted; endif; endif; /** * Filters whether the poll is accepting new votes or not. * * @param bool $acceptVotes True when new votes are accepted otherwise false. * @param ModelContract $poll Poll model object. * * @return bool * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/accept-vote', ! is_wp_error( $this->error ), $this ); } /** * Is poll results hidden. * * @return bool */ public function isResultsHidden() { $visibility = $this->getSettingsItem( 'results.visibility' ); $hidden = $visibility === 'voters' && !$this->hasVoted(); /** * Filters the poll results visibility. * * @param bool $hidden True when hidden otherwise false. * @param ModelContract $poll Poll model object. * * @return bool * @since 4.0.0 */ return apply_filters( 'totalpoll/filters/poll/results-hidden', $hidden, $this ); } /** * @return bool */ public function isPaginated() { return ! empty( $this->choicesPerPage ); } /** * @param $choiceUid * @param int $by * * @return bool|null */ public function incrementChoiceVotes( $choiceUid, $by = 1 ) { if ( ! isset( $this->choicesMap[ $choiceUid ] ) ) : return null; endif; $this->questions[ $this->choicesMap[ $choiceUid ] ]['votes'] += $by; $this->questions[ $this->choicesMap[ $choiceUid ] ]['choices'][ $choiceUid ]['votes'] += $by; $this->questions[ $this->choicesMap[ $choiceUid ] ]['choices'][ $choiceUid ]['receivedVotes'] += $by; $this->questions[ $this->choicesMap[ $choiceUid ] ]['receivedVotes'] += $by; return true; } /** * @return bool */ public function isPasswordProtected() { return post_password_required( $this->pollPost ); } /** * @return bool */ public function isMigrated() { return get_post_meta( $this->id, '_migrated', true ) || ! get_post_meta( $this->id, '_preset_id', true ); } /* * Load and apply user-provided expressions. * * @return void */ public function applyPollExpressions() { $locale = get_locale(); $localeFallback = substr( $locale, 0, 2 ); if ( ! is_admin() || Misc::isDoingAjax() ) : // Get expressions from the model // Check if there are expressions using the full locale (i.e. en_US, ar_AR, etc..) // If not, we fall back to short locale (en, ar, etc..), otherwise an empty array $expressions = Arrays::getDotNotation( $this->settings, "expressions.{$locale}", Arrays::getDotNotation( $this->settings, "expressions.{$localeFallback}", [] ) ); if ( ! empty( $expressions ) && ! empty( $GLOBALS['l10n']['totalpoll'] ) ): $domain = $GLOBALS['l10n']['totalpoll']; self::$originalExpressions = unserialize( serialize( $domain ) ); foreach ( $expressions as $expression => $expressionContent ) : if ( empty( $expressionContent['translations'] ) || empty( $expressionContent['translations'][0] ) ) : continue; endif; if ( empty( $domain->entries[ $expression ] ) ) : $entry = new \Translation_Entry( [ 'singular' => $expression, 'translations' => $expressionContent['translations'], ] ); $domain->add_entry( $entry ); else : $domain->entries[ $expression ]->translations = $expressionContent['translations']; endif; endforeach; endif; endif; } /* * Restore global poll expressions to avoid affecting other polls. * * @return void */ protected function restorePollExpressions() { if ( self::$originalExpressions ): $GLOBALS['l10n']['totalpoll'] = self::$originalExpressions; endif; } /** * Render poll. * * @return string * @since 1.0.0 */ public function render() { if ( $this->isPasswordProtected() ) : return ''; endif; $this->applyPollExpressions(); $renderedPoll = TotalPoll( 'polls.renderer', [ $this ] )->render(); $this->restorePollExpressions(); return $renderedPoll; } /** * JSON representation of poll. * * @return array * @since 1.0.0 */ #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); } /** * Get the instance as an array. * * @return array */ public function toArray() { $poll = [ 'id' => $this->getId(), 'title' => $this->getTitle(), 'permalink' => $this->getPermalink(), 'questions' => $this->getQuestions(), 'fields' => $this->getFields(), 'receivedVotes' => $this->getReceivedChoices(), ]; if ( is_admin() ) : $poll['admin'] = [ 'editLink' => $this->getAdminEditLink(), ]; endif; return $poll; } /** * @param mixed $offset * * @return bool */ #[\ReturnTypeWillChange] public function offsetExists( $offset ) { return isset( $this->{$offset} ); } /** * @param mixed $offset * * @return mixed */ #[\ReturnTypeWillChange] public function offsetGet( $offset ) { return isset( $this->{$offset} ) ? $this->{$offset} : null; } /** * @param mixed $offset * @param mixed $value */ #[\ReturnTypeWillChange] public function offsetSet( $offset, $value ) { } /** * @param mixed $offset */ #[\ReturnTypeWillChange] public function offsetUnset( $offset ) { } /** * Order by votes. * * @param $current * @param $next * * @return int * @since 1.0.0 */ private function orderByVotes( $current, $next ) { if ( $current['votes'] === $next['votes'] ) : return 0; elseif ( $current['votes'] < $next['votes'] ) : return - 1; else : return 1; endif; } /** * Order by label. * * @param $current * @param $next * * @return int * @since 1.0.0 */ private function orderByLabel( $current, $next ) { return 0; } /** * Shuffle. * * @param $current * @param $next * * @return int * @since 1.0.0 */ private function orderByRandom( $current, $next ) { return 0; } /** * Order by original position. * * @param $current * @param $next * * @return int * @since 1.0.0 */ private function orderByPosition( $current, $next ) { if ( $current['index'] === $next['index'] ) : return 0; elseif ( $current['index'] < $next['index'] ) : return - 1; else : return 1; endif; } /** * @param array $question * @param array $choices * * @return mixed|void */ public function addQuestion( $question, $choices = [] ) { // TODO: Implement addQuestion() method. } /** * @param array $choice * @param string $questionUid * @param bool $persistent * * @return array|bool */ public function addChoice( $choice, $questionUid, $persistent = true ) { $question = $this->getQuestion( $questionUid ); $choice = Arrays::parse( $choice, [ 'uid' => 'custom-' . Misc::generateUid(), 'type' => 'text', 'label' => '', 'visibility' => true, 'votes' => 0, 'votesOverride' => 0, ] ); if ( $question ) : if ( $persistent ) : $this->settings['questions'][ $question['index'] ]['choices'][] = $choice; endif; // Add to choices map $this->choicesMap[ $choice['uid'] ] = $question['uid']; $choice['index'] = count( $this->settings['questions'][ $question['index'] ]['choices'] ) - 1; $choice['questionUid'] = $question['uid']; $choice['receivedVotes'] = 0; // Cumulative of votes for question $this->questions[ $question['uid'] ]['votes'] += $choice['votes']; $this->totalVotes += $choice['votes']; $this->questions[ $question['uid'] ]['choices'][ $choice['uid'] ] = $choice; else : return false; endif; return $choice; } /** * @inheritDoc */ public function save() { // Remove WP filters if ( function_exists( 'wp_remove_targeted_link_rel_filters' ) ) { wp_remove_targeted_link_rel_filters(); } return ! is_wp_error( wp_update_post( [ 'ID' => $this->getId(), 'post_content' => wp_slash( json_encode( $this->settings, JSON_UNESCAPED_SLASHES ) ), ] ) ); } /** * @inheritDoc */ public function refreshUid() { $this->setSettingsItem( 'uid', Misc::generateUid() ); return $this->save(); } /** * @param $criteria * * @return array */ public function getQuestionsWhere( $criteria ) { return array_filter( $this->questions, function ( $question ) use ( $criteria ) { foreach ( $criteria as $key => $value ) : if ( ! isset( $question[ $key ] ) || $question[ $key ] != $value ) : return false; endif; endforeach; return true; } ); } /** * @param $criteria * * @return array */ public function getChoicesWhere( $criteria ) { return array_filter( $this->choicesMap, function ( $question ) use ( $criteria ) { foreach ( $criteria as $key => $value ) : if ( ! isset( $question[ $key ] ) || $question[ $key ] != $value ) : return false; endif; endforeach; return true; } ); } /** * Set question. * * @param string $questionUid * @param array $override * @param bool $persistent * * @return bool */ public function setQuestion( $questionUid, $override = [], $persistent = true ) { $question = $this->getQuestion( $questionUid ); if ( $question ) : $this->questions[ $question['uid'] ] = Arrays::parse( $override, $question ); if ( $persistent ) : $this->settings['questions'][ $question['index'] ] = Arrays::parse( $override, $this->settings['questions'][ $question['index'] ] ); endif; return true; endif; return false; } /** * Set choice. * * @param string $choiceUid * @param array $override * @param bool $persistent * * @return bool */ public function setChoice( $choiceUid, $override = [], $persistent = true ) { $choice = $this->getChoice( $choiceUid ); if ( $choice ) : $question = $this->getQuestion( $choice['questionUid'] ); $this->questions[ $question['uid'] ]['choices'][ $choice['uid'] ] = Arrays::parse( $override, $choice ); if ( $persistent ) : $this->settings['questions'][ $question['index'] ]['choices'][ $choice['index'] ] = Arrays::parse( $override, $this->settings['questions'][ $question['index'] ]['choices'][ $choice['index'] ] ); endif; return true; endif; return false; } /** * Remove question. * * @param string $questionUid * @param bool $persistent * * @return bool */ public function removeQuestion( $questionUid, $persistent = true ) { $question = $this->getQuestion( $questionUid ); if ( $question ) : foreach ( $question['choices'] as $choiceUid => $choice ) : $this->removeChoice( $choiceUid, $persistent ); endforeach; if ( $persistent ) : unset( $this->settings['questions'][ $question['index'] ] ); endif; return true; endif; return false; } /** * Remove choice. * * @param string $choiceUid * @param bool $persistent * * @return bool */ public function removeChoice( $choiceUid, $persistent = true ) { $choice = $this->getChoice( $choiceUid ); if ( $choice ) : $question = $this->getQuestion( $choice['questionUid'] ); unset( $this->questions[ $question['uid'] ]['choices'][ $choice['uid'] ], $this->choicesMap[ $choice['uid'] ] ); if ( $persistent ) : unset( $this->settings['questions'][ $question['index'] ]['choices'][ $choice['index'] ] ); endif; return true; endif; return false; } /** * @param string $screen * * @return bool */ public function isScreen( $screen ) { return $this->screen === $screen; } /** * @return bool */ public function isWelcomeScreen() { return $this->isScreen( self::WELCOME_SCREEN ); } /** * @return bool */ public function isVoteScreen() { return $this->isScreen( self::VOTE_ACTION ); } /** * @return bool */ public function isThankYouScreen() { return $this->isScreen( self::THANKYOU_SCREEN ); } /** * @return bool */ public function isResultsScreen() { return $this->isScreen( self::RESULTS_SCREEN ); } /** * @return array */ public function getViewVariables() { $variables = [ 'totalVotes' => number_format_i18n( $this->getTotalVotes() ), 'totalVotesWithLabel' => sprintf( _n( '%s vote', '%s votes', $this->getTotalVotes(), 'totalpoll' ), number_format_i18n( $this->getTotalVotes() ) ), ]; $questions = $this->getQuestions(); $questionIndex = 0; foreach ( $questions as $question ) : $questionIndex ++; $votes = $this->getQuestionVotes( $question['uid'] ); $variables["totalVotesForQuestion{$questionIndex}"] = number_format_i18n( $votes ); $variables["totalVotesWithLabelForQuestion{$questionIndex}"] = sprintf( _n( '%s vote', '%s votes', $votes, 'totalpoll' ), number_format_i18n( $votes ) ); endforeach; return apply_filters( 'totalpoll/filters/poll/view-variables', $variables, $this ); } /** * Check if choice was checked. * * @param $choiceUid * * @return bool */ public function isChoiceChecked( $choiceUid ) { return !empty($this->getChoice($choiceUid)['receivedVotes']); } }