r103584 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r103583‎ | r103584 | r103585 >
Date:11:53, 18 November 2011
Author:questpc
Status:deferred
Tags:
Comment:
Questions may optionally have name attribute in their headers, allowing to refer questions by names in the interpretation scripts. Consistent naming of qp_QuestionData instances. Minor cleanup and refactoring in Special:PollResults related classes.
Modified paths:
  • /trunk/extensions/QPoll/archives/qpoll_question_name.src (added) (history)
  • /trunk/extensions/QPoll/clientside/qp_results.css (modified) (history)
  • /trunk/extensions/QPoll/ctrl/poll/qp_abstractpoll.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/poll/qp_poll.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/qp_interpresult.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/question/qp_abstractquestion.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/question/qp_stubquestion.php (modified) (history)
  • /trunk/extensions/QPoll/ctrl/question/qp_tabularquestion.php (modified) (history)
  • /trunk/extensions/QPoll/i18n/qp.i18n.php (modified) (history)
  • /trunk/extensions/QPoll/interpretation/qp_interpret.php (modified) (history)
  • /trunk/extensions/QPoll/maintenance/qp_schemaupdater.php (modified) (history)
  • /trunk/extensions/QPoll/model/cache/qp_categorycache.php (modified) (history)
  • /trunk/extensions/QPoll/model/cache/qp_proposalcache.php (modified) (history)
  • /trunk/extensions/QPoll/model/cache/qp_questioncache.php (modified) (history)
  • /trunk/extensions/QPoll/model/qp_pollstore.php (modified) (history)
  • /trunk/extensions/QPoll/model/qp_questiondata.php (modified) (history)
  • /trunk/extensions/QPoll/qp_user.php (modified) (history)
  • /trunk/extensions/QPoll/specials/qp_results.php (modified) (history)
  • /trunk/extensions/QPoll/tables/qpoll_trunk.sql (modified) (history)
  • /trunk/extensions/QPoll/view/results/qp_questiondataresults.php (modified) (history)
  • /trunk/extensions/QPoll/view/results/qp_textquestiondataresults.php (modified) (history)
  • /trunk/extensions/QPoll/view/xls/qp_xlstabularquestion.php (modified) (history)

Diff [purge]

Index: trunk/extensions/QPoll/i18n/qp.i18n.php
@@ -103,6 +103,7 @@
104104 'qp_error_address_in_decl_mode' => 'Cannot get an address of the poll in declaration mode.',
105105 'qp_error_question_not_implemented' => 'Questions of such type are not implemented: $1.',
106106 'qp_error_invalid_question_type' => 'Invalid question type: $1.',
 107+ 'qp_error_invalid_question_name' => 'Invalid question name: $1.',
107108 'qp_error_type_in_stats_mode' => 'Question type cannot be defined in statistical display mode: $1.',
108109 'qp_error_no_poll_id' => 'Poll tag has no id attribute defined.',
109110 'qp_error_invalid_poll_id' => 'Invalid poll id (id=$1).
@@ -227,6 +228,7 @@
228229 'qp_error_address_in_decl_mode' => 'Poll "address" attribute is meaningless in poll declaration / voting mode.',
229230 'qp_error_question_not_implemented' => 'Invalid value of qustion xml-like "type" attribute was specified. There is no such type of question. Please read the manual for list of valid question types.',
230231 'qp_error_invalid_question_type' => '{{Identical|Invalid value of qustion xml-like "type" attribute was specified. There is no such type of question. Please read the manual for list of valid question types.}}',
 232+ 'qp_error_invalid_question_name' => '{{Identical|Invalid value of qustion xml-like "name" attribute was specified. Numeric names are not allowed due to possible index clash with integer question ids. Empty names are not allowed as impossible to reference. Too long names are not allowed, otherwise they will be improperly truncated when stored into DB field.}}',
231233 'qp_error_type_in_stats_mode' => 'Question\'s "type" xml-like attribute is meaningless in statistical display mode.',
232234 'qp_error_no_poll_id' => 'Every poll definition in declaration / voting mode must have "id" attribute.',
233235 'qp_error_too_long_dependance_value' => 'Parameters:
@@ -2755,6 +2757,7 @@
27562758 'qp_error_address_in_decl_mode' => 'Недопустимо задавать адрес опроса (address) в режиме определения',
27572759 'qp_error_question_not_implemented' => 'Вопросы данного типа не реализованы в коде расширения: $1',
27582760 'qp_error_invalid_question_type' => 'Недопустимый тип вопроса: $1',
 2761+ 'qp_error_invalid_question_name' => 'Недопустимое имя вопроса: $1',
