Index: trunk/extensions/QPoll/i18n/qp.i18n.php |
— | — | @@ -103,6 +103,7 @@ |
104 | 104 | 'qp_error_address_in_decl_mode' => 'Cannot get an address of the poll in declaration mode.', |
105 | 105 | 'qp_error_question_not_implemented' => 'Questions of such type are not implemented: $1.', |
106 | 106 | 'qp_error_invalid_question_type' => 'Invalid question type: $1.', |
| 107 | + 'qp_error_invalid_question_name' => 'Invalid question name: $1.', |
107 | 108 | 'qp_error_type_in_stats_mode' => 'Question type cannot be defined in statistical display mode: $1.', |
108 | 109 | 'qp_error_no_poll_id' => 'Poll tag has no id attribute defined.', |
109 | 110 | 'qp_error_invalid_poll_id' => 'Invalid poll id (id=$1). |
— | — | @@ -227,6 +228,7 @@ |
228 | 229 | 'qp_error_address_in_decl_mode' => 'Poll "address" attribute is meaningless in poll declaration / voting mode.', |
229 | 230 | '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.', |
230 | 231 | '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.}}', |
231 | 233 | 'qp_error_type_in_stats_mode' => 'Question\'s "type" xml-like attribute is meaningless in statistical display mode.', |
232 | 234 | 'qp_error_no_poll_id' => 'Every poll definition in declaration / voting mode must have "id" attribute.', |
233 | 235 | 'qp_error_too_long_dependance_value' => 'Parameters: |
— | — | @@ -2755,6 +2757,7 @@ |
2756 | 2758 | 'qp_error_address_in_decl_mode' => 'Недопустимо задавать адрес опроса (address) в режиме определения', |
2757 | 2759 | 'qp_error_question_not_implemented' => 'Вопросы данного типа не реализованы в коде расширения: $1', |
2758 | 2760 | 'qp_error_invalid_question_type' => 'Недопустимый тип вопроса: $1', |
| 2761 | + 'qp_error_invalid_question_name' => 'Недопустимое имя вопроса: $1', |
2759 | 2762 | 'qp_error_type_in_stats_mode' => 'Недопустимо определять тип вопроса в статистическом режиме: $1', |
2760 | 2763 | 'qp_error_no_poll_id' => 'Тэг опроса не имеет атрибута id.', |
2761 | 2764 | 'qp_error_invalid_poll_id' => 'Недопустимый идентификатор опроса (id=$1). Идентификатор опроса может содержать только буквы, цифры и символ пробела', |
Index: trunk/extensions/QPoll/maintenance/qp_schemaupdater.php |
— | — | @@ -105,6 +105,9 @@ |
106 | 106 | ), |
107 | 107 | 'qpoll_structured_interpretation.src' => array( |
108 | 108 | 'qp_users_polls' => array( 'structured_interpretation' ) |
| 109 | + ), |
| 110 | + 'qpoll_question_name.src' => array( |
| 111 | + 'qp_question_desc' => array( 'name' ) |
109 | 112 | ) |
110 | 113 | ); |
111 | 114 | |
Index: trunk/extensions/QPoll/clientside/qp_results.css |
— | — | @@ -1,4 +1,6 @@ |
2 | 2 | .qpoll .head {font-weight:bold; color:Gray;} |
| 3 | +.qpoll .question_id {font-weight:bold;} |
| 4 | +.qpoll .question_name {font-weight:bold; color:blue;} |
3 | 5 | .qpoll table.qdata {border-collapse: collapse;} |
4 | 6 | .qpoll table.qdata th {border: 1px Gray solid; background-color:LightGrey; padding: 3px;} |
5 | 7 | .qpoll table.qdata tr.spans { color:Navy; } |
Index: trunk/extensions/QPoll/model/cache/qp_questioncache.php |
— | — | @@ -17,7 +17,7 @@ |
18 | 18 | # DB index for replace |
19 | 19 | protected $replaceIndex = 'question'; |
20 | 20 | # 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' ); |
22 | 22 | |
23 | 23 | protected function numRowsToAssocRows() { |
24 | 24 | # build DBMS-like object rows from array rows |
— | — | @@ -77,17 +77,24 @@ |
78 | 78 | protected function buildReplaceRows() { |
79 | 79 | global $wgContLang; |
80 | 80 | $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; |
85 | 91 | # instead of calling $this->updateFromPollStore(), |
86 | 92 | # we build $this->memc_rows[] right here, |
87 | 93 | # to avoid double loop against self::$store->Questions |
88 | 94 | $this->memc_rows[] = array( |
89 | 95 | $qkey, |
90 | | - $ques->type, |
91 | | - $common_question |
| 96 | + $qdata->type, |
| 97 | + $common_question, |
| 98 | + $qdata->name |
92 | 99 | ); |
93 | 100 | } |
94 | 101 | } |
Index: trunk/extensions/QPoll/model/cache/qp_categorycache.php |
— | — | @@ -26,9 +26,9 @@ |
27 | 27 | protected function buildReplaceRows() { |
28 | 28 | global $wgContLang; |
29 | 29 | $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 ) { |
33 | 33 | $cat_name = $Cat['name']; |
34 | 34 | $this->replace[] = array( 'pid' => $pid, 'question_id' => $qkey, 'cat_id' => $catkey, 'cat_name' => $cat_name ); |
35 | 35 | # instead of calling $this->updateFromPollStore(), |
— | — | @@ -40,7 +40,7 @@ |
41 | 41 | $cat_name |
42 | 42 | ); |
43 | 43 | } |
44 | | - $ques->restoreSpans(); |
| 44 | + $qdata->restoreSpans(); |
45 | 45 | } |
46 | 46 | } |
47 | 47 | |
Index: trunk/extensions/QPoll/model/cache/qp_proposalcache.php |
— | — | @@ -26,10 +26,10 @@ |
27 | 27 | protected function buildReplaceRows() { |
28 | 28 | global $wgContLang; |
29 | 29 | $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; |
34 | 34 | } |
35 | 35 | $ptext = $wgContLang->truncate( $ptext, qp_Setup::$field_max_len['proposal_text'] , '' ); |
36 | 36 | $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 @@ |
26 | 26 | ## common properties |
27 | 27 | var $type; |
28 | 28 | var $CommonQuestion; |
| 29 | + var $name = null; |
29 | 30 | var $Categories; |
30 | 31 | var $CategorySpans; |
31 | 32 | var $ProposalText; |
— | — | @@ -55,6 +56,7 @@ |
56 | 57 | $this->question_id = $argv['qid']; |
57 | 58 | $this->type = $argv['type']; |
58 | 59 | $this->CommonQuestion = $argv['common_question']; |
| 60 | + $this->name = $argv['name']; |
59 | 61 | $this->Categories = array(); |
60 | 62 | $this->CategorySpans = array(); |
61 | 63 | $this->ProposalText = array(); |
— | — | @@ -203,6 +205,7 @@ |
204 | 206 | $this->question_id = $question->mQuestionId; |
205 | 207 | $this->type = $question->mType; |
206 | 208 | $this->CommonQuestion = $question->mCommonQuestion; |
| 209 | + $this->name = $question->mName; |
207 | 210 | $this->Categories = $question->mCategories; |
208 | 211 | $this->CategorySpans = $question->mCategorySpans; |
209 | 212 | $this->ProposalText = $question->mProposalText; |
Index: trunk/extensions/QPoll/model/qp_pollstore.php |
— | — | @@ -356,11 +356,12 @@ |
357 | 357 | if ( isset( $typeFromVer0_5[$row->type] ) ) { |
358 | 358 | $row->type = $typeFromVer0_5[$row->type]; |
359 | 359 | } |
360 | | - # create qp_QuestionData object from DB fields |
| 360 | + # create qp_QuestionData object from question description DB row |
361 | 361 | $this->Questions[$question_id] = qp_QuestionData::factory( array( |
362 | 362 | 'qid' => $question_id, |
363 | 363 | 'type' => $row->type, |
364 | | - 'common_question' => $row->common_question ) |
| 364 | + 'common_question' => $row->common_question, |
| 365 | + 'name' => $row->name ) |
365 | 366 | ); |
366 | 367 | } |
367 | 368 | $this->getCategories(); |
— | — | @@ -964,7 +965,11 @@ |
965 | 966 | $questions[$propkey] = $proposals; |
966 | 967 | } |
967 | 968 | } |
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 | + } |
969 | 974 | } |
970 | 975 | |
971 | 976 | # interpret the poll answer to get interpretation answer |
— | — | @@ -978,8 +983,8 @@ |
979 | 984 | */ |
980 | 985 | private function setAnswers( $db ) { |
981 | 986 | $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 ) { |
984 | 989 | foreach ( $prop_answers as $idkey => $catkey ) { |
985 | 990 | $insert[] = array( |
986 | 991 | 'uid' => $this->last_uid, |
— | — | @@ -987,7 +992,7 @@ |
988 | 993 | 'question_id' => $qkey, |
989 | 994 | 'proposal_id' => $propkey, |
990 | 995 | 'cat_id' => $catkey, |
991 | | - 'text_answer' => $ques->ProposalCategoryText[$propkey][$idkey] |
| 996 | + 'text_answer' => $qdata->ProposalCategoryText[$propkey][$idkey] |
992 | 997 | ); |
993 | 998 | } |
994 | 999 | } |
Index: trunk/extensions/QPoll/specials/qp_results.php |
— | — | @@ -98,7 +98,7 @@ |
99 | 99 | self::$PollsLink = $this->qpLink( $this->getTitle(), wfMsg( 'qp_polls_list' ), array( "style" => "font-weight:bold;" ) ); |
100 | 100 | } |
101 | 101 | $wgOut->addHTML( '<div class="qpoll">' ); |
102 | | - $output = ""; |
| 102 | + $output = ''; |
103 | 103 | $this->setHeaders(); |
104 | 104 | $cmd = $wgRequest->getVal( 'action' ); |
105 | 105 | if ( $cmd === null ) { |
— | — | @@ -115,9 +115,9 @@ |
116 | 116 | case 'stats': |
117 | 117 | if ( $pid !== null ) { |
118 | 118 | $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 ); |
122 | 122 | } |
123 | 123 | break; |
124 | 124 | case 'stats_xls': |
— | — | @@ -132,9 +132,9 @@ |
133 | 133 | if ( $pid !== null && $uid !== null ) { |
134 | 134 | $pid = intval( $pid ); |
135 | 135 | $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 ); |
139 | 139 | } |
140 | 140 | break; |
141 | 141 | case 'qpcusers': |
— | — | @@ -183,12 +183,12 @@ |
184 | 184 | ); |
185 | 185 | $interpTitle = $pollStore->getInterpTitle(); |
186 | 186 | if ( !( $interpTitle instanceof Title ) ) { |
187 | | - $tags[] = wfMsg( 'qp_poll_has_no_interpretation' ); |
| 187 | + $tags[] = array( '__tag' => 'div', wfMsg( 'qp_poll_has_no_interpretation' ) ); |
188 | 188 | return $tags; |
189 | 189 | } |
190 | 190 | # 'parentheses' key is unavailable in MediaWiki 1.15.x |
191 | 191 | $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 ) ); |
193 | 193 | $interpResultView = new qp_InterpResultView( true ); |
194 | 194 | $interpResultView->showInterpResults( $tags, $pollStore->interpResult, true ); |
195 | 195 | return $tags; |
— | — | @@ -216,23 +216,22 @@ |
217 | 217 | $poll_title = $pollStore->getTitle(); |
218 | 218 | # 'parentheses' key is unavailable in MediaWiki 1.15.x |
219 | 219 | $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"; |
222 | 220 | $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 ); |
224 | 224 | unset( $headerTags ); |
225 | 225 | foreach ( $pollStore->Questions as $qdata ) { |
226 | 226 | if ( $pollStore->isUsedQuestion( $qdata->question_id ) ) { |
227 | | - $output .= "<br />\n<b>" . $qdata->question_id . ".</b> " . qp_Setup::entities( $qdata->CommonQuestion ) . "<br />\n"; |
228 | 227 | $qview = $qdata->getView(); |
229 | | - $output .= $qview->displayUserQuestionVote(); |
| 228 | + $output .= $qview->displayUserVote(); |
230 | 229 | } |
231 | 230 | } |
232 | 231 | return $output; |
233 | 232 | } |
234 | 233 | |
235 | 234 | private function showStats( $pid ) { |
236 | | - $output = ""; |
| 235 | + $output = ''; |
237 | 236 | if ( $pid !== null ) { |
238 | 237 | $pollStore = new qp_PollStore( array( 'from' => 'pid', 'pid' => $pid ) ); |
239 | 238 | if ( $pollStore->pid !== null ) { |
— | — | @@ -248,12 +247,28 @@ |
249 | 248 | $interp_link = $this->qpLink( $interpTitle, $interpTitle->getPrefixedText() ); |
250 | 249 | $output .= wfMsg( 'qp_browse_to_interpretation', $interp_link ) . "<br />\n"; |
251 | 250 | } |
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"; |
255 | 270 | foreach ( $pollStore->Questions as $qdata ) { |
256 | 271 | $qview = $qdata->getView(); |
257 | | - $output .= $qview->displayQuestionStats( $this, $pid ); |
| 272 | + $output .= $qview->displayStats( $this, $pid ); |
258 | 273 | } |
259 | 274 | } |
260 | 275 | } |
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 @@ |
79 | 79 | * possible xml-like attributes the question may have |
80 | 80 | */ |
81 | 81 | var $questionAttributeKeys = array( |
82 | | - 't[yi]p[eo]', 'layout', 'textwidth', 'propwidth', 'showresults' |
| 82 | + 't[yi]p[eo]', 'name', 'layout', 'textwidth', 'propwidth', 'showresults' |
83 | 83 | ); |
84 | 84 | |
85 | 85 | /** |
Index: trunk/extensions/QPoll/ctrl/poll/qp_poll.php |
— | — | @@ -389,6 +389,18 @@ |
390 | 390 | if ( !array_key_exists( $type, qp_Setup::$questionTypes ) ) { |
391 | 391 | $error_msg = wfMsg( 'qp_error_invalid_question_type', qp_Setup::entities( $type ) ); |
392 | 392 | } |
| 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 | + } |
393 | 405 | } else { |
394 | 406 | $error_msg = wfMsg( 'qp_error_in_question_header', qp_Setup::entities( $header ) ); |
395 | 407 | } |
— | — | @@ -397,7 +409,8 @@ |
398 | 410 | $question = new qp_StubQuestion( |
399 | 411 | $this, |
400 | 412 | qp_StubQuestionView::newFromBaseView( $this->view ), |
401 | | - ++$this->mQuestionId |
| 413 | + ++$this->mQuestionId, |
| 414 | + $name |
402 | 415 | ); |
403 | 416 | $question->setState( 'error', $error_msg ); |
404 | 417 | return $question; |
— | — | @@ -407,7 +420,8 @@ |
408 | 421 | $question = new $qt['ctrl']( |
409 | 422 | $this, |
410 | 423 | call_user_func( array( $qt['view'], 'newFromBaseView' ), $this->view ), |
411 | | - ++$this->mQuestionId |
| 424 | + ++$this->mQuestionId, |
| 425 | + $name |
412 | 426 | ); |
413 | 427 | # set the question type and subtype corresponding to the header 'type' attribute |
414 | 428 | $question->mType = $qt['mType']; |
Index: trunk/extensions/QPoll/ctrl/qp_interpresult.php |
— | — | @@ -74,13 +74,19 @@ |
75 | 75 | } |
76 | 76 | |
77 | 77 | /** |
78 | | - * set question / proposal error message (for quizes) |
| 78 | + * Set question / proposal error message (for quizes). |
79 | 79 | * |
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) |
85 | 91 | */ |
86 | 92 | function setQPCerror( $msg, $qidx, $pidx, $cidx = null ) { |
87 | 93 | if ( !is_array( $this->qpcErrors ) ) { |
Index: trunk/extensions/QPoll/ctrl/question/qp_abstractquestion.php |
— | — | @@ -40,9 +40,12 @@ |
41 | 41 | /** |
42 | 42 | * Constructor |
43 | 43 | * @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 |
47 | 50 | */ |
48 | 51 | function __construct( qp_AbstractPoll $poll, qp_AbstractView $view, $questionId ) { |
49 | 52 | # the question collection is not sparce by default |
Index: trunk/extensions/QPoll/ctrl/question/qp_tabularquestion.php |
— | — | @@ -13,12 +13,18 @@ |
14 | 14 | /** |
15 | 15 | * Constructor |
16 | 16 | * @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 |
20 | 26 | */ |
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 ); |
23 | 29 | $this->mProposalPattern = '`^[^\|\!].*`u'; |
24 | 30 | $this->mCategoryPattern = '`^\|(\n|[^\|].*\n)`u'; |
25 | 31 | } |
Index: trunk/extensions/QPoll/ctrl/question/qp_stubquestion.php |
— | — | @@ -12,6 +12,9 @@ |
13 | 13 | */ |
14 | 14 | class qp_StubQuestion extends qp_AbstractQuestion { |
15 | 15 | |
| 16 | + # optional question literal name, used to address questions in interpretation scripts |
| 17 | + var $mName = null; |
| 18 | + |
16 | 19 | # current user voting taken from POST data (if available) |
17 | 20 | var $mProposalCategoryId = Array(); // user true/false answers to the question's proposal |
18 | 21 | var $mProposalCategoryText = Array(); // user text answers to the question's proposal |
— | — | @@ -22,14 +25,25 @@ |
23 | 26 | /** |
24 | 27 | * Constructor |
25 | 28 | * @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 |
29 | 38 | */ |
30 | | - function __construct( qp_AbstractPoll $poll, qp_StubQuestionView $view, $questionId ) { |
| 39 | + function __construct( qp_AbstractPoll $poll, qp_StubQuestionView $view, $questionId, $name ) { |
31 | 40 | parent::__construct( $poll, $view, $questionId ); |
| 41 | + $this->mName = $name; |
32 | 42 | } |
33 | 43 | |
| 44 | + function getQuestionKey() { |
| 45 | + return $this->mName === null ? $this->mQuestionId : $this->mName; |
| 46 | + } |
| 47 | + |
34 | 48 | # load some question fields from qp_QuestionData given |
35 | 49 | # (usually qp_QuestionData is an array property of qp_PollStore instance) |
36 | 50 | # @param $qdata - an instance of qp_QuestionData |
— | — | @@ -102,11 +116,14 @@ |
103 | 117 | */ |
104 | 118 | function getInterpErrors() { |
105 | 119 | $interpResult = $this->poll->pollStore->interpResult; |
106 | | - if ( !is_array( $interpResult->qpcErrors ) || |
107 | | - !isset( $interpResult->qpcErrors[$this->mQuestionId] ) ) { |
| 120 | + if ( !is_array( $interpResult->qpcErrors ) ) { |
108 | 121 | return false; |
109 | 122 | } |
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; |
111 | 128 | } |
112 | 129 | |
113 | 130 | /** |
Index: trunk/extensions/QPoll/qp_user.php |
— | — | @@ -255,6 +255,8 @@ |
256 | 256 | # limited due to performance improvements (to fit into DB row), |
257 | 257 | # and also to properly truncate UFT8 tails: |
258 | 258 | 'common_question' => 768, |
| 259 | + # limited to maximal length of DB field |
| 260 | + 'question_name' => 255, |
259 | 261 | # 'proposal_text' is not longer than DB field size (65535), |
260 | 262 | # otherwise unserialization of question type="text" proposal parts and |
261 | 263 | # category fields will be invalid: |
Index: trunk/extensions/QPoll/interpretation/qp_interpret.php |
— | — | @@ -117,21 +117,17 @@ |
118 | 118 | if ( isset( $result['error'] ) && is_array( $result['error'] ) ) { |
119 | 119 | # initialize $interpResult->qpcErrors[] member array |
120 | 120 | foreach ( $result['error'] as $qidx => $question ) { |
121 | | - if ( is_int( $qidx ) && is_array( $question ) ) { |
| 121 | + if ( is_array( $question ) ) { |
122 | 122 | foreach ( $question as $pidx => $prop_error ) { |
123 | 123 | # 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 ); |
135 | 128 | } |
| 129 | + } else { |
| 130 | + # error message for the whole proposal line |
| 131 | + $interpResult->setQPCerror( $prop_error, $qidx, $pidx ); |
136 | 132 | } |
137 | 133 | } |
138 | 134 | } |
Index: trunk/extensions/QPoll/tables/qpoll_trunk.sql |
— | — | @@ -26,6 +26,7 @@ |
27 | 27 | `question_id` int unsigned NOT NULL, |
28 | 28 | `type` tinytext NOT NULL, |
29 | 29 | `common_question` text NOT NULL, |
| 30 | + `name` tinytext default NULL, |
30 | 31 | PRIMARY KEY question (pid,question_id), |
31 | 32 | INDEX poll (pid) |
32 | 33 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
— | — | @@ -61,7 +62,7 @@ |
62 | 63 | `text_answer` text, |
63 | 64 | PRIMARY KEY answer (uid,pid,question_id,proposal_id,cat_id), |
64 | 65 | INDEX user_vote (uid,pid), |
65 | | - INDEX poll_question (pid,question_id) |
| 66 | + INDEX poll_question (pid,question_id), |
66 | 67 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
67 | 68 | |
68 | 69 | DROP TABLE IF EXISTS `qp_users_polls`; |
Index: trunk/extensions/QPoll/view/results/qp_questiondataresults.php |
— | — | @@ -28,23 +28,39 @@ |
29 | 29 | } |
30 | 30 | |
31 | 31 | /** |
32 | | - * @return string html representation of user vote for Special:Pollresults output |
| 32 | + * @return string html representation of question header |
33 | 33 | */ |
34 | | - function displayUserQuestionVote() { |
| 34 | + public function displayHeader() { |
35 | 35 | $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 | + ); |
49 | 65 | # multiple choice polls doesn't use real spans, instead, every column is like "span" |
50 | 66 | $spansUsed = count( $ctrl->CategorySpans ) > 0 || $ctrl->type == "multipleChoice"; |
51 | 67 | foreach ( $ctrl->ProposalText as $propkey => &$proposal_text ) { |
— | — | @@ -83,25 +99,25 @@ |
84 | 100 | } |
85 | 101 | |
86 | 102 | /** |
87 | | - * @return string html representation of question statistics for Special:Pollresults output |
| 103 | + * @return string html representation of question statistics |
88 | 104 | */ |
89 | | - function displayQuestionStats( qp_SpecialPage $page, $pid ) { |
| 105 | + public function displayStats( qp_SpecialPage $page, $pid ) { |
90 | 106 | $ctrl = $this->ctrl; |
91 | 107 | $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 | + ); |
106 | 122 | # multiple choice polls doesn't use real spans, instead, every column is like "span" |
107 | 123 | $spansUsed = count( $ctrl->CategorySpans ) > 0 || $ctrl->type == "multipleChoice"; |
108 | 124 | foreach ( $ctrl->ProposalText as $propkey => &$proposal_text ) { |
Index: trunk/extensions/QPoll/view/results/qp_textquestiondataresults.php |
— | — | @@ -15,9 +15,10 @@ |
16 | 16 | /** |
17 | 17 | * @return string html representation of user vote for Special:Pollresults output |
18 | 18 | */ |
19 | | - function displayUserQuestionVote() { |
| 19 | + function displayUserVote() { |
20 | 20 | $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"; |
22 | 23 | foreach ( $ctrl->ProposalText as $propkey => &$serialized_tokens ) { |
23 | 24 | if ( !is_array( $dbtokens = unserialize( $serialized_tokens ) ) ) { |
24 | 25 | throw new MWException( 'dbtokens is not an array in ' . __METHOD__ ); |
— | — | @@ -112,7 +113,7 @@ |
113 | 114 | /** |
114 | 115 | * @return string html representation of question statistics for Special:Pollresults output |
115 | 116 | */ |
116 | | - function displayQuestionStats( qp_SpecialPage $page, $pid ) { |
| 117 | + function displayStats( qp_SpecialPage $page, $pid ) { |
117 | 118 | return 'todo: implement'; |
118 | 119 | } |
119 | 120 | |
Index: trunk/extensions/QPoll/view/xls/qp_xlstabularquestion.php |
— | — | @@ -12,8 +12,12 @@ |
13 | 13 | } |
14 | 14 | |
15 | 15 | 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' ); |
18 | 22 | if ( count( $this->qdata->CategorySpans ) > 0 ) { |
19 | 23 | $row = array(); |
20 | 24 | foreach ( $this->qdata->CategorySpans as &$span ) { |