r89808 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r89807‎ | r89808 | r89809 >
Date:22:32, 9 June 2011
Author:krinkle
Status:deferred (Comments)
Tags:
Comment:
Initial check in, in an extension for now during development/evaluation/cross-browser testing.
Modified paths:
  • /trunk/extensions/StyleGuideDemo (added) (history)
  • /trunk/extensions/StyleGuideDemo/HTMLStyleForm.php (added) (history)
  • /trunk/extensions/StyleGuideDemo/README (added) (history)
  • /trunk/extensions/StyleGuideDemo/SpecialStyleGuideDemo.php (added) (history)
  • /trunk/extensions/StyleGuideDemo/StyleGuideDemo.alias.php (added) (history)
  • /trunk/extensions/StyleGuideDemo/StyleGuideDemo.i18n.php (added) (history)
  • /trunk/extensions/StyleGuideDemo/StyleGuideDemo.php (added) (history)
  • /trunk/extensions/StyleGuideDemo/modules (added) (history)
  • /trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo (added) (history)
  • /trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/ext.styleguidedemo.css (added) (history)
  • /trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/ext.styleguidedemo.js (added) (history)
  • /trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/images (added) (history)
  • /trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/images/MW-StyleGuide-Form-InputInnerShade.png (added) (history)

Diff [purge]

