http://www.mediawiki.org/wiki/Special:Code/MediaWiki/97611

Revision: 97611
Author:   questpc
Date:     2011-09-20 09:25:33 +0000 (Tue, 20 Sep 2011)
Log Message:
-----------
Implemented optional propwidth attribute. layout, propwidth, textwidth 
attributes now can be specified for qpoll tag, applied to the questions which 
do not define these attributes. Truncate too long proposaltext line to UTF-8 
characters bounds.

Modified Paths:
--------------
    trunk/extensions/QPoll/clientside/qp_user.css
    trunk/extensions/QPoll/ctrl/qp_abstractpoll.php
    trunk/extensions/QPoll/ctrl/qp_abstractquestion.php
    trunk/extensions/QPoll/ctrl/qp_tabularquestion.php
    trunk/extensions/QPoll/ctrl/qp_textquestion.php
    trunk/extensions/QPoll/qp_pollstore.php
    trunk/extensions/QPoll/qp_user.php
    trunk/extensions/QPoll/view/qp_stubquestionview.php
    trunk/extensions/QPoll/view/qp_tabularquestionview.php
    trunk/extensions/QPoll/view/qp_textquestionview.php

Modified: trunk/extensions/QPoll/clientside/qp_user.css
===================================================================
--- trunk/extensions/QPoll/clientside/qp_user.css       2011-09-20 09:20:27 UTC 
(rev 97610)
+++ trunk/extensions/QPoll/clientside/qp_user.css       2011-09-20 09:25:33 UTC 
(rev 97611)
@@ -43,7 +43,7 @@
 .qpoll .line_numbers { font-family: monospace, "Courier New"; white-space:pre; 
color:green; background-color: lightgray; float:left; border-left: 1px solid 
darkgray; border-top: 1px solid darkgray; border-bottom: 1px solid darkgray; 
padding: 0.5em; }
 .qpoll .script_view { font-family: monospace, "Courier New"; white-space:pre; 
overflow:auto; color:black; background-color: lightgray; border-right: 1px 
solid darkgray; border-top: 1px solid darkgray; border-bottom: 1px solid 
darkgray; padding: 0.5em; }
 
-/* LTR part (RTL is included from separate file */
+/* LTR part (RTL is included from separate file) */
 .qpoll .attempts_counter{ border: 1px solid gray; padding: 0.1em 0.5em 0.1em 
0.5em; color: black; background-color: lightblue; margin-left: 1em; }
 .qpoll .question { margin-left:2em; }
 .qpoll .margin { padding-left:20px; }

Modified: trunk/extensions/QPoll/ctrl/qp_abstractpoll.php
===================================================================
--- trunk/extensions/QPoll/ctrl/qp_abstractpoll.php     2011-09-20 09:20:27 UTC 
(rev 97610)
+++ trunk/extensions/QPoll/ctrl/qp_abstractpoll.php     2011-09-20 09:25:33 UTC 
(rev 97611)
@@ -67,13 +67,26 @@
 
        # current state of poll parsing (no error)
        var $mState = '';
-       # // true, when the poll is posted (answered)
+       # true, when the poll is posted (answered)
        var $mBeingCorrected = false;
 
-       // qp_pollStore instance that will be used to transfer poll data 
from/to DB
+       # qp_pollStore instance that will be used to transfer poll data from/to 
DB
        var $pollStore = null;
 
        /**
+        * default values of 'propwidth', 'textwidth' and 'layout' attributes
+        * will be applied to child questions that do not have these attributes 
defined
+        *
+        * 'showresults' currently is handled separately, because it has 
"multivalue"
+        * and can be partially merged from poll to question
+        */
+       var $defaultQuestionAttributes = array(
+               'propwidth' => null,
+               'layout' => null,
+               'textwidth' => null
+       );
+
+       /**
         * Constructor
         *
         * @public
@@ -102,7 +115,12 @@
                        }
                        $this->view->showResults = self::parseShowResults( 
$argv['showresults'] );
                }
-
+               # get default question attributes, if any
+               foreach ( $this->defaultQuestionAttributes as $attr => &$val ) {
+                       if ( array_key_exists( $attr, $argv ) ) {
+                               $val = $argv[$attr];
+                       }
+               }
                # every poll on the page should have unique poll id, to 
minimize the risk of collisions
                # it is required to be set manually via id="value" parameter
                # ( used only in "declaration" mode )
@@ -203,18 +221,23 @@
 
        /**
         * Parses attribute line of the question
-        * @param    $attr_str  attribute string from poll's header
+        * @param    $attr_str  attribute string from questions header
         * @modifies $paramkeys  array  key is attribute regexp, value is the 
value of attribute
         * @return   string  the value of question's type attribute
         */
        function getQuestionAttributes( $attr_str, &$paramkeys ) {
-               $paramkeys = array( 't[yi]p[eo]' => null, 'layout' => null, 
'textwidth' => null, 'showresults' => null );
+               $paramkeys = array( 't[yi]p[eo]' => null, 'layout' => null, 
'textwidth' => null, 'propwidth' => null, 'showresults' => null );
                foreach ( $paramkeys as $key => &$val ) {
                        preg_match( '`' . $key . '?="(.*?)"`u', $attr_str, $val 
);
+                       $val = ( count( $val ) > 1 ) ? $val[1] : null;
                }
-               $type = $paramkeys[ 't[yi]p[eo]' ];
-               $type = isset( $type[1] ) ? trim( $type[1] ) : '';
-               return $type;
+               # apply default question attributes, if any
+               foreach ( $this->defaultQuestionAttributes as $attr => $val ) {
+                       if ( is_null( $paramkeys[$attr] ) ) {
+                               $paramkeys[$attr] = $val;
+                       }
+               }
+               return isset( $paramkeys[ 't[yi]p[eo]' ] ) ? trim( $paramkeys[ 
't[yi]p[eo]' ] ) : '';
        }
 
        // parses source showresults xml parameter value and returns the 
