Index: trunk/extensions/PageTriage/SpecialPageTriage.php |
— | — | @@ -8,6 +8,9 @@ |
9 | 9 | * @author Ryan Kaldari |
10 | 10 | */ |
11 | 11 | class SpecialPageTriage extends SpecialPage { |
| 12 | + |
| 13 | + // Holds the various options for viewing the list |
| 14 | + protected $opts; |
12 | 15 | |
13 | 16 | /** |
14 | 17 | * Initialize the special page. |
— | — | @@ -23,11 +26,244 @@ |
24 | 27 | public function execute( $sub ) { |
25 | 28 | global $wgOut; |
26 | 29 | |
| 30 | + // Initialize variable to hold list view options |
| 31 | + $opts = new FormOptions(); |
| 32 | + |
| 33 | + // Set the defaults for the list view options |
| 34 | + $opts->add( 'showbots', true ); |
| 35 | + $opts->add( 'showredirs', false ); |
| 36 | + $opts->add( 'limit', (int)$this->getUser()->getOption( 'rclimit' ) ); |
| 37 | + $opts->add( 'offset', '' ); |
| 38 | + $opts->add( 'namespace', '0' ); |
| 39 | + |
| 40 | + // Get the option values from the page request |
| 41 | + $opts->fetchValuesFromRequest( $this->getRequest() ); |
| 42 | + |
| 43 | + // Validate the data for the options |
| 44 | + $opts->validateIntBounds( 'limit', 0, 5000 ); |
| 45 | + |
| 46 | + // Bind options to member variable |
| 47 | + $this->opts = $opts; |
| 48 | + |
27 | 49 | // Output the title of the page |
28 | 50 | $wgOut->setPageTitle( wfMessage( 'pagetriage' ) ); |
29 | 51 | |
30 | | - // Output the list (or something) |
31 | | - $wgOut->addHtml( 'Hello World!' ); |
| 52 | + // This will hold the HTML for the triage interface |
| 53 | + $triageInterface = ''; |
| 54 | + |
| 55 | + // Get triage header |
| 56 | + $triageInterface .= $this->getTriageHeader(); |
| 57 | + |
| 58 | + // Get the list of articles |
| 59 | + $triageInterface .= $this->getTriageList(); |
| 60 | + |
| 61 | + // Get triage footer |
| 62 | + $triageInterface .= $this->getTriageFooter(); |
| 63 | + |
| 64 | + // Output the HTML for the page |
| 65 | + $wgOut->addHtml( $triageInterface ); |
| 66 | + |
32 | 67 | } |
| 68 | + |
| 69 | + /** |
| 70 | + * Builds the header for the list. |
| 71 | + * This will include the filtering interface and some metadata about the list. |
| 72 | + * @return string HTML for the header |
| 73 | + */ |
| 74 | + public function getTriageHeader() { |
| 75 | + return Html::Element( 'p', array(), 'Page Triage Header goes here' ); |
| 76 | + } |
| 77 | + |
| 78 | + /** |
| 79 | + * Builds the footer for the list. |
| 80 | + * This will include the Top Triagers, more list metadata, and a link to detailed statastics. |
| 81 | + * @return string HTML for the footer |
| 82 | + */ |
| 83 | + public function getTriageFooter() { |
| 84 | + return Html::Element( 'p', array(), 'Page Triage Footer goes here' ); |
| 85 | + } |
33 | 86 | |
| 87 | + /** |
| 88 | + * Builds the list of articles to triage. |
| 89 | + * This is a paginated list of articles and associated metadata. |
| 90 | + * @return string HTML for the list |
| 91 | + */ |
| 92 | + public function getTriageList() { |
| 93 | + global $wgOut; |
| 94 | + |
| 95 | + $pager = new TriagePager( $this, $this->opts ); |
| 96 | + |
| 97 | + if( $pager->getNumRows() ) { |
| 98 | + $navigation = $pager->getNavigationBar(); |
| 99 | + $htmlOut = $navigation . $pager->getBody() . $navigation; |
| 100 | + } else { |
| 101 | + $htmlOut = wfMessage( 'specialpage-empty' ); |
| 102 | + } |
| 103 | + |
| 104 | + return $htmlOut; |
| 105 | + } |
| 106 | + |
34 | 107 | } |
| 108 | + |
| 109 | +class TriagePager extends ReverseChronologicalPager { |
| 110 | + |
| 111 | + // Holds the various options for viewing the list |
| 112 | + protected $opts; |
| 113 | + |
| 114 | + public function __construct( $special, FormOptions $opts ) { |
| 115 | + parent::__construct( $special->getContext() ); |
| 116 | + $this->mLimit = $opts->getValue( 'limit' ); |
| 117 | + $this->mOffset = $opts->getValue( 'offset' ); |
| 118 | + $this->opts = $opts; |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * Sort the list by rc_timestamp |
| 123 | + * @return string |
| 124 | + */ |
| 125 | + public function getIndexField() { |
| 126 | + return 'rc_timestamp'; |
| 127 | + } |
| 128 | + |
| 129 | + /** |
| 130 | + * Set the database query to retrieve all the pages that need triaging |
| 131 | + * @return array of query settings |
| 132 | + */ |
| 133 | + public function getQueryInfo() { |
| 134 | + |
| 135 | + $conds = array(); |
| 136 | + $conds['rc_new'] = 1; |
| 137 | + |
| 138 | + $namespace = $this->opts->getValue( 'namespace' ); |
| 139 | + if ( $namespace === 'all' ) { |
| 140 | + $namespace = false; |
| 141 | + } else { |
| 142 | + $namespace = intval( $namespace ); |
| 143 | + } |
| 144 | + |
| 145 | + if( $namespace !== false ) { |
| 146 | + $conds['rc_namespace'] = $namespace; |
| 147 | + $rcIndexes = array( 'new_name_timestamp' ); |
| 148 | + } else { |
| 149 | + $rcIndexes = array( 'rc_timestamp' ); |
| 150 | + } |
| 151 | + |
| 152 | + if( !$this->opts->getValue( 'showbots' ) ) { |
| 153 | + $conds['rc_bot'] = 0; |
| 154 | + } |
| 155 | + |
| 156 | + if ( !$this->opts->getValue( 'showredirs' ) ) { |
| 157 | + $conds['page_is_redirect'] = 0; |
| 158 | + } |
| 159 | + |
| 160 | + $tables = array( 'recentchanges', 'page' ); |
| 161 | + |
| 162 | + $fields = array( |
| 163 | + 'rc_namespace', 'rc_title', 'rc_cur_id', 'rc_user', 'rc_user_text', |
| 164 | + 'rc_comment', 'rc_timestamp', 'rc_patrolled','rc_id', 'rc_deleted', |
| 165 | + 'page_len AS length', 'page_latest AS rev_id', 'rc_this_oldid', |
| 166 | + 'page_namespace', 'page_title' |
| 167 | + ); |
| 168 | + $join_conds = array( 'page' => array( 'INNER JOIN', 'page_id=rc_cur_id' ) ); |
| 169 | + |
| 170 | + $info = array( |
| 171 | + 'tables' => $tables, |
| 172 | + 'fields' => $fields, |
| 173 | + 'conds' => $conds, |
| 174 | + 'join_conds' => $join_conds |
| 175 | + ); |
| 176 | + |
| 177 | + return $info; |
| 178 | + } |
| 179 | + |
| 180 | + public function formatRow( $result ) { |
| 181 | + |
| 182 | + // Create a revision object to work with |
| 183 | + $row = array( |
| 184 | + 'comment' => $result->rc_comment, |
| 185 | + 'deleted' => $result->rc_deleted, |
| 186 | + 'user_text' => $result->rc_user_text, |
| 187 | + 'user' => $result->rc_user, |
| 188 | + ); |
| 189 | + $rev = new Revision( $row ); |
| 190 | + |
| 191 | + $lang = $this->getLanguage(); |
| 192 | + |
| 193 | + $title = Title::newFromRow( $result ); |
| 194 | + $spanTime = Html::element( 'span', array( 'class' => 'mw-pagetriage-time' ), |
| 195 | + $lang->timeanddate( $result->rc_timestamp, true ) |
| 196 | + ); |
| 197 | + $time = Linker::linkKnown( |
| 198 | + $title, |
| 199 | + $spanTime, |
| 200 | + array(), |
| 201 | + array( 'oldid' => $result->rc_this_oldid ), |
| 202 | + array() |
| 203 | + ); |
| 204 | + |
| 205 | + $query = array( 'redirect' => 'no' ); |
| 206 | + |
| 207 | + // If the user is allowed to triage and the page hasn't been triaged yet, add an rcid param |
| 208 | + // to the article link. |
| 209 | + if( $this->getUser()->useNPPatrol() && !$result->rc_patrolled ) { |
| 210 | + $query['rcid'] = $result->rc_id; |
| 211 | + } |
| 212 | + |
| 213 | + $pageLink = Linker::linkKnown( |
| 214 | + $title, |
| 215 | + null, |
| 216 | + array( 'class' => 'mw-pagetriage-pagename' ), |
| 217 | + $query |
| 218 | + ); |
| 219 | + $histLink = Linker::linkKnown( |
| 220 | + $title, |
| 221 | + wfMsgHtml( 'hist' ), |
| 222 | + array(), |
| 223 | + array( 'action' => 'history' ) |
| 224 | + ); |
| 225 | + $history = Html::rawElement( 'span', array( 'class' => 'mw-pagetriage-history' ), wfMsg( 'parentheses', $histLink ) ); |
| 226 | + |
| 227 | + $length = Html::element( 'span', array( 'class' => 'mw-pagetriage-length' ), |
| 228 | + $this->msg( 'nbytes' )->numParams( $result->length )->text() |
| 229 | + ); |
| 230 | + |
| 231 | + $userLink = Linker::revUserTools( $rev ); |
| 232 | + $comment = Linker::revComment( $rev ); |
| 233 | + |
| 234 | + if ( $result->rc_patrolled ) { |
| 235 | + $class = 'mw-pagetriage-triaged'; |
| 236 | + } else { |
| 237 | + $class = 'mw-pagetriage-not-triaged'; |
| 238 | + } |
| 239 | + |
| 240 | + $htmlOut = ''; |
| 241 | + $htmlOut .= Xml::openElement( 'div', array( |
| 242 | + 'style' => 'border: 1px solid #CCCCCC; border-top: none;', |
| 243 | + 'class' => $class, |
| 244 | + ) ); |
| 245 | + $htmlOut .= "$pageLink $history · $length<br/>"; |
| 246 | + $htmlOut .= "    By $userLink"; |
| 247 | + $htmlOut .= Xml::closeElement( 'div' ); |
| 248 | + |
| 249 | + return $htmlOut; |
| 250 | + } |
| 251 | + |
| 252 | + /** |
| 253 | + * Begin div at the start of the list |
| 254 | + * @return string HTML |
| 255 | + */ |
| 256 | + public function getStartBody() { |
| 257 | + $htmlOut = Xml::openElement( 'div', array( 'style' => 'border-top: 1px solid #CCCCCC' ) ); |
| 258 | + return $htmlOut; |
| 259 | + } |
| 260 | + |
| 261 | + /** |
| 262 | + * Close div at the end of the list |
| 263 | + * @return string HTML |
| 264 | + */ |
| 265 | + public function getEndBody() { |
| 266 | + $htmlOut = Xml::closeElement( 'div' ); |
| 267 | + return $htmlOut; |
| 268 | + } |
| 269 | + |
| 270 | +} |