Index: trunk/phase3/includes/Html.php |
— | — | @@ -46,6 +46,14 @@ |
47 | 47 | 'keygen', 'link', 'meta', 'param', 'source' |
48 | 48 | ); |
49 | 49 | |
| 50 | + # Boolean attributes, which may have the value omitted entirely. Manually |
| 51 | + # collected from the HTML 5 spec as of 2009-08-10. |
| 52 | + private static $boolAttribs = array( 'async', 'autobuffer', 'autofocus', |
| 53 | + 'autoplay', 'checked', 'controls', 'defer', 'disabled', |
| 54 | + 'formnovalidate', 'hidden', 'ismap', 'loop', 'multiple', 'novalidate', |
| 55 | + 'open', 'readonly', 'required', 'reversed', 'scoped', 'seamless' |
| 56 | + ); |
| 57 | + |
50 | 58 | /** |
51 | 59 | * Returns an HTML element in a string. The major advantage here over |
52 | 60 | * manually typing out the HTML is that it will escape all attribute |
— | — | @@ -62,7 +70,7 @@ |
63 | 71 | * |
64 | 72 | * One notable difference to Xml::element() is that $contents is *not* |
65 | 73 | * escaped. This means that Html::element() can be usefully nested, rather |
66 | | - * than using the rather clumsy Xml::openElement() and Xml::closeElement(). |
| 74 | + * than using the rather clumsy Xml::openElement() and Xml::closeElement(). |
67 | 75 | * |
68 | 76 | * @param $element string The element's name, e.g., 'a' |
69 | 77 | * @param $attribs array Associative array of attributes, e.g., array( |
— | — | @@ -91,7 +99,8 @@ |
92 | 100 | * 'http://www.mediawiki.org/' ) becomes something like |
93 | 101 | * ' href="http://www.mediawiki.org"'. Again, this is like |
94 | 102 | * Xml::expandAttributes(), but it implements some HTML-specific logic. |
95 | | - * For instance, it will omit quotation marks if $wgWellFormedXml is false. |
| 103 | + * For instance, it will omit quotation marks if $wgWellFormedXml is false, |
| 104 | + * and will treat boolean attributes specially. |
96 | 105 | * |
97 | 106 | * @param $attribs array Associative array of attributes, e.g., array( |
98 | 107 | * 'href' => 'http://www.mediawiki.org/' ). Values will be HTML-escaped. |
— | — | @@ -99,7 +108,7 @@ |
100 | 109 | * (starting with a space if at least one attribute is output) |
101 | 110 | */ |
102 | 111 | public static function expandAttributes( $attribs ) { |
103 | | - global $wgWellFormedXml; |
| 112 | + global $wgHtml5, $wgWellFormedXml; |
104 | 113 | |
105 | 114 | $ret = ''; |
106 | 115 | foreach ( $attribs as $key => $value ) { |
— | — | @@ -115,18 +124,31 @@ |
116 | 125 | $quote = ''; |
117 | 126 | } |
118 | 127 | |
119 | | - # Apparently we need to entity-encode \n, \r, \t, although the spec |
120 | | - # doesn't mention that. Since we're doing strtr() anyway, and we |
121 | | - # don't need <> escaped here, we may as well not call |
122 | | - # htmlspecialchars(). FIXME: verify that we actually need to |
123 | | - # escape \n\r\t here, and explain why, exactly. |
124 | | - $ret .= " $key=$quote" . strtr( $value, array( |
125 | | - '&' => '&', |
126 | | - '"' => '"', |
127 | | - "\n" => ' ', |
128 | | - "\r" => ' ', |
129 | | - "\t" => '	' |
130 | | - ) ) . $quote; |
| 128 | + if ( in_array( $key, self::$boolAttribs ) ) { |
| 129 | + # In XHTML 1.0 Transitional, the value needs to be equal to the |
| 130 | + # key. In HTML 5, we can leave the value empty instead. If we |
| 131 | + # don't need well-formed XML, we can omit the = entirely. |
| 132 | + if ( !$wgWellFormedXml ) { |
| 133 | + $ret .= " $key"; |
| 134 | + } elseif ( $wgHtml5 ) { |
| 135 | + $ret .= " $key=\"\""; |
| 136 | + } else { |
| 137 | + $ret .= " $key=\"$key\""; |
| 138 | + } |
| 139 | + } else { |
| 140 | + # Apparently we need to entity-encode \n, \r, \t, although the |
| 141 | + # spec doesn't mention that. Since we're doing strtr() anyway, |
| 142 | + # and we don't need <> escaped here, we may as well not call |
| 143 | + # htmlspecialchars(). FIXME: verify that we actually need to |
| 144 | + # escape \n\r\t here, and explain why, exactly. |
| 145 | + $ret .= " $key=$quote" . strtr( $value, array( |
| 146 | + '&' => '&', |
| 147 | + '"' => '"', |
| 148 | + "\n" => ' ', |
| 149 | + "\r" => ' ', |
| 150 | + "\t" => '	' |
| 151 | + ) ) . $quote; |
| 152 | + } |
131 | 153 | } |
132 | 154 | return $ret; |
133 | 155 | } |
— | — | @@ -182,8 +204,8 @@ |
183 | 205 | |
184 | 206 | $attrs = array(); |
185 | 207 | if ( !$wgHtml5 ) { |
186 | | - # Technically we should probably add CDATA stuff here like with |
187 | | - # scripts, but in practice, stylesheets tend not to have |
| 208 | + # Technically we should probably add CDATA stuff here like with |
| 209 | + # scripts, but in practice, stylesheets tend not to have |
188 | 210 | # problematic characters anyway. |
189 | 211 | $attrs['type'] = 'text/css'; |
190 | 212 | } |
— | — | @@ -214,4 +236,41 @@ |
215 | 237 | } |
216 | 238 | return self::element( 'link', $attrs ); |
217 | 239 | } |
| 240 | + |
| 241 | + /** |
| 242 | + * Convenience function to produce an <input> element. This supports the |
| 243 | + * new HTML 5 input types and attributes, and will silently strip them if |
| 244 | + * $wgHtml5 is false. |
| 245 | + * |
| 246 | + * @param $name string name attribute |
| 247 | + * @param $value mixed value attribute (null = omit) |
| 248 | + * @param $type string type attribute |
| 249 | + * @param $attribs array Assocative array of miscellaneous extra attributes, |
| 250 | + * passed to Html::element() |
| 251 | + * @return string Raw HTML |
| 252 | + */ |
| 253 | + public static function input( $name, $value = null, $type = 'text', $attribs = array() ) { |
| 254 | + global $wgHtml5; |
| 255 | + |
| 256 | + if ( !$wgHtml5 ) { |
| 257 | + if ( !in_array( $type, array( 'hidden', 'text', 'password', |
| 258 | + 'checkbox', 'radio', 'file', 'submit', 'image', 'reset', 'button' |
| 259 | + ) ) ) { |
| 260 | + $type = 'text'; |
| 261 | + } |
| 262 | + foreach ( array( 'autocomplete', 'autofocus', 'max', 'min', 'multiple', |
| 263 | + 'pattern', 'placeholder', 'required', 'step' ) as $badAttr ) { |
| 264 | + unset( $attribs[$badAttr] ); |
| 265 | + } |
| 266 | + } |
| 267 | + if ( $type != 'text' ) { |
| 268 | + $attribs['type'] = $type; |
| 269 | + } |
| 270 | + if ( $value !== null ) { |
| 271 | + $attribs['value'] = $value; |
| 272 | + } |
| 273 | + $attribs['name'] = $name; |
| 274 | + |
| 275 | + return self::element( 'input', $attribs ); |
| 276 | + } |
218 | 277 | } |
Index: trunk/phase3/includes/specials/SpecialResetpass.php |
— | — | @@ -102,62 +102,58 @@ |
103 | 103 | array( |
104 | 104 | 'method' => 'post', |
105 | 105 | 'action' => $self->getLocalUrl(), |
106 | | - 'id' => 'mw-resetpass-form' ) ) . |
107 | | - Xml::hidden( 'token', $wgUser->editToken() ) . |
108 | | - Xml::hidden( 'wpName', $this->mUserName ) . |
109 | | - Xml::hidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) . |
110 | | - wfMsgExt( 'resetpass_text', array( 'parse' ) ) . |
111 | | - Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . |
| 106 | + 'id' => 'mw-resetpass-form' ) ) . "\n" . |
| 107 | + Xml::hidden( 'token', $wgUser->editToken() ) . "\n" . |
| 108 | + Xml::hidden( 'wpName', $this->mUserName ) . "\n" . |
| 109 | + Xml::hidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) . "\n" . |
| 110 | + wfMsgExt( 'resetpass_text', array( 'parse' ) ) . "\n" . |
| 111 | + Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . "\n" . |
112 | 112 | $this->pretty( array( |
113 | 113 | array( 'wpName', 'username', 'text', $this->mUserName ), |
114 | 114 | array( 'wpPassword', $oldpassMsg, 'password', $this->mOldpass ), |
115 | 115 | array( 'wpNewPassword', 'newpassword', 'password', '' ), |
116 | 116 | array( 'wpRetype', 'retypenew', 'password', '' ), |
117 | | - ) ) . |
| 117 | + ) ) . "\n" . |
118 | 118 | $rememberMe . |
119 | | - '<tr>' . |
120 | | - '<td></td>' . |
| 119 | + "<tr>\n" . |
| 120 | + "<td></td>\n" . |
121 | 121 | '<td class="mw-input">' . |
122 | 122 | Xml::submitButton( wfMsg( $submitMsg ) ) . |
123 | | - '</td>' . |
124 | | - '</tr>' . |
| 123 | + "</td>\n" . |
| 124 | + "</tr>\n" . |
125 | 125 | Xml::closeElement( 'table' ) . |
126 | 126 | Xml::closeElement( 'form' ) . |
127 | | - Xml::closeElement( 'fieldset' ) |
| 127 | + Xml::closeElement( 'fieldset' ) . "\n" |
128 | 128 | ); |
129 | 129 | } |
130 | 130 | |
131 | 131 | function pretty( $fields ) { |
132 | | - global $wgHtml5; |
133 | 132 | $out = ''; |
134 | | - foreach( $fields as $list ) { |
| 133 | + foreach ( $fields as $list ) { |
135 | 134 | list( $name, $label, $type, $value ) = $list; |
136 | 135 | if( $type == 'text' ) { |
137 | 136 | $field = htmlspecialchars( $value ); |
138 | 137 | } else { |
139 | | - $attribs = array( 'id' => $name, 'type' => $type ); |
140 | | - if ( $wgHtml5 ) { |
141 | | - # All three fields are required, and we should focus the |
142 | | - # first (wpPassword) |
143 | | - $attribs['required'] = ''; |
144 | | - if ( $name == 'wpPassword' ) { |
145 | | - $attribs['autofocus'] = ''; |
146 | | - } |
| 138 | + $attribs = array( 'id' => $name ); |
| 139 | + # All three fields are required, and we should focus the first |
| 140 | + # (wpPassword) |
| 141 | + $attribs['required'] = ''; |
| 142 | + if ( $name == 'wpPassword' ) { |
| 143 | + $attribs['autofocus'] = ''; |
147 | 144 | } |
148 | | - $field = Xml::input( $name, 20, $value, |
149 | | - $attribs ); |
| 145 | + $field = Html::input( $name, $value, $type, $attribs ); |
150 | 146 | } |
151 | | - $out .= '<tr>'; |
152 | | - $out .= "<td class='mw-label'>"; |
| 147 | + $out .= "<tr>\n"; |
| 148 | + $out .= "\t<td class='mw-label'>"; |
153 | 149 | if ( $type != 'text' ) |
154 | 150 | $out .= Xml::label( wfMsg( $label ), $name ); |
155 | 151 | else |
156 | 152 | $out .= wfMsgHtml( $label ); |
157 | | - $out .= '</td>'; |
158 | | - $out .= "<td class='mw-input'>"; |
| 153 | + $out .= "</td>\n"; |
| 154 | + $out .= "\t<td class='mw-input'>"; |
159 | 155 | $out .= $field; |
160 | | - $out .= '</td>'; |
161 | | - $out .= '</tr>'; |
| 156 | + $out .= "</td>\n"; |
| 157 | + $out .= "</tr>"; |
162 | 158 | } |
163 | 159 | return $out; |
164 | 160 | } |