r92150 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r92149‎ | r92150 | r92151 >
Date:11:28, 14 July 2011
Author:questpc
Status:deferred
Tags:
Comment:
Preliminary support for random questions
Modified paths:
  • /trunk/extensions/QPoll/ctrl/qp_abstractpoll.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/qp_abstractquestion.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/qp_poll.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/qp_pollstats.php (modified) (history)
  • /trunk/extensions/QPoll/qp_pollstore.php (modified) (history)
  • /trunk/extensions/QPoll/qp_question_collection.php (added) (history)
  • /trunk/extensions/QPoll/qp_results.php (modified) (history)
  • /trunk/extensions/QPoll/qp_user.php (modified) (history)
  • /trunk/extensions/QPoll/tables/qpoll_random_questions.src (added) (history)
  • /trunk/extensions/QPoll/tables/qpoll_trunk.sql (modified) (history)
  • /trunk/extensions/QPoll/view/qp_pollstatsview.php (modified) (history)
  • /trunk/extensions/QPoll/view/qp_pollview.php (modified) (history)
  • /trunk/extensions/QPoll/view/qp_questionview.php (modified) (history)

Diff [purge]

Index: trunk/extensions/QPoll/qp_results.php
@@ -37,8 +37,25 @@
3838 die( "This file is part of the QPoll extension. It is not a valid entry point.\n" );
3939 }
4040
41 -class PollResults extends SpecialPage {
 41+class qp_SpecialPage extends SpecialPage {
4242
 43+ static $linker = null;
 44+
 45+ public function __construct( $name = '', $restriction = '', $listed = true, $function = false, $file = 'default', $includable = false ) {
 46+ if ( self::$linker == null ) {
 47+ self::$linker = new Linker();
 48+ }
 49+ parent::__construct( $name, $restriction, $listed, $function, $file, $includable );
 50+ }
 51+
 52+ function qpLink( $target, $text = null, $customAttribs = array(), $query = array(), $options = array() ) {
 53+ return self::$linker->link( $target, $text, $customAttribs, $query, $options );
 54+ }
 55+
 56+}
 57+
 58+class PollResults extends qp_SpecialPage {
 59+
4360 public function __construct() {
4461 parent::__construct( 'PollResults', 'read' );
4562 # for MW 1.15 (still being used by many customers)
@@ -50,7 +67,6 @@
5168
5269 static $accessPermissions = array( 'read', 'pollresults' );
5370
54 - static $skin = null;
5571 static $UsersLink = "";
5672 static $PollsLink = "";
5773
@@ -92,14 +108,11 @@
93109 # MW < 1.17
94110 $wgOut->addExtensionStyle( qp_Setup::$ScriptPath . '/clientside/qp_results.css' );
95111 }
96 - if ( self::$skin == null ) {
97 - self::$skin = $wgUser->getSkin();
98 - }
99112 if ( self::$UsersLink == "" ) {
100 - self::$UsersLink = self::$skin->link( $this->getTitle(), wfMsg( 'qp_users_list' ), array( "style"=>"font-weight:bold;" ), array( 'action'=>'users' ) );
 113+ self::$UsersLink = $this->qpLink( $this->getTitle(), wfMsg( 'qp_users_list' ), array( "style"=>"font-weight:bold;" ), array( 'action'=>'users' ) );
101114 }
102115 if ( self::$PollsLink == "" ) {
103 - self::$PollsLink = self::$skin->link( $this->getTitle(), wfMsg( 'qp_polls_list' ), array( "style"=>"font-weight:bold;" ) );
 116+ self::$PollsLink = $this->qpLink( $this->getTitle(), wfMsg( 'qp_polls_list' ), array( "style"=>"font-weight:bold;" ) );
104117 }
105118 $wgOut->addHTML( '<div class="qpoll">' );
106119 $output = "";
@@ -192,6 +205,20 @@
193206 }
194207
195208 /**
 209+ * check for existence of multiple tables in the selected database
 210+ */
 211+ private function tablesExists( $tableset ) {
 212+ $db = & wfGetDB( DB_SLAVE );
 213+ $tablesFound = 0;
 214+ foreach ( $tableset as &$table ) {
 215+ if ( $db->tableExists( $table ) ) {
 216+ $tablesFound++;
 217+ }
 218+ }
 219+ return $tablesFound;
 220+ }
 221+
 222+ /**
196223 * check for the existence of multiple fields in the selected database table
197224 * @param $table table name
198225 * @param $fields field names
@@ -216,43 +243,50 @@
217244 private function checkTables() {
218245 $db = & wfGetDB( DB_SLAVE );
219246 $sql_tables = array(
220 - "qp_poll_desc",
221 - "qp_question_desc",
222 - "qp_question_categories",
223 - "qp_question_proposals",
224 - "qp_question_answers",
225 - "qp_users_polls",
226 - "qp_users");
 247+ 'qpoll_0.7.0.src' => array(
 248+ 'qp_poll_desc',
 249+ 'qp_question_desc',
 250+ 'qp_question_categories',
 251+ 'qp_question_proposals',
 252+ 'qp_question_answers',
 253+ 'qp_users_polls',
 254+ 'qp_users'
 255+ ),
 256+ 'qpoll_random_questions.src' => array(
 257+ 'qp_random_questions'
 258+ )
 259+ );
227260 $addFields = array(
228261 'qpoll_interpretation.src' => array(
229262 'qp_poll_desc' => array( 'interpretation_namespace', 'interpretation_title' ),
230263 'qp_users_polls' => array( 'attempts', 'short_interpretation', 'long_interpretation' )
231264 )
232265 );
233 - // check whether the tables were initialized
234 - $tablesFound = 0;
 266+ /* check whether the tables were initialized */
235267 $result = true;
236 - foreach ( $sql_tables as $table ) {
237 - if ( $db->tableExists( $table ) ) {
238 - $tablesFound++;
 268+ $tablesInit = array();
 269+ foreach ( $sql_tables as $sourceFile => &$tableset ) {
 270+ $tablesFound = $this->tablesExists( $tableset );
 271+ if ( $tablesFound == 0 ) {
 272+ $tablesInit = array_merge( $tablesInit, $tableset );
 273+ # no tables were found, initialize the DB completely with minimal version
 274+ if ( ( $r = $db->sourceFile( qp_Setup::$ExtDir . "/tables/{$sourceFile}" ) ) !== true ) {
 275+ return $r;
 276+ }
 277+ } elseif ( $tablesFound != count( $tableset ) ) {
 278+ # some tables are missing, serious DB error
 279+ return "Some of the extension database tables are missing.<br />Please restore from backup or drop the remaining extension tables, then reload this page.";
239280 }
240281 }
241 - if ( $tablesFound != count( $sql_tables ) ) {
242 - # some tables are missing, serious DB error
243 - return "Some of the extension database tables are missing.<br />Please restore from backup or drop the remaining extension tables, then reload this page.";
244 - }
245 - if ( $tablesFound == 0 ) {
246 - # no tables were found, initialize the DB completely with minimal version
247 - if ( ( $r = $db->sourceFile( qp_Setup::$ExtDir . '/tables/qpoll_0.7.0.src' ) ) !== true ) {
248 - return $r;
249 - }
250 - }
251282 /* start of SQL updates */
252 - $scriptsToRun = array();
 283+ $scriptsToRun = $tablesUpgrade = array();
253284 foreach( $addFields as $script => &$table_list ) {
254285 foreach( $table_list as $table => &$fields_list ) {
255286 if ( !$this->fieldsExists( $table, $fields_list ) ) {
256287 $scriptsToRun[$script] = true;
 288+ if ( array_search( $table, $tablesUpgrade ) === false ) {
 289+ array_push( $tablesUpgrade, $table );
 290+ }
257291 }
258292 }
259293 }
@@ -262,14 +296,17 @@
263297 }
264298 }
265299 /* end of SQL updates */
266 - if ( $tablesFound == 0 ) {
267 - $result = 'Tables were initialized.';
 300+ if ( count( $tablesInit ) > 0 ) {
 301+ $result = 'The following table(s) were initialized: ' . implode( ', ', $tablesInit ) . '<br />';
268302 }
269303 if ( count( $scriptsToRun ) > 0 ) {
270 - $result = 'Tables were upgraded.';
 304+ if ( !is_string( $result ) ) {
 305+ $result = '';
 306+ }
 307+ $result = 'The following table(s) were upgraded:' . implode( ', ', $tablesUpgrade ) . '<br />';
271308 }
272309 if ( is_string( $result ) ) {
273 - $result .= '<br />Please <a href="#" onclick="window.location.reload()">reload</a> this page to view future page edits.';
 310+ $result .= 'Please <a href="#" onclick="window.location.reload()">reload</a> this page to view future page edits.';
274311 }
275312 return $result;
276313 }
@@ -296,29 +333,35 @@
297334 }
298335
299336 private function showUserVote( $pid, $uid ) {
300 - $output = "";
301 - if ( $pid !== null && $uid !== null ) {
302 - $pollStore = new qp_PollStore( array( 'from'=>'pid', 'pid'=> $pid ) );
303 - if ( $pollStore->pid !== null ) {
304 - $pollStore->loadQuestions();
305 - $userName = $pollStore->getUserName( $uid );
306 - if ( $userName !== false ) {
307 - $userTitle = Title::makeTitleSafe( NS_USER, $userName );
308 - $user_link = self::$skin->link( $userTitle, $userName );
309 - $pollStore->setLastUser( $userName, false );
310 - if ( $pollStore->loadUserVote() ) {
311 - $poll_title = $pollStore->getTitle();
312 - # 'parentheses' is unavailable in MediaWiki 1.15.x
313 - $poll_link = self::$skin->link( $poll_title, $poll_title->getPrefixedText() . wfMsg( 'word-separator' ) . wfMsg( 'qp_parentheses', $pollStore->mPollId ) );
314 - $output .= wfMsg( 'qp_browse_to_user', $user_link ) . "<br />\n";
315 - $output .= wfMsg( 'qp_browse_to_poll', $poll_link ) . "<br />\n";
316 - $output .= $this->showAnswerHeader( $pollStore );
317 - foreach ( $pollStore->Questions as $qkey => &$qdata ) {
318 - $output .= "<br />\n<b>" . $qkey . ".</b> " . qp_Setup::entities( $qdata->CommonQuestion ) . "<br />\n";
319 - $output .= $this->displayUserQuestionVote( $qdata );
320 - }
321 - }
322 - }
 337+ if ( $pid === null || $uid === null ) {
 338+ return '';
 339+ }
 340+ $pollStore = new qp_PollStore( array( 'from'=>'pid', 'pid'=> $pid ) );
 341+ if ( $pollStore->pid === null ) {
 342+ return '';
 343+ }
 344+ $pollStore->loadQuestions();
 345+ $userName = $pollStore->getUserName( $uid );
 346+ if ( $userName === false ) {
 347+ return '';
 348+ }
 349+ $pollStore->loadRandomQuestions( $userName );
 350+ $userTitle = Title::makeTitleSafe( NS_USER, $userName );
 351+ $user_link = $this->qpLink( $userTitle, $userName );
 352+ $pollStore->setLastUser( $userName, false );
 353+ if ( !$pollStore->loadUserVote() ) {
 354+ return '';
 355+ }
 356+ $poll_title = $pollStore->getTitle();
 357+ # 'parentheses' key is unavailable in MediaWiki 1.15.x
 358+ $poll_link = $this->qpLink( $poll_title, $poll_title->getPrefixedText() . wfMsg( 'word-separator' ) . wfMsg( 'qp_parentheses', $pollStore->mPollId ) );
 359+ $output = wfMsg( 'qp_browse_to_user', $user_link ) . "<br />\n";
 360+ $output .= wfMsg( 'qp_browse_to_poll', $poll_link ) . "<br />\n";
 361+ $output .= $this->showAnswerHeader( $pollStore );
 362+ foreach ( $pollStore->Questions as &$qdata ) {
 363+ if ( $pollStore->isUsedQuestion( $qdata->question_id ) ) {
 364+ $output .= "<br />\n<b>" . $qdata->question_id . ".</b> " . qp_Setup::entities( $qdata->CommonQuestion ) . "<br />\n";
 365+ $output .= $this->displayUserQuestionVote( $qdata );
323366 }
324367 }
325368 return $output;
@@ -380,10 +423,10 @@
381424 $pollStore->calculateStatistics();
382425 $poll_title = $pollStore->getTitle();
383426 # 'parentheses' is unavailable in 1.14.x
384 - $poll_link = self::$skin->link( $poll_title, $poll_title->getPrefixedText() . wfMsg( 'word-separator' ) . wfMsg( 'qp_parentheses', $pollStore->mPollId ) );
 427+ $poll_link = $this->qpLink( $poll_title, $poll_title->getPrefixedText() . wfMsg( 'word-separator' ) . wfMsg( 'qp_parentheses', $pollStore->mPollId ) );
385428 $output .= wfMsg( 'qp_browse_to_poll', $poll_link ) . "<br />\n";
386 - $output .= self::$skin->link( $this->getTitle(), wfMsg( 'qp_export_to_xls' ), array( "style"=>"font-weight:bold;" ), array( 'action'=>'stats_xls', 'id'=>$pid ) ) . "<br />\n";
387 - $output .= self::$skin->link( $this->getTitle(), wfMsg( 'qp_voices_to_xls' ), array( "style"=>"font-weight:bold;" ), array( 'action'=>'voices_xls', 'id'=>$pid ) ) . "<br />\n";
 429+ $output .= $this->qpLink( $this->getTitle(), wfMsg( 'qp_export_to_xls' ), array( "style"=>"font-weight:bold;" ), array( 'action'=>'stats_xls', 'id'=>$pid ) ) . "<br />\n";
 430+ $output .= $this->qpLink( $this->getTitle(), wfMsg( 'qp_voices_to_xls' ), array( "style"=>"font-weight:bold;" ), array( 'action'=>'voices_xls', 'id'=>$pid ) ) . "<br />\n";
388431 foreach ( $pollStore->Questions as $qkey => &$qdata ) {
389432 $output .= "<br />\n<b>" . $qkey . ".</b> " . qp_Setup::entities( $qdata->CommonQuestion ) . "<br />\n";
390433 $output .= $this->displayQuestionStats( $pid, $qdata );
@@ -626,7 +669,7 @@
627670 if ( $cell == 0.0 && $qdata->question_id !==null ) {
628671 $cell = array( 0=> $formatted_cell, "style"=>"color:gray" );
629672 } else {
630 - $cell = array( 0=>self::$skin->link( $current_title, $formatted_cell,
 673+ $cell = array( 0=>$this->qpLink( $current_title, $formatted_cell,
631674 array( "title"=>wfMsgExt( 'qp_votes_count', array( 'parsemag' ), $qdata->Votes[ $propkey ][ $catkey ] ) ),
632675 array( "action"=>"qpcusers", "id"=>$pid, "qid"=>$qdata->question_id, "pid"=>$propkey, "cid"=>$catkey ) ) );
633676 }
@@ -666,27 +709,20 @@
667710 * We do not extend QueryPage anymore because it is purposely made incompatible in 1.18+
668711 * thus, it is much safer to implement a larger subset of pager itself
669712 */
670 -abstract class qp_QueryPage extends SpecialPage {
 713+abstract class qp_QueryPage extends qp_SpecialPage {
671714
672 - static $skin = null;
673715 var $listoutput = false;
674716
675717 public function __construct() {
676 - global $wgUser;
677 - if ( self::$skin == null ) {
678 - self::$skin = $wgUser->getSkin();
679 - }
680718 parent::__construct( $this->queryPageName() );
681719 }
682720
683721 function doQuery( $offset, $limit, $shownavigation=true ) {
684 - global $wgUser, $wgOut, $wgLang, $wgContLang;
 722+ global $wgOut, $wgContLang;
685723
686724 $res = $this->getIntervalResults( $offset, $limit );
687725 $num = count( $res );
688726
689 - $sk = $wgUser->getSkin();
690 -
691727 if($shownavigation) {
692728 $wgOut->addHTML( $this->getPageHeader() );
693729
@@ -713,7 +749,7 @@
714750 $s[] = $this->openList( $offset );
715751
716752 foreach ($res as $r) {
717 - $format = $this->formatResult( $sk, $r );
 753+ $format = $this->formatResult( $r );
718754 if ( $format ) {
719755 $s[] = $this->listoutput ? $format : "<li>{$format}</li>\n";
720756 }
@@ -783,10 +819,10 @@
784820 $this->cmd = $cmd;
785821 if ( $cmd == 'users' ) {
786822 $this->order_by = 'count(pid) DESC, name ASC ';
787 - $this->different_order_by_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_order_by_username' ), array(), array( "action"=>"users_a" ) );
 823+ $this->different_order_by_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_order_by_username' ), array(), array( "action"=>"users_a" ) );
788824 } else {
789825 $this->order_by = 'name ';
790 - $this->different_order_by_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_order_by_polls_count' ), array(), array( "action"=>"users" ) );
 826+ $this->different_order_by_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_order_by_polls_count' ), array(), array( "action"=>"users" ) );
791827 }
792828 }
793829
@@ -809,16 +845,16 @@
810846 return $result;
811847 }
812848
813 - function formatResult( $skin, $result ) {
 849+ function formatResult( $result ) {
814850 global $wgLang, $wgContLang;
815851 $link = "";
816852 if ( $result !== null ) {
817853 $uid = intval( $result->uid );
818854 $userName = $result->username;
819855 $userTitle = Title::makeTitleSafe( NS_USER, $userName );
820 - $user_link = self::$skin->link( $userTitle, $userName );
821 - $user_polls_link = self::$skin->link( $this->getTitle(), wfMsgExt( 'qp_user_polls_link', array( 'parsemag' ), $result->pidcount, $userName ) , array(), array( "uid"=>$uid, "action"=>"upolls" ) );
822 - $user_missing_polls_link = self::$skin->link( $this->getTitle(), wfMsgExt( 'qp_user_missing_polls_link', 'parsemag', $userName ) , array(), array( "uid"=>$uid, "action"=>"nupolls" ) );
 856+ $user_link = $this->qpLink( $userTitle, $userName );
 857+ $user_polls_link = $this->qpLink( $this->getTitle(), wfMsgExt( 'qp_user_polls_link', array( 'parsemag' ), $result->pidcount, $userName ) , array(), array( "uid"=>$uid, "action"=>"upolls" ) );
 858+ $user_missing_polls_link = $this->qpLink( $this->getTitle(), wfMsgExt( 'qp_user_missing_polls_link', 'parsemag', $userName ) , array(), array( "uid"=>$uid, "action"=>"nupolls" ) );
823859 $link = $user_link . ': ' . $user_polls_link . ', ' . $user_missing_polls_link;
824860 }
825861 return $link;
@@ -866,7 +902,7 @@
867903 }
868904 if ( $userName !== false ) {
869905 $userTitle = Title::makeTitleSafe( NS_USER, $userName );
870 - $user_link = self::$skin->link( $userTitle, $userName );
 906+ $user_link = $this->qpLink( $userTitle, $userName );
871907 return PollResults::getPollsLink() . PollResults::getUsersLink() . '<div class="head">' . $user_link . ': ' . ( $this->inverse ? wfMsgExt( 'qp_user_missing_polls_link', 'parsemag', $userName ) : wfMsgExt( 'qp_user_polls_link', array( 'parsemag' ), $pidcount, $userName ) ) . ' ' . '</div>';
872908 }
873909 }
@@ -892,13 +928,13 @@
893929 return $result;
894930 }
895931
896 - function formatResult( $skin, $result ) {
 932+ function formatResult( $result ) {
897933 global $wgLang, $wgContLang;
898934 $poll_title = Title::makeTitle( $result->ns, $result->title, qp_AbstractPoll::s_getPollTitleFragment( $result->poll_id, '' ) );
899935 $pagename = qp_Setup::specialchars( $wgContLang->convert( $poll_title->getPrefixedText() ) );
900936 $pollname = qp_Setup::specialchars( $result->poll_id );
901 - $goto_link = self::$skin->link( $poll_title, wfMsg( 'qp_source_link' ) );
902 - $voice_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_voice_link' . ($this->inverse ? "_inv" : "") ), array(), array( "id"=>intval( $result->pid), "uid"=>$this->uid, "action"=>"uvote" ) );
 937+ $goto_link = $this->qpLink( $poll_title, wfMsg( 'qp_source_link' ) );
 938+ $voice_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_voice_link' . ($this->inverse ? "_inv" : "") ), array(), array( "id"=>intval( $result->pid), "uid"=>$this->uid, "action"=>"uvote" ) );
903939 $link = wfMsg( 'qp_results_line_qupl', $pagename, $pollname, $voice_link );
904940 return $link;
905941 }
@@ -933,15 +969,15 @@
934970 return $result;
935971 }
936972
937 - function formatResult( $skin, $result ) {
 973+ function formatResult( $result ) {
938974 global $wgLang, $wgContLang;
939975 $poll_title = Title::makeTitle( $result->ns, $result->title, qp_AbstractPoll::getPollTitleFragment( $result->poll_id, '' ) );
940976 $pagename = qp_Setup::specialchars( $wgContLang->convert( $poll_title->getPrefixedText() ) );
941977 $pollname = qp_Setup::specialchars( $result->poll_id );
942 - $goto_link = self::$skin->link( $poll_title, wfMsg( 'qp_source_link' ) );
943 - $voices_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_stats_link' ), array(), array( "id"=>intval( $result->pid), "action"=>"stats" ) );
944 - $users_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_users_link' ), array(), array( "id"=>intval( $result->pid), "action"=>"pulist" ) );
945 - $not_participated_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_not_participated_link' ), array(), array( "id"=>intval( $result->pid), "action"=>"npulist" ) );
 978+ $goto_link = $this->qpLink( $poll_title, wfMsg( 'qp_source_link' ) );
 979+ $voices_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_stats_link' ), array(), array( "id"=>intval( $result->pid), "action"=>"stats" ) );
 980+ $users_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_users_link' ), array(), array( "id"=>intval( $result->pid), "action"=>"pulist" ) );
 981+ $not_participated_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_not_participated_link' ), array(), array( "id"=>intval( $result->pid), "action"=>"npulist" ) );
946982 $link = wfMsg( 'qp_results_line_qpl', $pagename, $pollname, $goto_link, $voices_link, $users_link, $not_participated_link );
947983 return $link;
948984 }
@@ -979,7 +1015,7 @@
9801016 $poll_title = Title::makeTitle( intval( $row->ns ), $row->title, qp_AbstractPoll::getPollTitleFragment( $row->poll_id, '' ) );
9811017 $pagename = qp_Setup::specialchars( $wgContLang->convert( $poll_title->getPrefixedText() ) );
9821018 $pollname = qp_Setup::specialchars( $row->poll_id );
983 - $goto_link = self::$skin->link( $poll_title, wfMsg( 'qp_source_link' ) );
 1019+ $goto_link = $this->qpLink( $poll_title, wfMsg( 'qp_source_link' ) );
9841020 $spec = wfMsg( 'qp_header_line_qpul', wfMsg( $this->inverse ? 'qp_not_participated_link' : 'qp_users_link'), $pagename, $pollname );
9851021 $head[] = PollResults::getPollsLink();
9861022 $head[] = PollResults::getUsersLink();
@@ -1008,15 +1044,15 @@
10091045 return $result;
10101046 }
10111047
1012 - function formatResult( $skin, $result ) {
 1048+ function formatResult( $result ) {
10131049 global $wgLang, $wgContLang;
10141050 $link = "";
10151051 if ( $result !== null ) {
10161052 $uid = intval( $result->uid );
10171053 $userName = $result->username;
10181054 $userTitle = Title::makeTitleSafe( NS_USER, $userName );
1019 - $user_link = self::$skin->link( $userTitle, $userName );
1020 - $voice_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_voice_link' . ($this->inverse ? "_inv" : "") ), array(), array( "id"=>intval( $this->pid), "uid"=>$uid, "action"=>"uvote" ) );
 1055+ $user_link = $this->qpLink( $userTitle, $userName );
 1056+ $voice_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_voice_link' . ($this->inverse ? "_inv" : "") ), array(), array( "id"=>intval( $this->pid), "uid"=>$uid, "action"=>"uvote" ) );
10211057 $link = wfMsg( 'qp_results_line_qpul', $user_link, $voice_link );
10221058 }
10231059 return $link;
@@ -1072,7 +1108,7 @@
10731109 $poll_title = Title::makeTitle( intval( $this->ns ), $this->title, qp_AbstractPoll::getPollTitleFragment( $this->poll_id, '' ) );
10741110 $pagename = qp_Setup::specialchars( $wgContLang->convert( $poll_title->getPrefixedText() ) );
10751111 $pollname = qp_Setup::specialchars( $this->poll_id );
1076 - $goto_link = self::$skin->link( $poll_title, wfMsg( 'qp_source_link' ) );
 1112+ $goto_link = $this->qpLink( $poll_title, wfMsg( 'qp_source_link' ) );
10771113 $spec = wfMsg( 'qp_header_line_qpul', wfMsg( 'qp_users_link' ), $pagename, $pollname );
10781114 $head[] = PollResults::getPollsLink();
10791115 $head[] = PollResults::getUsersLink();
@@ -1126,15 +1162,15 @@
11271163 return $result;
11281164 }
11291165
1130 - function formatResult( $skin, $result ) {
 1166+ function formatResult( $result ) {
11311167 global $wgLang, $wgContLang;
11321168 $link = "";
11331169 if ( $result !== null ) {
11341170 $uid = intval( $result->uid );
11351171 $userName = $result->username;
11361172 $userTitle = Title::makeTitleSafe( NS_USER, $userName );
1137 - $user_link = self::$skin->link( $userTitle, $userName );
1138 - $voice_link = self::$skin->link( $this->getTitle(), wfMsg( 'qp_voice_link' . ($this->inverse ? "_inv" : "" ) ), array(), array( "id"=>intval( $this->pid), "uid"=>$uid, "action"=>"uvote" ) );
 1173+ $user_link = $this->qpLink( $userTitle, $userName );
 1174+ $voice_link = $this->qpLink( $this->getTitle(), wfMsg( 'qp_voice_link' . ($this->inverse ? "_inv" : "" ) ), array(), array( "id"=>intval( $this->pid), "uid"=>$uid, "action"=>"uvote" ) );
11391175 $text_answer = ($result->text_answer == '') ? '' : '<i>' . qp_Setup::entities( $result->text_answer ) . '</i>';
11401176 $link = wfMsg( 'qp_results_line_qucl', $user_link, $voice_link, $text_answer );
11411177 }
Index: trunk/extensions/QPoll/ctrl/qp_abstractpoll.php
@@ -52,8 +52,9 @@
5353 static $sOrderId = 0; // order of polls on the page (used for sorting of the output)
5454 static $sPrevPollIDs = array(); // used to check uniqueness of PollId on the page
5555
56 - # array of question objects associated with current poll
57 - var $questions = array();
 56+ # collection of question objects associated with current poll
 57+ var $questions;
 58+
5859 # current user name
5960 var $username;
6061
Index: trunk/extensions/QPoll/ctrl/qp_abstractquestion.php
@@ -6,6 +6,15 @@
77
88 abstract class qp_AbstractQuestion {
99
 10+ # indicates whether current question is used or not;
 11+ # also provides sparce enumeration of questions (unused questions are not counted)
 12+ # 1..n when the question is active;
 13+ # false when the question is hidden (used by randomizer)
 14+ var $usedId = false;
 15+ # sequental number of question (starting from 1); matches to usedId
 16+ # when the collection of the questions is not sparce (was not randomized)
 17+ var $mQuestionId;
 18+
1019 var $mState = ''; // current state of question parsing (no error)
1120 # default type and subtype of the question; should always be properly initialized in derived $this->parseMainHeader();
1221 var $mType = 'unknown';
@@ -34,7 +43,8 @@
3544 function __construct( qp_AbstractPoll $poll, qp_AbstractView $view, $questionId ) {
3645 global $wgRequest;
3746 $this->mRequest = &$wgRequest;
38 - $this->mQuestionId = $questionId;
 47+ # the question collection is not sparce by default
 48+ $this->mQuestionId = $this->usedId = $questionId;
3949 $this->mProposalPattern = '`^[^\|\!].*`u';
4050 $this->mCategoryPattern = '`^\|(\n|[^\|].*\n)`u';
4151 $view->setController( $this );
Index: trunk/extensions/QPoll/ctrl/qp_pollstats.php
@@ -78,7 +78,7 @@
7979 * @return boolean true - stop further processing, false - continue processing
8080 */
8181 function parseInput( $input ) {
82 - $this->questions = array();
 82+ $this->questions = new qp_QuestionCollection();
8383 # question attributes split pattern
8484 $splitPattern = '`\s*{|}\s*\n*`u';
8585 # preg_split counts the matches starting from zero
@@ -103,19 +103,20 @@
104104 # there cannot be type attribute of question in statistical display mode
105105 $question->setState( 'error', wfMsg( 'qp_error_type_in_stats_mode', $type ) );
106106 }
107 - $this->questions[] = $question;
 107+ $this->questions->add( $question );
108108 }
109109 # analyze question headers
110110 # check for showresults attribute
111111 $questions_set = array();
112 - foreach ( $this->questions as &$question ) {
 112+ $this->questions->reset();
 113+ while ( is_object( $question = $this->questions->iterate() ) ) {
113114 if ( $question->view->hasShowResults() ) {
114115 $questions_set[] = $question->mQuestionId;
115116 }
116117 }
117118 # load the statistics for all/selective/none of questions
118119 if ( count( $questions_set ) > 0 ) {
119 - if ( count( $questions_set ) == count( $this->questions ) ) {
 120+ if ( count( $questions_set ) == $this->questions->totalCount() ) {
120121 $this->pollStore->loadTotals();
121122 } else {
122123 $this->pollStore->loadTotals( $questions_set );
@@ -123,7 +124,8 @@
124125 $this->pollStore->calculateStatistics();
125126 }
126127 # second pass: generate views
127 - foreach ( $this->questions as &$question ) {
 128+ $this->questions->reset();
 129+ while ( is_object( $question = $this->questions->iterate() ) ) {
128130 $this->parseStats( $question );
129131 }
130132 return false;
Index: trunk/extensions/QPoll/ctrl/qp_poll.php
@@ -45,7 +45,12 @@
4646 # optional address of the poll which must be answered first
4747 var $dependsOn = '';
4848 # optional template used to interpret user vote in the Special:Pollresults page
49 - var $interpretation = '';
 49+ var $interpretation = '';
 50+ # whether the questions of the poll has to be randomized
 51+ # 0: questions are not randomized
 52+ # 1..n: pull 1..n question from total number of defined questions;
 53+ # separately stored for every (poll,user) in the poll store
 54+ var $randomQuestionCount = 0;
5055 # maximal count of attepts of answer submission ( < 1 for infinite )
5156 var $maxAttempts = 0;
5257
@@ -59,6 +64,17 @@
6065 if ( array_key_exists('interpretation', $argv) ) {
6166 $this->interpretation = trim( $argv['interpretation'] );
6267 }
 68+ # randomize attr
 69+ if ( array_key_exists('randomize', $argv) ) {
 70+ if ( $argv['randomize'] === 'randomize' ) {
 71+ $this->randomQuestionCount = 1;
 72+ } else {
 73+ $this->randomQuestionCount = intval( trim( $argv['randomize'] ) );
 74+ if ( $this->randomQuestionCount < 0 ) {
 75+ $this->randomQuestionCount = 0;
 76+ }
 77+ }
 78+ }
6379 # max_attempts attr
6480 $this->maxAttempts = qp_Setup::$max_submit_attempts;
6581 if ( array_key_exists('max_attempts', $argv) ) {
@@ -71,7 +87,7 @@
7288 $this->maxAttempts = qp_Setup::$max_submit_attempts;
7389 }
7490 }
75 - # negative values are possible however meaningless (0 is infinite, >0 is finite)
 91+ # negative values are possible however meaningless (<=0 is infinite, >0 is finite)
7692 if ( $this->maxAttempts < 0 ) {
7793 $this->maxAttempts = 0;
7894 }
@@ -156,6 +172,35 @@
157173 return true;
158174 }
159175
 176+ function setUsedQuestions() {
 177+ # load random questions from DB (when available)
 178+ $this->pollStore->loadRandomQuestions( $this->username );
 179+ if ( $this->randomQuestionCount > 0 ) {
 180+ if ( $this->randomQuestionCount > $this->questions->totalCount() ) {
 181+ $this->randomQuestionCount = $this->questions->totalCount();
 182+ }
 183+ if ( is_array( $this->pollStore->randomQuestions ) ) {
 184+ if ( count( $this->pollStore->randomQuestions ) == $this->randomQuestionCount ) {
 185+ # count of random questions was not changed, no need to regenerate seed
 186+ $this->questions->setUsedQuestions( $this->pollStore->randomQuestions );
 187+ return;
 188+ }
 189+ }
 190+ # generate or regenerate random questions
 191+ $this->questions->randomize( $this->randomQuestionCount );
 192+ $this->pollStore->randomQuestions = $this->questions->getUsedQuestions();
 193+ } else {
 194+ if ( !is_array( $this->pollStore->randomQuestions ) ) {
 195+ # random questions are disabled and no previous seed in DB
 196+ return;
 197+ }
 198+ # there was stored random seed, will remove it at the end of this function
 199+ $this->pollStore->randomQuestions = false;
 200+ }
 201+ # store random questions into DB
 202+ $this->pollStore->setRandomQuestions();
 203+ }
 204+
160205 /**
161206 * Parses the text enclosed in poll tag
162207 * Votes, when user have submitted data successfully
@@ -164,8 +209,10 @@
165210 */
166211 function parseInput( $input ) {
167212 global $wgTitle;
168 - # parse the input; generates $this->questions[] array
169 - $this->parseQuestions( $input );
 213+ # parse the input; generates $this->questions collection
 214+ $this->parseQuestionsHeaders( $input );
 215+ $this->setUsedQuestions();
 216+ $this->parseQuestionsBodies();
170217 # check whether the poll was successfully submitted
171218 if ( $this->attemptsLeft() === false ) {
172219 # user has no attempts left, refuse to submit and
@@ -264,12 +311,11 @@
265312 }
266313
267314 /**
268 - * Creates the set of poll questions in $this->questions[]
269 - * Also calculates statistics for pollstore
 315+ * Creates the collection of poll questions in $this->questions
270316 * @param $input string poll in QPoll syntax
271317 */
272 - function parseQuestions( $input ) {
273 - $this->questions = array();
 318+ function parseQuestionsHeaders( $input ) {
 319+ $this->questions = new qp_QuestionCollection();
274320 $splitPattern = '`(^|\n\s*)\n\s*{`u';
275321 $unparsedQuestions = preg_split( $splitPattern, $input, -1, PREG_SPLIT_NO_EMPTY );
276322 $questionPattern = '`(.*?[^|\}])\}[ \t]*(\n(.*)|$)`su';
@@ -285,23 +331,30 @@
286332 $header = isset( $matches[1] ) ? $matches[1] : '';
287333 $body = isset( $matches[3] ) ? $matches[3] : null;
288334 $question = $this->parseQuestionHeader( $header, $body );
289 - $this->parseQuestionBody( $question );
290 - $this->questions[] = $question;
 335+ $this->questions->add( $question );
291336 } else {
292337 $buffer = $unparsedQuestion;
293338 }
294339 }
295 - # analyze question headers
 340+ }
 341+
 342+ /**
 343+ * Parses question bodies for every poll in collection
 344+ * Also loads statistics from pollstore
 345+ */
 346+ function parseQuestionsBodies() {
296347 # check for showresults attribute
297348 $questions_set = array();
298 - foreach( $this->questions as &$question ) {
 349+ $this->questions->reset();
 350+ while ( is_object( $question = $this->questions->iterate() ) ) {
 351+ $this->parseQuestionBody( $question );
299352 if ( $question->view->hasShowResults() ) {
300353 $questions_set[] = $question->mQuestionId;
301354 }
302355 }
303356 # load the statistics for all/selective/none of questions
304357 if ( count( $questions_set ) > 0 ) {
305 - if ( count( $questions_set ) == count( $this->questions ) ) {
 358+ if ( count( $questions_set ) == $this->questions->totalCount() ) {
306359 $this->pollStore->loadTotals();
307360 } else {
308361 $this->pollStore->loadTotals( $questions_set );
@@ -309,7 +362,7 @@
310363 $this->pollStore->calculateStatistics();
311364 }
312365 }
313 -
 366+
314367 # Convert a question on the page from QPoll syntax to HTML
315368 # @param $header : the text of question "main" header (common question and XML-like attrs)
316369 # $body : the text of question body (starting with body header which defines categories and spans, followed by proposal list)
@@ -343,35 +396,35 @@
344397 $question->view->addHeaderError();
345398 # http get: invalid question syntax, parse errors will cause submit button disabled
346399 $this->pollStore->stateError();
 400+ return;
 401+ }
 402+ # populate $question with raw source values
 403+ $question->getQuestionAnswer( $this->pollStore );
 404+ # check whether the global showresults level prohibits to show statistical data
 405+ # to the users who hasn't voted
 406+ if ( qp_Setup::$global_showresults <= 1 && !$question->alreadyVoted ) {
 407+ # suppress statistical results when the current user hasn't voted the question
 408+ $question->view->showResults = array( 'type'=>0 );
 409+ }
 410+ # parse the question body
 411+ # will populate $question->view which can be modified accodring to quiz results
 412+ # warning! parameters are passed only by value, not the reference
 413+ $question->{$question->mType . 'ParseBody'}();
 414+ if ( $this->mBeingCorrected ) {
 415+ if ( $question->getState() == '' ) {
 416+ # question is OK, store it into pollStore
 417+ $question->store( $this->pollStore );
 418+ } else {
 419+ # http post: not every proposals were answered: do not update DB
 420+ $this->pollStore->stateIncomplete();
 421+ }
347422 } else {
348 - # populate $question with raw source values
349 - $question->getQuestionAnswer( $this->pollStore );
350 - # check whether the global showresults level prohibits to show statistical data
351 - # to the users who hasn't voted
352 - if ( qp_Setup::$global_showresults <= 1 && !$question->alreadyVoted ) {
353 - # suppress statistical results when the current user hasn't voted the question
354 - $question->view->showResults = array( 'type'=>0 );
355 - }
356 - # parse the question body
357 - # will populate $question->view which can be modified accodring to quiz results
358 - # warning! parameters are passed only by value, not the reference
359 - $question->{$question->mType . 'ParseBody'}();
360 - if ( $this->mBeingCorrected ) {
361 - if ( $question->getState() == '' ) {
362 - # question is OK, store it into pollStore
363 - $question->store( $this->pollStore );
364 - } else {
365 - # http post: not every proposals were answered: do not update DB
366 - $this->pollStore->stateIncomplete();
367 - }
 423+ # this is the get, not the post: do not update DB
 424+ if ( $question->getState() == '' ) {
 425+ $this->pollStore->stateIncomplete();
368426 } else {
369 - # this is the get, not the post: do not update DB
370 - if ( $question->getState() == '' ) {
371 - $this->pollStore->stateIncomplete();
372 - } else {
373 - # http get: invalid question syntax, parse errors will cause submit button disabled
374 - $this->pollStore->stateError();
375 - }
 427+ # http get: invalid question syntax, parse errors will cause submit button disabled
 428+ $this->pollStore->stateError();
376429 }
377430 }
378431 }
Index: trunk/extensions/QPoll/qp_user.php
@@ -251,6 +251,9 @@
252252 self::autoLoad( array(
253253 'qp_user.php' => array( 'FormatJson', 'qp_Setup', 'qp_Renderer', 'qp_FunctionsHook' ),
254254
 255+ ## collection of the questions
 256+ 'qp_question_collection.php' => 'qp_QuestionCollection',
 257+
255258 ## controllers (polls and questions derived from separate abstract classes)
256259 # polls
257260 'ctrl/qp_abstractpoll.php' => 'qp_AbstractPoll',
@@ -276,7 +279,7 @@
277280 'qp_pollstore.php' => array( 'qp_QuestionData', 'qp_InterpAnswer', 'qp_PollStore' ),
278281
279282 # results page
280 - 'qp_results.php' => array( 'qp_QueryPage', 'PollResults' ),
 283+ 'qp_results.php' => array( 'qp_SpecialPage', 'qp_QueryPage', 'PollResults' ),
281284
282285 # interpretation of answers
283286 'qp_interpret.php' => 'qp_Interpret',
Index: trunk/extensions/QPoll/qp_question_collection.php
@@ -0,0 +1,177 @@
 2+<?php
 3+/**
 4+ * ***** BEGIN LICENSE BLOCK *****
 5+ * This file is part of QPoll.
 6+ * Uses parts of code from Quiz extension (c) 2007 Louis-Rémi BABE. All rights reserved.
 7+ *
 8+ * QPoll is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * QPoll is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with QPoll; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * ***** END LICENSE BLOCK *****
 23+ *
 24+ * QPoll is a poll tool for MediaWiki.
 25+ *
 26+ * To activate this extension :
 27+ * * Create a new directory named QPoll into the directory "extensions" of MediaWiki.
 28+ * * Place the files from the extension archive there.
 29+ * * Add this line at the end of your LocalSettings.php file :
 30+ * require_once "$IP/extensions/QPoll/qp_user.php";
 31+ *
 32+ * @version 0.8.0a
 33+ * @link http://www.mediawiki.org/wiki/Extension:QPoll
 34+ * @author QuestPC <questpc@rambler.ru>
 35+ */
 36+
 37+if ( !defined( 'MEDIAWIKI' ) ) {
 38+ die( "This file is part of the QPoll extension. It is not a valid entry point.\n" );
 39+}
 40+
 41+class qp_QuestionCollection {
 42+
 43+ # array of question objects associated with current poll
 44+ private $questions = array();
 45+ # array of $this->questions[] indexes for question iterator (used by randomizer)
 46+ private $usedQuestions = false;
 47+
 48+ /**
 49+ * From http://php.net/manual/en/function.mt-rand.php
 50+ * function returns a random integer between min and max, just like function rand() does.
 51+ * Difference to rand is that the random generated number will not use any of the values
 52+ * placed in $except. ($except must therefore be an array)
 53+ * function returns false if $except holds all values between $min and $max.
 54+ */
 55+ function rand_except( $min, $max, $except ) {
 56+ # first sort array values
 57+ sort( $except, SORT_NUMERIC );
 58+ # calculate average gap between except-values
 59+ $except_count = count( $except );
 60+ $avg_gap = ($max - $min + 1 - $except_count) / ($except_count + 1);
 61+ if ( $avg_gap <= 0 ) {
 62+ return false;
 63+ }
 64+ # now add min and max to $except, so all gaps between $except-values can be calculated
 65+ array_unshift( $except, $min - 1 );
 66+ array_push( $except, $max + 1 );
 67+ $except_count += 2;
 68+ # iterate through all values of except. If gap between 2 values is higher than average gap,
 69+ # create random in this gap
 70+ for ($i = 1; $i < $except_count; $i++) {
 71+ if ( $except[$i] - $except[$i - 1] - 1 >= $avg_gap ) {
 72+ return mt_rand( $except[$i - 1] + 1, $except[$i] - 1 );
 73+ }
 74+ }
 75+ return false;
 76+ }
 77+
 78+ function randomize( $randomQuestionCount ) {
 79+ $questionCount = count( $this->questions );
 80+ if ( $randomQuestionCount > $questionCount ) {
 81+ $randomQuestionCount = $questionCount;
 82+ }
 83+ $this->usedQuestions = array();
 84+ for ( $i = 0; $i < $randomQuestionCount; $i++ ) {
 85+ if ( ( $r = $this->rand_except( 1, $questionCount, $this->usedQuestions ) ) === false ) {
 86+ throw new MWException( 'Bug: too many random questions in ' . __METHOD__ );
 87+ }
 88+ $this->usedQuestions[] = $r;
 89+ }
 90+ sort( $this->usedQuestions, SORT_NUMERIC );
 91+ if ( count( $this->usedQuestions ) === 0 ) {
 92+ $this->usedQuestions = false;
 93+ }
 94+ }
 95+
 96+ function getUsedQuestions() {
 97+ return $this->usedQuestions;
 98+ }
 99+
 100+ function setUsedQuestions( $randomQuestions ) {
 101+ if ( !is_array( $randomQuestions ) ) {
 102+ foreach ( $this->questions as $qidx => &$question ) {
 103+ $question->usedId = $question->mQuestionId;
 104+ }
 105+ return;
 106+ }
 107+ sort( $randomQuestions, SORT_NUMERIC );
 108+ $this->usedQuestions = array();
 109+ # questions count from 1
 110+ $usedId = 1;
 111+ foreach ( $this->questions as $qidx => &$question ) {
 112+ if ( in_array( $qidx, $randomQuestions, true ) ) {
 113+ $this->usedQuestions[] = $qidx;
 114+ $question->usedId = $usedId++;
 115+ } else {
 116+ $question->usedId = false;
 117+ }
 118+ }
 119+ if ( count( $this->usedQuestions ) === 0 ) {
 120+ throw new MWException( 'At least one question should not be unused in ' . __METHOD__ );
 121+ }
 122+ }
 123+
 124+ function add( qp_AbstractQuestion $question ) {
 125+ if ( count( $this->questions ) === 0 ) {
 126+ $this->questions[1] = $question;
 127+ } else {
 128+ $this->questions[] = $question;
 129+ }
 130+ }
 131+
 132+ function totalCount() {
 133+ return count( $this->questions );
 134+ }
 135+
 136+ function usedCount() {
 137+ $used = 0;
 138+ foreach ( $this->questions as &$question ) {
 139+ if ( $question->usedId !== false ) {
 140+ $used++;
 141+ }
 142+ }
 143+ return $used;
 144+ }
 145+
 146+ /**
 147+ * Reset question iterator
 148+ */
 149+ function reset() {
 150+ reset( $this->questions );
 151+ if ( is_array( $this->usedQuestions ) ) {
 152+ reset( $this->usedQuestions );
 153+ }
 154+ }
 155+
 156+ /**
 157+ * Get current question and rewind to the next question
 158+ * @return instance of qp_AbstractQuestion or derivative or
 159+ * boolean false - when there are no more questions left
 160+ */
 161+ function iterate() {
 162+ if ( is_array( $this->usedQuestions ) ) {
 163+ while ( !is_null( key( $this->usedQuestions ) ) ) {
 164+ list( $key, $qidx ) = each( $this->usedQuestions );
 165+ if ( isset( $this->questions[$qidx] ) ) {
 166+ return $this->questions[$qidx];
 167+ }
 168+ }
 169+ return false;
 170+ }
 171+ if ( !is_null( key( $this->questions ) ) ) {
 172+ list( $key, $question ) = each( $this->questions );
 173+ return $question;
 174+ }
 175+ return false;
 176+ }
 177+
 178+}
Property changes on: trunk/extensions/QPoll/qp_question_collection.php
___________________________________________________________________
Added: svn:eol-style
1179 + native
Index: trunk/extensions/QPoll/qp_pollstore.php
@@ -184,6 +184,8 @@
185185 /// DB keys
186186 var $pid = null;
187187 var $last_uid = null;
 188+ # username is used for caching of setLastUser() method (which now may be called multiple times)
 189+ var $username = '';
188190 /// common properties
189191 var $mArticleId = null;
190192 # unique id of poll, used for addressing, also with 'qp_' prefix as the fragment part of the link
@@ -205,6 +207,9 @@
206208
207209 # array of QuestionData instances (data from/to DB)
208210 var $Questions = null;
 211+ # array of random indexes of Questions[] array (optional)
 212+ var $randomQuestions = false;
 213+
209214 # poll processing state, read with getState()
210215 #
211216 # 'NA' - object just was created
@@ -673,10 +678,56 @@
674679 }
675680 }
676681
 682+ function isUsedQuestion( $question_id ) {
 683+ return !is_array( $this->randomQuestions ) ||
 684+ in_array( $question_id, $this->randomQuestions, true );
 685+ }
 686+
 687+ function loadRandomQuestions( $username ) {
 688+ if ( is_null( $this->pid ) ) {
 689+ $this->setPid();
 690+ }
 691+ $this->setLastUser( $username );
 692+ $res = self::$db->select( 'qp_random_questions', 'question_id', array( 'uid' => $this->last_uid, 'pid' => $this->pid ), __METHOD__ );
 693+ $this->randomQuestions = array();
 694+ while ( $row = self::$db->fetchObject( $res ) ) {
 695+ $this->randomQuestions[] = intval( $row->question_id );
 696+ }
 697+ if ( count( $this->randomQuestions ) === 0 ) {
 698+ $this->randomQuestions = false;
 699+ }
 700+ }
 701+
 702+ function setRandomQuestions() {
 703+ if ( is_null( $this->pid ) || is_null( $this->last_uid ) ) {
 704+ throw new MWException( __METHOD__ . ' cannot be called when pid/uid was not set' );
 705+ }
 706+ if ( is_array( $this->randomQuestions ) ) {
 707+ $data = array();
 708+ foreach( $this->randomQuestions as $qidx ) {
 709+ $data[] = array( 'pid' => $this->pid, 'uid' => $this->last_uid, 'question_id' => $qidx );
 710+ }
 711+ $res = self::$db->replace( 'qp_random_questions',
 712+ 'user_poll_question',
 713+ $data,
 714+ __METHOD__ . ':random questions seed update' );
 715+ return;
 716+ }
 717+ self::$db->delete( 'qp_random_questions',
 718+ array( 'pid'=>$this->pid ),
 719+ __METHOD__ . ':remove question random seed'
 720+ );
 721+ }
 722+
677723 function setLastUser( $username, $store_new_user_to_db = true ) {
678724 if ( $this->pid === null ) {
679725 return;
680726 }
 727+ # do no query DB for the same user more than once
 728+ if ( $this->username === $username ) {
 729+ return;
 730+ }
 731+ $this->username = $username;
681732 $res = self::$db->select( 'qp_users','uid','name=' . self::$db->addQuotes( $username ), __METHOD__ );
682733 $row = self::$db->fetchObject( $res );
683734 if ( $row == false ) {
@@ -768,7 +819,7 @@
769820 );
770821 }
771822 }
772 -
 823+
773824 private function setQuestionDesc() {
774825 $insert = array();
775826 foreach ( $this->Questions as $qkey => &$ques ) {
Index: trunk/extensions/QPoll/tables/qpoll_random_questions.src
@@ -0,0 +1,6 @@
 2+CREATE TABLE /*$wgDBprefix*/qp_random_questions (
 3+ `uid` int unsigned NOT NULL,
 4+ `pid` int unsigned NOT NULL,
 5+ `question_id` int unsigned NOT NULL,
 6+ PRIMARY KEY user_poll_question (uid,pid,question_id)
 7+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Index: trunk/extensions/QPoll/tables/qpoll_trunk.sql
@@ -78,3 +78,11 @@
7979 INDEX user_id (uid),
8080 INDEX username (name(64))
8181 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 82+
 83+DROP TABLE IF EXISTS `qp_random_questions`;
 84+CREATE TABLE `qp_random_questions` (
 85+ `uid` int unsigned NOT NULL,
 86+ `pid` int unsigned NOT NULL,
 87+ `question_id` int unsigned NOT NULL,
 88+ PRIMARY KEY user_poll_question (uid,pid,question_id)
 89+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Index: trunk/extensions/QPoll/view/qp_pollstatsview.php
@@ -64,7 +64,8 @@
6565 $write_row = array();
6666 $write_col = array();
6767 # render the body
68 - foreach ( $this->ctrl->questions as &$question ) {
 68+ $this->ctrl->questions->reset();
 69+ while ( is_object( $question = $this->ctrl->questions->iterate() ) ) {
6970 # render the question statistics only when showResuls isn't 0 (suppress stats)
7071 if ( $question->view->showResults['type'] != 0 ) {
7172 if ( $this->perRow > 1 ) {
Index: trunk/extensions/QPoll/view/qp_pollview.php
@@ -64,7 +64,8 @@
6565 $write_row = array();
6666 $write_col = array();
6767 # render the body
68 - foreach ( $this->ctrl->questions as &$question ) {
 68+ $this->ctrl->questions->reset();
 69+ while ( is_object( $question = $this->ctrl->questions->iterate() ) ) {
6970 $question->view->renderInterpErrors();
7071 if ( $this->perRow > 1 ) {
7172 $write_col[] = array( '__tag'=>'td', 'valign'=>'top', 0=>$question->view->renderQuestion(), '__end'=>"\n" );
Index: trunk/extensions/QPoll/view/qp_questionview.php
@@ -419,7 +419,7 @@
420420 $output_table[] = array( '__tag'=>'tbody', '__end'=>"\n", 0=>$this->renderTable() );
421421 $tags = array( '__tag'=>'div', '__end'=>"\n", 'class'=>'question',
422422 0=>array( '__tag'=>'div', '__end'=>"\n", 'class'=>'header',
423 - 0=>array( '__tag'=>'span', 'class'=>'questionId', 0=>$this->ctrl->mQuestionId )
 423+ 0=>array( '__tag'=>'span', 'class'=>'questionId', 0=>$this->ctrl->usedId )
424424 ),
425425 1=>array( '__tag'=>'div', 0=>$this->rtp( $this->ctrl->mCommonQuestion ) )
426426 );

Status & tagging log