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

Revision: 100690
Author:   questpc
Date:     2011-10-25 11:52:58 +0000 (Tue, 25 Oct 2011)
Log Message:
-----------
Fixed regression in question type text when prefilled text inputs were not 
displayed. Added textarea and select multiple view modes for question type text 
categories via introduced height and multiple category attributes. Allow 
unquoted integer value for xml-like attributes.

Modified Paths:
--------------
    trunk/extensions/QPoll/clientside/qp_user.js
    trunk/extensions/QPoll/ctrl/question/qp_textquestion.php
    trunk/extensions/QPoll/qp_user.php
    trunk/extensions/QPoll/specials/qp_results.php
    trunk/extensions/QPoll/view/proposal/qp_textquestionproposalview.php
    trunk/extensions/QPoll/view/question/qp_textquestionview.php

Modified: trunk/extensions/QPoll/clientside/qp_user.js
===================================================================
--- trunk/extensions/QPoll/clientside/qp_user.js        2011-10-25 11:52:39 UTC 
(rev 100689)
+++ trunk/extensions/QPoll/clientside/qp_user.js        2011-10-25 11:52:58 UTC 
(rev 100690)
@@ -48,7 +48,7 @@
                /**
                 * Parses coordinate of poll's input stored in id and
                 * stores it into self.catCoord;
-                * @param  id  id attribute of input / select element
+                * @param  id  id attribute of input / textarea / select element
                 * @return  true, when value of id has valid coordinate, false 
otherwise.
                 */
                setCatCoord : function( id ) {
@@ -87,9 +87,14 @@
                applyRadio : function( catElem ) {
                        if ( self.radioIsClicked ) {
                                // deselect all inputs
-                               if ( catElem.nodeName == 'SELECT' || 
catElem.type == 'text' ) {
+                               if ( catElem.nodeName != 'INPUT' || 
catElem.type == 'text' ) {
+                                       // text controls
                                        catElem.value = '';
+                                       if ( catElem.nodeName == 'TEXTAREA' ) {
+                                               catElem.innerHTML = '';
+                                       }
                                } else {
+                                       // switching controls
                                        catElem.checked = false;
                                }
                        } else {
@@ -160,6 +165,15 @@
                        }
                },
 
+               setTextRowHandler : function( parent, tagName ) {
+                       var tags = parent.getElementsByTagName( tagName );
+                       for ( j = 0; j < tags.length; j++ ) {
+                               if ( tags[j].id && tags[j].id.slice( 0, 2 ) == 
'tx' ) {
+                                       addEvent( tags[j], "click", 
self.clickTextRow );
+                               }
+                       }
+               },
+
                /**
                 * Prepare the Poll for "javascriptable" browsers
                 */
@@ -198,13 +212,8 @@
                                                }
                                        }
                                }
-                               var select = 
bodyContentDiv[i].getElementsByTagName( 'select' );
-                               for ( j = 0; j < select.length; j++ ) {
-                                       // selects currently are used only with 
question type="text", type="text!"
-                                       if ( select[j].id && 
select[j].id.slice( 0, 2 ) == 'tx' ) {
-                                               addEvent( select[j], "click", 
self.clickTextRow );
-                                       }
-                               }
+                               self.setTextRowHandler( bodyContentDiv[i], 
'select' );
+                               self.setTextRowHandler( bodyContentDiv[i], 
'textarea' );
                        }
                }
        };

Modified: trunk/extensions/QPoll/ctrl/question/qp_textquestion.php
===================================================================
--- trunk/extensions/QPoll/ctrl/question/qp_textquestion.php    2011-10-25 
11:52:39 UTC (rev 100689)
+++ trunk/extensions/QPoll/ctrl/question/qp_textquestion.php    2011-10-25 
11:52:58 UTC (rev 100690)
@@ -23,23 +23,34 @@
 
        # whether the current option has xml-like attributes specified
        var $hasAttributes = false;
+       ## Category attributes;
+       #    Defined as xml-like attribute in the first element of options list.
        var $attributes = array(
-               ## a value of input text field width in 'em'
-               # possible values: null, positive int
-               # defined as first element xml-like attribute of options list, 
for example:
-               # <<:: width="12">> or <<:: width="15"|test>>
-               # currently, it is used only for text inputs (not for 
select/option list)
+               ## width of input text field
+               #    possible values: null, positive int, 'auto'
+               # <<:: width="12">> or <<:: width="15"|test>> or <<:: 
width="auto"|asd|fgh>>
+               # currently, it is used only for text input / textarea (not for 
select/option list)
                'width' => null,