Index: trunk/extensions/StyleGuideDemo/HTMLStyleForm.php
@@ -0,0 +1,1903 @@
 2+<?php
 3+/**
 4+ * Object handling generic submission, CSRF protection, layout and
 5+ * other logic for UI forms. in a reusable manner.
 6+ *
 7+ * In order to generate the form, the HTMLForm object takes an array
 8+ * structure detailing the form fields available. Each element of the
 9+ * array is a basic property-list, including the type of field, the
 10+ * label it is to be given in the form, callbacks for validation and
 11+ * 'filtering', and other pertinent information.
 12+ *
 13+ * Field types are implemented as subclasses of the generic HTMLFormField
 14+ * object, and typically implement at least getInputHTML, which generates
 15+ * the HTML for the input field to be placed in the form.
 16+ *
 17+ * The constructor input is an associative array of $fieldname => $info,
 18+ * where $info is an Associative Array with any of the following:
 19+ *
 20+ * 'class' -- the subclass of HTMLFormField that will be used
 21+ * to create the object. *NOT* the CSS class!
 22+ * 'type' -- roughly translates into the <select> type attribute.
 23+ * if 'class' is not specified, this is used as a map
 24+ * through HTMLForm::$typeMappings to get the class name.
 25+ * 'default' -- default value when the form is displayed
 26+ * 'id' -- HTML id attribute
 27+ * 'cssclass' -- CSS class
 28+ * 'options' -- varies according to the specific object.
 29+ * 'label-message' -- message key for a message to use as the label.
 30+ * can be an array of msg key and then parameters to
 31+ * the message.
 32+ * 'label' -- alternatively, a raw text message. Overridden by
 33+ * label-message
 34+ * 'tip' -- Additional hint to be displayed above the label
 35+ * Hidden by default and shown when hovering [?] (tipsy).
 36+ * Always shown if javascript is disabled.
 37+ * 'help-message' -- message key for a message to use as a help text.
 38+ * can be an array of msg key and then parameters to
 39+ * the message.
 40+ * Overwrites 'help-messages'.
 41+ * 'help-messages' -- array of message key. As above, each item can
 42+ * be an array of msg key and then parameters.
 43+ * Overwrites 'help'.
 44+ * 'help' -- alternatively, raw help text. Overriden by 'help-message'
 45+ * and help-messages'.
 46+ * 'required' -- passed through to the object, indicating that it
 47+ * is a required field.
 48+ * 'size' -- the length of text fields
 49+ * 'filter-callback -- a function name to give you the chance to
 50+ * massage the inputted value before it's processed.
 51+ * @see HTMLForm::filter()
 52+ * 'validation-callback' -- a function name to give you the chance
 53+ * to impose extra validation on the field input.
 54+ * @see HTMLForm::validate()
 55+ * 'name' -- By default, the 'name' attribute of the input field
 56+ * is "wp{$fieldname}". If you want a different name
 57+ * (eg one without the "wp" prefix), specify it here and
 58+ * it will be used without modification.
 59+ *
 60+ * TODO: Document 'section' / 'subsection' stuff
 61+ */
 62+class HTMLStyleForm {
 63+
 64+ # A mapping of 'type' inputs onto standard HTMLFormField subclasses
 65+ static $typeMappings = array(
 66+ 'text' => 'HTMLTextField',
 67+ 'textarea' => 'HTMLTextAreaField',
 68+ 'select' => 'HTMLSelectField',
 69+ 'radio' => 'HTMLRadioField',
 70+ 'multiselect' => 'HTMLMultiSelectField',
 71+ 'check' => 'HTMLCheckField',
 72+ 'toggle' => 'HTMLCheckField',
 73+ 'int' => 'HTMLIntField',
 74+ 'float' => 'HTMLFloatField',
 75+ 'info' => 'HTMLInfoField',
 76+ 'selectorother' => 'HTMLSelectOrOtherField',
 77+ 'selectandother' => 'HTMLSelectAndOtherField',
 78+ 'submit' => 'HTMLSubmitField',
 79+ 'hidden' => 'HTMLHiddenField',
 80+ 'edittools' => 'HTMLEditTools',
 81+
 82+ # HTMLTextField will output the correct type="" attribute automagically.
 83+ # There are about four zillion other HTML5 input types, like url, but
 84+ # we don't use those at the moment, so no point in adding all of them.
 85+ 'email' => 'HTMLTextField',
 86+ 'password' => 'HTMLTextField',
 87+ );
 88+
 89+ protected $mMessagePrefix;
 90+ protected $mFlatFields;
 91+ protected $mFieldTree;
 92+ protected $mShowReset = false;
 93+ public $mFieldData;
 94+
 95+ protected $mSubmitCallback;
 96+ protected $mValidationErrorMessage;
 97+
 98+ protected $mPre = '';
 99+ protected $mHeader = '';
 100+ protected $mFooter = '';
 101+ protected $mSectionHeaders = array();
 102+ protected $mSectionFooters = array();
 103+ protected $mPost = '';
 104+ protected $mId;
 105+
 106+ protected $mSubmitID;
 107+ protected $mSubmitName;
 108+ protected $mSubmitText;
 109+ protected $mSubmitTooltip;
 110+
 111+ protected $mContext; // <! RequestContext
 112+ protected $mTitle;
 113+ protected $mMethod = 'post';
 114+
 115+ protected $mUseMultipart = false;
 116+ protected $mHiddenFields = array();
 117+ protected $mButtons = array();
 118+
 119+ protected $mWrapperLegend = false;
 120+
 121+ /**
 122+ * Build a new HTMLForm from an array of field attributes
 123+ * @param $descriptor Array of Field constructs, as described above
 124+ * @param $context RequestContext available since 1.18, will become compulsory in 1.19.
 125+ * Obviates the need to call $form->setTitle()
 126+ * @param $messagePrefix String a prefix to go in front of default messages
 127+ */
 128+ public function __construct( $descriptor, /*RequestContext*/ $context = null, $messagePrefix = '' ) {
 129+ if( $context instanceof RequestContext ){
 130+ $this->mContext = $context;
 131+ $this->mTitle = false; // We don't need them to set a title
 132+ $this->mMessagePrefix = $messagePrefix;
 133+ } else {
 134+ // B/C since 1.18
 135+ if( is_string( $context ) && $messagePrefix === '' ){
 136+ // it's actually $messagePrefix
 137+ $this->mMessagePrefix = $context;
 138+ }
 139+ }
 140+
 141+ // Expand out into a tree.
 142+ $loadedDescriptor = array();
 143+ $this->mFlatFields = array();
 144+
 145+ foreach ( $descriptor as $fieldname => $info ) {
 146+ $section = isset( $info['section'] )
 147+ ? $info['section']
 148+ : '';
 149+
 150+ if ( isset( $info['type'] ) && $info['type'] == 'file' ) {
 151+ $this->mUseMultipart = true;
 152+ }
 153+
 154+ $field = self::loadInputFromParameters( $fieldname, $info );
 155+ $field->mParent = $this;
 156+
 157+ $setSection =& $loadedDescriptor;
 158+ if ( $section ) {
 159+ $sectionParts = explode( '/', $section );
 160+
 161+ while ( count( $sectionParts ) ) {
 162+ $newName = array_shift( $sectionParts );
 163+
 164+ if ( !isset( $setSection[$newName] ) ) {
 165+ $setSection[$newName] = array();
 166+ }
 167+
 168+ $setSection =& $setSection[$newName];
 169+ }
 170+ }
 171+
 172+ $setSection[$fieldname] = $field;
 173+ $this->mFlatFields[$fieldname] = $field;
 174+ }
 175+
 176+ $this->mFieldTree = $loadedDescriptor;
 177+ }
 178+
 179+ /**
 180+ * Add the HTMLForm-specific JavaScript, if it hasn't been
 181+ * done already.
 182+ * @deprecated since 1.18 load modules with ResourceLoader instead
 183+ */
 184+ static function addJS() { }
 185+
 186+ /**
 187+ * Initialise a new Object for the field
 188+ * @param $descriptor input Descriptor, as described above
 189+ * @return HTMLFormField subclass
 190+ */
 191+ static function loadInputFromParameters( $fieldname, $descriptor ) {
 192+ if ( isset( $descriptor['class'] ) ) {
 193+ $class = $descriptor['class'];
 194+ } elseif ( isset( $descriptor['type'] ) ) {
 195+ $class = self::$typeMappings[$descriptor['type']];
 196+ $descriptor['class'] = $class;
 197+ } else {
 198+ $class = null;
 199+ }
 200+
 201+ if ( !$class ) {
 202+ throw new MWException( "Descriptor with no class: " . print_r( $descriptor, true ) );
 203+ }
 204+
 205+ $descriptor['fieldname'] = $fieldname;
 206+
 207+ $obj = new $class( $descriptor );
 208+
 209+ return $obj;
 210+ }
 211+
 212+ /**
 213+ * Prepare form for submission
 214+ */
 215+ function prepareForm() {
 216+ # Check if we have the info we need
 217+ if ( !$this->mTitle instanceof Title && $this->mTitle !== false ) {
 218+ throw new MWException( "You must call setTitle() on an HTMLForm" );
 219+ }
 220+
 221+ # Load data from the request.
 222+ $this->loadData();
 223+ }
 224+
 225+ /**
 226+ * Try submitting, with edit token check first
 227+ * @return Status|boolean
 228+ */
 229+ function tryAuthorizedSubmit() {
 230+ $editToken = $this->getRequest()->getVal( 'wpEditToken' );
 231+
 232+ $result = false;
 233+ if ( $this->getMethod() != 'post' || $this->getUser()->matchEditToken( $editToken ) ) {
 234+ $result = $this->trySubmit();
 235+ }
 236+ return $result;
 237+ }
 238+
 239+ /**
 240+ * The here's-one-I-made-earlier option: do the submission if
 241+ * posted, or display the form with or without funky valiation
 242+ * errors
 243+ * @return Bool or Status whether submission was successful.
 244+ */
 245+ function show() {
 246+ $this->prepareForm();
 247+
 248+ $result = $this->tryAuthorizedSubmit();
 249+ if ( $result === true || ( $result instanceof Status && $result->isGood() ) ){
 250+ return $result;
 251+ }
 252+
 253+ $this->displayForm( $result );
 254+ return false;
 255+ }
 256+
 257+ /**
 258+ * Validate all the fields, and call the submision callback
 259+ * function if everything is kosher.
 260+ * @return Mixed Bool true == Successful submission, Bool false
 261+ * == No submission attempted, anything else == Error to
 262+ * display.
 263+ */
 264+ function trySubmit() {
 265+ # Check for validation
 266+ foreach ( $this->mFlatFields as $fieldname => $field ) {
 267+ if ( !empty( $field->mParams['nodata'] ) ) {
 268+ continue;
 269+ }
 270+ if ( $field->validate(
 271+ $this->mFieldData[$fieldname],
 272+ $this->mFieldData )
 273+ !== true
 274+ ) {
 275+ return isset( $this->mValidationErrorMessage )
 276+ ? $this->mValidationErrorMessage
 277+ : array( 'htmlform-invalid-input' );
 278+ }
 279+ }
 280+
 281+ $callback = $this->mSubmitCallback;
 282+
 283+ $data = $this->filterDataForSubmit( $this->mFieldData );
 284+
 285+ $res = call_user_func( $callback, $data );
 286+
 287+ return $res;
 288+ }
 289+
 290+ /**
 291+ * Set a callback to a function to do something with the form
 292+ * once it's been successfully validated.
 293+ * @param $cb String function name. The function will be passed
 294+ * the output from HTMLForm::filterDataForSubmit, and must
 295+ * return Bool true on success, Bool false if no submission
 296+ * was attempted, or String HTML output to display on error.
 297+ */
 298+ function setSubmitCallback( $cb ) {
 299+ $this->mSubmitCallback = $cb;
 300+ }
 301+
 302+ /**
 303+ * Set a message to display on a validation error.
 304+ * @param $msg Mixed String or Array of valid inputs to wfMsgExt()
 305+ * (so each entry can be either a String or Array)
 306+ */
 307+ function setValidationErrorMessage( $msg ) {
 308+ $this->mValidationErrorMessage = $msg;
 309+ }
 310+
 311+ /**
 312+ * Set the introductory message, overwriting any existing message.
 313+ * @param $msg String complete text of message to display
 314+ */
 315+ function setIntro( $msg ) { $this->mPre = $msg; }
 316+
 317+ /**
 318+ * Add introductory text.
 319+ * @param $msg String complete text of message to display
 320+ */
 321+ function addPreText( $msg ) { $this->mPre .= $msg; }
 322+
 323+ /**
 324+ * Add header text, inside the form.
 325+ * @param $msg String complete text of message to display
 326+ */
 327+ function addHeaderText( $msg, $section = null ) {
 328+ if ( is_null( $section ) ) {
 329+ $this->mHeader .= $msg;
 330+ } else {
 331+ if ( !isset( $this->mSectionHeaders[$section] ) ) {
 332+ $this->mSectionHeaders[$section] = '';
 333+ }
 334+ $this->mSectionHeaders[$section] .= $msg;
 335+ }
 336+ }
 337+
 338+ /**
 339+ * Add footer text, inside the form.
 340+ * @param $msg String complete text of message to display
 341+ */
 342+ function addFooterText( $msg, $section = null ) {
 343+ if ( is_null( $section ) ) {
 344+ $this->mFooter .= $msg;
 345+ } else {
 346+ if ( !isset( $this->mSectionFooters[$section] ) ) {
 347+ $this->mSectionFooters[$section] = '';
 348+ }
 349+ $this->mSectionFooters[$section] .= $msg;
 350+ }
 351+ }
 352+
 353+ /**
 354+ * Add text to the end of the display.
 355+ * @param $msg String complete text of message to display
 356+ */
 357+ function addPostText( $msg ) { $this->mPost .= $msg; }
 358+
 359+ /**
 360+ * Add a hidden field to the output
 361+ * @param $name String field name. This will be used exactly as entered
 362+ * @param $value String field value
 363+ * @param $attribs Array
 364+ */
 365+ public function addHiddenField( $name, $value, $attribs = array() ) {
 366+ $attribs += array( 'name' => $name );
 367+ $this->mHiddenFields[] = array( $value, $attribs );
 368+ }
 369+
 370+ public function addButton( $name, $value, $id = null, $attribs = null ) {
 371+ $this->mButtons[] = compact( 'name', 'value', 'id', 'attribs' );
 372+ }
 373+
 374+ /**
 375+ * Display the form (sending to wgOut), with an appropriate error
 376+ * message or stack of messages, and any validation errors, etc.
 377+ * @param $submitResult Mixed output from HTMLForm::trySubmit()
 378+ */
 379+ function displayForm( $submitResult ) {
 380+ # For good measure (it is the default)
 381+ $this->getOutput()->preventClickjacking();
 382+ $this->getOutput()->addModules( 'mediawiki.htmlform' );
 383+
 384+ $html = ''
 385+ . $this->getErrors( $submitResult )
 386+ . $this->mHeader
 387+ . $this->getBody()
 388+ . $this->getHiddenFields()
 389+ . $this->getButtons()
 390+ . $this->mFooter
 391+ ;
 392+
 393+ $html = $this->wrapForm( $html );
 394+
 395+ $this->getOutput()->addHTML( ''
 396+ . $this->mPre
 397+ . $html
 398+ . $this->mPost
 399+ );
 400+ }
 401+
 402+ /**
 403+ * Wrap the form innards in an actual <form> element
 404+ * @param $html String HTML contents to wrap.
 405+ * @return String wrapped HTML.
 406+ */
 407+ function wrapForm( $html ) {
 408+
 409+ # Include a <fieldset> wrapper for style, if requested.
 410+ if ( $this->mWrapperLegend !== false ) {
 411+ $html = Xml::fieldset( $this->mWrapperLegend, $html );
 412+ }
 413+ # Use multipart/form-data
 414+ $encType = $this->mUseMultipart
 415+ ? 'multipart/form-data'
 416+ : 'application/x-www-form-urlencoded';
 417+ # Attributes
 418+ $attribs = array(
 419+ 'action' => $this->getTitle()->getFullURL(),
 420+ 'method' => $this->mMethod,
 421+ 'class' => 'mw-htmlform-form visualClear',
 422+ 'enctype' => $encType,
 423+ );
 424+ if ( !empty( $this->mId ) ) {
 425+ $attribs['id'] = $this->mId;
 426+ }
 427+
 428+ return Html::rawElement( 'form', $attribs, $html );
 429+ }
 430+
 431+ /**
 432+ * Get the hidden fields that should go inside the form.
 433+ * @return String HTML.
 434+ */
 435+ function getHiddenFields() {
 436+ $html = '';
 437+ if( $this->getMethod() == 'post' ){
 438+ $html .= Html::hidden( 'wpEditToken', $this->getUser()->editToken(), array( 'id' => 'wpEditToken' ) ) . "\n";
 439+ $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n";
 440+ }
 441+
 442+ foreach ( $this->mHiddenFields as $data ) {
 443+ list( $value, $attribs ) = $data;
 444+ $html .= Html::hidden( $attribs['name'], $value, $attribs ) . "\n";
 445+ }
 446+
 447+ return $html;
 448+ }
 449+
 450+ /**
 451+ * Get the submit and (potentially) reset buttons.
 452+ * @return String HTML.
 453+ */
 454+ function getButtons() {
 455+ $html = '';
 456+ $attribs = array();
 457+
 458+ if ( isset( $this->mSubmitID ) ) {
 459+ $attribs['id'] = $this->mSubmitID;
 460+ }
 461+
 462+ if ( isset( $this->mSubmitName ) ) {
 463+ $attribs['name'] = $this->mSubmitName;
 464+ }
 465+
 466+ if ( isset( $this->mSubmitTooltip ) ) {
 467+ $attribs += Linker::tooltipAndAccessKeyAttribs( $this->mSubmitTooltip );
 468+ }
 469+
 470+ $attribs['class'] = 'mw-htmlform-submit';
 471+
 472+ $html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
 473+
 474+ if ( $this->mShowReset ) {
 475+ $html .= Html::element(
 476+ 'input',
 477+ array(
 478+ 'type' => 'reset',
 479+ 'value' => wfMsg( 'htmlform-reset' )
 480+ )
 481+ ) . "\n";
 482+ }
 483+
 484+ foreach ( $this->mButtons as $button ) {
 485+ $attrs = array(
 486+ 'type' => 'submit',
 487+ 'name' => $button['name'],
 488+ 'value' => $button['value']
 489+ );
 490+
 491+ if ( $button['attribs'] ) {
 492+ $attrs += $button['attribs'];
 493+ }
 494+
 495+ if ( isset( $button['id'] ) ) {
 496+ $attrs['id'] = $button['id'];
 497+ }
 498+
 499+ $html .= Html::element( 'input', $attrs );
 500+ }
 501+
 502+ return $html;
 503+ }
 504+
 505+ /**
 506+ * Get the whole body of the form.
 507+ */
 508+ function getBody() {
 509+ return $this->displaySection( $this->mFieldTree );
 510+ }
 511+
 512+ /**
 513+ * Format and display an error message stack.
 514+ * @param $errors String|Array|Status
 515+ * @return String
 516+ */
 517+ function getErrors( $errors ) {
 518+ if ( $errors instanceof Status ) {
 519+ if ( $errors->isOK() ) {
 520+ $errorstr = '';
 521+ } else {
 522+ $errorstr = $this->getOutput()->parse( $errors->getWikiText() );
 523+ }
 524+ } elseif ( is_array( $errors ) ) {
 525+ $errorstr = $this->formatErrors( $errors );
 526+ } else {
 527+ $errorstr = $errors;
 528+ }
 529+
 530+ return $errorstr
 531+ ? Html::rawElement( 'div', array( 'class' => 'error' ), $errorstr )
 532+ : '';
 533+ }
 534+
 535+ /**
 536+ * Format a stack of error messages into a single HTML string
 537+ * @param $errors Array of message keys/values
 538+ * @return String HTML, a <ul> list of errors
 539+ */
 540+ public static function formatErrors( $errors ) {
 541+ $errorstr = '';
 542+
 543+ foreach ( $errors as $error ) {
 544+ if ( is_array( $error ) ) {
 545+ $msg = array_shift( $error );
 546+ } else {
 547+ $msg = $error;
 548+ $error = array();
 549+ }
 550+
 551+ $errorstr .= Html::rawElement(
 552+ 'li',
 553+ array(),
 554+ wfMsgExt( $msg, array( 'parseinline' ), $error )
 555+ );
 556+ }
 557+
 558+ $errorstr = Html::rawElement( 'ul', array(), $errorstr );
 559+
 560+ return $errorstr;
 561+ }
 562+
 563+ /**
 564+ * Set the text for the submit button
 565+ * @param $t String plaintext.
 566+ */
 567+ function setSubmitText( $t ) {
 568+ $this->mSubmitText = $t;
 569+ }
 570+
 571+ /**
 572+ * Get the text for the submit button, either customised or a default.
 573+ * @return unknown_type
 574+ */
 575+ function getSubmitText() {
 576+ return $this->mSubmitText
 577+ ? $this->mSubmitText
 578+ : wfMsg( 'htmlform-submit' );
 579+ }
 580+
 581+ public function setSubmitName( $name ) {
 582+ $this->mSubmitName = $name;
 583+ }
 584+
 585+ public function setSubmitTooltip( $name ) {
 586+ $this->mSubmitTooltip = $name;
 587+ }
 588+
 589+ /**
 590+ * Set the id for the submit button.
 591+ * @param $t String.
 592+ * @todo FIXME: Integrity of $t is *not* validated
 593+ */
 594+ function setSubmitID( $t ) {
 595+ $this->mSubmitID = $t;
 596+ }
 597+
 598+ public function setId( $id ) {
 599+ $this->mId = $id;
 600+ }
 601+ /**
 602+ * Prompt the whole form to be wrapped in a <fieldset>, with
 603+ * this text as its <legend> element.
 604+ * @param $legend String HTML to go inside the <legend> element.
 605+ * Will be escaped
 606+ */
 607+ public function setWrapperLegend( $legend ) { $this->mWrapperLegend = $legend; }
 608+
 609+ /**
 610+ * Set the prefix for various default messages
 611+ * TODO: currently only used for the <fieldset> legend on forms
 612+ * with multiple sections; should be used elsewhre?
 613+ * @param $p String
 614+ */
 615+ function setMessagePrefix( $p ) {
 616+ $this->mMessagePrefix = $p;
 617+ }
 618+
 619+ /**
 620+ * Set the title for form submission
 621+ * @param $t Title of page the form is on/should be posted to
 622+ */
 623+ function setTitle( $t ) {
 624+ $this->mTitle = $t;
 625+ }
 626+
 627+ /**
 628+ * Get the title
 629+ * @return Title
 630+ */
 631+ function getTitle() {
 632+ return $this->mTitle === false
 633+ ? $this->getContext()->title
 634+ : $this->mTitle;
 635+ }
 636+
 637+ /**
 638+ * @return RequestContext
 639+ */
 640+ public function getContext(){
 641+ return $this->mContext instanceof RequestContext
 642+ ? $this->mContext
 643+ : RequestContext::getMain();
 644+ }
 645+
 646+ /**
 647+ * @return OutputPage
 648+ */
 649+ public function getOutput(){
 650+ return $this->getContext()->output;
 651+ }
 652+
 653+ /**
 654+ * @return WebRequest
 655+ */
 656+ public function getRequest(){
 657+ return $this->getContext()->request;
 658+ }
 659+
 660+ /**
 661+ * @return User
 662+ */
 663+ public function getUser(){
 664+ return $this->getContext()->user;
 665+ }
 666+
 667+ /**
 668+ * Set the method used to submit the form
 669+ * @param $method String
 670+ */
 671+ public function setMethod( $method='post' ){
 672+ $this->mMethod = $method;
 673+ }
 674+
 675+ public function getMethod(){
 676+ return $this->mMethod;
 677+ }
 678+
 679+ /**
 680+ * TODO: Document
 681+ * @param $fields
 682+ */
 683+ function displaySection( $fields, $sectionName = '' ) {
 684+ $formInnerHtml = '';
 685+ $subsectionHtml = '';
 686+ $hasLeftColumn = false;
 687+
 688+ foreach ( $fields as $key => $value ) {
 689+ if ( is_object( $value ) ) {
 690+ $v = empty( $value->mParams['nodata'] )
 691+ ? $this->mFieldData[$key]
 692+ : $value->getDefault();
 693+ $formInnerHtml .= $value->getFormRow( $v );
 694+
 695+ if ( $value->getLabel() != '&#160;' )
 696+ $hasLeftColumn = true;
 697+ } elseif ( is_array( $value ) ) {
 698+ $section = $this->displaySection( $value, $key );
 699+ $legend = $this->getLegend( $key );
 700+ if ( isset( $this->mSectionHeaders[$key] ) ) {
 701+ $section = $this->mSectionHeaders[$key] . $section;
 702+ }
 703+ if ( isset( $this->mSectionFooters[$key] ) ) {
 704+ $section .= $this->mSectionFooters[$key];
 705+ }
 706+ $subsectionHtml .= Xml::fieldset( $legend, $section ) . "\n";
 707+ }
 708+ }
 709+
 710+ $classes = array();
 711+
 712+ if ( !$hasLeftColumn ) { // Avoid strange spacing when no labels exist
 713+ $classes[] = 'mw-htmlform-nolabel';
 714+ }
 715+
 716+ $attribs = array(
 717+ 'class' => implode( ' ', $classes ),
 718+ );
 719+
 720+ if ( $sectionName ) {
 721+ $attribs['id'] = Sanitizer::escapeId( "mw-htmlform-$sectionName" );
 722+ }
 723+
 724+ return $subsectionHtml . "\n$formInnerHtml\n";
 725+ }
 726+
 727+ /**
 728+ * Construct the form fields from the Descriptor array
 729+ */
 730+ function loadData() {
 731+ $fieldData = array();
 732+
 733+ foreach ( $this->mFlatFields as $fieldname => $field ) {
 734+ if ( !empty( $field->mParams['nodata'] ) ) {
 735+ continue;
 736+ } elseif ( !empty( $field->mParams['disabled'] ) ) {
 737+ $fieldData[$fieldname] = $field->getDefault();
 738+ } else {
 739+ $fieldData[$fieldname] = $field->loadDataFromRequest( $this->getRequest() );
 740+ }
 741+ }
 742+
 743+ # Filter data.
 744+ foreach ( $fieldData as $name => &$value ) {
 745+ $field = $this->mFlatFields[$name];
 746+ $value = $field->filter( $value, $this->mFlatFields );
 747+ }
 748+
 749+ $this->mFieldData = $fieldData;
 750+ }
 751+
 752+ /**
 753+ * Stop a reset button being shown for this form
 754+ * @param $suppressReset Bool set to false to re-enable the
 755+ * button again
 756+ */
 757+ function suppressReset( $suppressReset = true ) {
 758+ $this->mShowReset = !$suppressReset;
 759+ }
 760+
 761+ /**
 762+ * Overload this if you want to apply special filtration routines
 763+ * to the form as a whole, after it's submitted but before it's
 764+ * processed.
 765+ * @param $data
 766+ * @return unknown_type
 767+ */
 768+ function filterDataForSubmit( $data ) {
 769+ return $data;
 770+ }
 771+
 772+ /**
 773+ * Get a string to go in the <legend> of a section fieldset. Override this if you
 774+ * want something more complicated
 775+ * @param $key String
 776+ * @return String
 777+ */
 778+ public function getLegend( $key ) {
 779+ return wfMsg( "{$this->mMessagePrefix}-$key" );
 780+ }
 781+}
 782+
 783+/**
 784+ * The parent class to generate form fields. Any field type should
 785+ * be a subclass of this.
 786+ */
 787+abstract class HTMLFormField {
 788+
 789+ protected $mValidationCallback;
 790+ protected $mFilterCallback;
 791+ protected $mName;
 792+ public $mParams;
 793+ protected $mLabel; # String label. Set on construction
 794+ protected $mID;
 795+ protected $mClass = '';
 796+ protected $mDefault;
 797+
 798+ /**
 799+ * @var HTMLForm
 800+ */
 801+ public $mParent;
 802+
 803+ /**
 804+ * This function must be implemented to return the HTML to generate
 805+ * the input object itself. It should not implement the surrounding
 806+ * form elements, <div> tags, or labels/help messages.
 807+ * @param $value String the value to set the input to; eg a default
 808+ * text for a text input.
 809+ * @return String valid HTML.
 810+ */
 811+ abstract function getInputHTML( $value );
 812+
 813+ /**
 814+ * Override this function to add specific validation checks on the
 815+ * field input. Don't forget to call parent::validate() to ensure
 816+ * that the user-defined callback mValidationCallback is still run
 817+ * @param $value String the value the field was submitted with
 818+ * @param $alldata Array the data collected from the form
 819+ * @return Mixed Bool true on success, or String error to display.
 820+ */
 821+ function validate( $value, $alldata ) {
 822+ if ( isset( $this->mValidationCallback ) ) {
 823+ return call_user_func( $this->mValidationCallback, $value, $alldata );
 824+ }
 825+
 826+ if ( isset( $this->mParams['required'] ) && $value === '' ) {
 827+ return wfMsgExt( 'htmlform-required', 'parseinline' );
 828+ }
 829+
 830+ return true;
 831+ }
 832+
 833+ function filter( $value, $alldata ) {
 834+ if ( isset( $this->mFilterCallback ) ) {
 835+ $value = call_user_func( $this->mFilterCallback, $value, $alldata );
 836+ }
 837+
 838+ return $value;
 839+ }
 840+
 841+ /**
 842+ * Should this field have a label, or is there no input element with the
 843+ * appropriate id for the label to point to?
 844+ *
 845+ * @return bool True to output a label, false to suppress
 846+ */
 847+ protected function needsLabel() {
 848+ return true;
 849+ }
 850+
 851+ /**
 852+ * Get the value that this input has been set to from a posted form,
 853+ * or the input's default value if it has not been set.
 854+ * @param $request WebRequest
 855+ * @return String the value
 856+ */
 857+ function loadDataFromRequest( $request ) {
 858+ if ( $request->getCheck( $this->mName ) ) {
 859+ return $request->getText( $this->mName );
 860+ } else {
 861+ return $this->getDefault();
 862+ }
 863+ }
 864+
 865+ /**
 866+ * Initialise the object
 867+ * @param $params array Associative Array. See HTMLForm doc for syntax.
 868+ */
 869+ function __construct( $params ) {
 870+ $this->mParams = $params;
 871+
 872+ # Generate the label from a message, if possible
 873+ if ( isset( $params['label-message'] ) ) {
 874+ $msgInfo = $params['label-message'];
 875+
 876+ if ( is_array( $msgInfo ) ) {
 877+ $msg = array_shift( $msgInfo );
 878+ } else {
 879+ $msg = $msgInfo;
 880+ $msgInfo = array();
 881+ }
 882+
 883+ $this->mLabel = wfMsgExt( $msg, 'parseinline', $msgInfo ) . wfMsg( 'colon-separator' );
 884+ } elseif ( isset( $params['label'] ) ) {
 885+ $this->mLabel = $params['label'];
 886+ }
 887+
 888+ $this->mName = "wp{$params['fieldname']}";
 889+ if ( isset( $params['name'] ) ) {
 890+ $this->mName = $params['name'];
 891+ }
 892+
 893+ $validName = Sanitizer::escapeId( $this->mName );
 894+ if ( $this->mName != $validName && !isset( $params['nodata'] ) ) {
 895+ throw new MWException( "Invalid name '{$this->mName}' passed to " . __METHOD__ );
 896+ }
 897+
 898+ $this->mID = "mw-input-{$this->mName}";
 899+
 900+ if ( isset( $params['default'] ) ) {
 901+ $this->mDefault = $params['default'];
 902+ }
 903+
 904+ if ( isset( $params['id'] ) ) {
 905+ $id = $params['id'];
 906+ $validId = Sanitizer::escapeId( $id );
 907+
 908+ if ( $id != $validId ) {
 909+ throw new MWException( "Invalid id '$id' passed to " . __METHOD__ );
 910+ }
 911+
 912+ $this->mID = $id;
 913+ }
 914+
 915+ if ( isset( $params['cssclass'] ) ) {
 916+ $this->mClass = $params['cssclass'];
 917+ }
 918+
 919+ if ( isset( $params['validation-callback'] ) ) {
 920+ $this->mValidationCallback = $params['validation-callback'];
 921+ }
 922+
 923+ if ( isset( $params['filter-callback'] ) ) {
 924+ $this->mFilterCallback = $params['filter-callback'];
 925+ }
 926+ }
 927+
 928+ /**
 929+ * Get the complete form "row" for the input, including help text,
 930+ * labels, and whatever.
 931+ * @param $value String the value to set the input to.
 932+ * @return String complete HTML form row.
 933+ */
 934+ function getFormRow( $value ) {
 935+ # Check for invalid data.
 936+
 937+ $errors = $this->validate( $value, $this->mParent->mFieldData );
 938+
 939+ $attributes = array();
 940+
 941+ if ( $errors === true || ( !$this->mParent->getRequest()->wasPosted() && ( $this->mParent->getMethod() == 'post' ) ) ) {
 942+ $errors = '';
 943+ $errorClass = '';
 944+ } else {
 945+ $errors = self::formatErrors( $errors );
 946+ $errorClass = 'mw-htmlform-invalid-input';
 947+ }
 948+
 949+ # Hint / Tip
 950+ $hinttext = null;
 951+ $hinthtml = '';
 952+
 953+ if ( isset( $this->mParams['hint-message'] ) ) {
 954+ $msg = call_user_func_array( 'wfMessage', (array)$this->mParams['hint-message'] );
 955+ if ( $msg->exists() ) {
 956+ $hinttext = $msg->parse();
 957+ }
 958+ } elseif ( isset( $this->mParams['hint'] ) ) {
 959+ $hinttext = $this->mParams['hint'];
 960+ }
 961+
 962+ if ( !is_null( $hinttext ) ) {
 963+ $hinthtml = "<div class=\"mw-help-field-container\">\n" .
 964+ "<span class=\"mw-help-field-hint\"></span>\n" .
 965+ "<span class=\"mw-help-field-data\">" . $hinttext . "</span>\n" .
 966+ "</div>\n";
 967+ }
 968+
 969+ # Label
 970+ $label = $this->getLabelHtml( $attributes, $hinthtml );
 971+
 972+ # Help message (optional)
 973+ $helptext = null;
 974+ $helphtml = '';
 975+
 976+ if ( isset( $this->mParams['help-message'] ) ) {
 977+ $msg = call_user_func_array( 'wfMessage', (array)$this->mParams['help-message'] );
 978+ if ( $msg->exists() ) {
 979+ $helptext = $msg->parse();
 980+ }
 981+ } elseif ( isset( $this->mParams['help-messages'] ) ) {
 982+ # help-message can be passed a message key (string) or an array containing
 983+ # a message key and additional parameters. This makes it impossible to pass
 984+ # an array of message key
 985+ foreach( $this->mParams['help-messages'] as $name ) {
 986+ $msg = wfMessage( $name );
 987+ if( $msg->exists() ) {
 988+ $helptext .= $msg->parse(); // append message
 989+ }
 990+ }
 991+ } elseif ( isset( $this->mParams['help'] ) ) {
 992+ $helptext = $this->mParams['help'];
 993+ }
 994+
 995+ if ( !is_null( $helptext ) ) {
 996+ $helphtml = Html::rawElement( 'div', array( 'class' => 'mw-htmlform-tip' ), $helptext );
 997+ }
 998+
 999+ # Input
 1000+ $field = Html::rawElement(
 1001+ 'div',
 1002+ array( 'class' => 'mw-input' ) + $attributes,
 1003+ $this->getInputHTML( $value ) . "\n$errors"
 1004+ );
 1005+
 1006+ $fieldType = get_class( $this );
 1007+
 1008+ $html = Html::rawElement( 'div',
 1009+ array( 'class' => "mw-htmlform-field mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
 1010+ $label . $helphtml . $field );
 1011+
 1012+ return $html;
 1013+ }
 1014+
 1015+ function getLabel() {
 1016+ return $this->mLabel;
 1017+ }
 1018+ function getLabelHtml( $attributes = array(), $hintHtml = '' ) {
 1019+ $labelAttrs = array();
 1020+
 1021+ if ( $this->needsLabel() ) {
 1022+ $labelAttrs['for'] = $this->mID;
 1023+ }
 1024+
 1025+ return Html::rawElement( 'div', array( 'class' => 'mw-label' ) + $attributes,
 1026+ $hintHtml . Html::rawElement( 'label', $labelAttrs, $this->getLabel() )
 1027+ );
 1028+ }
 1029+
 1030+ function getDefault() {
 1031+ if ( isset( $this->mDefault ) ) {
 1032+ return $this->mDefault;
 1033+ } else {
 1034+ return null;
 1035+ }
 1036+ }
 1037+
 1038+ /**
 1039+ * Returns the attributes required for the tooltip and accesskey.
 1040+ *
 1041+ * @return array Attributes
 1042+ */
 1043+ public function getTooltipAndAccessKey() {
 1044+ if ( empty( $this->mParams['tooltip'] ) ) {
 1045+ return array();
 1046+ }
 1047+ return Linker::tooltipAndAccessKeyAttribs( $this->mParams['tooltip'] );
 1048+ }
 1049+
 1050+ /**
 1051+ * flatten an array of options to a single array, for instance,
 1052+ * a set of <options> inside <optgroups>.
 1053+ * @param $options Associative Array with values either Strings
 1054+ * or Arrays
 1055+ * @return Array flattened input
 1056+ */
 1057+ public static function flattenOptions( $options ) {
 1058+ $flatOpts = array();
 1059+
 1060+ foreach ( $options as $value ) {
 1061+ if ( is_array( $value ) ) {
 1062+ $flatOpts = array_merge( $flatOpts, self::flattenOptions( $value ) );
 1063+ } else {
 1064+ $flatOpts[] = $value;
 1065+ }
 1066+ }
 1067+
 1068+ return $flatOpts;
 1069+ }
 1070+
 1071+ /**
 1072+ * Formats one or more errors as accepted by field validation-callback.
 1073+ * @param $errors String|Message|Array of strings or Message instances
 1074+ * @return String html
 1075+ * @since 1.18
 1076+ */
 1077+ protected static function formatErrors( $errors ) {
 1078+ if ( is_array( $errors ) && count( $errors ) === 1 ) {
 1079+ $errors = array_shift( $errors );
 1080+ }
 1081+
 1082+ if ( is_array( $errors ) ) {
 1083+ $lines = array();
 1084+ foreach ( $errors as $error ) {
 1085+ if ( $error instanceof Message ) {
 1086+ $lines[] = Html::rawElement( 'li', array(), $error->parse() );
 1087+ } else {
 1088+ $lines[] = Html::rawElement( 'li', array(), $error );
 1089+ }
 1090+ }
 1091+ return Html::rawElement( 'ul', array( 'class' => 'error' ), implode( "\n", $lines ) );
 1092+ } else {
 1093+ if ( $errors instanceof Message ) {
 1094+ $errors = $errors->parse();
 1095+ }
 1096+ return Html::rawElement( 'span', array( 'class' => 'error' ), $errors );
 1097+ }
 1098+ }
 1099+}
 1100+
 1101+class HTMLTextField extends HTMLFormField {
 1102+ function getSize() {
 1103+ return isset( $this->mParams['size'] )
 1104+ ? $this->mParams['size']
 1105+ : 45;
 1106+ }
 1107+
 1108+ function getInputHTML( $value ) {
 1109+ $attribs = array(
 1110+ 'id' => $this->mID,
 1111+ 'name' => $this->mName,
 1112+ 'size' => $this->getSize(),
 1113+ 'value' => $value,
 1114+ ) + $this->getTooltipAndAccessKey();
 1115+
 1116+ if ( isset( $this->mParams['maxlength'] ) ) {
 1117+ $attribs['maxlength'] = $this->mParams['maxlength'];
 1118+ }
 1119+
 1120+ if ( !empty( $this->mParams['disabled'] ) ) {
 1121+ $attribs['disabled'] = 'disabled';
 1122+ }
 1123+
 1124+ # TODO: Enforce pattern, step, required, readonly on the server side as
 1125+ # well
 1126+ foreach ( array( 'min', 'max', 'pattern', 'title', 'step',
 1127+ 'placeholder' ) as $param ) {
 1128+ if ( isset( $this->mParams[$param] ) ) {
 1129+ $attribs[$param] = $this->mParams[$param];
 1130+ }
 1131+ }
 1132+
 1133+ foreach ( array( 'required', 'autofocus', 'multiple', 'readonly' ) as $param ) {
 1134+ if ( isset( $this->mParams[$param] ) ) {
 1135+ $attribs[$param] = '';
 1136+ }
 1137+ }
 1138+
 1139+ # Implement tiny differences between some field variants
 1140+ # here, rather than creating a new class for each one which
 1141+ # is essentially just a clone of this one.
 1142+ if ( isset( $this->mParams['type'] ) ) {
 1143+ switch ( $this->mParams['type'] ) {
 1144+ case 'email':
 1145+ $attribs['type'] = 'email';
 1146+ break;
 1147+ case 'int':
 1148+ $attribs['type'] = 'number';
 1149+ break;
 1150+ case 'float':
 1151+ $attribs['type'] = 'number';
 1152+ $attribs['step'] = 'any';
 1153+ break;
 1154+ # Pass through
 1155+ case 'password':
 1156+ case 'file':
 1157+ $attribs['type'] = $this->mParams['type'];
 1158+ break;
 1159+ }
 1160+ }
 1161+
 1162+ return Html::element( 'input', $attribs );
 1163+ }
 1164+}
 1165+class HTMLTextAreaField extends HTMLFormField {
 1166+ function getCols() {
 1167+ return isset( $this->mParams['cols'] )
 1168+ ? $this->mParams['cols']
 1169+ : 80;
 1170+ }
 1171+
 1172+ function getRows() {
 1173+ return isset( $this->mParams['rows'] )
 1174+ ? $this->mParams['rows']
 1175+ : 25;
 1176+ }
 1177+
 1178+ function getInputHTML( $value ) {
 1179+ $attribs = array(
 1180+ 'id' => $this->mID,
 1181+ 'name' => $this->mName,
 1182+ 'cols' => $this->getCols(),
 1183+ 'rows' => $this->getRows(),
 1184+ ) + $this->getTooltipAndAccessKey();
 1185+
 1186+
 1187+ if ( !empty( $this->mParams['disabled'] ) ) {
 1188+ $attribs['disabled'] = 'disabled';
 1189+ }
 1190+
 1191+ if ( !empty( $this->mParams['readonly'] ) ) {
 1192+ $attribs['readonly'] = 'readonly';
 1193+ }
 1194+
 1195+ foreach ( array( 'required', 'autofocus' ) as $param ) {
 1196+ if ( isset( $this->mParams[$param] ) ) {
 1197+ $attribs[$param] = '';
 1198+ }
 1199+ }
 1200+
 1201+ return Html::element( 'textarea', $attribs, $value );
 1202+ }
 1203+}
 1204+
 1205+/**
 1206+ * A field that will contain a numeric value
 1207+ */
 1208+class HTMLFloatField extends HTMLTextField {
 1209+ function getSize() {
 1210+ return isset( $this->mParams['size'] )
 1211+ ? $this->mParams['size']
 1212+ : 20;
 1213+ }
 1214+
 1215+ function validate( $value, $alldata ) {
 1216+ $p = parent::validate( $value, $alldata );
 1217+
 1218+ if ( $p !== true ) {
 1219+ return $p;
 1220+ }
 1221+
 1222+ $value = trim( $value );
 1223+
 1224+ # http://dev.w3.org/html5/spec/common-microsyntaxes.html#real-numbers
 1225+ # with the addition that a leading '+' sign is ok.
 1226+ if ( !preg_match( '/^((\+|\-)?\d+(\.\d+)?(E(\+|\-)?\d+)?)?$/i', $value ) ) {
 1227+ return wfMsgExt( 'htmlform-float-invalid', 'parse' );
 1228+ }
 1229+
 1230+ # The "int" part of these message names is rather confusing.
 1231+ # They make equal sense for all numbers.
 1232+ if ( isset( $this->mParams['min'] ) ) {
 1233+ $min = $this->mParams['min'];
 1234+
 1235+ if ( $min > $value ) {
 1236+ return wfMsgExt( 'htmlform-int-toolow', 'parse', array( $min ) );
 1237+ }
 1238+ }
 1239+
 1240+ if ( isset( $this->mParams['max'] ) ) {
 1241+ $max = $this->mParams['max'];
 1242+
 1243+ if ( $max < $value ) {
 1244+ return wfMsgExt( 'htmlform-int-toohigh', 'parse', array( $max ) );
 1245+ }
 1246+ }
 1247+
 1248+ return true;
 1249+ }
 1250+}
 1251+
 1252+/**
 1253+ * A field that must contain a number
 1254+ */
 1255+class HTMLIntField extends HTMLFloatField {
 1256+ function validate( $value, $alldata ) {
 1257+ $p = parent::validate( $value, $alldata );
 1258+
 1259+ if ( $p !== true ) {
 1260+ return $p;
 1261+ }
 1262+
 1263+ # http://dev.w3.org/html5/spec/common-microsyntaxes.html#signed-integers
 1264+ # with the addition that a leading '+' sign is ok. Note that leading zeros
 1265+ # are fine, and will be left in the input, which is useful for things like
 1266+ # phone numbers when you know that they are integers (the HTML5 type=tel
 1267+ # input does not require its value to be numeric). If you want a tidier
 1268+ # value to, eg, save in the DB, clean it up with intval().
 1269+ if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) )
 1270+ ) {
 1271+ return wfMsgExt( 'htmlform-int-invalid', 'parse' );
 1272+ }
 1273+
 1274+ return true;
 1275+ }
 1276+}
 1277+
 1278+/**
 1279+ * A checkbox field
 1280+ */
 1281+class HTMLCheckField extends HTMLFormField {
 1282+ function getInputHTML( $value ) {
 1283+ if ( !empty( $this->mParams['invert'] ) ) {
 1284+ $value = !$value;
 1285+ }
 1286+
 1287+ $attr = $this->getTooltipAndAccessKey();
 1288+ $attr['id'] = $this->mID;
 1289+
 1290+ if ( !empty( $this->mParams['disabled'] ) ) {
 1291+ $attr['disabled'] = 'disabled';
 1292+ }
 1293+
 1294+ return Xml::check( $this->mName, $value, $attr ) . '&#160;' .
 1295+ Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
 1296+ }
 1297+
 1298+ /**
 1299+ * For a checkbox, the label goes on the right hand side, and is
 1300+ * added in getInputHTML(), rather than HTMLFormField::getRow()
 1301+ */
 1302+ function getLabel() {
 1303+ return '&#160;';
 1304+ }
 1305+
 1306+ /**
 1307+ * @param $request WebRequest
 1308+ * @return String
 1309+ */
 1310+ function loadDataFromRequest( $request ) {
 1311+ $invert = false;
 1312+ if ( isset( $this->mParams['invert'] ) && $this->mParams['invert'] ) {
 1313+ $invert = true;
 1314+ }
 1315+
 1316+ // GetCheck won't work like we want for checks.
 1317+ if ( $request->getCheck( 'wpEditToken' ) || $this->mParent->getMethod() != 'post' ) {
 1318+ // XOR has the following truth table, which is what we want
 1319+ // INVERT VALUE | OUTPUT
 1320+ // true true | false
 1321+ // false true | true
 1322+ // false false | false
 1323+ // true false | true
 1324+ return $request->getBool( $this->mName ) xor $invert;
 1325+ } else {
 1326+ return $this->getDefault();
 1327+ }
 1328+ }
 1329+}
 1330+
 1331+/**
 1332+ * A select dropdown field. Basically a wrapper for Xmlselect class
 1333+ */
 1334+class HTMLSelectField extends HTMLFormField {
 1335+ function validate( $value, $alldata ) {
 1336+ $p = parent::validate( $value, $alldata );
 1337+
 1338+ if ( $p !== true ) {
 1339+ return $p;
 1340+ }
 1341+
 1342+ $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
 1343+
 1344+ if ( in_array( $value, $validOptions ) )
 1345+ return true;
 1346+ else
 1347+ return wfMsgExt( 'htmlform-select-badoption', 'parseinline' );
 1348+ }
 1349+
 1350+ function getInputHTML( $value ) {
 1351+ $select = new XmlSelect( $this->mName, $this->mID, strval( $value ) );
 1352+
 1353+ # If one of the options' 'name' is int(0), it is automatically selected.
 1354+ # because PHP sucks and thinks int(0) == 'some string'.
 1355+ # Working around this by forcing all of them to strings.
 1356+ foreach( $this->mParams['options'] as &$opt ){
 1357+ if( is_int( $opt ) ){
 1358+ $opt = strval( $opt );
 1359+ }
 1360+ }
 1361+ unset( $opt ); # PHP keeps $opt around as a reference, which is a bit scary
 1362+
 1363+ if ( !empty( $this->mParams['disabled'] ) ) {
 1364+ $select->setAttribute( 'disabled', 'disabled' );
 1365+ }
 1366+
 1367+ $select->addOptions( $this->mParams['options'] );
 1368+
 1369+ return $select->getHTML();
 1370+ }
 1371+}
 1372+
 1373+/**
 1374+ * Select dropdown field, with an additional "other" textbox.
 1375+ */
 1376+class HTMLSelectOrOtherField extends HTMLTextField {
 1377+ static $jsAdded = false;
 1378+
 1379+ function __construct( $params ) {
 1380+ if ( !in_array( 'other', $params['options'], true ) ) {
 1381+ $params['options'][wfMsg( 'htmlform-selectorother-other' )] = 'other';
 1382+ }
 1383+
 1384+ parent::__construct( $params );
 1385+ }
 1386+
 1387+ static function forceToStringRecursive( $array ) {
 1388+ if ( is_array( $array ) ) {
 1389+ return array_map( array( __CLASS__, 'forceToStringRecursive' ), $array );
 1390+ } else {
 1391+ return strval( $array );
 1392+ }
 1393+ }
 1394+
 1395+ function getInputHTML( $value ) {
 1396+ $valInSelect = false;
 1397+
 1398+ if ( $value !== false ) {
 1399+ $valInSelect = in_array(
 1400+ $value,
 1401+ HTMLFormField::flattenOptions( $this->mParams['options'] )
 1402+ );
 1403+ }
 1404+
 1405+ $selected = $valInSelect ? $value : 'other';
 1406+
 1407+ $opts = self::forceToStringRecursive( $this->mParams['options'] );
 1408+
 1409+ $select = new XmlSelect( $this->mName, $this->mID, $selected );
 1410+ $select->addOptions( $opts );
 1411+
 1412+ $select->setAttribute( 'class', 'mw-htmlform-select-or-other' );
 1413+
 1414+ $tbAttribs = array( 'id' => $this->mID . '-other', 'size' => $this->getSize() );
 1415+
 1416+ if ( !empty( $this->mParams['disabled'] ) ) {
 1417+ $select->setAttribute( 'disabled', 'disabled' );
 1418+ $tbAttribs['disabled'] = 'disabled';
 1419+ }
 1420+
 1421+ $select = $select->getHTML();
 1422+
 1423+ if ( isset( $this->mParams['maxlength'] ) ) {
 1424+ $tbAttribs['maxlength'] = $this->mParams['maxlength'];
 1425+ }
 1426+
 1427+ $textbox = Html::input(
 1428+ $this->mName . '-other',
 1429+ $valInSelect ? '' : $value,
 1430+ 'text',
 1431+ $tbAttribs
 1432+ );
 1433+
 1434+ return "$select<br />\n$textbox";
 1435+ }
 1436+
 1437+ /**
 1438+ * @param $request WebRequest
 1439+ * @return String
 1440+ */
 1441+ function loadDataFromRequest( $request ) {
 1442+ if ( $request->getCheck( $this->mName ) ) {
 1443+ $val = $request->getText( $this->mName );
 1444+
 1445+ if ( $val == 'other' ) {
 1446+ $val = $request->getText( $this->mName . '-other' );
 1447+ }
 1448+
 1449+ return $val;
 1450+ } else {
 1451+ return $this->getDefault();
 1452+ }
 1453+ }
 1454+}
 1455+
 1456+/**
 1457+ * Multi-select field
 1458+ */
 1459+class HTMLMultiSelectField extends HTMLFormField {
 1460+
 1461+ public function __construct( $params ){
 1462+ parent::__construct( $params );
 1463+ if( isset( $params['flatlist'] ) ){
 1464+ $this->mClass .= ' mw-htmlform-multiselect-flatlist';
 1465+ }
 1466+ }
 1467+
 1468+ function validate( $value, $alldata ) {
 1469+ $p = parent::validate( $value, $alldata );
 1470+
 1471+ if ( $p !== true ) {
 1472+ return $p;
 1473+ }
 1474+
 1475+ if ( !is_array( $value ) ) {
 1476+ return false;
 1477+ }
 1478+
 1479+ # If all options are valid, array_intersect of the valid options
 1480+ # and the provided options will return the provided options.
 1481+ $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
 1482+
 1483+ $validValues = array_intersect( $value, $validOptions );
 1484+ if ( count( $validValues ) == count( $value ) ) {
 1485+ return true;
 1486+ } else {
 1487+ return wfMsgExt( 'htmlform-select-badoption', 'parseinline' );
 1488+ }
 1489+ }
 1490+
 1491+ function getInputHTML( $value ) {
 1492+ $html = $this->formatOptions( $this->mParams['options'], $value );
 1493+
 1494+ return $html;
 1495+ }
 1496+
 1497+ function formatOptions( $options, $value ) {
 1498+ $html = '';
 1499+
 1500+ $attribs = array();
 1501+
 1502+ if ( !empty( $this->mParams['disabled'] ) ) {
 1503+ $attribs['disabled'] = 'disabled';
 1504+ }
 1505+
 1506+ foreach ( $options as $label => $info ) {
 1507+ if ( is_array( $info ) ) {
 1508+ $html .= Html::rawElement( 'h1', array(), $label ) . "\n";
 1509+ $html .= $this->formatOptions( $info, $value );
 1510+ } else {
 1511+ $thisAttribs = array( 'id' => "{$this->mID}-$info", 'value' => $info );
 1512+
 1513+ $checkbox = Xml::check(
 1514+ $this->mName . '[]',
 1515+ in_array( $info, $value, true ),
 1516+ $attribs + $thisAttribs );
 1517+ $checkbox .= '&#160;' . Html::rawElement( 'label', array( 'for' => "{$this->mID}-$info" ), $label );
 1518+
 1519+ $html .= ' ' . Html::rawElement( 'div', array( 'class' => 'mw-htmlform-multiselect-item' ), $checkbox );
 1520+ }
 1521+ }
 1522+
 1523+ return $html;
 1524+ }
 1525+
 1526+ /**
 1527+ * @param $request WebRequest
 1528+ * @return String
 1529+ */
 1530+ function loadDataFromRequest( $request ) {
 1531+ if ( $this->mParent->getMethod() == 'post' ) {
 1532+ if( $request->wasPosted() ){
 1533+ # Checkboxes are just not added to the request arrays if they're not checked,
 1534+ # so it's perfectly possible for there not to be an entry at all
 1535+ return $request->getArray( $this->mName, array() );
 1536+ } else {
 1537+ # That's ok, the user has not yet submitted the form, so show the defaults
 1538+ return $this->getDefault();
 1539+ }
 1540+ } else {
 1541+ # This is the impossible case: if we look at $_GET and see no data for our
 1542+ # field, is it because the user has not yet submitted the form, or that they
 1543+ # have submitted it with all the options unchecked? We will have to assume the
 1544+ # latter, which basically means that you can't specify 'positive' defaults
 1545+ # for GET forms.
 1546+ # @todo FIXME...
 1547+ return $request->getArray( $this->mName, array() );
 1548+ }
 1549+ }
 1550+
 1551+ function getDefault() {
 1552+ if ( isset( $this->mDefault ) ) {
 1553+ return $this->mDefault;
 1554+ } else {
 1555+ return array();
 1556+ }
 1557+ }
 1558+
 1559+ protected function needsLabel() {
 1560+ return false;
 1561+ }
 1562+}
 1563+
 1564+/**
 1565+ * Double field with a dropdown list constructed from a system message in the format
 1566+ * * Optgroup header
 1567+ * ** <option value>|<option name>
 1568+ * ** <option value == option name>
 1569+ * * New Optgroup header
 1570+ * Plus a text field underneath for an additional reason. The 'value' of the field is
 1571+ * ""<select>: <extra reason>"", or "<extra reason>" if nothing has been selected in the
 1572+ * select dropdown.
 1573+ * @todo FIXME: If made 'required', only the text field should be compulsory.
 1574+ */
 1575+class HTMLSelectAndOtherField extends HTMLSelectField {
 1576+
 1577+ function __construct( $params ) {
 1578+ if ( array_key_exists( 'other', $params ) ) {
 1579+ } elseif( array_key_exists( 'other-message', $params ) ){
 1580+ $params['other'] = wfMsg( $params['other-message'] );
 1581+ } else {
 1582+ $params['other'] = wfMsg( 'htmlform-selectorother-other' );
 1583+ }
 1584+
 1585+ if ( array_key_exists( 'options', $params ) ) {
 1586+ # Options array already specified
 1587+ } elseif( array_key_exists( 'options-message', $params ) ){
 1588+ # Generate options array from a system message
 1589+ $params['options'] = self::parseMessage( wfMsg( $params['options-message'], $params['other'] ) );
 1590+ } else {
 1591+ # Sulk
 1592+ throw new MWException( 'HTMLSelectAndOtherField called without any options' );
 1593+ }
 1594+ $this->mFlatOptions = self::flattenOptions( $params['options'] );
 1595+
 1596+ parent::__construct( $params );
 1597+ }
 1598+
 1599+ /**
 1600+ * Build a drop-down box from a textual list.
 1601+ * @param $string String message text
 1602+ * @param $otherName String name of "other reason" option
 1603+ * @return Array
 1604+ * TODO: this is copied from Xml::listDropDown(), deprecate/avoid duplication?
 1605+ */
 1606+ public static function parseMessage( $string, $otherName=null ) {
 1607+ if( $otherName === null ){
 1608+ $otherName = wfMsg( 'htmlform-selectorother-other' );
 1609+ }
 1610+
 1611+ $optgroup = false;
 1612+ $options = array( $otherName => 'other' );
 1613+
 1614+ foreach ( explode( "\n", $string ) as $option ) {
 1615+ $value = trim( $option );
 1616+ if ( $value == '' ) {
 1617+ continue;
 1618+ } elseif ( substr( $value, 0, 1) == '*' && substr( $value, 1, 1) != '*' ) {
 1619+ # A new group is starting...
 1620+ $value = trim( substr( $value, 1 ) );
 1621+ $optgroup = $value;
 1622+ } elseif ( substr( $value, 0, 2) == '**' ) {
 1623+ # groupmember
 1624+ $opt = trim( substr( $value, 2 ) );
 1625+ $parts = array_map( 'trim', explode( '|', $opt, 2 ) );
 1626+ if( count( $parts ) === 1 ){
 1627+ $parts[1] = $parts[0];
 1628+ }
 1629+ if( $optgroup === false ){
 1630+ $options[$parts[1]] = $parts[0];
 1631+ } else {
 1632+ $options[$optgroup][$parts[1]] = $parts[0];
 1633+ }
 1634+ } else {
 1635+ # groupless reason list
 1636+ $optgroup = false;
 1637+ $parts = array_map( 'trim', explode( '|', $option, 2 ) );
 1638+ if( count( $parts ) === 1 ){
 1639+ $parts[1] = $parts[0];
 1640+ }
 1641+ $options[$parts[1]] = $parts[0];
 1642+ }
 1643+ }
 1644+
 1645+ return $options;
 1646+ }
 1647+
 1648+ function getInputHTML( $value ) {
 1649+ $select = parent::getInputHTML( $value[1] );
 1650+
 1651+ $textAttribs = array(
 1652+ 'id' => $this->mID . '-other',
 1653+ 'size' => $this->getSize(),
 1654+ );
 1655+
 1656+ foreach ( array( 'required', 'autofocus', 'multiple', 'disabled' ) as $param ) {
 1657+ if ( isset( $this->mParams[$param] ) ) {
 1658+ $textAttribs[$param] = '';
 1659+ }
 1660+ }
 1661+
 1662+ $textbox = Html::input(
 1663+ $this->mName . '-other',
 1664+ $value[2],
 1665+ 'text',
 1666+ $textAttribs
 1667+ );
 1668+
 1669+ return "$select<br />\n$textbox";
 1670+ }
 1671+
 1672+ /**
 1673+ * @param $request WebRequest
 1674+ * @return Array( <overall message>, <select value>, <text field value> )
 1675+ */
 1676+ function loadDataFromRequest( $request ) {
 1677+ if ( $request->getCheck( $this->mName ) ) {
 1678+
 1679+ $list = $request->getText( $this->mName );
 1680+ $text = $request->getText( $this->mName . '-other' );
 1681+
 1682+ if ( $list == 'other' ) {
 1683+ $final = $text;
 1684+ } elseif( !in_array( $list, $this->mFlatOptions ) ){
 1685+ # User has spoofed the select form to give an option which wasn't
 1686+ # in the original offer. Sulk...
 1687+ $final = $text;
 1688+ } elseif( $text == '' ) {
 1689+ $final = $list;
 1690+ } else {
 1691+ $final = $list . wfMsgForContent( 'colon-separator' ) . $text;
 1692+ }
 1693+
 1694+ } else {
 1695+ $final = $this->getDefault();
 1696+ $list = $text = '';
 1697+ }
 1698+ return array( $final, $list, $text );
 1699+ }
 1700+
 1701+ function getSize() {
 1702+ return isset( $this->mParams['size'] )
 1703+ ? $this->mParams['size']
 1704+ : 45;
 1705+ }
 1706+
 1707+ function validate( $value, $alldata ) {
 1708+ # HTMLSelectField forces $value to be one of the options in the select
 1709+ # field, which is not useful here. But we do want the validation further up
 1710+ # the chain
 1711+ $p = parent::validate( $value[1], $alldata );
 1712+
 1713+ if ( $p !== true ) {
 1714+ return $p;
 1715+ }
 1716+
 1717+ if( isset( $this->mParams['required'] ) && $value[1] === '' ){
 1718+ return wfMsgExt( 'htmlform-required', 'parseinline' );
 1719+ }
 1720+
 1721+ return true;
 1722+ }
 1723+}
 1724+
 1725+/**
 1726+ * Radio checkbox fields.
 1727+ */
 1728+class HTMLRadioField extends HTMLFormField {
 1729+ function validate( $value, $alldata ) {
 1730+ $p = parent::validate( $value, $alldata );
 1731+
 1732+ if ( $p !== true ) {
 1733+ return $p;
 1734+ }
 1735+
 1736+ if ( !is_string( $value ) && !is_int( $value ) ) {
 1737+ return false;
 1738+ }
 1739+
 1740+ $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
 1741+
 1742+ if ( in_array( $value, $validOptions ) ) {
 1743+ return true;
 1744+ } else {
 1745+ return wfMsgExt( 'htmlform-select-badoption', 'parseinline' );
 1746+ }
 1747+ }
 1748+
 1749+ /**
 1750+ * This returns a block of all the radio options, in one cell.
 1751+ * @see includes/HTMLFormField#getInputHTML()
 1752+ */
 1753+ function getInputHTML( $value ) {
 1754+ $html = $this->formatOptions( $this->mParams['options'], $value );
 1755+
 1756+ return $html;
 1757+ }
 1758+
 1759+ function formatOptions( $options, $value ) {
 1760+ $html = '';
 1761+
 1762+ $attribs = array();
 1763+ if ( !empty( $this->mParams['disabled'] ) ) {
 1764+ $attribs['disabled'] = 'disabled';
 1765+ }
 1766+
 1767+ # TODO: should this produce an unordered list perhaps?
 1768+ foreach ( $options as $label => $info ) {
 1769+ if ( is_array( $info ) ) {
 1770+ $html .= Html::rawElement( 'h1', array(), $label ) . "\n";
 1771+ $html .= $this->formatOptions( $info, $value );
 1772+ } else {
 1773+ $id = Sanitizer::escapeId( $this->mID . "-$info" );
 1774+ $html .= Xml::radio(
 1775+ $this->mName,
 1776+ $info,
 1777+ $info == $value,
 1778+ $attribs + array( 'id' => $id )
 1779+ );
 1780+ $html .= '&#160;' .
 1781+ Html::rawElement( 'label', array( 'for' => $id ), $label );
 1782+
 1783+ $html .= "<br />\n";
 1784+ }
 1785+ }
 1786+
 1787+ return $html;
 1788+ }
 1789+
 1790+ protected function needsLabel() {
 1791+ return false;
 1792+ }
 1793+}
 1794+
 1795+/**
 1796+ * An information field (text blob), not a proper input.
 1797+ */
 1798+class HTMLInfoField extends HTMLFormField {
 1799+ function __construct( $info ) {
 1800+ $info['nodata'] = true;
 1801+
 1802+ parent::__construct( $info );
 1803+ }
 1804+
 1805+ function getInputHTML( $value ) {
 1806+ return !empty( $this->mParams['raw'] ) ? $value : htmlspecialchars( $value );
 1807+ }
 1808+
 1809+ function getFormRow( $value ) {
 1810+ if ( !empty( $this->mParams['rawrow'] ) ) {
 1811+ return $value;
 1812+ }
 1813+
 1814+ return parent::getFormRow( $value );
 1815+ }
 1816+
 1817+ protected function needsLabel() {
 1818+ return false;
 1819+ }
 1820+}
 1821+
 1822+class HTMLHiddenField extends HTMLFormField {
 1823+ public function __construct( $params ) {
 1824+ parent::__construct( $params );
 1825+
 1826+ # Per HTML5 spec, hidden fields cannot be 'required'
 1827+ # http://dev.w3.org/html5/spec/states-of-the-type-attribute.html#hidden-state
 1828+ unset( $this->mParams['required'] );
 1829+ }
 1830+
 1831+ public function getFormRow( $value ) {
 1832+ $params = array();
 1833+ if ( $this->mID ) {
 1834+ $params['id'] = $this->mID;
 1835+ }
 1836+
 1837+ $this->mParent->addHiddenField(
 1838+ $this->mName,
 1839+ $this->mDefault,
 1840+ $params
 1841+ );
 1842+
 1843+ return '';
 1844+ }
 1845+
 1846+ public function getInputHTML( $value ) { return ''; }
 1847+}
 1848+
 1849+/**
 1850+ * Add a submit button inline in the form (as opposed to
 1851+ * HTMLForm::addButton(), which will add it at the end).
 1852+ */
 1853+class HTMLSubmitField extends HTMLFormField {
 1854+
 1855+ function __construct( $info ) {
 1856+ $info['nodata'] = true;
 1857+ parent::__construct( $info );
 1858+ }
 1859+
 1860+ function getInputHTML( $value ) {
 1861+ return Xml::submitButton(
 1862+ $value,
 1863+ array(
 1864+ 'class' => 'mw-htmlform-submit',
 1865+ 'name' => $this->mName,
 1866+ 'id' => $this->mID,
 1867+ )
 1868+ );
 1869+ }
 1870+
 1871+ protected function needsLabel() {
 1872+ return false;
 1873+ }
 1874+
 1875+ /**
 1876+ * Button cannot be invalid
 1877+ */
 1878+ public function validate( $value, $alldata ){
 1879+ return true;
 1880+ }
 1881+}
 1882+
 1883+class HTMLEditTools extends HTMLFormField {
 1884+ public function getInputHTML( $value ) {
 1885+ return '';
 1886+ }
 1887+
 1888+ public function getFormRow( $value ) {
 1889+ if ( empty( $this->mParams['message'] ) ) {
 1890+ $msg = wfMessage( 'edittools' );
 1891+ } else {
 1892+ $msg = wfMessage( $this->mParams['message'] );
 1893+ if ( $msg->isDisabled() ) {
 1894+ $msg = wfMessage( 'edittools' );
 1895+ }
 1896+ }
 1897+ $msg->inContentLanguage();
 1898+
 1899+
 1900+ return '<div class="mw-htmlform-field-HTMLEditTools"><div class="mw-input">'
 1901+ . '<div class="mw-editTools">' . $msg->parseAsBlock() . "</div>"
 1902+ . "</div></div>\n";
 1903+ }
 1904+}
