/** * Apply current view layout from questions view to proposal view * * Please clone this method in derived classes until the code is * moved from early to late static binding */ static function applyViewState( qp_StubQuestionView $view ) { parent::applyViewState( $view ); # initialize cell template for selected showresults # todo: this is self (PHP 5.2), not static (PHP 5.3) # do not forget to clone this method in ancestors self::$spanState = (object) array( 'id' => 0, 'prevId' => -1, 'wasChecked' => true, 'isDrawing' => false, 'cellsLeft' => 0, 'className' => 'sign' ); if ( self::$showResults['type'] != 0 ) { call_user_func( array( __CLASS__, self::$cellTemplateMethod ) ); } }
/** * Creates question view which should be renreded and * also may be altered during the poll generation * todo: this method is too long, split to smaller parts */ function parseBody() { global $wgContLang; $proposalPattern = '/^'; foreach ( $this->mCategories as $catDesc ) { $proposalPattern .= '(\[\]|\(\)|<>)'; } $proposalPattern .= '(.*)/su'; $proposalId = -1; # set static view state for the future qp_TabularQuestionProposalView instances qp_TabularQuestionProposalView::applyViewState( $this->view ); $prop_attrs = qp_Setup::$propAttrs; $prop_attrs->setQuestion( $this ); while ( $prop_attrs->iterate() ) { # new proposal view $pview = new qp_TabularQuestionProposalView( $proposalId + 1, $this ); # get the list of categories ($matches) if ( preg_match( $proposalPattern, $prop_attrs->cpdef, $matches ) ) { $prop_attrs->dbText = array_pop( $matches ); // current proposal text array_shift( $matches ); // remove "at whole" match $last_matches = $matches; } else { if ( $proposalId >= 0 ) { # shortened syntax: use the pattern from the last row where it's been defined $prop_attrs->dbText = $prop_attrs->cpdef; $matches = $last_matches; } } if ( $prop_attrs->dbText === null ) { continue; } $pview->text = $prop_attrs->dbText; $proposalId++; # set proposal name (if any) if ( is_string( $prop_attrs->error ) ) { $pview->prependErrorMessage( wfMsg( $prop_attrs->error, 'error' ) ); } elseif ( $prop_attrs->name !== '' ) { $this->mProposalNames[$proposalId] = $prop_attrs->name; } $this->mProposalText[$proposalId] = strval( $prop_attrs ); # Determine a type ID, according to the questionType and the number of signes. foreach ( $this->mCategories as $catId => $catDesc ) { $typeId = $matches[$catId]; # start new input field tag (category) $pview->addNewCategory( $catId ); $inp = array( '__tag' => 'input' ); # Determine the input's name and value. $name = "q{$this->mQuestionId}p{$proposalId}s{$catId}"; switch ( $typeId ) { case '<>': $value = ''; $inputType = 'text'; break; case '[]': $value = "s{$catId}"; $inputType = 'checkbox'; break; case '()': $value = "s{$catId}"; $inputType = 'radio'; break; } # Determine if the input has to be checked. $input_checked = false; $text_answer = ''; if ( $this->poll->mBeingCorrected && qp_Setup::$request->getVal( $name ) !== null ) { if ( $inputType == 'text' ) { $text_answer = trim( qp_Setup::$request->getText( $name ) ); if ( strlen( $text_answer ) > qp_Setup::$field_max_len['text_answer'] ) { $text_answer = $wgContLang->truncate( $text_answer, qp_Setup::$field_max_len['text_answer'] , '' ); } if ( $prop_attrs->emptytext || $text_answer != '' ) { $input_checked = true; } } else { $inp['checked'] = 'checked'; $input_checked = true; } } if ( ( $prev_text_answer = $this->answerExists( $inputType, $proposalId, $catId ) ) !== false ) { $input_checked = true; if ( $inputType == 'text' ) { $text_answer = $prev_text_answer; } else { $inp['checked'] = 'checked'; } } if ( $input_checked === true ) { # add category to the list of user answers for current proposal (row) $this->mProposalCategoryId[ $proposalId ][] = $catId; $this->mProposalCategoryText[ $proposalId ][] = $text_answer; } # always borderless (mixed questions do not have spans) $pview->setCategorySpan(); # unique (poll,question,proposal,category) "coordinate" for javascript $inp['id'] = "mx{$this->poll->mOrderId}q{$this->mQuestionId}p{$proposalId}c{$catId}"; $inp['class'] = 'check'; $inp['type'] = $inputType; $inp['name'] = $name; if ( $inputType == 'text' ) { $inp['value'] = qp_Setup::specialchars( $text_answer ); if ( $this->view->textInputStyle != '' ) { $inp['style'] = $this->view->textInputStyle; } } else { $inp['value'] = $value; } $pview->setCat( $inp ); } try { # if there is only one category defined and it is not a textfield, # the question has a syntax error if ( count( $matches ) < 2 && $matches[0] != '<>' ) { $pview->setErrorMessage( wfMsg( 'qp_error_too_few_categories' ), 'error' ); throw new Exception( 'qp_error' ); } # If the proposal text is empty, the question has a syntax error. if ( trim( $pview->text ) == '' ) { $pview->setErrorMessage( wfMsg( 'qp_error_proposal_text_empty' ), 'error' ); throw new Exception( 'qp_error' ); } if ( $this->poll->mBeingCorrected && $prop_attrs->hasMissingCategories( $answered_cats_count = $this->getAnsweredCatCount( $proposalId ), count( $this->mCategories ) ) ) { # the proposal was submitted but has not enough categories answered $pview->prependErrorMessage( ($answered_cats_count > 0) ? wfMsg( 'qp_error_not_enough_categories_answered' ) : wfMsg( 'qp_error_no_answer' ) , 'NA' ); # if there was no previous errors, hightlight the whole row if ( $this->getState() == '' ) { throw new Exception( 'qp_error' ); } } } catch ( Exception $e ) { if ( $e->getMessage() == 'qp_error' ) { $pview->addCellsClass( 'error' ); } else { throw new MWException( $e->getMessage() ); } } $this->view->addProposal( $proposalId, $pview ); } }
static function cellTemplate2() { # statical styles $percentstyle = ''; if ( isset( self::$showResults['textcolor'] ) ) { $percentstyle = 'color:' . self::$showResults['textcolor'] . ';'; } if ( isset( self::$showResults['textbackground'] ) ) { $percentstyle .= 'background-color:' . self::$showResults['textbackground'] . ';'; } # html arrays used in templates below $bar = array( '__tag' => 'div', 'class' => 'stats1', 0 => array( '__tag' => 'div', 'class' => 'bar0', 0 => &self::$cellTemplateParam['inp'] ), 1 => array( '__tag' => 'div', 'class' => 'bar1', 'style' => &self::$cellTemplateParam['bar1style'], 0 => ' ' ), 2 => array( '__tag' => 'div', 'class' => 'bar2', 'style' => &self::$cellTemplateParam['bar2style'], 0 => ' ' ), 3 => array( '__tag' => 'div', 'class' => 'bar0', 'style' => $percentstyle, 0 => &self::$cellTemplateParam['percents'] ) ); $bar2 = array( '__tag' => 'div', 'class' => 'stats1', 0 => array( '__tag' => 'div', 'class' => 'bar0', 0 => ' ' ), 1 => &$bar[1], 2 => &$bar[2], 3 => &$bar[3] ); # has two available templates ('bar','textinput') self::$cellTemplate = array( 'bar' => $bar, 'textinput' => array( '__tag' => 'table', 'class' => 'stats', 0 => array( '__tag' => 'tr', 0 => array( '__tag' => 'td', 0 => &self::$cellTemplateParam['inp'] ), ), 1 => array( '__tag' => 'tr', 0 => array( '__tag' => 'td', 0 => $bar2 ) ) ), # the following entries are not real templates, but pre-calculated values of css attributes taken from showresults parameter 'bar1showres' => '', 'bar2showres' => '' ); # dynamical styles, width: in percents will be added during rendering in addShowResults if ( isset( self::$showResults['color'] ) ) { self::$cellTemplate['bar1showres'] .= 'background:' . self::$showResults['color'] . ';'; } if ( isset( self::$showResults['background'] ) ) { self::$cellTemplate['bar2showres'] .= 'background:' . self::$showResults['background'] . ';'; } }
/** * Creates question view which should be renreded and * also may be altered during the poll generation */ function questionParseBody( $inputType ) { $proposalId = -1; # set static view state for the future qp_TabularQuestionProposalView instances qp_TabularQuestionProposalView::applyViewState( $this->view ); $prop_attrs = qp_Setup::$propAttrs; $prop_attrs->setQuestion( $this ); while ( $prop_attrs->iterate() ) { # new proposal view $pview = new qp_TabularQuestionProposalView( $proposalId + 1, $this ); $proposalId++; $prop_attrs->dbText = $pview->text = $prop_attrs->cpdef; if ( is_string( $prop_attrs->error ) ) { $pview->prependErrorMessage( wfMsg( $prop_attrs->error ), 'error' ); } elseif ( $prop_attrs->name !== '' ) { $this->mProposalNames[$proposalId] = $prop_attrs->name; } $this->mProposalText[$proposalId] = strval( $prop_attrs ); foreach ( $this->mCategories as $catId => $catDesc ) { # start new input field tag (category) $pview->addNewCategory( $catId ); $inp = array( '__tag' => 'input' ); $pview->resetSpanState(); # Determine the input's name and value. switch( $this->mType ) { case 'multipleChoice': $name = "q{$this->mQuestionId}p{$proposalId}s{$catId}"; $value = "s{$catId}"; break; case 'singleChoice': $name = "q{$this->mQuestionId}p{$proposalId}"; $value = "s{$catId}"; # category spans have sense only with single choice proposals $pview->renderSpan( $name, $value, $catDesc ); break; } # Determine if the input had to be checked. if ( $this->poll->mBeingCorrected && qp_Setup::$request->getVal( $name ) == $value ) { $inp[ 'checked' ] = 'checked'; } if ( $this->answerExists( $inputType, $proposalId, $catId ) !== false ) { $inp[ 'checked' ] = 'checked'; } if ( array_key_exists( 'checked', $inp ) ) { if ( $this->mSubType == 'unique' ) { if ( $this->poll->mBeingCorrected && !$this->isUniqueProposalCategoryId( $proposalId, $catId ) ) { $pview->prependErrorMessage( wfMsg( 'qp_error_non_unique_choice' ), 'NA' ); unset( $inp[ 'checked' ] ); qp_Renderer::addClass( $row[ $catId ], 'error' ); } } else { $pview->spanWasChecked( true ); } } if ( array_key_exists( 'checked', $inp ) ) { # add category to the list of user answers for current proposal (row) $this->mProposalCategoryId[ $proposalId ][] = $catId; $this->mProposalCategoryText[ $proposalId ][] = ''; } $pview->setCategorySpan(); if ( $this->mSubType == 'unique' ) { # unique (orderid,question,proposal,category) "coordinate" for javascript $inp['id'] = "uq{$this->poll->mOrderId}q{$this->mQuestionId}p{$proposalId}c{$catId}"; # If type='unique()' question has more proposals than categories, such question is impossible to complete if ( count( $this->mProposalText ) > count( $this->mCategories ) ) { # if there was no previous errors, hightlight the whole row if ( $this->getState() == '' ) { $pview->addCellsClass( 'error' ); } $pview->prependErrorMessage( wfMsg( 'qp_error_unique' ), 'error' ); } } $inp['class'] = 'check'; $inp['type'] = $inputType; $inp['name'] = $name; $inp['value'] = $value; $pview->setCat( $inp ); } # If the proposal text is empty, the question has a syntax error. if ( $pview->text !== null && trim( $pview->text ) == '' ) { $pview->setErrorMessage( wfMsg( 'qp_error_proposal_text_empty' ), 'error' ); $pview->addCellsClass( 'error' ); } if ( $inputType === 'radio' && $prop_attrs->catreq > 1 ) { # radio buttons row always require not more than one category, # otherwise the poll will be impossible to submit sucessfully. $prop_attrs->catreq = 1; } # If the proposal was submitted but unanswered if ( $this->poll->mBeingCorrected && $prop_attrs->hasMissingCategories( $answered_cats_count = $this->getAnsweredCatCount( $proposalId ), count( $this->mCategories ) ) ) { # if there was no previous errors, hightlight the whole row if ( $this->getState() == '' ) { $pview->addCellsClass( 'error' ); } # the proposal was submitted but has not enough categories answered $pview->prependErrorMessage( ($answered_cats_count > 0) ? wfMsg( 'qp_error_not_enough_categories_answered' ) : wfMsg( 'qp_error_no_answer' ) , 'NA' ); } if ( $pview->text !== null ) { $this->view->addProposal( $proposalId, $pview ); } } }