+               ## number of text lines in an select / textarea
+               #    possible values: null, positive int, 'auto'
+               #    when there is no options or only one option, it produces 
an textarea
+               #    when there is more than one option, it produces a 
scrollable select / options list
+               #    value 'auto' is meaningful only when there are more than 
one option given;
+               # <<:: height="4">> or <<:: height="10"|prefilled text>>
+               'height' => null,
                ## whether the text options of current category has to be 
sorted;
-               # possible values: null (do not sort), 'asc', 'desc'
-               # defined as first element xml-like attribute of options list, 
for example:
+               #    possible values: null (do not sort), 'asc', 'desc'
                # <<:: sorting="desc"|a|b|c>>
                'sorting' => null,
                ## whether the checkbox type option of current category has to 
be checked by default;
-               # possible value: null (not checked), not null (checked)
-               # defined as first element xml-like attribute of options list, 
for example:
+               #    possible values: null (not checked), not null (checked)
                # <[checked=""]>
-               'checked' => null
+               'checked' => null,
+               ## whether the select for current category can have multiple 
options selected
+               #    possible values: null (no multiple selection), not null 
(multiple selection)
+               #    it is meaningful only for text categories with multiple 
options defined:
+               # <<:: multiple=""|1|2|3>>
+               'multiple' => null
        );
        # a pointer to last element in $this->input_options array
        var $iopt_last;
@@ -103,7 +114,7 @@
                                # because it is checked in 
$this->addEmptyOption()
                                $this->hasAttributes = true;
                                # parse attributes string