Property changes on: trunk/extensions/StyleGuideDemo/HTMLStyleForm.php
___________________________________________________________________
Added: svn:eol-style
11905 + native
Index: trunk/extensions/StyleGuideDemo/StyleGuideDemo.alias.php
@@ -0,0 +1,14 @@
 2+<?php
 3+/**
 4+ * Aliases for Special:StyleGuideDemo
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$specialPageAliases = array();
 11+
 12+/** English (English) */
 13+$specialPageAliases['en'] = array(
 14+ 'StyleGuideDemo' => array( 'StyleGuideDemo' ),
 15+);
Property changes on: trunk/extensions/StyleGuideDemo/StyleGuideDemo.alias.php
___________________________________________________________________
Added: svn:eol-style
116 + native
Index: trunk/extensions/StyleGuideDemo/StyleGuideDemo.i18n.php
@@ -0,0 +1,36 @@
 2+<?php
 3+$messages = array();
 4+
 5+/** English
 6+ * @author Timo Tijhof
 7+ */
 8+$messages['en'] = array(
 9+ 'styleguidedemo' => 'Style guide demonstration',
 10+ 'styleguidedemo-desc' => 'An extension to demonstrate and find out how the style guide should be used.',
 11+ /* Special:StyleGuideDemo */
 12+ 'styleguidedemo-head-createform' => 'Create Account',
 13+ 'styleguidedemo-username' => 'Username',
 14+ 'styleguidedemo-username-hint' => 'Proin at eros non eros adipiscing mollis. Quisque aliquet, quam elementum condimentum feugiat, tellus odio consectetuer wisi, vel nonummy sem neque in elit. Curabitur eleifend wisi iaculis ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.',
 15+ 'styleguidedemo-username-help' => 'Please view the [[$1|User Name Policy]].',
 16+ 'styleguidedemo-username-infopage' => '{{ns:project}}:User Name Policy',
 17+ 'styleguidedemo-password' => 'Password',
 18+ 'styleguidedemo-password-help' => 'Please view the [[$1|Password Guidelines]].',
 19+ 'styleguidedemo-password-infopage' => '{{ns:project}}:Password Guidelines',
 20+ 'styleguidedemo-confirmpassword' => 'Confirm Password',
 21+ 'styleguidedemo-confirmpassword-help' => 'Please re-enter your password.',
 22+ 'styleguidedemo-email' => 'Email Address',
 23+ 'styleguidedemo-email-hint' => 'In non velit non ligula laoreet ultrices. Praesent ultricies facilisis nisl. Vivamus luctus elit sit amet mi. Phasellus pellentesque, erat eget elementum volutpat, dolor nisl porta neque, vitae sodales ipsum nibh in ligula. Maecenas mattis pulvinar diam. Curabitur sed leo.',
 24+ 'styleguidedemo-email-help' => 'Learn why you should [[$1|Provide an Email Address]].',
 25+ 'styleguidedemo-email-infopage' => '{{ns:project}}:Autoconfirmed',
 26+ 'styleguidedemo-email-placeholder' => 'yourname@domain.com', // Optional
 27+ 'styleguidedemo-captcha-hint' => 'Donec semper turpis sed diam. Sed consequat ligula nec tortor. Integer eget sem. Ut vitae enim eu est vehicula gravida. Morbi ipsum ipsum, porta nec, tempor id, auctor vitae, purus. Pellentesque neque. Nulla luctus erat vitae libero. Integer nec enim. Phasellus aliquam enim et tortor.',
 28+);
 29+
 30+/** Message documentation (Message documentation)
 31+ * @author Timo Tijhof
 32+ */
 33+$messages['qqq'] = array(
 34+ 'styleguidedemo' => 'The title of special page.',
 35+ 'styleguidedemo-desc' => '{{desc}}',
 36+ 'styleguidedemo-email-placeholder' => '{{Optional}}',
 37+);