27592762 'qp_error_type_in_stats_mode' => 'Недопустимо определять тип вопроса в статистическом режиме: $1',
27602763 'qp_error_no_poll_id' => 'Тэг опроса не имеет атрибута id.',
27612764 'qp_error_invalid_poll_id' => 'Недопустимый идентификатор опроса (id=$1). Идентификатор опроса может содержать только буквы, цифры и символ пробела',
Index: trunk/extensions/QPoll/maintenance/qp_schemaupdater.php
@@ -105,6 +105,9 @@
106106 ),
107107 'qpoll_structured_interpretation.src' => array(
108108 'qp_users_polls' => array( 'structured_interpretation' )
 109+ ),
 110+ 'qpoll_question_name.src' => array(
 111+ 'qp_question_desc' => array( 'name' )
109112 )
110113 );
111114
Index: trunk/extensions/QPoll/clientside/qp_results.css
@@ -1,4 +1,6 @@
22 .qpoll .head {font-weight:bold; color:Gray;}
 3+.qpoll .question_id {font-weight:bold;}
 4+.qpoll .question_name {font-weight:bold; color:blue;}
35 .qpoll table.qdata {border-collapse: collapse;}
46 .qpoll table.qdata th {border: 1px Gray solid; background-color:LightGrey; padding: 3px;}
57 .qpoll table.qdata tr.spans { color:Navy; }
Index: trunk/extensions/QPoll/model/cache/qp_questioncache.php
@@ -17,7 +17,7 @@
1818 # DB index for replace
1919 protected $replaceIndex = 'question';
2020 # DB table fields to select / replace
21 - protected $fields = array( 'question_id', 'type', 'common_question' );
 21+ protected $fields = array( 'question_id', 'type', 'common_question', 'name' );