-                               $option_attributes = 
qp_Setup::getXmlLikeAttributes( $matches[1], array( 'width', 'sorting' ) );
+                               $option_attributes = 
qp_Setup::getXmlLikeAttributes( $matches[1], array( 'width', 'height', 
'sorting', 'multiple' ) );
                                # apply attributes to current option
                                foreach ( $option_attributes as $attr_name => 
$attr_val ) {
                                        $this->attributes[$attr_name] = 
$attr_val;
@@ -256,14 +267,21 @@
        function loadProposalCategory( qp_TextQuestionOptions $opt, 
$proposalId, $catId ) {
                global $wgContLang;
                $name = "q{$this->mQuestionId}p{$proposalId}s{$catId}";
-               # default value for unanswered category
-               # boolean true "checked" checkbox / radiobutton
-               # string - text input / select option value
-               $text_answer = false;
+               $answered = false;
+               $text_answer = '';
                # try to load from POST data
-               if ( $this->poll->mBeingCorrected && $this->mRequest->getVal( 
$name ) !== null ) {
+               if ( $this->poll->mBeingCorrected &&
+                               ( $ta = $this->mRequest->getArray( $name ) ) 
!== null ) {
                        if ( $opt->type === 'text' ) {
-                               if ( ( $ta = trim( $this->mRequest->getText( 
$name ) ) ) != '' ) {
+                               if ( count( $ta ) === 1 ) {
+                                       # fallback to WebRequest::getText(), 
because it offers useful preprocessing
+                                       $ta = trim( $this->mRequest->getText( 
$name ) );
+                               } else {
+                                       # select multiple values are separated 
with new lines
+                                       $ta = implode( "\n", array_map( 'trim', 
$ta ) );
+                               }
+                               if ( $ta != '' ) {
+                                       $answered = true;
                                        if ( strlen( $ta ) > 
qp_Setup::$field_max_len['text_answer'] ) {
                                                $text_answer = 
$wgContLang->truncate( $ta, qp_Setup::$field_max_len['text_answer'] , '' );
                                        } else {
@@ -271,34 +289,28 @@
                                        }
                                }
                        } else {
-                               $text_answer = true;
+                               $answered = true;
                        }
                }
                # try to load from pollStore
                # pollStore optionally overrides POST data
-               $prev_text_answer = $this->answerExists( $opt->type, 
$proposalId, $catId );
-               if ( is_string( $prev_text_answer ) ) {
-                       $text_answer = $prev_text_answer;
-               } else {
-                       if ( $prev_text_answer === true ) {
-                               $text_answer = true;
+               if ( ( $prev_text_answer = $this->answerExists( $opt->type, 
$proposalId, $catId ) ) !== false ) {
+                       $answered = true;
+                       if ( is_string( $prev_text_answer ) ) {
+                               $text_answer = $prev_text_answer;
                        }
                }
-               if ( $text_answer !== false ) {
+               if ( $answered !== false ) {
                        # add category to the list of user answers for current 
proposal (row)
                        $this->mProposalCategoryId[ $proposalId ][] = $catId;
-                       if ( is_string( $text_answer ) ) {
-                               $this->mProposalCategoryText[ $proposalId ][] = 
$text_answer;
-                       } else {
-                               $this->mProposalCategoryText[ $proposalId ][] = 
'';
-                               if ( $opt->type !== 'text' ) {
-                                       $opt->attributes['checked'] = true;
-                               }
+                       $this->mProposalCategoryText[ $proposalId ][] = 
$text_answer;
+                       if ( $opt->type !== 'text' ) {
+                               $opt->attributes['checked'] = true;
                        }
                }
                # finally, add new category input options for the view
                $opt->closeCategory();
-               $this->propview->addCatDef( $opt, $name, $text_answer, 
$this->poll->mBeingCorrected && $text_answer === false );
+               $this->propview->addCatDef( $opt, $name, $text_answer, 
$this->poll->mBeingCorrected && !$answered );
        }
 
        /**

Modified: trunk/extensions/QPoll/qp_user.php
===================================================================
--- trunk/extensions/QPoll/qp_user.php  2011-10-25 11:52:39 UTC (rev 100689)
+++ trunk/extensions/QPoll/qp_user.php  2011-10-25 11:52:58 UTC (rev 100690)
@@ -469,8 +469,9 @@
                $attr_vals = array();
                $match = array();
                foreach ( $attr_list as $attr_name ) {
-                       preg_match( '/' . $attr_name . '\s?=\s?"(.*?)"/u', 
$attr_str, $match );
-                       $attr_vals[$attr_name] = ( count( $match ) > 1 ) ? 
$match[1] : null;
+                       preg_match( '/' . $attr_name . 
'\s?=\s?(?:"(.*?)"|(\d+))/u', $attr_str, $match );
+                       # array_pop() "prefers" to match (\d+), when available
+                       $attr_vals[$attr_name] = ( count( $match ) > 1 ) ? 
array_pop( $match ) : null;
                }
                return $attr_vals;
        }

Modified: trunk/extensions/QPoll/specials/qp_results.php
===================================================================
--- trunk/extensions/QPoll/specials/qp_results.php      2011-10-25 11:52:39 UTC 
(rev 100689)
+++ trunk/extensions/QPoll/specials/qp_results.php      2011-10-25 11:52:58 UTC 
(rev 100690)
@@ -313,7 +313,12 @@
                        @unlink( $xls_fname );
                        exit();
                } catch ( Exception $e ) {
-                       die( "Error while exporting poll statistics to Excel 
table\n" );
+                       if ( $e instanceof MWException ) {
+                               $e->reportHTML();
+                               exit();
+                       } else {
+                               die( "Error while exporting poll statistics to 
Excel table\n" );
+                       }
                }
        }
 

Modified: trunk/extensions/QPoll/view/proposal/qp_textquestionproposalview.php
===================================================================
--- trunk/extensions/QPoll/view/proposal/qp_textquestionproposalview.php        
2011-10-25 11:52:39 UTC (rev 100689)
+++ trunk/extensions/QPoll/view/proposal/qp_textquestionproposalview.php        
2011-10-25 11:52:58 UTC (rev 100690)
@@ -45,6 +45,8 @@
         * @param  $name  string  name of input/select element (used in the 
view)
         * @param  $text_answer  string user's POSTed category answer
         *         (empty string '' means no answer)
+        * @param  $unanswered  boolean indicates whether the category of 
submitted poll
+        *                      was non-blank (true) or not (false)
         * @return  stdClass object with viewtokens entry
         */
        function addCatDef( qp_TextQuestionOptions $opt, $name, $text_answer, 
$unanswered ) {
@@ -57,10 +59,7 @@
                # property 'value' contains value previousely chosen
                #          by user (if any)
                # property 'attributes' contain extra atttibutes of current 
category definition
-               # property 'unanswered'  boolean
-               #          true - the question was POSTed but category is 
unanswered
-               #          false - the question was not POSTed or category is 
answered
-               $this->viewtokens[] = (object) array(
+               $viewtoken = (object) array(
                        'type' => $opt->type,
                        'options' => $opt->input_options,
                        'name' => $name,
@@ -68,6 +67,21 @@
                        'unanswered' => $unanswered,
                        'attributes' => $opt->attributes
                );
+               # fix values of measurable attributes (allow only non-negative 
integer values)
+               # zero value means attribute is unused
+               foreach ( array( 'width', 'height' ) as $measurable ) {
+                       $val = &$viewtoken->attributes[$measurable];
+                       if ( $val === null ) {
+                               $val = 0;
+                       } elseif ( is_numeric( $val ) ) {
+                               if ( ( $val = intval( $val ) ) < 1 ) {
+                                       $val = 0;
+                               }
+                       } else {
+                               $val = 'auto';
+                       }
+               }
+               $this->viewtokens[] = $viewtoken;
                $this->lastTokenType = 'category';
        }
 

Modified: trunk/extensions/QPoll/view/question/qp_textquestionview.php
===================================================================
--- trunk/extensions/QPoll/view/question/qp_textquestionview.php        
2011-10-25 11:52:39 UTC (rev 100689)
+++ trunk/extensions/QPoll/view/question/qp_textquestionview.php        
2011-10-25 11:52:58 UTC (rev 100690)
@@ -51,6 +51,7 @@
        # depending on $this->tabularDisplay value
        var $row;
        # tagarray with error elements will be merged into adjascent cells
+       # (otherwise tabular layout will be broken by proposal errors)
        var $error;
        # tagarray with current cell builded for row
        # cell contains one or multiple tags, describing proposal part or 
category
@@ -63,6 +64,9 @@
                $this->reset( '' );
        }
 
+       /**
+        * Prepare current instance for new proposal row
+        */
        function reset( $id_prefix ) {
                $this->id_prefix = $id_prefix;
                $this->row = array();
@@ -72,6 +76,9 @@
                $this->ckey = 0;
        }
 
+       /**
+        * Add proposal error tagarray
+        */
        function addError( $elem ) {
                $this->cell[] = array(
                        '__tag' => 'span',
@@ -80,57 +87,98 @@
                );
        }
 
+       /**
+        * Add category as input type text / checkbox / radio / textarea 
tagarray
+        */
        function addInput( $elem, $className ) {
+               $tagName = ( $elem->type === 'text' && 
$elem->attributes['height'] !== 0 ) ? 'textarea' : 'input';
+               $lines_count = 1;
+               # get category value
                $value = $elem->value;
                # check, whether the definition of category has "pre-filled" 
value
                # single, non-unanswered, non-empty option is a pre-filled value
                if ( !$elem->unanswered && $elem->value === '' && 
$elem->options[0] !== '' ) {
                        # input text pre-fill
                        $value = $elem->options[0];
+                       if ( $tagName === 'textarea' ) {
+                               # oversimplicated regexp, but it's enough for 
our needs
+                               $value = preg_replace( 
'/<br[\sA-Z\d="]*\/{0,1}>/i', "\n", $value, -1, $lines_count );
+                               $lines_count++;
+                       }
                        $className .= ' cat_prefilled';
                }
-               $input = array(
-                       '__tag' => 'input',
+               $tag = array(
+                       '__tag' => $tagName,
                        # unique 
(poll_type,order_id,question,proposal,category) "coordinate" for javascript
                        'id' => "{$this->id_prefix}c{$this->ckey}",
                        'class' => $className,
-                       'type' => $elem->type,
                        'name' => $elem->name,
-                       'value' => qp_Setup::specialchars( $value )
                );
+               if ( $tagName === 'input' ) {
+                       $tag['type'] = $elem->type;
+                       $tag['value'] = qp_Setup::specialchars( $value );
+               } else { /* 'textarea' */
+                       $tag[] = qp_Setup::specialchars( $value );
+                       if ( is_int( $elem->attributes['height'] ) ) {
+                               $tag['rows'] = $elem->attributes['height'];
+                       } else { /* 'auto' */
+                               # todo: allow multiline prefilled text and 
calculate number of new lines
+                               $tag['rows'] = $lines_count;
+                       }
+               }
                $this->ckey++;
-               if ( $elem->type !== 'text' && $elem->attributes['checked'] === 
true ) {
-                       $input['checked'] = 'checked';
+               if ( $elem->type === 'text' ) {
+                       # input type text and textarea
+                       if ( $this->owner->textInputStyle != '' ) {
+                               # apply poll's textwidth attribute
+                               $tag['style'] = $this->owner->textInputStyle;
+                       }
+                       if ( $elem->attributes['width'] !== 0 ) {
+                               # apply current category width attribute
+                               if ( is_int( $elem->attributes['width'] ) ) {
+                                       $tag['style'] = 'width:' . 
$elem->attributes['width'] . 'em;';
+                               } else { /* 'auto' */
+                                       $tag['style'] = 'width:99%;';
+                               }
+                       }
+               } else {
+                       # checkbox or radiobutton
+                       if ( $elem->attributes['checked'] === true ) {
+                               $tag['checked'] = 'checked';
+                       }
                }
-               if ( $elem->type === 'text' && $this->owner->textInputStyle != 
'' ) {
-                       # apply poll's textwidth attribute
-                       $input['style'] = $this->owner->textInputStyle;
-               }
-               if ( $elem->attributes['width'] !== null ) {
-                       # apply current category width attribute
-                       $input['style'] = 'width:' . intval( 
$elem->attributes['width'] ) . 'em;';
-               }
-               $this->cell[] = $input;
+               $this->cell[] = $tag;
        }
 
+       /**
+        * Add category as select / option list tagarray
+        */
        function addSelect( $elem, $className ) {
                if ( $elem->options[0] !== '' ) {
-                       # default element in select/option set always must be 
empty option
+                       # default element in select/option set always must be 
an empty option
                        array_unshift( $elem->options, '' );
                }
                $html_options = array();
+               # prepare the list of selected values
+               if ( $elem->attributes['multiple'] !== null ) {
+                       # new lines are separator for selected multiple options
+                       $selected_values = explode( "\n", $elem->value );
+               } else {
+                       $selected_values = array( $elem->value );
+               }
+               # generate options list
                foreach ( $elem->options as $option ) {
                        $html_option = array(
                                '__tag' => 'option',
                                'value' => qp_Setup::entities( $option ),
                                qp_Setup::specialchars( $option )
                        );
-                       if ( $option === $elem->value ) {
+                       if ( in_array( $option, $selected_values ) ) {
                                $html_option['selected'] = 'selected';
                        }
                        $html_options[] = $html_option;
                }
-               $this->cell[] = array(
+               $select = array(
                        '__tag' => 'select',
                        # unique 
(poll_type,order_id,question,proposal,category) "coordinate" for javascript
                        'id' => "{$this->id_prefix}c{$this->ckey}",
@@ -138,9 +186,29 @@
                        'name' => $elem->name,
                        $html_options
                );
+               # multiple options 'name' attribute should have array hint []
+               if ( $elem->attributes['multiple'] !== null ) {
+                       $select['multiple'] = 'multiple';
+                       $select['name'] .= '[]';
+               }
+               # determine visual height of select options list
+               if ( ( $size = $elem->attributes['height'] ) !== 0 ) {
+                       if ( is_int( $size ) ) {
+                               if ( count( $elem->options ) < $size ) {
+                                       $size = count( $elem->options );
+                               }
+                       } else { /* 'auto' */
+                               $size = count( $elem->options );
+                       }
+                       $select['size'] = $size;
+               }
+               $this->cell[] = $select;
                $this->ckey++;
        }
 
+       /**
+        * Add tagarray representation of proposal part
+        */
        function addProposalPart( $elem ) {
                $this->cell[] = array(
                        '__tag' => 'span',
@@ -149,6 +217,11 @@
                );
        }
 
+       /**
+        * Build "final" cell which contain tagarray representation of
+        * proposal parts, proposal errors and one adjascent category
+        * and then add it to the row
+        */
        function addCell() {
                if ( count( $this->error ) > 0 ) {
                        # merge previous errors to current cell
@@ -179,6 +252,11 @@
        # whether the resulting display table should be transposed
        # meaningful only when $this->tabularDisplay is true
        var $transposed = false;
+       # how many characters will hold horizontal line of textarea;
+       # currently is unused, because textarea 'cols' attribute renders
+       # poorly in table cells in modern versions of Firefox, so
+       # we are using CSS $this->textInputStyle instead
+       # var $textwidth = 0;
 
        # default style of text input
        var $textInputStyle = '';
@@ -206,9 +284,11 @@
                        $this->transposed = strpos( $layout, 'transpose' ) !== 
false;
                }
                if ( $textwidth !== null ) {
-                       $textwidth = intval( $textwidth );
-                       if ( $textwidth > 0 ) {
+                       if ( is_numeric( $textwidth ) &&
+                                       $textwidth = intval( $textwidth ) > 0 ) 
{
                                $this->textInputStyle = 'width:' . $textwidth . 
'em;';
+                       } elseif ( $textwidth === 'auto' ) {
+                               $this->textInputStyle = 'width:auto;';
                        }
                }
        }
@@ -290,9 +370,9 @@
                                                # create view for 
proposal/category error message
                                                $vr->addError( $elem );
                                        }
-                                       # create view for the input options part
+                                       # create view for the input / textarea 
options part
                                        if ( count( $elem->options ) === 1 ) {
-                                               # one option produces html text 
/ radio / checkbox input
+                                               # one option produces html text 
/ radio / checkbox input or an textarea
                                                $vr->addInput( $elem, 
$className );
                                                $vr->addCell();
                                                continue;


_______________________________________________
MediaWiki-CVS mailing list
MediaWiki-CVS@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to