Property changes on: trunk/extensions/StyleGuideDemo/StyleGuideDemo.i18n.php
___________________________________________________________________
Added: svn:eol-style
138 + native
Index: trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/images/MW-StyleGuide-Form-InputInnerShade.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/images/MW-StyleGuide-Form-InputInnerShade.png
___________________________________________________________________
Added: svn:mime-type
239 + image/png
Index: trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/ext.styleguidedemo.css
@@ -0,0 +1,51 @@
 2+/**
 3+ * Base classes reflecting the MediaWiki Style guide.
 4+ *
 5+ * Note: This must not depend on JavaScript as it is
 6+ * also loaded with javascript disabled.
 7+ */
 8+
 9+.mw-htmlform-form {
 10+ padding: 10px;
 11+}
 12+
 13+.mw-htmlform-field {
 14+ margin: 0px 0px 21px 0px;
 15+}
 16+
 17+.mw-label {
 18+ display: block;
 19+}
 20+
 21+.mw-label .mw-help-field-hint {
 22+ margin-bottom: -15px;
 23+ height: 12px;
 24+ width: 12px;
 25+}
 26+
 27+.mw-label label {
 28+ margin: 0px 0px 0px 16px;
 29+ font-weight: bold;
 30+}
 31+
 32+.mw-htmlform-tip {
 33+ margin: 0px 0px 0px 30px;
 34+ font-size: 10px;
 35+ line-height: 14px;
 36+ color: #777777;
 37+}
 38+
 39+.mw-htmlform-field-HTMLTextField .mw-input {
 40+ margin: 0px 0px 0px 30px;
 41+}
 42+
 43+.mw-htmlform-field-HTMLTextField .mw-input input {
 44+ background: #f7f6f6 url(images/MW-StyleGuide-Form-InputInnerShade.png) top left no-repeat;
 45+ border: 1px solid #6b6b6b;
 46+ margin: 0;
 47+ padding: 6px 4px 4px 10px;
 48+ width: 200px; /* width: 214px; */
 49+}
 50+
 51+.mw-htmlform-submit {
 52+}
