<?php
/**
* This file implements the Poll class.
*
* This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
* See also {@link https://github.com/b2evolution/b2evolution}.
*
* @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
*
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}.
*
* @license http://b2evolution.net/about/license.html GNU General Public License (GPL)
*
* @package evocore
*/
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
load_class( '_core/model/dataobjects/_dataobject.class.php', 'DataObject' );
/**
* Poll Class
*
* @package evocore
*/
class Poll extends DataObject
{
var $owner_user_ID;
var $question_text;
var $max_answers;
/**
* @var array PollOption objects
*/
var $poll_options;
/**
* @var array with keys:
* - 'votes' - a number of votes of this Poll from all voters,
* - 'voters' - a number of users which voted on at least one option of this poll.
*/
var $vote_nums = NULL;
/**
* Constructor
*
* @param object table Database row
*/
function __construct( $db_row = NULL )
{
// Call parent constructor:
parent::__construct( 'T_polls__question', 'pqst_', 'pqst_ID' );
if( $db_row != NULL )
{ // Get poll data from DB:
$this->ID = $db_row->pqst_ID;
$this->owner_user_ID = $db_row->pqst_owner_user_ID;
$this->question_text = $db_row->pqst_question_text;
$this->max_answers = $db_row->pqst_max_answers;
}
else
{ // Set default poll data for new poll:
if( is_logged_in() )
{
global $current_User;
$this->set( 'owner_user_ID', $current_User->ID );
$this->set( 'max_answers', 1 );
}
}
}
/**
* Get delete cascade settings
*
* @return array
*/
static function get_delete_cascades()
{
return array(
array( 'table' => 'T_polls__option', 'fk' => 'popt_pqst_ID', 'msg' => T_('%d poll options') ),
array( 'table' => 'T_polls__answer', 'fk' => 'pans_pqst_ID', 'msg' => T_('%d poll answers') ),
);
}
/**
* Load data from Request form fields.
*
* @return boolean true if loaded data seems valid.
*/
function load_from_Request()
{
global $current_User;
// Owner:
if( check_user_perm( 'polls', 'edit' ) )
{ // Update the owner if current user has a permission to edit all polls:
$pqst_owner_login = param( 'pqst_owner_login', 'string', NULL );
param_check_not_empty( 'pqst_owner_login', T_('Please enter the owner\'s login.') );
if( ! empty( $pqst_owner_login ) )
{ // If the login is entered:
$UserCache = & get_UserCache();
$owner_User = & $UserCache->get_by_login( $pqst_owner_login );
if( empty( $owner_User ) )
{ // Wrong entered login:
param_error( 'pqst_owner_login', sprintf( T_('User «%s» does not exist!'), $pqst_owner_login ) );
}
else
{ // Set new login:
$this->set( 'owner_user_ID', $owner_User->ID );
$this->owner_User = & $owner_User;
}
}
}
elseif( empty( $this->ID ) )
{ // Set owner user ID on creating new poll:
$this->set( 'owner_user_ID', $current_User->ID );
$this->owner_User = & $current_User;
}
// Question:
param( 'pqst_question_text', 'string', false );
param_check_not_empty( 'pqst_question_text', T_('Please enter the text for the poll\'s question.') );
$this->set_from_Request( 'question_text' );
// Maximum number of answers:
$pqst_max_answers = param( 'pqst_max_answers', 'integer', 1 );
if( empty( $this->ID ) )
{
$options = array();
$answer_options = param( 'answer_options', 'array:string', array() );
foreach( $answer_options as $answer_option_text )
{
if( empty( $answer_option_text ) )
{ // Skip empty option:
continue;
}
$options[] = $answer_option_text;
}
}
else
{
$options = $this->get_poll_options();
}
if( $pqst_max_answers > count( $options ) )
{
param_error( 'pqst_max_answers', T_('Maximum allowed answers must not exceed total number of answer options.') );
}
else
{
$this->set_from_Request( 'max_answers' );
}
return ! param_errors_detected();
}
/**
* Insert object into DB based on previously recorded changes.
*
* @return boolean true on success
*/
function dbinsert()
{
global $DB;
$DB->begin( 'SERIALIZABLE' );
if( parent::dbinsert() )
{ // The poll was inserted successful:
// Insert the entered answer options:
$answer_options = param( 'answer_options', 'array:string', array() );
$answer_option_order = 1;
foreach( $answer_options as $answer_option_text )
{
if( empty( $answer_option_text ) )
{ // Skip empty option:
continue;
}
$new_PollOption = new PollOption();
$new_PollOption->set( 'pqst_ID', $this->ID );
$new_PollOption->set( 'option_text', $answer_option_text );
$new_PollOption->set( 'order', $answer_option_order );
if( ! $new_PollOption->dbinsert() )
{ // Rollback if the poll option could not be inserted:
$DB->rollback();
return false;
}
$answer_option_order++;
}
$DB->commit();
return true;
}
// Could not insert the poll object:
$DB->rollback();
return false;
}
/**
* Get name of the question. It is a string as first 200 chars of the question text:
*
* @return string
*/
function get_name()
{
return strmaxlen( $this->get( 'question_text' ), 200 );
}
/**
* Get user object of this poll owner
*
* @return object User
*/
function & get_owner_User()
{
if( ! isset( $this->owner_User ) )
{ // Get the owner User only first time:
$UserCache = & get_UserCache();
$this->owner_User = & $UserCache->get_by_ID( $this->owner_user_ID );
}
return $this->owner_User;
}
/**
* Get numbers of votes and voters for this Poll
*
* @param string|NULL NULL - to return an array of all possible numbers,
* 'votes' - to return a number of votes of this Poll from all voters,
* 'voters' - to return a number of users which voted on at least one option of this poll.
* @return array|integer Depends on param $type
*/
function get_vote_nums( $type = NULL )
{
global $DB;
if( $this->vote_nums === NULL )
{ // Get from DB once and store in cache array:
$nums_SQL = new SQL( 'Get a count of votes and voters for the edited poll #'.$this->ID );
$nums_SQL->SELECT( 'COUNT( pans_pqst_ID ) AS votes, COUNT( DISTINCT pans_user_ID ) AS voters' );
$nums_SQL->FROM( 'T_polls__answer' );
$nums_SQL->WHERE( 'pans_pqst_ID = '.$this->ID );
$this->vote_nums = $DB->get_row( $nums_SQL, ARRAY_A );
}
if( $type === NULL )
{ // Get all numbers:
return $this->vote_nums;
}
else
{ // Get a specific number:
if( ! isset( $this->vote_nums[ $type ] ) )
{
debug_die( 'Wrong param type "'.$type.'" for Poll::get_vote_nums()' );
}
return $this->vote_nums[ $type ];
}
}
/**
* Get all options of this poll
*
* @return array PollOption objects
*/
function get_poll_options()
{
if( empty( $this->ID ) )
{ // New creating poll doesn't have the options, Return empty array:
return array();
}
if( $this->poll_options === NULL )
{ // Get options from DB only first time, and store them in cache array:
global $DB;
// Get numbers of votes and voters for the edited poll:
$poll_voters_num = $this->get_vote_nums( 'voters' );
if( $poll_voters_num == 0 )
{ // To don't devide by zero
$poll_voters_num = 1;
}
$SQL = new SQL( 'Get all options of this poll' );
$SQL->SELECT( 'popt_ID AS ID, popt_pqst_ID AS pqst_ID, popt_option_text AS option_text,' );
//$SQL->SELECT_add( 'COUNT( * ) AS answers_count,' );
$SQL->SELECT_add( 'ROUND( COUNT( pans_pqst_ID ) / '.$poll_voters_num.' * 100 ) AS percent' );
$SQL->FROM( 'T_polls__option' );
$SQL->FROM_add( 'LEFT JOIN T_polls__answer ON pans_popt_ID = popt_ID' );
$SQL->WHERE( 'popt_pqst_ID = '.$this->ID );
$SQL->GROUP_BY( 'popt_ID' );
$SQL->ORDER_BY( 'popt_order' );
$this->poll_options = $DB->get_results( $SQL );
}
return $this->poll_options;
}
/**
* Get max percent of poll options
*
* @return integer Max percent
*/
function get_max_poll_options_percent()
{
// Get all poll options:
$poll_options = $this->get_poll_options();
$max_percent = 0;
foreach( $poll_options as $poll_option )
{ // Find max percent in all poll options:
if( $poll_option->percent > $max_percent )
{
$max_percent = $poll_option->percent;
}
}
return $max_percent;
}
/**
* Get votes of the user
*
* @param integer User ID of specific user
* @return array|boolean Poll option IDs OR FALSE if current user didn't vote on this poll yet
*/
function get_user_vote( $user_ID = NULL )
{
global $current_User, $DB;
if( ! empty( $user_ID ) )
{
$UserCache = & get_UserCache();
$user = & $UserCache->get_by_ID( $user_ID, false, false );
}
elseif( is_logged_in() )
{
$user = $current_User;
}
if( empty( $user ) )
{
return false;
}
// Get answer of user for this poll:
$poll_option_SQL = new SQL( 'Get answer of current user on the poll question' );
$poll_option_SQL->SELECT( 'pans_popt_ID' );
$poll_option_SQL->FROM( 'T_polls__answer' );
$poll_option_SQL->WHERE( 'pans_pqst_ID = '.$this->ID );
$poll_option_SQL->WHERE_and( 'pans_user_ID = '.$user->ID );
$poll_option_IDs = $DB->get_col( $poll_option_SQL );
return empty( $poll_option_IDs ) ? false : $poll_option_IDs;
}
/**
* Clears all votes of the user
*
* @param integer User ID of specific user
*/
function clear_user_votes( $user_ID = NULL )
{
global $current_User, $DB;
if( ! empty( $user_ID ) )
{
$UserCache = & get_UserCache();
$user = & $UserCache->get_by_ID( $user_ID, false, false );
}
elseif( is_logged_in() )
{
$user = $current_User;
}
if( empty( $user ) )
{
return false;
}
$DB->query( 'DELETE FROM T_polls__answer WHERE pans_user_ID = '.$user->ID.' AND pans_pqst_ID = '.$this->ID );
return true;
}
}
?>