Merge "+Test for Status->CleanParams with a callback"
[lhc/web/wiklou.git] / includes / specialpage / ChangesListSpecialPage.php
1 <?php
2 /**
3 * Special page which uses a ChangesList to show query results.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup SpecialPage
22 */
23
24 /**
25 * Special page which uses a ChangesList to show query results.
26 * @todo Way too many public functions, most of them should be protected
27 *
28 * @ingroup SpecialPage
29 */
30 abstract class ChangesListSpecialPage extends SpecialPage {
31 var $rcSubpage, $rcOptions; // @todo Rename these, make protected
32 protected $customFilters;
33
34 /**
35 * Main execution point
36 * @todo This should totally do things
37 *
38 * @param string $subpage
39 */
40 public function execute( $subpage ) {
41 $this->rcSubpage = $subpage;
42 throw new MWException( "Not implemented" );
43 }
44
45 /**
46 * Get the current FormOptions for this request
47 * @todo Not called by anything, should be called by execute()
48 *
49 * @return FormOptions
50 */
51 public function getOptions() {
52 if ( $this->rcOptions === null ) {
53 $this->rcOptions = $this->setup( $this->rcSubpage );
54 }
55
56 return $this->rcOptions;
57 }
58
59 /**
60 * Create a FormOptions object with options as specified by the user
61 *
62 * @param array $parameters
63 *
64 * @return FormOptions
65 */
66 public function setup( $parameters ) {
67 $opts = $this->getDefaultOptions();
68 foreach ( $this->getCustomFilters() as $key => $params ) {
69 $opts->add( $key, $params['default'] );
70 }
71
72 $opts = $this->fetchOptionsFromRequest( $opts );
73
74 // Give precedence to subpage syntax
75 if ( $parameters !== null ) {
76 $this->parseParameters( $parameters, $opts );
77 }
78
79 $this->validateOptions( $opts );
80
81 return $opts;
82 }
83
84 /**
85 * Get a FormOptions object containing the default options. By default returns some basic options,
86 * you might not call parent method and discard them.
87 *
88 * @return FormOptions
89 */
90 public function getDefaultOptions() {
91 $opts = new FormOptions();
92
93 $opts->add( 'namespace', '', FormOptions::INTNULL );
94 $opts->add( 'invert', false );
95 $opts->add( 'associated', false );
96
97 return $opts;
98 }
99
100 /**
101 * Get custom show/hide filters
102 *
103 * @return array Map of filter URL param names to properties (msg/default)
104 */
105 protected function getCustomFilters() {
106 // @todo Fire a Special{$this->getName()}Filters hook here
107 return array();
108 }
109
110 /**
111 * Fetch values for a FormOptions object from the WebRequest associated with this instance.
112 *
113 * Intended for subclassing, e.g. to add a backwards-compatibility layer.
114 *
115 * @param FormOptions $parameters
116 * @return FormOptions
117 */
118 protected function fetchOptionsFromRequest( $opts ) {
119 $opts->fetchValuesFromRequest( $this->getRequest() );
120 return $opts;
121 }
122
123 /**
124 * Process $par and put options found in $opts. Used when including the page.
125 *
126 * @param string $par
127 * @param FormOptions $opts
128 */
129 public function parseParameters( $par, FormOptions $opts ) {
130 // nothing by default
131 }
132
133 /**
134 * Validate a FormOptions object generated by getDefaultOptions() with values already populated.
135 *
136 * @param FormOptions $opts
137 */
138 public function validateOptions( FormOptions $opts ) {
139 // nothing by default
140 }
141
142 /**
143 * Return an array of conditions depending of options set in $opts
144 * @todo This should build some basic conditions here…
145 * @todo Not called by anything, should be called by execute()
146 *
147 * @param FormOptions $opts
148 * @return array
149 */
150 abstract public function buildMainQueryConds( FormOptions $opts );
151
152 /**
153 * Process the query
154 * @todo This should build some basic processing here…
155 * @todo Not called by anything, should be called by execute()
156 *
157 * @param array $conds
158 * @param FormOptions $opts
159 * @return bool|ResultWrapper Result or false (for Recentchangeslinked only)
160 */
161 abstract public function doMainQuery( $conds, $opts );
162
163 /**
164 * Send output to the OutputPage object, only called if not used feeds
165 * @todo This should do most, if not all, of the outputting now done by subclasses
166 * @todo Not called by anything, should be called by execute()
167 *
168 * @param array $rows Database rows
169 * @param FormOptions $opts
170 */
171 abstract public function webOutput( $rows, $opts );
172
173 /**
174 * Return the text to be displayed above the changes
175 * @todo Not called by anything, should be called by webOutput()
176 *
177 * @param FormOptions $opts
178 * @return string XHTML
179 */
180 public function doHeader( $opts ) {
181 $this->setTopText( $opts );
182
183 // @todo Lots of stuff should be done here.
184
185 $this->setBottomText( $opts );
186 }
187
188 /**
189 * Send the text to be displayed before the options. Should use $this->getOutput()->addWikiText()
190 * or similar methods to print the text.
191 *
192 * @param FormOptions $opts
193 */
194 function setTopText( FormOptions $opts ) {
195 // nothing by default
196 }
197
198 /**
199 * Send the text to be displayed after the options. Should use $this->getOutput()->addWikiText()
200 * or similar methods to print the text.
201 *
202 * @param FormOptions $opts
203 */
204 function setBottomText( FormOptions $opts ) {
205 // nothing by default
206 }
207
208 /**
209 * Get options to be displayed in a form
210 * @todo This should handle options returned by getDefaultOptions().
211 * @todo Not called by anything, should be called by something… doHeader() maybe?
212 *
213 * @param FormOptions $opts
214 * @return array
215 */
216 function getExtraOptions( $opts ) {
217 return array();
218 }
219
220 /**
221 * Return the legend displayed within the fieldset
222 * @todo This should not be static, then we can drop the parameter
223 * @todo Not called by anything, should be called by doHeader()
224 *
225 * @param $context the object available as $this in non-static functions
226 * @return string
227 */
228 public static function makeLegend( IContextSource $context ) {
229 global $wgRecentChangesFlags;
230 $user = $context->getUser();
231 # The legend showing what the letters and stuff mean
232 $legend = Xml::openElement( 'dl' ) . "\n";
233 # Iterates through them and gets the messages for both letter and tooltip
234 $legendItems = $wgRecentChangesFlags;
235 if ( !$user->useRCPatrol() ) {
236 unset( $legendItems['unpatrolled'] );
237 }
238 foreach ( $legendItems as $key => $legendInfo ) { # generate items of the legend
239 $label = $legendInfo['title'];
240 $letter = $legendInfo['letter'];
241 $cssClass = isset( $legendInfo['class'] ) ? $legendInfo['class'] : $key;
242
243 $legend .= Xml::element( 'dt',
244 array( 'class' => $cssClass ), $context->msg( $letter )->text()
245 ) . "\n";
246 if ( $key === 'newpage' ) {
247 $legend .= Xml::openElement( 'dd' );
248 $legend .= $context->msg( $label )->escaped();
249 $legend .= ' ' . $context->msg( 'recentchanges-legend-newpage' )->parse();
250 $legend .= Xml::closeElement( 'dd' ) . "\n";
251 } else {
252 $legend .= Xml::element( 'dd', array(),
253 $context->msg( $label )->text()
254 ) . "\n";
255 }
256 }
257 # (+-123)
258 $legend .= Xml::tags( 'dt',
259 array( 'class' => 'mw-plusminus-pos' ),
260 $context->msg( 'recentchanges-legend-plusminus' )->parse()
261 ) . "\n";
262 $legend .= Xml::element(
263 'dd',
264 array( 'class' => 'mw-changeslist-legend-plusminus' ),
265 $context->msg( 'recentchanges-label-plusminus' )->text()
266 ) . "\n";
267 $legend .= Xml::closeElement( 'dl' ) . "\n";
268
269 # Collapsibility
270 $legend =
271 '<div class="mw-changeslist-legend">' .
272 $context->msg( 'recentchanges-legend-heading' )->parse() .
273 '<div class="mw-collapsible-content">' . $legend . '</div>' .
274 '</div>';
275
276 return $legend;
277 }
278
279 /**
280 * Add page-specific modules.
281 * @todo Not called by anything, should be called by execute()
282 */
283 protected function addModules() {
284 $out = $this->getOutput();
285 // Styles and behavior for the legend box (see makeLegend())
286 $out->addModuleStyles( 'mediawiki.special.changeslist.legend' );
287 $out->addModules( 'mediawiki.special.changeslist.legend.js' );
288 }
289
290 protected function getGroupName() {
291 return 'changes';
292 }
293 }