Property changes on: trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/ext.styleguidedemo.css
___________________________________________________________________
Added: svn:eol-style
153 + native
Index: trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/ext.styleguidedemo.js
@@ -0,0 +1,19 @@
 2+/**
 3+ * Script for the Style guide demo extension.
 4+ */
 5+jQuery( document ).ready(function( $ ) {
 6+
 7+ // Set up the help system
 8+ $( '.mw-help-field-data' )
 9+ .hide()
 10+ .closest( '.mw-help-field-container' )
 11+ .find( '.mw-help-field-hint' )
 12+ .show()
 13+ .click( function() {
 14+ $(this)
 15+ .closest( '.mw-help-field-container' )
 16+ .find( '.mw-help-field-data' )
 17+ .slideToggle( 'fast' );
 18+ } );
 19+
 20+});
Property changes on: trunk/extensions/StyleGuideDemo/modules/ext.styleguidedemo/ext.styleguidedemo.js
___________________________________________________________________
Added: svn:eol-style
121 + native
Index: trunk/extensions/StyleGuideDemo/StyleGuideDemo.php
@@ -0,0 +1,54 @@
 2+<?php
 3+/**
 4+ * Style guide demonstration extension
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ *
 9+ * Dual-licensed
 10+ * @license Creative Commons Attribution 3.0 Unported license
 11+ * @license GPL v2 or later
 12+ * @author Timo Tijhof <krinklemail@gmail.com>
 13+ * @version 0.1.0
 14+ */
 15+
 16+/* Setup */
 17+
 18+$wgExtensionCredits['other'][] = array(
 19+ 'path' => __FILE__,
 20+ 'name' => 'Style guide demonstration',
 21+ 'author' => array(
 22+ 'Timo Tijhof',
 23+ ),
 24+ 'version' => '0.1.0',
 25+ 'descriptionmsg' => 'styleguidedemo-desc',
 26+ 'url' => 'http://www.mediawiki.org/wiki/Extension:StyleGuideDemo'
 27+);
 28+
 29+// Autoloading
 30+$dir = dirname( __FILE__ ) . '/';
 31+// @todo Integrate HTMLStyleForm into core HTMLForm or come up with a better name
 32+$wgAutoloadClasses['HTMLStyleForm'] = $dir . 'HTMLStyleForm.php';
 33+$wgAutoloadClasses['SpecialStyleGuideDemo'] = $dir . 'SpecialStyleGuideDemo.php';
 34+$wgExtensionMessagesFiles['StyleGuideDemo'] = $dir . 'StyleGuideDemo.i18n.php';
 35+$wgExtensionAliasesFiles['StyleGuideDemo'] = $dir . 'StyleGuideDemo.alias.php';
 36+
 37+// Special Page
 38+$wgSpecialPages['StyleGuideDemo'] = 'SpecialStyleGuideDemo';
 39+$wgSpecialPageGroups['StyleGuideDemo'] = 'other';
 40+
 41+// Modules
 42+$wgResourceModules['ext.styleguidedemo.css'] = array(
 43+ 'localBasePath' => dirname( __FILE__ ) . '/modules/ext.styleguidedemo',
 44+ 'remoteExtPath' => 'StyleGuideDemo/modules/ext.styleguidedemo',
 45+ 'styles' => 'ext.styleguidedemo.css',
 46+ 'position' => 'top',
 47+);
 48+$wgResourceModules['ext.styleguidedemo.js'] = array(
 49+ 'localBasePath' => dirname( __FILE__ ) . '/modules/ext.styleguidedemo',
 50+ 'remoteExtPath' => 'StyleGuideDemo/modules/ext.styleguidedemo',
 51+ 'scripts' => 'ext.styleguidedemo.js',
 52+ 'messages' => array(),
 53+ 'position' => 'top',
 54+);
 55+