2222
2323 protected function numRowsToAssocRows() {
2424 # build DBMS-like object rows from array rows
@@ -77,17 +77,24 @@
7878 protected function buildReplaceRows() {
7979 global $wgContLang;
8080 $pid = self::$store->pid;
81 - foreach ( self::$store->Questions as $qkey => $ques ) {
82 - $common_question = $wgContLang->truncate( $ques->CommonQuestion, qp_Setup::$field_max_len['common_question'] , '' );
83 - $this->replace[] = array( 'pid' => $pid, 'question_id' => $qkey, 'type' => $ques->type, 'common_question' => $common_question );
84 - $ques->question_id = $qkey;
 81+ foreach ( self::$store->Questions as $qkey => $qdata ) {
 82+ $common_question = $wgContLang->truncate( $qdata->CommonQuestion, qp_Setup::$field_max_len['common_question'] , '' );
 83+ $this->replace[] = array(
 84+ 'pid' => $pid,
 85+ 'question_id' => $qkey,
 86+ 'type' => $qdata->type,
 87+ 'common_question' => $common_question,
 88+ 'name' => $qdata->name
 89+ );
 90+ $qdata->question_id = $qkey;
8591 # instead of calling $this->updateFromPollStore(),
8692 # we build $this->memc_rows[] right here,
8793 # to avoid double loop against self::$store->Questions
8894 $this->memc_rows[] = array(
8995 $qkey,
90 - $ques->type,
91 - $common_question
 96+ $qdata->type,
 97+ $common_question,
 98+ $qdata->name
9299 );
93100 }
94101 }
Index: trunk/extensions/QPoll/model/cache/qp_categorycache.php
@@ -26,9 +26,9 @@
2727 protected function buildReplaceRows() {
2828 global $wgContLang;
2929 $pid = self::$store->pid;
30 - foreach ( self::$store->Questions as $qkey => $ques ) {
31 - $ques->packSpans();
32 - foreach ( $ques->Categories as $catkey => &$Cat ) {
 30+ foreach ( self::$store->Questions as $qkey => $qdata ) {
 31+ $qdata->packSpans();
 32+ foreach ( $qdata->Categories as $catkey => &$Cat ) {
3333 $cat_name = $Cat['name'];
3434 $this->replace[] = array( 'pid' => $pid, 'question_id' => $qkey, 'cat_id' => $catkey, 'cat_name' => $cat_name );
3535 # instead of calling $this->updateFromPollStore(),
@@ -40,7 +40,7 @@
4141 $cat_name
4242 );
4343 }
44 - $ques->restoreSpans();
 44+ $qdata->restoreSpans();
4545 }
4646 }
4747
Index: trunk/extensions/QPoll/model/cache/qp_proposalcache.php
@@ -26,10 +26,10 @@
2727 protected function buildReplaceRows() {
2828 global $wgContLang;
2929 $pid = self::$store->pid;
30 - foreach ( self::$store->Questions as $qkey => $ques ) {
31 - foreach ( $ques->ProposalText as $propkey => $ptext ) {
32 - if ( isset( $ques->ProposalNames[$propkey] ) ) {
33 - $ptext = qp_QuestionData::getProposalNamePrefix( $ques->ProposalNames[$propkey] ) . $ptext;
 30+ foreach ( self::$store->Questions as $qkey => $qdata ) {
 31+ foreach ( $qdata->ProposalText as $propkey => $ptext ) {
 32+ if ( isset( $qdata->ProposalNames[$propkey] ) ) {
 33+ $ptext = qp_QuestionData::getProposalNamePrefix( $qdata->ProposalNames[$propkey] ) . $ptext;
3434 }
3535 $ptext = $wgContLang->truncate( $ptext, qp_Setup::$field_max_len['proposal_text'] , '' );
3636 $this->replace[] = array( 'pid' => $pid, 'question_id' => $qkey, 'proposal_id' => $propkey, 'proposal_text' => $ptext );
Index: trunk/extensions/QPoll/model/qp_questiondata.php
@@ -25,6 +25,7 @@
2626 ## common properties
2727 var $type;
2828 var $CommonQuestion;
 29+ var $name = null;
2930 var $Categories;
3031 var $CategorySpans;
3132 var $ProposalText;
@@ -55,6 +56,7 @@
5657 $this->question_id = $argv['qid'];
5758 $this->type = $argv['type'];
5859 $this->CommonQuestion = $argv['common_question'];
 60+ $this->name = $argv['name'];
5961 $this->Categories = array();
6062 $this->CategorySpans = array();
6163 $this->ProposalText = array();
@@ -203,6 +205,7 @@
204206 $this->question_id = $question->mQuestionId;
205207 $this->type = $question->mType;
206208 $this->CommonQuestion = $question->mCommonQuestion;
 209+ $this->name = $question->mName;
207210 $this->Categories = $question->mCategories;
208211 $this->CategorySpans = $question->mCategorySpans;
209212 $this->ProposalText = $question->mProposalText;
Index: trunk/extensions/QPoll/model/qp_pollstore.php
@@ -356,11 +356,12 @@
357357 if ( isset( $typeFromVer0_5[$row->type] ) ) {
358358 $row->type = $typeFromVer0_5[$row->type];
359359 }
360 - # create qp_QuestionData object from DB fields
 360+ # create qp_QuestionData object from question description DB row
361361 $this->Questions[$question_id] = qp_QuestionData::factory( array(
362362 'qid' => $question_id,
363363 'type' => $row->type,
364 - 'common_question' => $row->common_question )
 364+ 'common_question' => $row->common_question,
 365+ 'name' => $row->name )
365366 );
366367 }
367368 $this->getCategories();
@@ -964,7 +965,11 @@
965966 $questions[$propkey] = $proposals;
966967 }
967968 }
968 - $poll_answer[$qdata->question_id] = $questions;
 969+ if ( $qdata->name !== null ) {
 970+ $poll_answer[$qdata->name] = $questions;
 971+ } else {
 972+ $poll_answer[$qdata->question_id] = $questions;
 973+ }
969974 }
970975
971976 # interpret the poll answer to get interpretation answer
@@ -978,8 +983,8 @@
979984 */
980985 private function setAnswers( $db ) {
981986 $insert = array();
982 - foreach ( $this->Questions as $qkey => $ques ) {
983 - foreach ( $ques->ProposalCategoryId as $propkey => &$prop_answers ) {
 987+ foreach ( $this->Questions as $qkey => $qdata ) {
 988+ foreach ( $qdata->ProposalCategoryId as $propkey => &$prop_answers ) {
984989 foreach ( $prop_answers as $idkey => $catkey ) {
985990 $insert[] = array(
986991 'uid' => $this->last_uid,
@@ -987,7 +992,7 @@
988993 'question_id' => $qkey,
989994 'proposal_id' => $propkey,
990995 'cat_id' => $catkey,
991 - 'text_answer' => $ques->ProposalCategoryText[$propkey][$idkey]
 996+ 'text_answer' => $qdata->ProposalCategoryText[$propkey][$idkey]
992997 );
993998 }
994999 }
Index: trunk/extensions/QPoll/specials/qp_results.php
@@ -98,7 +98,7 @@
9999 self::$PollsLink = $this->qpLink( $this->getTitle(), wfMsg( 'qp_polls_list' ), array( "style" => "font-weight:bold;" ) );
100100 }
101101 $wgOut->addHTML( '<div class="qpoll">' );
102 - $output = "";
 102+ $output = '';
103103 $this->setHeaders();
104104 $cmd = $wgRequest->getVal( 'action' );
105105 if ( $cmd === null ) {
@@ -115,9 +115,9 @@
116116 case 'stats':
117117 if ( $pid !== null ) {
118118 $pid = intval( $pid );
119 - $output = self::getPollsLink();
120 - $output .= self::getUsersLink();
121 - $output .= $this->showStats( $pid );
 119+ $output = self::getPollsLink() .
 120+ self::getUsersLink() .
 121+ $this->showStats( $pid );
122122 }
123123 break;
124124 case 'stats_xls':
@@ -132,9 +132,9 @@
133133 if ( $pid !== null && $uid !== null ) {
134134 $pid = intval( $pid );
135135 $uid = intval( $uid );
136 - $output = self::getPollsLink();
137 - $output .= self::getUsersLink();
138 - $output .= $this->showUserVote( $pid, $uid );
 136+ $output = self::getPollsLink() .
 137+ self::getUsersLink() .
 138+ $this->showUserVote( $pid, $uid );
139139 }
140140 break;
141141 case 'qpcusers':
@@ -183,12 +183,12 @@
184184 );
185185 $interpTitle = $pollStore->getInterpTitle();
186186 if ( !( $interpTitle instanceof Title ) ) {
187 - $tags[] = wfMsg( 'qp_poll_has_no_interpretation' );
 187+ $tags[] = array( '__tag' => 'div', wfMsg( 'qp_poll_has_no_interpretation' ) );
188188 return $tags;
189189 }
190190 # 'parentheses' key is unavailable in MediaWiki 1.15.x
191191 $interp_link = $this->qpLink( $interpTitle, $interpTitle->getPrefixedText() );
192 - $tags[] = wfMsg( 'qp_browse_to_interpretation', $interp_link );
 192+ $tags[] = array( '__tag' => 'div', wfMsg( 'qp_browse_to_interpretation', $interp_link ) );
193193 $interpResultView = new qp_InterpResultView( true );
194194 $interpResultView->showInterpResults( $tags, $pollStore->interpResult, true );
195195 return $tags;
@@ -216,23 +216,22 @@
217217 $poll_title = $pollStore->getTitle();
218218 # 'parentheses' key is unavailable in MediaWiki 1.15.x
219219 $poll_link = $this->qpLink( $poll_title, $poll_title->getPrefixedText() . wfMsg( 'word-separator' ) . wfMsg( 'qp_parentheses', $pollStore->mPollId ) );
220 - $output = wfMsg( 'qp_browse_to_user', $user_link ) . "<br />\n";
221 - $output .= wfMsg( 'qp_browse_to_poll', $poll_link ) . "<br />\n";
222220 $headerTags = $this->getAnswerHeader( $pollStore );
223 - $output .= qp_Renderer::renderTagArray( $headerTags );
 221+ $output = wfMsg( 'qp_browse_to_user', $user_link ) . "<br />\n" .
 222+ wfMsg( 'qp_browse_to_poll', $poll_link ) . "<br />\n" .
 223+ qp_Renderer::renderTagArray( $headerTags );
224224 unset( $headerTags );
225225 foreach ( $pollStore->Questions as $qdata ) {
226226 if ( $pollStore->isUsedQuestion( $qdata->question_id ) ) {
227 - $output .= "<br />\n<b>" . $qdata->question_id . ".</b> " . qp_Setup::entities( $qdata->CommonQuestion ) . "<br />\n";
228227 $qview = $qdata->getView();
229 - $output .= $qview->displayUserQuestionVote();
 228+ $output .= $qview->displayUserVote();
230229 }
231230 }
232231 return $output;
233232 }
234233
235234 private function showStats( $pid ) {
236 - $output = "";
 235+ $output = '';
237236 if ( $pid !== null ) {
238237 $pollStore = new qp_PollStore( array( 'from' => 'pid', 'pid' => $pid ) );
239238 if ( $pollStore->pid !== null ) {
@@ -248,12 +247,28 @@
249248 $interp_link = $this->qpLink( $interpTitle, $interpTitle->getPrefixedText() );
250249 $output .= wfMsg( 'qp_browse_to_interpretation', $interp_link ) . "<br />\n";
251250 }
252 - $output .= $this->qpLink( $this->getTitle(), wfMsg( 'qp_export_to_xls' ), array( "style" => "font-weight:bold;" ), array( 'action' => 'stats_xls', 'id' => $pid ) ) . "<br />\n";
253 - $output .= $this->qpLink( $this->getTitle(), wfMsg( 'qp_voices_to_xls' ), array( "style" => "font-weight:bold;" ), array( 'action' => 'voices_xls', 'id' => $pid ) ) . "<br />\n";
254 - $output .= $this->qpLink( $this->getTitle(), wfMsg( 'qp_interpretation_results_to_xls' ), array( "style" => "font-weight:bold;" ), array( 'action' => 'interpretation_xls', 'id' => $pid ) ) . "<br />\n";
 251+ $output .=
 252+ $this->qpLink(
 253+ $this->getTitle(),
 254+ wfMsg( 'qp_export_to_xls' ),
 255+ array( "style" => "font-weight:bold;" ),
 256+ array( 'action' => 'stats_xls', 'id' => $pid )
 257+ ) . "<br />\n" .
 258+ $this->qpLink(
 259+ $this->getTitle(),
 260+ wfMsg( 'qp_voices_to_xls' ),
 261+ array( "style" => "font-weight:bold;" ),
 262+ array( 'action' => 'voices_xls', 'id' => $pid )
 263+ ) . "<br />\n" .
 264+ $this->qpLink(
 265+ $this->getTitle(),
 266+ wfMsg( 'qp_interpretation_results_to_xls' ),
 267+ array( "style" => "font-weight:bold;" ),
 268+ array( 'action' => 'interpretation_xls', 'id' => $pid )
 269+ ) . "<br />\n";
255270 foreach ( $pollStore->Questions as $qdata ) {
256271 $qview = $qdata->getView();
257 - $output .= $qview->displayQuestionStats( $this, $pid );
 272+ $output .= $qview->displayStats( $this, $pid );
258273 }
259274 }
260275 }
Index: trunk/extensions/QPoll/archives/qpoll_question_name.src
@@ -0,0 +1,3 @@
 2+-- field to generate text answers statistics
 3+ALTER TABLE /*$wgDBprefix*/qp_question_desc
 4+ ADD COLUMN name tinytext default NULL;
Index: trunk/extensions/QPoll/ctrl/poll/qp_abstractpoll.php
@@ -78,7 +78,7 @@
7979 * possible xml-like attributes the question may have
8080 */
8181 var $questionAttributeKeys = array(
82 - 't[yi]p[eo]', 'layout', 'textwidth', 'propwidth', 'showresults'
 82+ 't[yi]p[eo]', 'name', 'layout', 'textwidth', 'propwidth', 'showresults'
8383 );
8484
8585 /**
Index: trunk/extensions/QPoll/ctrl/poll/qp_poll.php
@@ -389,6 +389,18 @@
390390 if ( !array_key_exists( $type, qp_Setup::$questionTypes ) ) {
391391 $error_msg = wfMsg( 'qp_error_invalid_question_type', qp_Setup::entities( $type ) );
392392 }
 393+ # assume that question name does not exists (by default)
 394+ $name = null;
 395+ if ( $paramkeys['name'] !== null &&
 396+ ( $name = trim( $paramkeys['name'] ) ) === '' ||
 397+ is_numeric( $name ) ||
 398+ strlen( $name ) > qp_Setup::$field_max_len['question_name'] ) {
 399+ # empty name is not allowed;
 400+ # numeric name is not allowed;
 401+ # name longer than maximal DB field length is not allowed;
 402+ $error_msg = wfMsg( 'qp_error_invalid_question_name', qp_Setup::entities( $name ) );
 403+ $name = null;
 404+ }
393405 } else {
394406 $error_msg = wfMsg( 'qp_error_in_question_header', qp_Setup::entities( $header ) );
395407 }
@@ -397,7 +409,8 @@
398410 $question = new qp_StubQuestion(
399411 $this,
400412 qp_StubQuestionView::newFromBaseView( $this->view ),
401 - ++$this->mQuestionId
 413+ ++$this->mQuestionId,
 414+ $name
402415 );
403416 $question->setState( 'error', $error_msg );
404417 return $question;
@@ -407,7 +420,8 @@
408421 $question = new $qt['ctrl'](
409422 $this,
410423 call_user_func( array( $qt['view'], 'newFromBaseView' ), $this->view ),
411 - ++$this->mQuestionId
 424+ ++$this->mQuestionId,
 425+ $name
412426 );
413427 # set the question type and subtype corresponding to the header 'type' attribute
414428 $question->mType = $qt['mType'];
Index: trunk/extensions/QPoll/ctrl/qp_interpresult.php
@@ -74,13 +74,19 @@
7575 }
7676
7777 /**
78 - * set question / proposal error message (for quizes)
 78+ * Set question / proposal error message (for quizes).
7979 *
80 - * @param $msg string error message for [question][proposal] pair;
81 - * non-string for default message
82 - * @param $qidx int index of poll's question
83 - * @param $pidx int index of question's proposal
84 - * @param $cidx int index of proposal's category (optional)
 80+ * @param $msg mixed
 81+ * string error message for [question][proposal][category];
 82+ * non-string will cause displaying default message for that qp / qpc;
 83+ * @param $qidx mixed
 84+ * string question name
 85+ * int index of poll's question
 86+ * @param $pidx mixed
 87+ * string proposal name
 88+ * int index of question's proposal
 89+ * @param $cidx integer
 90+ * index of proposal's category (optional)
8591 */
8692 function setQPCerror( $msg, $qidx, $pidx, $cidx = null ) {
8793 if ( !is_array( $this->qpcErrors ) ) {
Index: trunk/extensions/QPoll/ctrl/question/qp_abstractquestion.php
@@ -40,9 +40,12 @@
4141 /**
4242 * Constructor
4343 * @public
44 - * @param $poll an instance of question's parent controller
45 - * @param $view an instance of question view "linked" to this question
46 - * @param $questionId the identifier of the question used to generate input names
 44+ * @param $poll object
 45+ * an instance of question's parent controller
 46+ * @param $view object
 47+ * an instance of question view "linked" to this question
 48+ * @param $questionId integer
 49+ * identifier of the question used to generate input names
4750 */
4851 function __construct( qp_AbstractPoll $poll, qp_AbstractView $view, $questionId ) {
4952 # the question collection is not sparce by default
Index: trunk/extensions/QPoll/ctrl/question/qp_tabularquestion.php
@@ -13,12 +13,18 @@
1414 /**
1515 * Constructor
1616 * @public
17 - * @param $poll an instance of question's parent controller
18 - * @param $view an instance of question view "linked" to this question
19 - * @param $questionId the identifier of the question used to generate input names
 17+ * @param $poll object
 18+ * an instance of question's parent controller
 19+ * @param $view object
 20+ * an instance of question view "linked" to this question
 21+ * @param $questionId integer
 22+ * identifier of the question used to generate input names
 23+ * @param $name mixed
 24+ * null when question has no name / invalid name
 25+ * string valid question name
2026 */
21 - function __construct( qp_AbstractPoll $poll, qp_StubQuestionView $view, $questionId ) {
22 - parent::__construct( $poll, $view, $questionId );
 27+ function __construct( qp_AbstractPoll $poll, qp_StubQuestionView $view, $questionId, $name ) {
 28+ parent::__construct( $poll, $view, $questionId, $name );
2329 $this->mProposalPattern = '`^[^\|\!].*`u';
2430 $this->mCategoryPattern = '`^\|(\n|[^\|].*\n)`u';
2531 }
Index: trunk/extensions/QPoll/ctrl/question/qp_stubquestion.php
@@ -12,6 +12,9 @@
1313 */
1414 class qp_StubQuestion extends qp_AbstractQuestion {
1515
 16+ # optional question literal name, used to address questions in interpretation scripts
 17+ var $mName = null;
 18+
1619 # current user voting taken from POST data (if available)
1720 var $mProposalCategoryId = Array(); // user true/false answers to the question's proposal
1821 var $mProposalCategoryText = Array(); // user text answers to the question's proposal
@@ -22,14 +25,25 @@
2326 /**
2427 * Constructor
2528 * @public
26 - * @param $poll an instance of question's parent controller
27 - * @param $view an instance of question view "linked" to this question
28 - * @param $questionId the identifier of the question used to generate input names
 29+ * @param $poll object
 30+ * an instance of question's parent controller
 31+ * @param $view object
 32+ * an instance of question view "linked" to this question
 33+ * @param $questionId integer
 34+ * identifier of the question used to generate input names
 35+ * @param $name mixed
 36+ * null when question has no name / invalid name
 37+ * string valid question name
2938 */
30 - function __construct( qp_AbstractPoll $poll, qp_StubQuestionView $view, $questionId ) {
 39+ function __construct( qp_AbstractPoll $poll, qp_StubQuestionView $view, $questionId, $name ) {
3140 parent::__construct( $poll, $view, $questionId );
 41+ $this->mName = $name;
3242 }
3343
 44+ function getQuestionKey() {
 45+ return $this->mName === null ? $this->mQuestionId : $this->mName;
 46+ }
 47+
3448 # load some question fields from qp_QuestionData given
3549 # (usually qp_QuestionData is an array property of qp_PollStore instance)
3650 # @param $qdata - an instance of qp_QuestionData
@@ -102,11 +116,14 @@
103117 */
104118 function getInterpErrors() {
105119 $interpResult = $this->poll->pollStore->interpResult;
106 - if ( !is_array( $interpResult->qpcErrors ) ||
107 - !isset( $interpResult->qpcErrors[$this->mQuestionId] ) ) {
 120+ if ( !is_array( $interpResult->qpcErrors ) ) {
108121 return false;
109122 }
110 - return $interpResult->qpcErrors[$this->mQuestionId];
 123+ $key = $this->getQuestionKey();
 124+ if ( isset( $interpResult->qpcErrors[$key] ) ) {
 125+ return $interpResult->qpcErrors[$key];
 126+ }
 127+ return false;
111128 }
112129
113130 /**
Index: trunk/extensions/QPoll/qp_user.php
@@ -255,6 +255,8 @@
256256 # limited due to performance improvements (to fit into DB row),
257257 # and also to properly truncate UFT8 tails:
258258 'common_question' => 768,
 259+ # limited to maximal length of DB field
 260+ 'question_name' => 255,
259261 # 'proposal_text' is not longer than DB field size (65535),
260262 # otherwise unserialization of question type="text" proposal parts and
261263 # category fields will be invalid:
Index: trunk/extensions/QPoll/interpretation/qp_interpret.php
@@ -117,21 +117,17 @@
118118 if ( isset( $result['error'] ) && is_array( $result['error'] ) ) {
119119 # initialize $interpResult->qpcErrors[] member array
120120 foreach ( $result['error'] as $qidx => $question ) {
121 - if ( is_int( $qidx ) && is_array( $question ) ) {
 121+ if ( is_array( $question ) ) {
122122 foreach ( $question as $pidx => $prop_error ) {
123123 # integer indicates proposal id; string - proposal name
124 - if ( is_int( $pidx ) || is_string( $pidx ) ) {
125 - if ( is_array( $prop_error ) ) {
126 - # separate error messages list for proposal categories
127 - foreach ( $prop_error as $cidx => $cat_error ) {
128 - if ( is_int( $cidx ) ) {
129 - $interpResult->setQPCerror( $cat_error, $qidx, $pidx, $cidx );
130 - }
131 - }
132 - } else {
133 - # error message for the whole proposal line
134 - $interpResult->setQPCerror( $prop_error, $qidx, $pidx );
 124+ if ( is_array( $prop_error ) ) {
 125+ # separate error messages list for proposal categories
 126+ foreach ( $prop_error as $cidx => $cat_error ) {
 127+ $interpResult->setQPCerror( $cat_error, $qidx, $pidx, $cidx );
135128 }
 129+ } else {
 130+ # error message for the whole proposal line
 131+ $interpResult->setQPCerror( $prop_error, $qidx, $pidx );
136132 }
137133 }
138134 }
Index: trunk/extensions/QPoll/tables/qpoll_trunk.sql
@@ -26,6 +26,7 @@
2727 `question_id` int unsigned NOT NULL,
2828 `type` tinytext NOT NULL,
2929 `common_question` text NOT NULL,
 30+ `name` tinytext default NULL,
3031 PRIMARY KEY question (pid,question_id),
3132 INDEX poll (pid)
3233 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -61,7 +62,7 @@
6263 `text_answer` text,
6364 PRIMARY KEY answer (uid,pid,question_id,proposal_id,cat_id),
6465 INDEX user_vote (uid,pid),
65 - INDEX poll_question (pid,question_id)
 66+ INDEX poll_question (pid,question_id),
6667 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
6768
6869 DROP TABLE IF EXISTS `qp_users_polls`;
Index: trunk/extensions/QPoll/view/results/qp_questiondataresults.php
@@ -28,23 +28,39 @@
2929 }
3030
3131 /**
32 - * @return string html representation of user vote for Special:Pollresults output
 32+ * @return string html representation of question header
3333 */
34 - function displayUserQuestionVote() {
 34+ public function displayHeader() {
3535 $ctrl = $this->ctrl;
36 - $output = "<div class=\"qpoll\">\n" . "<table class=\"qdata\">\n";
37 - $output .= qp_Renderer::displayRow(
38 - array_map( array( $this, 'categoryentities' ), $ctrl->CategorySpans ),
39 - array( 'class' => 'spans' ),
40 - 'th',
41 - array( 'count' => 'colspan', 'name' => 0 )
42 - );
43 - $output .= qp_Renderer::displayRow(
44 - array_map( array( $this, 'categoryentities' ), $ctrl->Categories ),
45 - array(),
46 - 'th',
47 - array( 'name' => 0 )
48 - );
 36+ return "<div class=\"question_header\">\n" .
 37+ "<span class=\"question_id\">{$ctrl->question_id}</span> " .
 38+ ( ( $ctrl->name === null ) ?
 39+ '' :
 40+ ' <span class="question_name">' . qp_Setup::entities( $ctrl->name ) . '</span>'
 41+ ) .
 42+ ' <span class="common_question">' . qp_Setup::entities( $ctrl->CommonQuestion ) .
 43+ "</span></div>\n";
 44+ }
 45+
 46+ /**
 47+ * @return string html representation of user vote
 48+ */
 49+ public function displayUserVote() {
 50+ $ctrl = $this->ctrl;
 51+ $output = $this->displayHeader() .
 52+ "<div class=\"qpoll\">\n" . "<table class=\"qdata\">\n" .
 53+ qp_Renderer::displayRow(
 54+ array_map( array( $this, 'categoryentities' ), $ctrl->CategorySpans ),
 55+ array( 'class' => 'spans' ),
 56+ 'th',
 57+ array( 'count' => 'colspan', 'name' => 0 )
 58+ ) .
 59+ qp_Renderer::displayRow(
 60+ array_map( array( $this, 'categoryentities' ), $ctrl->Categories ),
 61+ array(),
 62+ 'th',
 63+ array( 'name' => 0 )
 64+ );
4965 # multiple choice polls doesn't use real spans, instead, every column is like "span"
5066 $spansUsed = count( $ctrl->CategorySpans ) > 0 || $ctrl->type == "multipleChoice";
5167 foreach ( $ctrl->ProposalText as $propkey => &$proposal_text ) {
@@ -83,25 +99,25 @@
84100 }
85101
86102 /**
87 - * @return string html representation of question statistics for Special:Pollresults output
 103+ * @return string html representation of question statistics
88104 */
89 - function displayQuestionStats( qp_SpecialPage $page, $pid ) {
 105+ public function displayStats( qp_SpecialPage $page, $pid ) {
90106 $ctrl = $this->ctrl;
91107 $current_title = $page->getTitle();
92 - $output = "<br />\n<b>" . $ctrl->question_id . ".</b> " . qp_Setup::entities( $ctrl->CommonQuestion ) . "<br />\n";
93 - $output .= "<div class=\"qpoll\">\n" . "<table class=\"qdata\">\n";
94 - $output .= qp_Renderer::displayRow(
95 - array_map( array( $this, 'categoryentities' ), $ctrl->CategorySpans ),
96 - array( 'class' => 'spans' ),
97 - 'th',
98 - array( 'count' => 'colspan', 'name' => 0 )
99 - );
100 - $output .= qp_Renderer::displayRow(
101 - array_map( array( $this, 'categoryentities' ), $ctrl->Categories ),
102 - array(),
103 - 'th',
104 - array( 'name' => 0 )
105 - );
 108+ $output = $this->displayHeader() .
 109+ "<div class=\"qpoll\">\n" . "<table class=\"qdata\">\n" .
 110+ qp_Renderer::displayRow(
 111+ array_map( array( $this, 'categoryentities' ), $ctrl->CategorySpans ),
 112+ array( 'class' => 'spans' ),
 113+ 'th',
 114+ array( 'count' => 'colspan', 'name' => 0 )
 115+ ) .
 116+ qp_Renderer::displayRow(
 117+ array_map( array( $this, 'categoryentities' ), $ctrl->Categories ),
 118+ array(),
 119+ 'th',
 120+ array( 'name' => 0 )
 121+ );
106122 # multiple choice polls doesn't use real spans, instead, every column is like "span"
107123 $spansUsed = count( $ctrl->CategorySpans ) > 0 || $ctrl->type == "multipleChoice";
108124 foreach ( $ctrl->ProposalText as $propkey => &$proposal_text ) {
Index: trunk/extensions/QPoll/view/results/qp_textquestiondataresults.php
@@ -15,9 +15,10 @@
1616 /**
1717 * @return string html representation of user vote for Special:Pollresults output
1818 */
19 - function displayUserQuestionVote() {
 19+ function displayUserVote() {
2020 $ctrl = $this->ctrl;
21 - $output = "<div class=\"qpoll\">\n" . "<table class=\"qdata\">\n";
 21+ $output = $this->displayHeader();
 22+ $output .= "<div class=\"qpoll\">\n" . "<table class=\"qdata\">\n";
2223 foreach ( $ctrl->ProposalText as $propkey => &$serialized_tokens ) {
2324 if ( !is_array( $dbtokens = unserialize( $serialized_tokens ) ) ) {
2425 throw new MWException( 'dbtokens is not an array in ' . __METHOD__ );
@@ -112,7 +113,7 @@
113114 /**
114115 * @return string html representation of question statistics for Special:Pollresults output
115116 */
116 - function displayQuestionStats( qp_SpecialPage $page, $pid ) {
 117+ function displayStats( qp_SpecialPage $page, $pid ) {
117118 return 'todo: implement';
118119 }
119120
Index: trunk/extensions/QPoll/view/xls/qp_xlstabularquestion.php
@@ -12,8 +12,12 @@
1313 }
1414
1515 function writeHeader() {
16 - $this->write( 0, $this->qdata->question_id, 'heading' );
17 - $this->writeLn( 1, $this->qdata->CommonQuestion, 'heading' );
 16+ $col = 0;
 17+ $this->write( $col++, $this->qdata->question_id, 'heading' );
 18+ if ( $this->qdata->name !== null ) {
 19+ $this->write( $col, $this->qdata->name, 'heading' );
 20+ }
 21+ $this->writeLn( ++$col, $this->qdata->CommonQuestion, 'heading' );
1822 if ( count( $this->qdata->CategorySpans ) > 0 ) {
1923 $row = array();
2024 foreach ( $this->qdata->CategorySpans as &$span ) {

Status & tagging log