Expose form field objects in HTMLForm
[lhc/web/wiklou.git] / includes / htmlform / HTMLForm.php
index 8ac4cf2..3c88594 100644 (file)
  *    'help-messages'       -- array of message keys/objects. As above, each item can
  *                             be an array of msg key and then parameters.
  *                             Overwrites 'help'.
+ *    'notice'              -- message text for a message to use as a notice in the field.
+ *                             Currently used by OOUI form fields only.
+ *    'notice-messages'     -- array of message keys/objects to use for notice.
+ *                             Overrides 'notice'.
+ *    'notice-message'      -- message key or object to use as a notice.
  *    'required'            -- passed through to the object, indicating that it
  *                             is a required field.
  *    'size'                -- the length of text fields
@@ -190,6 +195,7 @@ class HTMLForm extends ContextSource {
        protected $mSubmitText;
        protected $mSubmitTooltip;
 
+       protected $mFormIdentifier;
        protected $mTitle;
        protected $mMethod = 'post';
        protected $mWasSubmitted = false;
@@ -269,14 +275,12 @@ class HTMLForm extends ContextSource {
 
                switch ( $displayFormat ) {
                        case 'vform':
-                               $reflector = new ReflectionClass( 'VFormHTMLForm' );
-                               return $reflector->newInstanceArgs( $arguments );
+                               return ObjectFactory::constructClassInstance( VFormHTMLForm::class, $arguments );
                        case 'ooui':
-                               $reflector = new ReflectionClass( 'OOUIHTMLForm' );
-                               return $reflector->newInstanceArgs( $arguments );
+                               return ObjectFactory::constructClassInstance( OOUIHTMLForm::class, $arguments );
                        default:
-                               $reflector = new ReflectionClass( 'HTMLForm' );
-                               $form = $reflector->newInstanceArgs( $arguments );
+                               /** @var HTMLForm $form */
+                               $form = ObjectFactory::constructClassInstance( HTMLForm::class, $arguments );
                                $form->setDisplayFormat( $displayFormat );
                                return $form;
                }
@@ -350,6 +354,26 @@ class HTMLForm extends ContextSource {
                $this->mFieldTree = $loadedDescriptor;
        }
 
+       /**
+        * @param string $fieldname
+        * @return bool
+        */
+       public function hasField( $fieldname ) {
+               return isset( $this->mFlatFields[$fieldname] );
+       }
+
+       /**
+        * @param string $fieldname
+        * @return HTMLFormField
+        * @throws DomainException on invalid field name
+        */
+       public function getField( $fieldname ) {
+               if ( !$this->hasField( $fieldname ) ) {
+                       throw new DomainException( __METHOD__ . ': no field named ' . $fieldname );
+               }
+               return $this->mFlatFields[$fieldname];
+       }
+
        /**
         * Set format in which to display the form
         *
@@ -480,7 +504,14 @@ class HTMLForm extends ContextSource {
                }
 
                # Load data from the request.
-               $this->loadData();
+               if (
+                       $this->mFormIdentifier === null ||
+                       $this->getRequest()->getVal( 'wpFormIdentifier' ) === $this->mFormIdentifier
+               ) {
+                       $this->loadData();
+               } else {
+                       $this->mFieldData = [];
+               }
 
                return $this;
        }
@@ -492,22 +523,29 @@ class HTMLForm extends ContextSource {
        public function tryAuthorizedSubmit() {
                $result = false;
 
-               $submit = false;
+               $identOkay = false;
+               if ( $this->mFormIdentifier === null ) {
+                       $identOkay = true;
+               } else {
+                       $identOkay = $this->getRequest()->getVal( 'wpFormIdentifier' ) === $this->mFormIdentifier;
+               }
+
+               $tokenOkay = false;
                if ( $this->getMethod() !== 'post' ) {
-                       $submit = true; // no session check needed
+                       $tokenOkay = true; // no session check needed
                } elseif ( $this->getRequest()->wasPosted() ) {
                        $editToken = $this->getRequest()->getVal( 'wpEditToken' );
                        if ( $this->getUser()->isLoggedIn() || $editToken !== null ) {
                                // Session tokens for logged-out users have no security value.
                                // However, if the user gave one, check it in order to give a nice
                                // "session expired" error instead of "permission denied" or such.
-                               $submit = $this->getUser()->matchEditToken( $editToken, $this->mTokenSalt );
+                               $tokenOkay = $this->getUser()->matchEditToken( $editToken, $this->mTokenSalt );
                        } else {
-                               $submit = true;
+                               $tokenOkay = true;
                        }
                }
 
-               if ( $submit ) {
+               if ( $tokenOkay && $identOkay ) {
                        $this->mWasSubmitted = true;
                        $result = $this->trySubmit();
                }
@@ -1042,6 +1080,12 @@ class HTMLForm extends ContextSource {
         */
        public function getHiddenFields() {
                $html = '';
+               if ( $this->mFormIdentifier !== null ) {
+                       $html .= Html::hidden(
+                               'wpFormIdentifier',
+                               $this->mFormIdentifier
+                       ) . "\n";
+               }
                if ( $this->getMethod() === 'post' ) {
                        $html .= Html::hidden(
                                'wpEditToken',
@@ -1327,6 +1371,27 @@ class HTMLForm extends ContextSource {
                return $this;
        }
 
+       /**
+        * Set an internal identifier for this form. It will be submitted as a hidden form field, allowing
+        * HTMLForm to determine whether the form was submitted (or merely viewed). Setting this serves
+        * two purposes:
+        *
+        * - If you use two or more forms on one page, it allows HTMLForm to identify which of the forms
+        *   was submitted, and not attempt to validate the other ones.
+        * - If you use checkbox or multiselect fields inside a form using the GET method, it allows
+        *   HTMLForm to distinguish between the initial page view and a form submission with all
+        *   checkboxes or select options unchecked.
+        *
+        * @since 1.28
+        * @param string $ident
+        * @return $this
+        */
+       public function setFormIdentifier( $ident ) {
+               $this->mFormIdentifier = $ident;
+
+               return $this;
+       }
+
        /**
         * Stop a default submit button being shown for this form. This implies that an
         * alternate submit method must be provided manually.