Property changes on: trunk/extensions/StyleGuideDemo/StyleGuideDemo.php
___________________________________________________________________
Added: svn:eol-style
156 + native
Index: trunk/extensions/StyleGuideDemo/SpecialStyleGuideDemo.php
@@ -0,0 +1,65 @@
 2+<?php
 3+/**
 4+ * SpecialPage for StyleGuideDemo extension
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+class SpecialStyleGuideDemo extends SpecialPage {
 11+
 12+ /* Methods */
 13+
 14+ public function __construct() {
 15+ parent::__construct( 'StyleGuideDemo' );
 16+ }
 17+
 18+ public function execute( $par ) {
 19+ global $wgUser, $wgOut;
 20+
 21+ $wgOut->addModuleStyles( 'ext.styleguidedemo.css' );
 22+ $wgOut->addModules( 'ext.styleguidedemo.js' );
 23+
 24+ $this->setHeaders();
 25+
 26+ // Form One
 27+ $wgOut->wrapWikiMsg( "<h2 class=\"mw-specialpagesgroup\" id=\"mw-styleguidedemo-createform\">$1</h2>\n", 'styleguidedemo-head-createform' );
 28+
 29+ $formOne = array(
 30+ 'Username' => array(
 31+ 'label-message' => 'styleguidedemo-username',
 32+ 'required' => 1,
 33+ 'type' => 'text',
 34+ 'help-message' => array( 'styleguidedemo-username-help', wfMsg( 'styleguidedemo-username-infopage' ) ),
 35+ 'hint-message' => 'styleguidedemo-username-hint',
 36+ 'placeholder' => 'John Doe',
 37+ ),
 38+ 'Password' => array(
 39+ 'label-message' => 'styleguidedemo-password',
 40+ 'required' => 1,
 41+ 'type' => 'password',
 42+ 'help-message' => array( 'styleguidedemo-password-help', wfMsg( 'styleguidedemo-password-infopage' ) ),
 43+ ),
 44+ 'ConfirmPassword' => array(
 45+ 'label-message' => 'styleguidedemo-confirmpassword',
 46+ 'type' => 'password',
 47+ 'help-message' => 'styleguidedemo-confirmpassword-help',
 48+ ),
 49+ 'Email' => array(
 50+ 'label-message' => 'styleguidedemo-email',
 51+ 'type' => 'text',
 52+ 'help-message' => array( 'styleguidedemo-email-help', wfMsg( 'styleguidedemo-email-infopage' ) ),
 53+ 'hint-message' => 'styleguidedemo-email-hint',
 54+ 'placeholder' => wfMsg( 'styleguidedemo-email-placeholder' ),
 55+ ),
 56+ );
 57+ $form = new HTMLStyleForm( $formOne );
 58+ $form->setTitle( $wgOut->getTitle() );
 59+ $form->show();
 60+
 61+
 62+ }
 63+
 64+ /* Protected Methods */
 65+
 66+}
