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