corresponding showResults array

Modified: trunk/extensions/QPoll/ctrl/qp_abstractquestion.php
===================================================================
--- trunk/extensions/QPoll/ctrl/qp_abstractquestion.php 2011-09-20 09:20:27 UTC 
(rev 97610)
+++ trunk/extensions/QPoll/ctrl/qp_abstractquestion.php 2011-09-20 09:25:33 UTC 
(rev 97611)
@@ -89,6 +89,7 @@
        function applyAttributes( $paramkeys ) {
                $this->view->setLayout( $paramkeys[ 'layout' ], $paramkeys[ 
'textwidth' ] );
                $this->view->setShowResults( $paramkeys[ 'showresults' ] );
+               $this->view->setPropWidth( $paramkeys[ 'propwidth' ] );
        }
 
        function getPercents( $proposalId, $catId ) {

Modified: trunk/extensions/QPoll/ctrl/qp_tabularquestion.php
===================================================================
--- trunk/extensions/QPoll/ctrl/qp_tabularquestion.php  2011-09-20 09:20:27 UTC 
(rev 97610)
+++ trunk/extensions/QPoll/ctrl/qp_tabularquestion.php  2011-09-20 09:25:33 UTC 
(rev 97611)
@@ -107,6 +107,9 @@
                # analyze previousely built "raw" categories array
                # Less than two categories is a syntax error.
                if ( $this->mType != 'mixedChoice' && count( $categories ) < 2 
) {
+                       if ( !isset( $categories[0] ) ) {
+                               $categories[0] = '';
+                       }
                        $categories[0] .= $this->view->bodyErrorMessage( wfMsg( 
'qp_error_too_few_categories' ), 'error' );
                }
                foreach ( $categories as $catkey => $category ) {

Modified: trunk/extensions/QPoll/ctrl/qp_textquestion.php
===================================================================
--- trunk/extensions/QPoll/ctrl/qp_textquestion.php     2011-09-20 09:20:27 UTC 
(rev 97610)
+++ trunk/extensions/QPoll/ctrl/qp_textquestion.php     2011-09-20 09:25:33 UTC 
(rev 97611)
@@ -65,7 +65,7 @@
         * @param  $token  string current value of token between pipe separators
         * Also, _optionally_ overrides textwidth property
         */
-       function setLastOption( $token ) {
+       function addToLastOption( $token ) {
                # first entry of category options might be definition of
                # the current category input textwidth instead
                $matches = array();
@@ -76,7 +76,7 @@
                        $this->textwidth = intval( $matches[1] );
                } else {
                        # add new input option
-                       $this->iopt_last .= trim( $token );
+                       $this->iopt_last .= $token;
                }
        }
 
@@ -86,7 +86,7 @@
        function closeCategory() {
                $this->isCatDef = false;
                # prepare new category input choice (text questions have no 
category names)
-               $unique_options = array_unique( $this->input_options, 
SORT_STRING );
+               $unique_options = array_unique( array_map( 'trim', 
$this->input_options ), SORT_STRING );
                $this->input_options = array();
                foreach ( $unique_options as $option ) {
                        # make sure unique elements keys are consequitive 
starting from 0
@@ -189,8 +189,12 @@
                                switch ( $token ) {
                                case '|' :
                                        if ( $opt->isCatDef ) {
-                                               $opt->addEmptyOption();
-                                               $isContinue = true;
+                                               if ( count( $brace_stack ) == 1 
&& $brace_stack[0] === '>>' ) {
+                                                       # pipe char starts new 
option only at top brace level,
+                                                       # with angled braces
+                                                       $opt->addEmptyOption();
+                                                       $isContinue = true;
+                                               }
                                        }
                                        break;
                                case '[[' :
@@ -230,7 +234,7 @@
                                        continue;
                                }
                                if ( $opt->isCatDef ) {
-                                       $opt->setLastOption( $token );
+                                       $opt->addToLastOption( $token );
                                } else {
                                        # add new proposal part
                                        $this->dbtokens[] = strval( $token );
@@ -249,16 +253,24 @@
                                $this->viewtokens->prependErrorToken( wfMsg( 
'qp_error_too_long_proposal_text' ), 'error' );
                        }
                        $this->mProposalText[$proposalId] = $proposal_text;
-                       # If the proposal was submitted but has _any_ 
unanswered category
-                       if ( $this->poll->mBeingCorrected &&
-                                       ( !array_key_exists( $proposalId, 
$this->mProposalCategoryId ) ||
-                                               count( 
$this->mProposalCategoryId[$proposalId] ) !== $catId )
-                               ) {
-                               # todo: apply 'error' style to the whole row
-                               $prev_state = $this->getState();
-                               $this->viewtokens->prependErrorToken( wfMsg( 
'qp_error_no_answer' ), 'NA' );
-                               if ( $prev_state == '' ) {
-                                       # todo: if there was no previous 
errors, hightlight the whole row
+                       if ( $this->poll->mBeingCorrected ) {
+                               # check for unanswered categories
+                               try {
+                                       if ( !array_key_exists( $proposalId, 
$this->mProposalCategoryId ) ) {
+                                               # the whole line is unanswered
+                                               throw new Exception( 'qp_error' 
);
+                                       }
+                                       # how many categories has to be 
answered,
+                                       # all defined in row or at least one?
+                                       $countRequired = ($this->mSubType === 
'requireAllCategories') ? $catId : 1;
+                                       if ( count( 
$this->mProposalCategoryId[$proposalId] ) < $countRequired ) {
+                                               throw new Exception( 'qp_error' 
);
+                                       }
+                               } catch ( Exception $e ) {
+                                       if ( $e->getMessage() == 'qp_error' ) {
+                                               $prev_state = $this->getState();
+                                               
$this->viewtokens->prependErrorToken( wfMsg( 'qp_error_no_answer' ), 'NA' );
+                                       }
                                }
                        }
                        $this->view->addProposal( $proposalId, 
$this->viewtokens->tokenslist );

Modified: trunk/extensions/QPoll/qp_pollstore.php
===================================================================
--- trunk/extensions/QPoll/qp_pollstore.php     2011-09-20 09:20:27 UTC (rev 
97610)
+++ trunk/extensions/QPoll/qp_pollstore.php     2011-09-20 09:25:33 UTC (rev 
97611)
@@ -867,8 +867,8 @@
        private function setProposals() {
                $insert = Array();
                foreach ( $this->Questions as $qkey => &$ques ) {
-                       foreach ( $ques->ProposalText as $propkey => &$ptext ) {
-                               $insert[] = array( 'pid' => $this->pid, 
'question_id' => $qkey, 'proposal_id' => $propkey, 'proposal_text' => $ptext );
+                       foreach ( $ques->ProposalText as $propkey => $ptext ) {
+                               $insert[] = array( 'pid' => $this->pid, 
'question_id' => $qkey, 'proposal_id' => $propkey, 'proposal_text' => 
qp_Setup::limitProposalLength( $ptext ) );
                        }
                }
                if ( count( $insert ) > 0 ) {

Modified: trunk/extensions/QPoll/qp_user.php
===================================================================
--- trunk/extensions/QPoll/qp_user.php  2011-09-20 09:20:27 UTC (rev 97610)
+++ trunk/extensions/QPoll/qp_user.php  2011-09-20 09:25:33 UTC (rev 97611)
@@ -138,30 +138,36 @@
        static $user; // User instance we got from hook parameter
 
        static $questionTypes = array(
-               'mixed' => array(
-                       'ctrl' => 'qp_MixedQuestion',
-                       'view' => 'qp_TabularQuestionView',
-                       'mType' => 'mixedChoice'
-               ),
-               'unique()' => array(
+               '[]' => array(
                        'ctrl' => 'qp_TabularQuestion',
                        'view' => 'qp_TabularQuestionView',
-                       'mType' => 'singleChoice',
-                       'mSubType' => 'unique'
+                       'mType' => 'multipleChoice'
                ),
                '()' => array(
                        'ctrl' => 'qp_TabularQuestion',
                        'view' => 'qp_TabularQuestionView',
                        'mType' => 'singleChoice'
                ),
-               '[]' => array(
+               'unique()' => array(
                        'ctrl' => 'qp_TabularQuestion',
                        'view' => 'qp_TabularQuestionView',
-                       'mType' => 'multipleChoice'
+                       'mType' => 'singleChoice',
+                       'mSubType' => 'unique'
                ),
+               'mixed' => array(
+                       'ctrl' => 'qp_MixedQuestion',
+                       'view' => 'qp_TabularQuestionView',
+                       'mType' => 'mixedChoice'
+               ),
                'text' => array(
                        'ctrl' => 'qp_TextQuestion',
                        'view' => 'qp_TextQuestionView',
+                       'mType' => 'textQuestion',
+                       'mSubType' => 'requireAllCategories'
+               ),
+               'text!' => array(
+                       'ctrl' => 'qp_TextQuestion',
+                       'view' => 'qp_TextQuestionView',
                        'mType' => 'textQuestion'
                )
        );
@@ -207,6 +213,33 @@
        }
 
        /**
+        * Limit the maximum length of proposal line with respect to UTF-8 
character bounds
+        *
+        * Question type=text proposal lengths are additionally checked in the 
question controller
+        * because these are stored in serialized format.
+        * Questions of another types have their proposal texts optionally cut 
down, because
+        * the whole text of proposal is not important.
+        *
+        * @param  $ptext  string  proposal text
+        * @return  string  proposal text either cut down or unaltered
+        */
+       private function limitProposalLength( $ptext ) {
+               if ( strlen( $ptext ) <= self::$proposal_max_length ) {
+                       return $ptext;
+               }
+               for ( $curr_len = self::$proposal_max_length;/* noop 
*/;$curr_len-- ) {
+                       $pcut = substr( $ptext, 0, $curr_len );
+                       if ( mb_substr( $ptext, 0, mb_strlen( $pcut, 'UTF-8' ), 
'UTF-8' ) === $pcut ) {
+                               # valid UTF-8 cut
+                               break;
+                       }
+                       # will decrease the $curr_len until valid cut is 
achieved;
+                       # should not be more than 5 iterations, very often 1..3
+               }
+               return $pcut;
+       }
+
+       /**
         * Autoload classes from the map provided
         */
        static function autoLoad( $map ) {

Modified: trunk/extensions/QPoll/view/qp_stubquestionview.php
===================================================================
--- trunk/extensions/QPoll/view/qp_stubquestionview.php 2011-09-20 09:20:27 UTC 
(rev 97610)
+++ trunk/extensions/QPoll/view/qp_stubquestionview.php 2011-09-20 09:25:33 UTC 
(rev 97611)
@@ -58,6 +58,8 @@
        # proposal views (indexed, sortable rows)
        var $pview = array();
 
+       var $propWidth = '';
+
        /**
         * @param $parser
         * @param $frame
@@ -90,6 +92,10 @@
                /* does nothing */
        }
 
+       function setPropWidth( $propwidth ) {
+               /* does nothing */
+       }
+
        /**
         * @param $tagarray  array / string row to add to the question's header
         */
@@ -186,6 +192,9 @@
         */
        function renderQuestion() {
                $output_table = array( '__tag' => 'table', '__end' => "\n", 
'class' => 'object' );
+               if ( $this->propWidth !== '' ) {
+                       $output_table['style'] = 'width:100%;';
+               }
                # Determine the side border color the question.
                if ( $this->ctrl->getState() != '' ) {
                        if ( isset( $output_table['class'] ) ) {
@@ -206,7 +215,7 @@
                        );
                }
                $tags[] = array( '__tag' => 'div', 0 => $this->rtp( 
$this->ctrl->mCommonQuestion ) );
-               $tags = array( '__tag' => 'div', '__end' => "\n", 'class' => 
'question', $tags );
+               $tags = array( '__tag' => 'div', '__end' => "\n", 'class' => 
'question question_mod4_' . ( $this->ctrl->usedId % 4 ), $tags );
                $tags[] = &$output_table;
                return qp_Renderer::renderTagArray( $tags );
        }

Modified: trunk/extensions/QPoll/view/qp_tabularquestionview.php
===================================================================
--- trunk/extensions/QPoll/view/qp_tabularquestionview.php      2011-09-20 
09:20:27 UTC (rev 97610)
+++ trunk/extensions/QPoll/view/qp_tabularquestionview.php      2011-09-20 
09:25:33 UTC (rev 97611)
@@ -92,9 +92,9 @@
        }
 
        function setLayout( $layout, $textwidth ) {
-               if ( count( $layout ) > 0 ) {
-                       $this->transposed = strpos( $layout[1], 'transpose' ) 
!== false;
-                       $this->proposalsFirst = strpos( $layout[1], 'proposals' 
) !== false;
+               if ( $layout !== null ) {
+                       $this->transposed = strpos( $layout, 'transpose' ) !== 
false;
+                       $this->proposalsFirst = strpos( $layout, 'proposals' ) 
!== false;
                }
                # setup question layout parameters
                if ( $this->transposed ) {
@@ -110,8 +110,8 @@
                        $this->proposalTextStyle = 'vertical-align:middle; ';
                        $this->proposalTextStyle .= ( $this->proposalsFirst ) ? 
'padding-right: 10px;' : 'padding-left: 10px;';
                }
-               if ( count( $textwidth ) > 0 ) {
-                       $textwidth = intval( $textwidth[1] );
+               if ( $textwidth !== null ) {
+                       $textwidth = intval( $textwidth );
                        if ( $textwidth > 0 ) {
                                $this->textInputStyle = 'width:' . $textwidth . 
'em;';
                        }
@@ -120,9 +120,9 @@
 
        function setShowResults( $showresults ) {
                # setup question's showresults when global showresults != 0
-               if ( qp_Setup::$global_showresults != 0 && count( $showresults 
) > 0 ) {
+               if ( qp_Setup::$global_showresults != 0 && $showresults !== 
null ) {
                        # use the value from the question
-                       $this->showResults = qp_AbstractPoll::parseShowResults( 
$showresults[1] );
+                       $this->showResults = qp_AbstractPoll::parseShowResults( 
$showresults );
                        # apply undefined attributes from the poll's 
showresults definition
                        foreach ( $this->pollShowResults as $attr => $val ) {
                                if ( $attr != 'type' && !isset( 
$this->showResults[$attr] ) ) {
@@ -143,6 +143,24 @@
        }
 
        /**
+        * Checks, whether the supplied CSS length value is valid
+        * @return  boolean  true for valid value, false otherwise
+        */
+       function isCSSLengthValid( $width ) {
+               preg_match( '`^\s*(\d+)(px|em|en|%|)\s*$`', $width, $matches );
+               return count( $matches > 1 ) && $matches[1] > 0;
+       }
+
+       function setPropWidth( $attr ) {
+               if ( $attr !== null && $this->isCSSLengthValid( $attr ) ) {
+                       $this->propWidth = trim( $attr );
+               }
+               if ( $this->propWidth !== '' ) {
+                       $this->proposalTextStyle .= " 
width:{$this->propWidth};";
+               }
+       }
+
+       /**
         * Builds tagarray of categories
         * @param     $categories  "raw" categories
         */

Modified: trunk/extensions/QPoll/view/qp_textquestionview.php
===================================================================
--- trunk/extensions/QPoll/view/qp_textquestionview.php 2011-09-20 09:20:27 UTC 
(rev 97610)
+++ trunk/extensions/QPoll/view/qp_textquestionview.php 2011-09-20 09:25:33 UTC 
(rev 97611)
@@ -142,8 +142,8 @@
 
        function setLayout( $layout, $textwidth ) {
                /* todo: implement vertical layout */
-               if ( count( $textwidth ) > 0 ) {
-                       $textwidth = intval( $textwidth[1] );
+               if ( $textwidth !== null ) {
+                       $textwidth = intval( $textwidth );
                        if ( $textwidth > 0 ) {
                                $this->textInputStyle = 'width:' . $textwidth . 
'em;';
                        }
@@ -168,7 +168,10 @@
                foreach ( $viewtokens as $elem ) {
                        if ( is_object( $elem ) ) {
                                if ( isset( $elem->options ) ) {
-                                       $className = $elem->unanswered ? 
'cat_noanswer' : 'cat_part';
+                                       $className = 'cat_part';
+                                       if ( $this->ctrl->mSubType === 
'requireAllCategories' && $elem->unanswered ) {
+                                               $className = 'cat_noanswer';
+                                       }
                                        # create view for the input options part
                                        if ( count( $elem->options ) === 1 ) {
                                                # one option produces html text 
input


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to