Property changes on: trunk/extensions/StyleGuideDemo/SpecialStyleGuideDemo.php
___________________________________________________________________
Added: svn:eol-style
167 + native
Index: trunk/extensions/StyleGuideDemo/README
@@ -0,0 +1,6 @@
 2+Made for trunk
 3+
 4+1) To install:
 5+ require_once( "$IP/extensions/StyleGuideDemo/StyleGuideDemo.php" );
 6+
 7+2) Browse to Special:StyleGuideDemo

Comments

#Comment by 😂 (talk | contribs)   04:06, 10 June 2011

Is HTMLStyleForm supposed to be a fork of HTMLForm, a rewrite, a copy...or what? Seems like it's duplicating a lot of functionality there.

#Comment by Werdna (talk | contribs)   04:17, 10 June 2011

It looks like it's a copy-and-modify.

I think you should subclass it if the factoring allows for it, or potentially even replace the existing one.

#Comment by 😂 (talk | contribs)   04:21, 10 June 2011

I was going to say, might as well push improvements/changes back into core, rather than here in an extension :)

#Comment by P858snake (talk | contribs)   10:05, 10 June 2011

.

#Comment by Krinkle (talk | contribs)   16:24, 10 June 2011

I could've branched the entire trunk for a few small fixes, but since is a background proccess that may take longer, I rather stay as close to trunk as possible, so I forked the one file I needed to modify and work on that (this also makes it a lot easier for people to "check it out" (in both meanings of "to check out").

When this is done (that is, all components of the Style guide] that replace browser or jquery defaults (input field, labels, forms, modal boxes, selection etc.), then it will most likely be moved back into core.

However it has to be done at once.

Quote part of the commit:

+// @todo Integrate HTMLStyleForm into core HTMLForm
#Comment by Raymond (talk | contribs)   14:05, 25 June 2011

Please give me a ping when this extension is ready for Translatewiki. Thanks.

#Comment by Krinkle (talk | contribs)   17:00, 25 June 2011

Thanks, I'll remember to do so. Awesome.

Status & tagging log