Index: trunk/extensions/Tasks/tasks.sql |
— | — | @@ -0,0 +1,17 @@ |
| 2 | +CREATE TABLE /*_*/tasks ( |
| 3 | + task_id int(8) unsigned NOT NULL auto_increment, |
| 4 | + task_page_id int(8) unsigned NOT NULL default '0', |
| 5 | + task_page_title varchar(255) NOT NULL default '', |
| 6 | + task_user_id int(8) unsigned NOT NULL default '0', |
| 7 | + task_user_text varchar(255) NOT NULL default '', |
| 8 | + task_user_assigned int(8) unsigned NOT NULL default '0', |
| 9 | + task_status int(4) unsigned NOT NULL default '0', |
| 10 | + task_comment mediumtext NOT NULL, |
| 11 | + task_type int(4) unsigned NOT NULL default '0', |
| 12 | + task_timestamp varchar(14) binary NOT NULL default '', |
| 13 | + task_user_close int(8) unsigned NOT NULL default '0', |
| 14 | + task_timestamp_closed varchar(14) NOT NULL default '', |
| 15 | + PRIMARY KEY (task_id), |
| 16 | + KEY task_page_id (task_page_id,task_status,task_type), |
| 17 | + KEY task_page_title (task_page_title) |
| 18 | +) ENGINE=InnoDB; |
Property changes on: trunk/extensions/Tasks/tasks.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 19 | + native |
Index: trunk/extensions/Tasks/Tasks.body.php |
— | — | @@ -0,0 +1,1092 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class SpecialTasks extends IncludableSpecialPage { |
| 5 | + |
| 6 | + var $status_types = array( |
| 7 | + MW_TASK_OPEN => 'open', |
| 8 | + MW_TASK_ASSIGNED => 'assigned', |
| 9 | + MW_TASK_CLOSED => 'closed', |
| 10 | + MW_TASK_WONTFIX => 'wontfix' |
| 11 | + ); |
| 12 | + var $task_types; # e.g., 0 => 'cleanup' |
| 13 | + var $task_types_text; # e.g., 'cleanup' => 'Clean up |
| 14 | + var $creation_tasks; # e.g., ( 1, 2, 3 ) |
| 15 | + var $task_order = array(); # e.g., 'delete' => 5 |
| 16 | + var $pagemode = '' ; |
| 17 | + |
| 18 | + /** |
| 19 | + * Constructor |
| 20 | + */ |
| 21 | + function __construct() { # Checked for HTML and MySQL insertion attacks |
| 22 | + parent::__construct( 'Tasks' ); |
| 23 | + $this->update_types(); |
| 24 | + } |
| 25 | + |
| 26 | + /** |
| 27 | + * Returns the order of the type, or 0 if not defined |
| 28 | + * @param string $type key like 'create' or 'cleanup' |
| 29 | + * @return int |
| 30 | + */ |
| 31 | + function get_task_order( $type ) { # Checked for HTML and MySQL insertion attacks |
| 32 | + if( isset( $this->task_order[$type] ) ) { |
| 33 | + return $this->task_order[$type]; |
| 34 | + } else { |
| 35 | + return 0; |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + /** |
| 40 | + * Returns the type constant associated with the text key, or MW_TASK_INVALID |
| 41 | + * @param string $type text key |
| 42 | + * @return int |
| 43 | + */ |
| 44 | + function get_task_num( $type ) { # Checked for HTML and MySQL insertion attacks |
| 45 | + $key = array_search( trim( strtolower( $type ) ), $this->task_types ); |
| 46 | + if( $key === false ) { |
| 47 | + wfDebug( 'Tasks: get_task_num was passed illegal text key : ' . $type . " (out of range)\n" ); |
| 48 | + return MW_TASK_INVALID; |
| 49 | + } else { |
| 50 | + return $key; |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + /** |
| 55 | + * Returns the text key for a given task type constant |
| 56 | + * @param $num numeric key |
| 57 | + * @return string |
| 58 | + */ |
| 59 | + function get_task_type( $num ) { # Checked for HTML and MySQL insertion attacks |
| 60 | + if( !isset( $this->task_types[$num] ) ) { |
| 61 | + wfDebug( 'Tasks: get_task_type was passed illegal num : ' . $num . " (out of range)\n" ); |
| 62 | + return MW_TASK_INVALID; |
| 63 | + } |
| 64 | + return $this->task_types[$num]; |
| 65 | + } |
| 66 | + |
| 67 | + /** |
| 68 | + * Updates task_types and creation_tasks from wfMsg |
| 69 | + * @fixme Provide localized display names for user's UI language |
| 70 | + */ |
| 71 | + function update_types() { # Checked for HTML and MySQL insertion attacks |
| 72 | + wfLoadExtensionMessages('Tasks'); |
| 73 | + |
| 74 | + # task type numeric key, text key, localized text |
| 75 | + $this->task_types = array(); |
| 76 | + $s = wfMsgForContent( 'tasks_task_types' ); |
| 77 | + $s = explode( '|', $s ); |
| 78 | + foreach( $s as $line ) { |
| 79 | + $bits = explode( ':', $line, 3 ); |
| 80 | + if( count( $bits ) != 3 ) { |
| 81 | + # Invalid line |
| 82 | + continue; |
| 83 | + } |
| 84 | + $keyNum = intval( $bits[0] ); |
| 85 | + $keyName = trim( $bits[1] ); |
| 86 | + $localName = trim( $bits[2] ); |
| 87 | + if( $keyNum < 1 ) { |
| 88 | + wfDebug( 'SpecialTasks::update_types: expected positive integer for key, got ' . $keyNum . "\n" ); |
| 89 | + continue; |
| 90 | + } |
| 91 | + $this->task_types[$keyNum] = $keyName; |
| 92 | + $this->task_types_text[$keyName] = $localName; |
| 93 | + } |
| 94 | + |
| 95 | + # List of creation-type tasks |
| 96 | + $this->creation_tasks = array(); |
| 97 | + $s = wfMsgForContent( 'tasks_creation_tasks' ); |
| 98 | + $s = explode( ',', $s ); |
| 99 | + foreach( $s as $line ) { |
| 100 | + $keyNum = intval( $line ); |
| 101 | + if( $keyNum < 1 ) { |
| 102 | + wfDebug( 'SpecialTasks::update_types: expected positive integer for key in tasks_creation_tasks, got ' . $keyNum . "\n" ); |
| 103 | + continue; |
| 104 | + } |
| 105 | + $this->creation_tasks[] = $keyNum; |
| 106 | + } |
| 107 | + |
| 108 | + $this->task_order = array(); |
| 109 | + $s = wfMsgForContent( 'tasks_significance_order' ); |
| 110 | + $s = explode( '<' , $s ); |
| 111 | + $count = 1; |
| 112 | + foreach( $s as $line ) { |
| 113 | + $line = trim( $line ); |
| 114 | + if( $line != '' ) { |
| 115 | + $this->task_order[$line] = $count++; |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + /** |
| 121 | + * @param string $type_key 'open', 'wontfix', etc |
| 122 | + * @return string localized name as text: 'Open', 'Nefarinda', etc |
| 123 | + */ |
| 124 | + function get_type_text( $type_key ) { # Checked for HTML and MySQL insertion attacks |
| 125 | + if( !isset( $this->task_types_text[$type_key] ) ) { |
| 126 | + wfDebug( 'Tasks: get_type_text was passed illegal type_key : ' . $type_key . " (out of range)\n" ); |
| 127 | + return ''; |
| 128 | + } |
| 129 | + return $this->task_types_text[$type_key]; |
| 130 | + } |
| 131 | + |
| 132 | + /** |
| 133 | + * @param string $type_key |
| 134 | + * @return string HTML-escaped localized name |
| 135 | + */ |
| 136 | + function get_type_html( $type_key ) { |
| 137 | + return htmlspecialchars( $this->get_type_text( $type_key ) ); |
| 138 | + } |
| 139 | + |
| 140 | + /** |
| 141 | + * @param int $task_type key |
| 142 | + * @return bool |
| 143 | + */ |
| 144 | + function is_creation_task( $task_type ) { # Checked for HTML and MySQL insertion attacks |
| 145 | + return in_array( $task_type, $this->creation_tasks ); |
| 146 | + } |
| 147 | + |
| 148 | + /** |
| 149 | + * @param int $status key |
| 150 | + * @return bool |
| 151 | + */ |
| 152 | + function is_open( $status ) { # Checked for HTML and MySQL insertion attacks |
| 153 | + return ( $status == MW_TASK_OPEN || $status == MW_TASK_ASSIGNED ); |
| 154 | + } |
| 155 | + |
| 156 | + /** |
| 157 | + * @param int $status key |
| 158 | + * @return bool |
| 159 | + */ |
| 160 | + function is_closed( $status ) { # Checked for HTML and MySQL insertion attacks |
| 161 | + return !$this->is_open( $status ); |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * Takes a title and a list of existing tasks, and decides which new tasks can be created. |
| 166 | + * There's no point in having a dozen "wikify" tasks for a single article, now is there? :-) |
| 167 | + * @param Title $title |
| 168 | + * @param array $tasks out-parameter, will receive the set of existing tasks |
| 169 | + * @return array set of creatable tasks....? |
| 170 | + */ |
| 171 | + function get_valid_new_tasks( $title, &$tasks ) { # Checked for HTML and MySQL insertion attacks |
| 172 | + $exists = $title->exists(); |
| 173 | + $tasks = $this->get_tasks_for_page( $title ); |
| 174 | + $new_tasks = array(); |
| 175 | + $tg = array(); |
| 176 | + |
| 177 | + foreach( $tasks as $t ) { |
| 178 | + # Assemble types; if multiple of one type, assemble open ones |
| 179 | + if( !isset( $tg[$t->task_type] ) || $this->is_open( $t->task_status ) ) { |
| 180 | + $tg[$t->task_type] = $t->task_status; |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + foreach( array_keys( $this->task_types ) as $a ) { |
| 185 | + if( $exists == $this->is_creation_task( $a ) ) { |
| 186 | + # Creation task and existence exclude each other |
| 187 | + continue; |
| 188 | + } |
| 189 | + if( isset( $tg[$a] ) && $this->is_open( $tg[$a] ) ) { |
| 190 | + # Task exists and is not closed |
| 191 | + continue; |
| 192 | + } |
| 193 | + $new_tasks[$a] = $this->get_task_type( $a ); |
| 194 | + } |
| 195 | + return $new_tasks; |
| 196 | + } |
| 197 | + |
| 198 | + /** |
| 199 | + * The form for creating a new task from a form ("tasks" tab) |
| 200 | + * @return string HTML output |
| 201 | + * @fixme Display error messages on invalid input |
| 202 | + */ |
| 203 | + function create_from_form( $title ) { # Checked for HTML and MySQL insertion attacks |
| 204 | + global $wgRequest, $wgUser; |
| 205 | + if( $wgRequest->getText( 'create_task', '' ) == '' ) { |
| 206 | + # No form |
| 207 | + return ''; |
| 208 | + } |
| 209 | + |
| 210 | + $out = ''; |
| 211 | + $tasks = array(); |
| 212 | + $type = $wgRequest->getInt( 'type' ); |
| 213 | + $name = $wgRequest->getText( 'username' ); |
| 214 | + |
| 215 | + $comment = $wgRequest->getText( 'text', '' ); # Not evaluated here; stored in database through safe database function |
| 216 | + $new_tasks = $this->get_valid_new_tasks( $title, $tasks ); |
| 217 | + if( !isset( $new_tasks[$type] ) ) { |
| 218 | + # Trying to create a task that isn't available |
| 219 | + $out .= '<p>' . wfMsgHtml('tasks_error1') . '</p>'; |
| 220 | + } else { |
| 221 | + $this->add_new_task( $title, $comment, $type, $name ); |
| 222 | + $out .= '<p>' . wfMsgHtml( 'tasks_ok1' ) . '</p>'; |
| 223 | + } |
| 224 | + return $out; |
| 225 | + } |
| 226 | + |
| 227 | + /** |
| 228 | + * Adds a new task |
| 229 | + */ |
| 230 | + function add_new_task( $title, $comment, $type, $name ) { |
| 231 | + global $wgUser; |
| 232 | + $dbw = wfGetDB( DB_MASTER ); |
| 233 | + $user_id = 0; |
| 234 | + if ( !is_null($name) ) { |
| 235 | + $user_id = $wgUser->idFromName($name); |
| 236 | + if ( !ctype_digit($user_id) ) { |
| 237 | + $user_id = 0; # (int)0 indicates assigned to "no one" |
| 238 | + } |
| 239 | + } |
| 240 | + $dbw->insert( 'tasks', |
| 241 | + array( |
| 242 | + 'task_page_id' => $title->getArticleID(), |
| 243 | + 'task_page_title' => $title->getPrefixedDBkey(), |
| 244 | + 'task_user_id' => $wgUser->getID(), |
| 245 | + 'task_user_text' => $wgUser->getName(), |
| 246 | + 'task_user_assigned' => $user_id, |
| 247 | + 'task_status' => $this->get_status_number( 'open' ), |
| 248 | + 'task_comment' => $comment, |
| 249 | + 'task_type' => $type, |
| 250 | + 'task_timestamp' => $dbw->timestamp() |
| 251 | + ) ); |
| 252 | + } |
| 253 | + |
| 254 | + /** |
| 255 | + * For a list of tasks, get a single table row |
| 256 | + * This function is heavy on output! |
| 257 | + */ |
| 258 | + function get_task_table_row( &$task, &$title, $show_page = false, $returnto = '' ) { # Checked for HTML and MySQL insertion attacks |
| 259 | + global $wgContLang, $wgUser, $wgTasksNamespace, $wgExtraNamespaces; |
| 260 | + $out = ''; |
| 261 | + $sk =& $wgUser->getSkin(); |
| 262 | + $ct = $wgContLang->timeanddate( $task->task_timestamp ); # Time object from string of digits |
| 263 | + $cu = Title::makeTitleSafe( NS_USER, $task->task_user_text ); # Safe user name |
| 264 | + $comment = htmlspecialchars( $task->task_comment ); # Safe user comment, no HTML allowed |
| 265 | + $comment = nl2br( $comment ); # display newlines as they were in the edit box |
| 266 | + $status = $task->task_status; # Integer |
| 267 | + $tid = $task->task_id; # Integer |
| 268 | + $encType = $this->get_type_html( $this->get_task_type( $task->task_type ) ); # Will catch illegal types and wfDebug them |
| 269 | + if( $returnto != '' ) { |
| 270 | + $returnto = '&returnto=' . urlencode( $returnto ); |
| 271 | + } |
| 272 | + |
| 273 | + $out .= '<tr>'; |
| 274 | + if( $show_page ) { |
| 275 | + $out .= '<td align="left" valign="top">'; |
| 276 | + $out .= $sk->makeLinkObj( $title ); |
| 277 | + $out .= '<br />'; |
| 278 | + $out .= $sk->makeLinkObj( $title, wfMsgHTML('tasks_see_page_tasks'), 'action=tasks' ); |
| 279 | + $out .= '</td>'; |
| 280 | + } |
| 281 | + $out .= '<td valign="top" align="left" nowrap class="tasks_status_bgcol_' . $this->status_types[$status] . '">'; |
| 282 | + $out .= '<b>' . $encType . '</b><br /><i>'; |
| 283 | + $out .= wfMsgForContent( 'tasks_status_' . $this->status_types[$status] ); |
| 284 | + $out .= '</i><br />' ; |
| 285 | + |
| 286 | + # Additional info |
| 287 | + $help_title = Title::makeTitleSafe( NS_HELP, wfMsgForContent('tasks_help_page') ); |
| 288 | + $help_title->mFragment = $encType ; |
| 289 | + $ext1 = $sk->makeLinkObj( $help_title , wfMsgForContent('tasks_help_page_link') ); |
| 290 | + $more_title = SpecialPage::getTitleFor( 'Tasks' ); # This special page |
| 291 | + $ext2 = $sk->makeLinkObj( $more_title , wfMsgForContent('tasks_more_like_it') , 'task_type='.$task->task_type ); |
| 292 | + $out .= wfMsgForContent ( 'tasks_help_separator' , $ext1 , $ext2 ) ; |
| 293 | + |
| 294 | + |
| 295 | + $out .= '</td>'; |
| 296 | + $out .= '<td align="left" valign="top" nowrap>'; |
| 297 | + $out .= wfMsgHTML( 'tasks_created_by', $sk->makeLinkObj( $cu, htmlspecialchars( $task->task_user_text ) ) ); |
| 298 | + $out .= '<br />' . $ct ; |
| 299 | + |
| 300 | + # Closing information |
| 301 | + if( $task->task_user_close != 0 && $this->is_closed( $status ) ) { |
| 302 | + $user_close = new User; |
| 303 | + $user_close->setID( $task->task_user_close ); |
| 304 | + $uct = Title::makeTitleSafe( NS_USER, $user_close->getName() ); # Assigned user title |
| 305 | + $out .= '<br />' . wfMsgHTML( 'tasks_closedby', $sk->makeLinkObj( $uct, htmlspecialchars( $user_close->getName() ) ) ); |
| 306 | + if( $task->task_timestamp_closed != "" ) { |
| 307 | + $out .= '<br />' . $wgContLang->timeanddate( $task->task_timestamp_closed ); # Time object from string of digits |
| 308 | + } |
| 309 | + } |
| 310 | + $out .= '</td>'; |
| 311 | + |
| 312 | + $out .= '<td align="left" valign="top">' . $comment . '</td>' ; # Comment is HTML-stripped |
| 313 | + $out .= '<td align="left" valign="top">'; |
| 314 | + if( $task->task_user_assigned == 0 ) { |
| 315 | + # Noone is assigned this task |
| 316 | + $out .= '<form method="get">' // Added a form in place of old "assign to me" link |
| 317 | + . wfMsgHTML( 'tasks_assign_to' ) . ' <input type="text" name="username" />' |
| 318 | + . '<input type="hidden" name="action" value="tasks" />' |
| 319 | + . '<input type="hidden" name="mode" value="assignto" />' |
| 320 | + . '<input type="hidden" name="title" value="'.htmlspecialchars($title->getPrefixedText()).'" />' |
| 321 | + . '<input type="hidden" name="taskid" value="'.$tid.'" />' |
| 322 | + . '<input type="submit" name="submit" value="' . wfMsgHTML( 'ok' ) . '" />' |
| 323 | + . '</form>'; |
| 324 | + } else { |
| 325 | + # Someone is assigned this task |
| 326 | + $au = new User(); # Assigned user |
| 327 | + $au->setID( $task->task_user_assigned ); |
| 328 | + $aut = Title::makeTitleSafe( NS_USER, $au->getName() ); # Assigned user title |
| 329 | + $out .= wfMsgHTML( 'tasks_assignedto', $sk->makeLinkObj( $aut, htmlspecialchars( $au->getName() ) ) ); |
| 330 | + } |
| 331 | + if( $wgUser->isLoggedIn() ) { |
| 332 | + $txt = array(); |
| 333 | + if( $this->is_open( $status ) ) { |
| 334 | + # Assignment |
| 335 | + if( $wgUser->getID() != $task->task_user_assigned ) { |
| 336 | + # Assign myself |
| 337 | + $txt[] = $sk->makeLinkObj( $title, |
| 338 | + wfMsgHTML( 'tasks_assign_me' ), |
| 339 | + "action=tasks&mode=assignme&taskid={$tid}{$returnto}" ); # tid is integer, returnto is safe |
| 340 | + } else { |
| 341 | + # Unassign myself |
| 342 | + $txt[] = $sk->makeLinkObj( $title, |
| 343 | + wfMsgHTML( 'tasks_unassign_me' ), |
| 344 | + "action=tasks&mode=unassignme&taskid={$tid}{$returnto}" ); # tid is integer, returnto is safe |
| 345 | + } |
| 346 | + } |
| 347 | + if( $this->is_open( $status ) ) { |
| 348 | + # Open or assigned |
| 349 | + $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_close' ), "action=tasks&mode=close&taskid={$tid}{$returnto}" ); |
| 350 | + $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_wontfix' ), "action=tasks&mode=wontfix&taskid={$tid}{$returnto}" ); |
| 351 | + } else if( $this->get_task_type( $task->task_type ) != 'create' ) { |
| 352 | + # Closed or wontfix, can reopen (maybe) |
| 353 | + $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_reopen' ), "action=tasks&mode=reopen&taskid={$tid}{$returnto}" ); |
| 354 | + } |
| 355 | + |
| 356 | + if( $wgUser->isAllowed( 'delete' ) ) { |
| 357 | + $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_delete' ), "action=tasks&mode=delete&taskid={$tid}{$returnto}" ); |
| 358 | + } |
| 359 | + |
| 360 | + if( count( $txt ) > 0 ) { |
| 361 | + $out .= '<br />' . implode( ' - ', $txt ); |
| 362 | + } |
| 363 | + |
| 364 | + } |
| 365 | + $tdp = $this->get_task_discussion_page( $task ); |
| 366 | + $out .= '<br />' . $sk->makeLinkObj( $tdp, wfMsgHTML('tasks_discussion_page_link') ); |
| 367 | + $out .= '</td></tr>' ; |
| 368 | + |
| 369 | + # Transclude comments page, if wanted |
| 370 | + if( $wgUser->getOption( 'show_task_comments' ) ) { |
| 371 | + if( $this->pagemode == 'search' || $this->pagemode == 'tasks_of_page' ) { |
| 372 | + $out .= $this->transclude_comments( $tdp, $show_page ? 5 : 4 ) ; |
| 373 | + } |
| 374 | + } |
| 375 | + return $out; |
| 376 | + } |
| 377 | + |
| 378 | + /** |
| 379 | + * Returns the |
| 380 | + * @param Title $title of task page to load |
| 381 | + * @param int $col_compensator Number of table columns to span |
| 382 | + * @return string HTML table row, or empty string |
| 383 | + * @access private |
| 384 | + */ |
| 385 | + function transclude_comments( $title, $col_compensator ) { |
| 386 | + if( !$title->exists() ) { |
| 387 | + # Nothing to transclude |
| 388 | + return ''; |
| 389 | + } |
| 390 | + |
| 391 | + global $wgOut; |
| 392 | + $art = new Article( $title ); |
| 393 | + $ret = $art->getContent( false ); |
| 394 | + $ret = $wgOut->parse( $ret ); |
| 395 | + return '<tr><td id="task_transcluded_comment" colspan="' . $col_compensator . '">' . $ret . '</td></tr>' ; |
| 396 | + } |
| 397 | + |
| 398 | + /** |
| 399 | + * @param Task $task |
| 400 | + * @return Title |
| 401 | + */ |
| 402 | + function get_task_discussion_page( &$task ) { # Checked for HTML and MySQL insertion attacks |
| 403 | + global $wgTasksNamespace; |
| 404 | + # Format : "Task:123" |
| 405 | + return Title::makeTitle( $wgTasksNamespace, strval( $task->task_id ) ); |
| 406 | + } |
| 407 | + |
| 408 | + /** |
| 409 | + * On the "tasks" tab, show the list of existing tasks for that article |
| 410 | + */ |
| 411 | + function show_existing_tasks( &$title, &$tasks ) { # Checked for HTML and MySQL insertion attacks |
| 412 | + $out = ''; |
| 413 | + foreach( $tasks as $task ) { |
| 414 | + $out .= $this->get_task_table_row( $task, $title ); # Assumed safe |
| 415 | + } |
| 416 | + if( $out == '' ) { |
| 417 | + return ''; |
| 418 | + } |
| 419 | + |
| 420 | + $out = "<h2>" . wfMsgHTML( 'tasks_existing_header' ) . "</h2>\n" . |
| 421 | + "<table border='1' cellspacing='1' cellpadding='2'>" . |
| 422 | + "<tr>" . self::getTableHeader() . "</tr>" . |
| 423 | + $out . "</table>"; |
| 424 | + return $out; |
| 425 | + } |
| 426 | + |
| 427 | + /** |
| 428 | + * Checks if there's a "mode" set in the URL of the current page |
| 429 | + * (performs changes on tasks, like assigning or closing them) |
| 430 | + * @return HTML output |
| 431 | + * @fixme There is no output! Should there be? No error output either. |
| 432 | + */ |
| 433 | + function check_mode( $title ) { |
| 434 | + global $wgUser, $wgRequest; |
| 435 | + |
| 436 | + $mode = trim( $wgRequest->getVal( 'mode' ) ); |
| 437 | + $name = trim( $wgRequest->getVal( 'username' ) ); |
| 438 | + $taskid = $wgRequest->getInt( 'taskid', 0 ); |
| 439 | + |
| 440 | + if( $mode == '' || $taskid == 0 ) { |
| 441 | + # Simple validation |
| 442 | + return ''; |
| 443 | + } |
| 444 | + if( !$wgUser->isLoggedIn() ) { |
| 445 | + # Needs to be logged in |
| 446 | + return ''; |
| 447 | + } |
| 448 | + |
| 449 | + $out = ''; |
| 450 | + $dbw = wfGetDB( DB_MASTER ); |
| 451 | + |
| 452 | + switch( $mode ) { |
| 453 | + |
| 454 | + case 'assignto': |
| 455 | + case 'assignme': |
| 456 | + case 'unassignme': |
| 457 | + |
| 458 | + // Assign or unassign an existing used to this task |
| 459 | + $conditions = array( 'task_id' => $taskid ); |
| 460 | + $user_id = $wgUser->getId() ; # Assign |
| 461 | + if( $mode == 'unassignme' ) { |
| 462 | + # Unassign me; this can be invoked for every user by editing the URL! |
| 463 | + $user_id = 0; |
| 464 | + } else if ( $mode == 'assignto' ) { |
| 465 | + $user_id = $wgUser->idFromName($name); |
| 466 | + if ( empty($user_id) ) { |
| 467 | + break; # break as though "mode" were undefined (no action) |
| 468 | + } |
| 469 | + } |
| 470 | + $do_set = array( # SET |
| 471 | + 'task_user_assigned' => $user_id, # Coming from $wgUser, so assumed safe |
| 472 | + 'task_status' => ( $mode == 'assignme' ) |
| 473 | + ? $this->get_status_number( 'assigned' ) |
| 474 | + : $this->get_status_number( 'open' ), # Integer |
| 475 | + ); |
| 476 | + $dbw->update( 'tasks', |
| 477 | + $do_set, |
| 478 | + $conditions, |
| 479 | + __METHOD__ ); |
| 480 | + |
| 481 | + $title = $this->get_title_from_task( $taskid, $task ); |
| 482 | + $act = wfMsgHTML( 'tasks_assigned_myself_log', |
| 483 | + $this->get_type_html( $this->get_task_type( $task->task_type ) ) ); |
| 484 | + $log = new LogPage( 'tasks' ); |
| 485 | + $log->addEntry( 'tasks', $title, $act ); |
| 486 | + |
| 487 | + break; |
| 488 | + |
| 489 | + case 'close': |
| 490 | + case 'wontfix': |
| 491 | + case 'reopen': |
| 492 | + |
| 493 | + # Changing task status |
| 494 | + if( $mode == 'reopen' ) { |
| 495 | + $mode = 'open'; |
| 496 | + } |
| 497 | + if( $mode == 'close' ) { |
| 498 | + $mode = 'closed'; |
| 499 | + } |
| 500 | + $new_status = $this->get_status_number( $mode ); |
| 501 | + $this->change_task_status( $taskid, $new_status ); |
| 502 | + |
| 503 | + break; |
| 504 | + |
| 505 | + case 'delete': |
| 506 | + |
| 507 | + # Delete this task; sysops only! |
| 508 | + if( $wgUser->isAllowed( 'delete' ) ) { |
| 509 | + # OK, deleting |
| 510 | + $dbw->delete( 'tasks', |
| 511 | + array( 'task_id' => $taskid ), |
| 512 | + __METHOD__ ); |
| 513 | + |
| 514 | + # Log task deletion |
| 515 | + $act = wfMsgForContent( 'tasks_action_delete' ); |
| 516 | + $log = new LogPage( 'tasks' ); |
| 517 | + $log->addEntry( 'tasks', $title, $act ); |
| 518 | + $out .= wfMsgHtml( 'tasks_task_was_deleted' ); |
| 519 | + } else { |
| 520 | + # No-no! |
| 521 | + global $wgOut; |
| 522 | + $wgOut->setPageTitle( wfMsg( 'tasks_no_task_delete_title' ) ); |
| 523 | + $wgOut->addWikiText( wfMsg( 'tasks_no_task_delete_text' ) ); |
| 524 | + return ''; |
| 525 | + } |
| 526 | + |
| 527 | + break; |
| 528 | + |
| 529 | + default: |
| 530 | + # Unknown mode |
| 531 | + return ''; |
| 532 | + } // end switch() |
| 533 | + |
| 534 | + return $out; |
| 535 | + } |
| 536 | + |
| 537 | + /** |
| 538 | + * Returns the number for the status |
| 539 | + * @param string $status key |
| 540 | + * @return int |
| 541 | + */ |
| 542 | + function get_status_number( $status ) { # Checked for HTML and MySQL insertion attacks |
| 543 | + foreach( $this->status_types as $k => $v ) { |
| 544 | + if( $v == $status ) { |
| 545 | + return $k; |
| 546 | + } |
| 547 | + } |
| 548 | + # Invalid status |
| 549 | + return 0; |
| 550 | + } |
| 551 | + |
| 552 | + /** |
| 553 | + * Changes the status of a task, performs some associated cleanup, and logs the action |
| 554 | + */ |
| 555 | + function change_task_status( $taskid, $new_status ) { # Checked for HTML and MySQL insertion attacks |
| 556 | + global $wgUser; |
| 557 | + $dbw = wfGetDB( DB_MASTER ); |
| 558 | + |
| 559 | + if( !is_numeric( $new_status ) || !is_numeric( $taskid ) ) { |
| 560 | + # Paranoia |
| 561 | + return; |
| 562 | + } |
| 563 | + |
| 564 | + # What to change: |
| 565 | + $as = array( 'task_status' => $new_status ); |
| 566 | + # Where to change it: |
| 567 | + $aw = array( 'task_id' => $taskid ); |
| 568 | + |
| 569 | + if( $this->is_closed( $new_status ) ) { |
| 570 | + # When closing, set closing user ID, and reset assignment |
| 571 | + $as['task_user_close'] = $wgUser->getID(); |
| 572 | + $as['task_user_assigned'] = 0; |
| 573 | + $as['task_timestamp_closed'] = $dbw->timestamp(); # Assumed safe |
| 574 | + } elseif( $new_status == $this->get_status_number( 'open' ) ) { |
| 575 | + # Change to "open", no assigned user or closing user |
| 576 | + $as['task_user_assigned'] = 0; |
| 577 | + $as['task_user_close'] = 0; |
| 578 | + $as['task_timestamp_closed'] = ''; |
| 579 | + } |
| 580 | + |
| 581 | + $dbw->update( 'tasks', |
| 582 | + $as, # SET |
| 583 | + $aw, # WHERE |
| 584 | + __METHOD__ ); |
| 585 | + |
| 586 | + # Logging |
| 587 | + $title = $this->get_title_from_task( $taskid, $task ); |
| 588 | + $act = wfMsgHTML( 'tasks_action_' . $this->status_types[$new_status], |
| 589 | + $this->get_type_html( $this->get_task_type( $task->task_type ) ) ); |
| 590 | + $log = new LogPage( 'tasks' ); |
| 591 | + $log->addEntry( 'tasks', $title, $act ); |
| 592 | + } |
| 593 | + |
| 594 | + |
| 595 | + /** |
| 596 | + * Returns the list of active tasks for this page, for display in the sidebar |
| 597 | + */ |
| 598 | + function get_open_task_list( &$title, $useCache = false ) { # Checked for HTML and MySQL insertion attacks |
| 599 | + global $wgTaskExtensionTasksCachedTitle , $wgTaskExtensionTasksCache ; |
| 600 | + |
| 601 | + if( $useCache && $wgTaskExtensionTasksCachedTitle == $title->getPrefixedText() ) { |
| 602 | + # Return the cache, thus skip the query and increase shareholder value |
| 603 | + return $wgTaskExtensionTasksCache; |
| 604 | + } |
| 605 | + |
| 606 | + $tasks = $this->get_tasks_for_page( $title ); |
| 607 | + $ret = array(); |
| 608 | + foreach( $tasks as $task ) { |
| 609 | + if( $this->is_open( $task->task_status ) ) { |
| 610 | + $ret[$this->get_type_html( $this->get_task_type( $task->task_type ) )] = $task; |
| 611 | + } |
| 612 | + } |
| 613 | + ksort( $ret ); |
| 614 | + if( $useCache ) { |
| 615 | + # Store results in cache for further use |
| 616 | + $wgTaskExtensionTasksCache = $ret; |
| 617 | + $wgTaskExtensionTasksCachedTitle = $title->getPrefixedText(); |
| 618 | + } |
| 619 | + return $ret; |
| 620 | + } |
| 621 | + |
| 622 | + /** |
| 623 | + * Returns the title object for a task, and the task data through reference |
| 624 | + * If no task can be found, return '' |
| 625 | + * @param int $task_id |
| 626 | + * @param Task $task warning: out-param |
| 627 | + * @return Title |
| 628 | + */ |
| 629 | + function get_title_from_task( $task_id, &$task ) { # Checked for HTML and MySQL insertion attacks |
| 630 | + $task = $this->get_task_from_id( $task_id ); |
| 631 | + |
| 632 | + if( $task == '' ) { |
| 633 | + return ''; |
| 634 | + } elseif ( $task->task_page_id == 0 ) { # Non-existing page |
| 635 | + $title = Title::newFromDBkey( $task->task_page_title ); |
| 636 | + } else { # Existing page |
| 637 | + $title = Title::newFromID( $task->task_page_id ); |
| 638 | + } |
| 639 | + return $title; |
| 640 | + } |
| 641 | + |
| 642 | + /** |
| 643 | + * Returns a single task by its ID |
| 644 | + */ |
| 645 | + function get_task_from_id( $task_id ) { # Checked for HTML and MySQL insertion attacks |
| 646 | + if( !is_numeric( $task_id ) ) { |
| 647 | + # Paranoia |
| 648 | + return null; |
| 649 | + } |
| 650 | + $dbr = wfGetDB( DB_SLAVE ); |
| 651 | + $res = $dbr->select( |
| 652 | + /* FROM */ 'tasks', |
| 653 | + /* SELECT */ '*', |
| 654 | + /* WHERE */ array( 'task_id' => $task_id ) |
| 655 | + ); |
| 656 | + $task = $dbr->fetchObject( $res ); |
| 657 | + $dbr->freeResult( $res ); |
| 658 | + return $task; |
| 659 | + } |
| 660 | + |
| 661 | + /** |
| 662 | + * Sets the article ID (on page creation) |
| 663 | + */ |
| 664 | + function set_new_article_id( &$title ) { # Checked for HTML and MySQL insertion attacks |
| 665 | + $dbw = wfGetDB( DB_MASTER ); |
| 666 | + $dbw->update( 'tasks', |
| 667 | + array( 'task_page_id' => $title->getArticleID() ), # SET |
| 668 | + array( 'task_page_title' => $title->getPrefixedDBkey() ), # WHERE |
| 669 | + __METHOD__ ); |
| 670 | + } |
| 671 | + |
| 672 | + /** |
| 673 | + * Deletes all tasks associated with an article; done on article deletion |
| 674 | + * @fixme this is only used on page deletion at the moment; will all conditions be used? |
| 675 | + */ |
| 676 | + function delete_all_tasks( $title ) { # Checked for HTML and MySQL insertion attacks |
| 677 | + if( $title->exists() ) { |
| 678 | + $conds = array( 'task_page_id' => $title->getArticleID() ); |
| 679 | + } else { |
| 680 | + $conds = array( 'task_page_title' => $title->getPrefixedDBkey() ); |
| 681 | + } |
| 682 | + $dbw = wfGetDB( DB_MASTER ); |
| 683 | + $dbw->delete( 'tasks', |
| 684 | + $conds, |
| 685 | + __METHOD__ ); |
| 686 | + } |
| 687 | + |
| 688 | + /** |
| 689 | + * Called for page moves |
| 690 | + */ |
| 691 | + function rename_tasks_page( $old_title, $new_title ) { # Checked for HTML and MySQL insertion attacks |
| 692 | + $dbw = wfGetDB( DB_MASTER ); |
| 693 | + $dbw->update( 'tasks', |
| 694 | + array( 'task_page_title' => $new_title->getPrefixedDBkey() ), # SET |
| 695 | + array( 'task_page_title' => $old_title->getPrefixedDBkey() ), # WHERE |
| 696 | + __METHOD__ ); |
| 697 | + } |
| 698 | + |
| 699 | + /** |
| 700 | + * THIS IS THE MAIN FUNCTION FOR THE TAB-BASED INTERFACE |
| 701 | + */ |
| 702 | + function page_management( $title ) { # Checked for HTML and MySQL insertion attacks |
| 703 | + if( $title->isTalkPage() ) { |
| 704 | + # No tasks for talk pages, no need to bother the database... |
| 705 | + return true; |
| 706 | + } |
| 707 | + |
| 708 | + global $wgOut, $action, $wgRequest, $wgUser; |
| 709 | + $out = ''; |
| 710 | + $tasks = array(); |
| 711 | + $wgOut->addLink(array( |
| 712 | + 'rel' => 'stylesheet', |
| 713 | + 'type' => 'text/css', |
| 714 | + 'media' => 'screen', |
| 715 | + 'href' => TASKS_CSS, |
| 716 | + )); |
| 717 | + $wgOut->setSubtitle( wfMsgHTML( 'tasks_title', $title->getPrefixedText() ) ); |
| 718 | + $this->pagemode = 'tasks_of_page' ; |
| 719 | + |
| 720 | + # Create from form |
| 721 | + $out .= $this->create_from_form( $title ); |
| 722 | + |
| 723 | + # Check for mode |
| 724 | + $out .= $this->check_mode( $title ); |
| 725 | + |
| 726 | + # Get list of tasks that can be created |
| 727 | + $new_tasks = $this->get_valid_new_tasks( $title, $tasks ); |
| 728 | + |
| 729 | + # Show task creation form, if tasks can be created |
| 730 | + $out .= $this->generate_form( $new_tasks ); |
| 731 | + |
| 732 | + # Existing tasks |
| 733 | + $out .= $this->show_existing_tasks( $title, $tasks ); |
| 734 | + |
| 735 | + # And ... out! |
| 736 | + $returnto = $wgRequest->getVal( 'returnto' ); |
| 737 | + if( $this->isValidRedirect( $returnto ) ) { |
| 738 | + # Forward to other page |
| 739 | + $wgOut->redirect( $returnto ); |
| 740 | + |
| 741 | + $skin = $wgUser->getSkin(); |
| 742 | + $link = $skin->makeExternalLink( $returnto, wfMsgHTML( 'tasks_here' ) ); |
| 743 | + $msg = wfMsgHTML( 'tasks_returnto', $link ); |
| 744 | + $wgOut->addHTML( $msg ); |
| 745 | + } else { |
| 746 | + $this->setHeaders(); |
| 747 | + $wgOut->addHTML( $out ); |
| 748 | + } |
| 749 | + return false; |
| 750 | + } |
| 751 | + |
| 752 | + /** |
| 753 | + * Confirm that the given redirect page is local to this site |
| 754 | + * FIXME: this can still pass you anywhere on the domain, |
| 755 | + * or perhaps be an invalid URL altogether. |
| 756 | + * @param string $url |
| 757 | + * @return bool |
| 758 | + */ |
| 759 | + function isValidRedirect( $url ) { |
| 760 | + if( $url == '' ) { |
| 761 | + return false; |
| 762 | + } |
| 763 | + |
| 764 | + global $wgTitle; |
| 765 | + $url1 = $wgTitle->getFullURL(); |
| 766 | + $url1 = explode( '/', $url1 ); |
| 767 | + $url1 = $url1[0] . '/' . $url1[1] .'/' . $url1[2]; |
| 768 | + $url2 = explode( '/', $url ); |
| 769 | + $url2 = $url2[0] . '/' . $url2[1] .'/' . $url2[2]; |
| 770 | + |
| 771 | + return ( $url1 == $url2 ); |
| 772 | + } |
| 773 | + |
| 774 | + /** |
| 775 | + * Generates a form for creating a new task |
| 776 | + */ |
| 777 | + function generate_form( &$new_tasks ) { # Checked for HTML and MySQL insertion attacks |
| 778 | + if( count( $new_tasks ) == 0 ) { |
| 779 | + return ''; |
| 780 | + } |
| 781 | + |
| 782 | + // Heading and table heading |
| 783 | + $out = '<h2>' . wfMsgHTML( 'tasks_create_header' ) . "</h2>\n" |
| 784 | + . '<form method="post">' |
| 785 | + . '<table border="0" width="100%"><tr><td valign="top" nowrap><b>' |
| 786 | + . wfMsgHTML( 'tasks_form_new' ) |
| 787 | + . '</b></td><td width="100%" align="center">' |
| 788 | + . '<b>' . wfMsgHTML( 'tasks_form_comment' ) . '</b> ' |
| 789 | + . wfMsgHTML( 'tasks_plain_text_only' ) |
| 790 | + . '</td></tr><tr>'; |
| 791 | + |
| 792 | + // Select possible tasks |
| 793 | + $out .= '<td valign="top" nowrap><select name="type" size="7" style="width:100%">'; |
| 794 | + $o = array(); |
| 795 | + foreach( $new_tasks as $k => $v ) { |
| 796 | + $o[$v] = '<option value="' . $k . '">' . $this->get_type_html( $v ) . '</option>'; |
| 797 | + } |
| 798 | + ksort( $o ); |
| 799 | + $out .= implode( '', $o ); |
| 800 | + $out .= '</select>'; |
| 801 | + |
| 802 | + $out .= '</td><td valign="top">' |
| 803 | + . '<textarea name="text" rows="4" cols="20" style="width:100%"></textarea><br />' |
| 804 | + . wfMsgHTML( 'tasks_assign_to' ) . ' <input type="text" name="username" />' |
| 805 | + . '<input type="submit" name="create_task" value="' . wfMsgHTML( 'ok' ) . '" />' |
| 806 | + . '</td></tr></table>' |
| 807 | + . '</form>'; |
| 808 | + return $out; |
| 809 | + } |
| 810 | + |
| 811 | + /** |
| 812 | + * Returns the exisiting tasks for a single page |
| 813 | + */ |
| 814 | + function get_tasks_for_page( &$title, $force_dbtitle = false ) { # Checked for HTML and MySQL insertion attacks |
| 815 | + $dbr = wfGetDB( DB_SLAVE ); |
| 816 | + $id = $title->getArticleID(); |
| 817 | + |
| 818 | + if( $id == 0 || $force_dbtitle ) { |
| 819 | + $conds = array( 'task_page_title' => $title->getPrefixedDBkey() ); |
| 820 | + } else { |
| 821 | + $conds = array( 'task_page_id' => $id ); |
| 822 | + } |
| 823 | + |
| 824 | + $res = $dbr->select( |
| 825 | + /* FROM */ 'tasks', |
| 826 | + /* SELECT */ '*', |
| 827 | + /* WHERE */ $conds |
| 828 | + ); |
| 829 | + |
| 830 | + $ret = array(); |
| 831 | + while( $line = $dbr->fetchObject( $res ) ) { |
| 832 | + $ret[$line->task_timestamp.':'.$line->task_id] = $line; |
| 833 | + } |
| 834 | + $dbr->freeResult($res); |
| 835 | + krsort( $ret ); |
| 836 | + return $ret; |
| 837 | + } |
| 838 | + |
| 839 | + function get_assigned_tasks( $userid ) { # Checked for HTML and MySQL insertion attacks |
| 840 | + if( !is_numeric( $userid ) ) { |
| 841 | + # Paranoia |
| 842 | + return null; |
| 843 | + } |
| 844 | + |
| 845 | + $dbr = wfGetDB( DB_SLAVE ); |
| 846 | + |
| 847 | + $res = $dbr->select( |
| 848 | + /* FROM */ 'tasks', |
| 849 | + /* SELECT */ '*', |
| 850 | + /* WHERE */ array( 'task_user_assigned' => $userid ) |
| 851 | + ); |
| 852 | + |
| 853 | + $ret = array(); |
| 854 | + while( $line = $dbr->fetchObject( $res ) ) { |
| 855 | + $ret[$line->task_timestamp.':'.$line->task_id] = $line; |
| 856 | + } |
| 857 | + $dbr->freeResult( $res ); |
| 858 | + krsort( $ret ); |
| 859 | + return $ret; |
| 860 | + } |
| 861 | + |
| 862 | + /** |
| 863 | + * Special page main function |
| 864 | + */ |
| 865 | + function execute( $par ) { # Checked for HTML and MySQL insertion attacks |
| 866 | + global $wgOut, $wgRequest, $wgUser, $wgLang; |
| 867 | + |
| 868 | + $out = ''; |
| 869 | + $mode = trim( $wgRequest->getVal( 'mode' ) ); |
| 870 | + $skin = $wgUser->getSkin(); |
| 871 | + $dbr = wfGetDB( DB_SLAVE ); |
| 872 | + |
| 873 | + # Assignments |
| 874 | + if( $wgUser->isLoggedIn() ) { |
| 875 | + if( $mode == 'myassignments' ) { |
| 876 | + # Show my assignments |
| 877 | + $tasks = $this->get_assigned_tasks( $wgUser->getId() ); |
| 878 | + if( count( $tasks ) == 0 ) { |
| 879 | + $out .= '<p>' . wfMsgHTML( 'tasks_you_have_no_assignments' ) . '</p>'; |
| 880 | + } else { |
| 881 | + $out .= '<h2>' . wfMsgExt( 'tasks_my_assignments', array( 'escape', 'parsemag' ), count( $tasks ) ) . "</h2>\n" |
| 882 | + . '<br /><table border="1" cellspacing="1" cellpadding="2">' |
| 883 | + . '<tr>' . self::getTableHeader( true ) . '</tr>'; |
| 884 | + foreach( $tasks as $task ) { |
| 885 | + $page_title = $this->get_title_from_task( $task->task_id, $task ); |
| 886 | + $returnto = $this->getTitle( $par )->getFullURL( 'mode=myassignments' ); |
| 887 | + $out .= $this->get_task_table_row( $task, $page_title, true, $returnto ); |
| 888 | + } |
| 889 | + $out.= '</table>'; |
| 890 | + } |
| 891 | + } else { # default |
| 892 | + $res = $dbr->select( |
| 893 | + /* FROM */ 'tasks', |
| 894 | + /* SELECT */ ' COUNT(task_id) AS num', |
| 895 | + /* WHERE */ array( 'task_user_assigned' => $wgUser->getId() ), |
| 896 | + /* FNAME */ __METHOD__ |
| 897 | + ); |
| 898 | + $tasks = array(); |
| 899 | + $data = $dbr->fetchObject( $res ); |
| 900 | + $dbr->freeResult( $res ); |
| 901 | + if( !isset ( $data ) || !isset ( $data->num ) ) { |
| 902 | + # Paranoia dummy |
| 903 | + $data->num = 0; |
| 904 | + } |
| 905 | + |
| 906 | + $specialTasks = SpecialPage::getTitleFor( 'Tasks' ); |
| 907 | + $link = $skin->makeLinkObj( $specialTasks, |
| 908 | + wfMsgHTML( 'tasks_link_your_assignments' ), 'mode=myassignments' ); |
| 909 | + $out .= '<p>'; |
| 910 | + if( $data->num == 0 ) { |
| 911 | + $out .= wfMsgHTML( 'tasks_you_have_no_assignments' ) . '.' ; |
| 912 | + } else { |
| 913 | + $out .= wfMsgExt( 'tasks_see_your_assignments', |
| 914 | + array( 'escape', 'parsemag' ), |
| 915 | + $wgLang->formatNum( $data->num ), $link ) ; |
| 916 | + } |
| 917 | + $out .= '</p>'; |
| 918 | + |
| 919 | + } |
| 920 | + } |
| 921 | + |
| 922 | + # Read former form |
| 923 | + $task_type = array(); |
| 924 | + $status_type = array( 1 => 1 ) ; # Default : open tasks |
| 925 | + if( isset( $_POST['task_type'] ) ) { |
| 926 | + $task_type = $_POST['task_type']; |
| 927 | + } |
| 928 | + if( isset( $_POST['status_type'] ) ) { |
| 929 | + $status_type = $_POST['status_type']; |
| 930 | + } |
| 931 | + $ascending = $wgRequest->getCheck( 'ascending' ); |
| 932 | + |
| 933 | + $get_task_type = $wgRequest->getInt( 'task_type' , 0 ) ; |
| 934 | + if ( count ( $task_type ) == 0 && $get_task_type > 0 ) { |
| 935 | + $task_type = array() ; |
| 936 | + $task_type[$get_task_type] = 1 ; |
| 937 | + } |
| 938 | + |
| 939 | + if( !is_array( $status_type ) ) { |
| 940 | + return; |
| 941 | + } |
| 942 | + if( !is_array( $task_type ) ) { |
| 943 | + return; |
| 944 | + } |
| 945 | + |
| 946 | + $out .= '<form method="post" action="' . $this->getTitle( $par )->escapeLocalURL() . '">'; |
| 947 | + |
| 948 | + # Search results |
| 949 | + if( $wgRequest->getVal( 'doit' ) . $wgRequest->getVal( 'prev' ) . $wgRequest->getVal( 'next' ) != "" || $get_task_type > 0 ) { |
| 950 | + $this->pagemode = 'search' ; |
| 951 | + $search_tasks = array_keys( $task_type ); |
| 952 | + if( count( $task_type ) == 0 ) { |
| 953 | + # No choice => search all |
| 954 | + $search_tasks = array_keys( $this->task_types ); |
| 955 | + } |
| 956 | + $search_status = array_keys( $status_type ); |
| 957 | + if( count( $search_status ) == 0 ) { |
| 958 | + # No choice => search all |
| 959 | + $search_status = array_keys( $this->status_types ); |
| 960 | + } |
| 961 | + |
| 962 | + $limit = intval( wfMsg( 'tasks_search_limit' ) ); |
| 963 | + $offset = $wgRequest->getInt( 'offset', 0 ); |
| 964 | + if( $wgRequest->getCheck( 'next' ) ) { |
| 965 | + $offset += $limit; |
| 966 | + } |
| 967 | + if( $wgRequest->getCheck( 'prev' ) && $offset >= $limit ) { |
| 968 | + $offset -= $limit; |
| 969 | + } |
| 970 | + |
| 971 | + # Search |
| 972 | + $conds = array( |
| 973 | + 'task_type' => $search_tasks, |
| 974 | + 'task_status' => $search_status, |
| 975 | + ); |
| 976 | + $options = array( |
| 977 | + 'LIMIT' => $limit, |
| 978 | + 'OFFSET' => $offset, |
| 979 | + 'ORDER BY' => 'task_timestamp' . ( $ascending == '1' ? ' DESC' : '' ), |
| 980 | + ); |
| 981 | + $res = $dbr->select( |
| 982 | + /* FROM */ 'tasks', |
| 983 | + /* SELECT */ '*', |
| 984 | + /* WHERE */ $conds, |
| 985 | + /* FNAME */ __METHOD__, |
| 986 | + /* OPTIONS */$options |
| 987 | + ); |
| 988 | + $tasks = array(); |
| 989 | + while( $line = $dbr->fetchObject( $res ) ) { |
| 990 | + $tasks[] = $line; |
| 991 | + } |
| 992 | + $dbr->freeResult( $res ); |
| 993 | + |
| 994 | + if( count( $tasks ) > 0 ) { |
| 995 | + |
| 996 | + $out .= '<h2>' . wfMsgHTML( 'tasks_search_results' ) . "</h2>\n"; |
| 997 | + |
| 998 | + # Last/next form |
| 999 | + |
| 1000 | + if( $offset >= $limit ) { |
| 1001 | + $out .= '<input type="submit" name="prev" value="' |
| 1002 | + . wfMsgHTML('tasks_previous') . '" /> ' ; |
| 1003 | + } |
| 1004 | + $out .= ($offset + 1) . ' .. ' . ($offset + count( $tasks )) . ' '; |
| 1005 | + |
| 1006 | + if( count( $tasks ) >= $limit ) { |
| 1007 | + $out .= '<input type="submit" name="next" value="' . |
| 1008 | + wfMsgHTML( 'tasks_next' ) . '" />' ; |
| 1009 | + } |
| 1010 | + |
| 1011 | + $out .= '<input type="hidden" name="offset" value="' . $offset . '" />' |
| 1012 | + . '<br /><table border="1" cellspacing="1" cellpadding="2">' |
| 1013 | + . '<tr>' . self::getTableHeader( true ) . '</tr>'; |
| 1014 | + |
| 1015 | + $returnto = $this->getTitle( $par )->getFullURL(); # Return to this page |
| 1016 | + |
| 1017 | + foreach( $tasks as $task ) { |
| 1018 | + $page_title = $this->get_title_from_task( $task->task_id, $task ); |
| 1019 | + $out .= $this->get_task_table_row( $task, $page_title, true, $returnto ); |
| 1020 | + } |
| 1021 | + $out .= '</table>'; |
| 1022 | + } |
| 1023 | + } |
| 1024 | + |
| 1025 | + # Search form |
| 1026 | + $out .= '<h2>' . wfMsgHTML( 'tasks_search_form_title' ) . '</h2>' |
| 1027 | + . '<table border="0">' |
| 1028 | + . '<tr><th align="left">' . wfMsgHTML( 'tasks_search_tasks' ) . '</th>' |
| 1029 | + . '<td>'; |
| 1030 | + foreach( $this->task_types as $k => $v ) { |
| 1031 | + $out .= $this->checkbox( "task_type[$k]", isset( $task_type[$k] ) ); |
| 1032 | + $out .= $this->get_type_html( $v ) . " "; |
| 1033 | + } |
| 1034 | + $out .= wfMsgHTML( 'tasks_search_no_tasks_chosen_note' ); |
| 1035 | + $out .= '</td></tr>'; |
| 1036 | + |
| 1037 | + $out .= '<tr><th align="left">' . wfMsgHTML( 'tasks_search_status' ) . '</th>'; |
| 1038 | + $out .= '<td>'; |
| 1039 | + foreach( $this->status_types as $k => $v ) { |
| 1040 | + $out .= $this->checkbox( "status_type[$k]", isset( $status_type[$k] ) ); |
| 1041 | + $out .= wfMsgHTML( 'tasks_status_' . $v ) . " "; |
| 1042 | + } |
| 1043 | + $out .= "</td></tr>\n<tr><th>"; |
| 1044 | + $out .= wfMsgHTML('tasks_sort') . "</th><td>"; |
| 1045 | + $out .= $this->checkbox( 'ascending', $ascending ); |
| 1046 | + $out .= wfMsgHTML( 'tasks_ascending' ); |
| 1047 | + $out .= "</td></tr></table>"; |
| 1048 | + |
| 1049 | + $out .= '<input type="submit" name="doit" value="' . wfMsgHTML( 'search' ) . '" />'; |
| 1050 | + $out .= '</form>'; |
| 1051 | + |
| 1052 | + # and ... out! |
| 1053 | + $wgOut->addLink(array( |
| 1054 | + 'rel' => 'stylesheet', |
| 1055 | + 'type' => 'text/css', |
| 1056 | + 'media' => 'screen', |
| 1057 | + 'href' => TASKS_CSS, |
| 1058 | + )); |
| 1059 | + $this->setHeaders(); |
| 1060 | + $wgOut->addHTML( $out ); |
| 1061 | + } |
| 1062 | + |
| 1063 | + /** |
| 1064 | + * Format an HTML checkbox |
| 1065 | + * @param string $name |
| 1066 | + * @param bool $checked |
| 1067 | + * @return string |
| 1068 | + * @access private |
| 1069 | + */ |
| 1070 | + function checkbox( $name, $checked ) { |
| 1071 | + $attribs = array( |
| 1072 | + 'type' => 'checkbox', |
| 1073 | + 'name' => $name, |
| 1074 | + 'value' => '1' ); |
| 1075 | + if( $checked ) { |
| 1076 | + $attribs['checked'] = 'checked'; |
| 1077 | + } |
| 1078 | + return Xml::element( 'input', $attribs ); |
| 1079 | + } |
| 1080 | + |
| 1081 | + /** |
| 1082 | + * Return the header |
| 1083 | + */ |
| 1084 | + public static function getTableHeader( $with_title = false ) { |
| 1085 | + $s = wfMsgHTML( 'tasks_existing_table_header' ); |
| 1086 | + if( $with_title ) { |
| 1087 | + $s = wfMsgHTML( 'tasks_table_header_page' ) . '|' . $s; |
| 1088 | + } |
| 1089 | + $s = '<th>' . str_replace( '|', '</th><th>', $s ) . '</th>'; |
| 1090 | + return $s; |
| 1091 | + } |
| 1092 | + |
| 1093 | +} # end of class |
Property changes on: trunk/extensions/Tasks/Tasks.body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 1094 | + native |
Index: trunk/extensions/Tasks/Tasks.php |
— | — | @@ -1,9 +1,8 @@ |
2 | 2 | <?php |
3 | | -// FIXME: needs to be split in different files (Special page, possibly others like hooks). |
4 | 3 | /* |
5 | 4 | To activate, put something like this in your LocalSettings.php: |
6 | 5 | define( 'TASKS_CSS' , 'http://yourhost.com/name/wiki/extensions/Tasks/tasks.css' ); |
7 | | - require_once( "extensions/Tasks/Tasks.php" ); |
| 6 | + require_once( "$IP/extensions/Tasks/Tasks.php" ); |
8 | 7 | $wgTasksNamespace = 200; |
9 | 8 | $wgExtraNamespaces[$wgTasksNamespace] = "Task"; |
10 | 9 | $wgExtraNamespaces[$wgTasksNamespace+1] = "Task_Talk"; |
— | — | @@ -11,27 +10,6 @@ |
12 | 11 | The TASKS_CSS define is only needed if you use a 'non-standard' extensions |
13 | 12 | directory. |
14 | 13 | |
15 | | - |
16 | | -Also, you need to run the following SQL statement (with respect to your table prefix!): |
17 | | -CREATE TABLE tasks ( |
18 | | - task_id int(8) unsigned NOT NULL auto_increment, |
19 | | - task_page_id int(8) unsigned NOT NULL default '0', |
20 | | - task_page_title varchar(255) NOT NULL default '', |
21 | | - task_user_id int(8) unsigned NOT NULL default '0', |
22 | | - task_user_text varchar(255) NOT NULL default '', |
23 | | - task_user_assigned int(8) unsigned NOT NULL default '0', |
24 | | - task_status int(4) unsigned NOT NULL default '0', |
25 | | - task_comment mediumtext NOT NULL, |
26 | | - task_type int(4) unsigned NOT NULL default '0', |
27 | | - task_timestamp varchar(14) binary NOT NULL default '', |
28 | | - task_user_close int(8) unsigned NOT NULL default '0', |
29 | | - task_timestamp_closed varchar(14) NOT NULL default '', |
30 | | - PRIMARY KEY (task_id), |
31 | | - KEY task_page_id (task_page_id,task_status,task_type), |
32 | | - KEY task_page_title (task_page_title) |
33 | | -) TYPE=InnoDB; |
34 | | - |
35 | | - |
36 | 14 | Known bugs: |
37 | 15 | * FIXME: sidebar task list for Monobook only? |
38 | 16 | |
— | — | @@ -64,8 +42,8 @@ |
65 | 43 | /** |
66 | 44 | * Global variable to cache tasks for a page, so sidebar and header check only have to read them once |
67 | 45 | */ |
68 | | -$wgTaskExtensionTasksCache = array () ; |
69 | | -$wgTaskExtensionTasksCachedTitle = '' ; |
| 46 | +$wgTaskExtensionTasksCache = array (); |
| 47 | +$wgTaskExtensionTasksCachedTitle = ''; |
70 | 48 | |
71 | 49 | # Integrating into the MediaWiki environment |
72 | 50 | $wgExtensionCredits['Tasks'][] = array( |
— | — | @@ -76,1521 +54,32 @@ |
77 | 55 | 'url' => 'http://www.mediawiki.org/wiki/Extension:Tasks', |
78 | 56 | ); |
79 | 57 | |
80 | | -$wgExtensionFunctions[] = 'wfTasksExtension'; |
81 | 58 | $wgExtensionMessagesFiles['Tasks'] = dirname( __FILE__ ) . '/Tasks.i18n.php'; |
82 | 59 | |
83 | | -# Misc hooks |
84 | | -$wgHooks['SkinTemplatePreventOtherActiveTabs'][] = 'wfTasksExtensionPreventOtherActiveTabs'; |
85 | | -$wgHooks['SkinTemplateTabs'][] = 'wfTasksExtensionTab'; |
86 | | -$wgHooks['UnknownAction'][] = 'wfTasksExtensionAction'; |
87 | | -$wgHooks['ArticleInsertComplete'][] = 'wfTasksExtensionArticleSaveComplete'; |
88 | | -$wgHooks['ArticleDeleteComplete'][] = 'wfTasksExtensionArticleDeleteComplete'; |
89 | | -$wgHooks['SpecialMovepageAfterMove'][] = 'wfTasksExtensionAfterMove'; |
90 | | -$wgHooks['SkinTemplateToolboxEnd'][] = 'wfTasksExtensionAfterToolbox'; |
91 | | -$wgHooks['ArticleViewHeader'][] = 'wfTaskExtensionHeaderHook'; |
92 | | -$wgHooks['EditPage::showEditForm:initial'][] = 'wfTaskExtensionEditFormInitialHook'; |
93 | | -$wgHooks['ParserTestTables'][] = 'wfTasksTestTables'; |
94 | | -$wgHooks['GetPreferences'][] = 'wfTasksOnGetPreferences'; |
| 60 | +$wgAutoloadClasses['SpecialTasks'] = dirname( __FILE__ ) . '/Tasks.body.php'; |
| 61 | +$wgAutoloadClasses['TasksHooks'] = dirname( __FILE__ ) . '/Tasks.hooks.php'; |
95 | 62 | |
96 | | -# BEGIN logging functions |
97 | | -$wgHooks['LogPageValidTypes'][] = 'wfTasksAddLogType'; |
98 | | -$wgHooks['LogPageLogName'][] = 'wfTasksAddLogName'; |
99 | | -$wgHooks['LogPageLogHeader'][] = 'wfTasksAddLogHeader'; |
100 | | -$wgHooks['LogPageActionText'][] = 'wfTasksAddActionText'; |
| 63 | +# Special page |
| 64 | +$wgSpecialPages['Tasks'] = 'SpecialTasks'; |
101 | 65 | |
102 | | -function wfTasksAddLogType( &$types ) { # Checked for HTML and MySQL insertion attacks |
103 | | - if( !in_array( 'tasks', $types ) ) { |
104 | | - $types[] = 'tasks'; |
105 | | - } |
106 | | - return true; |
107 | | -} |
| 66 | +# Misc hooks |
| 67 | +$wgHooks['EditPage::showEditForm:initial'][] = 'TasksHooks::onEditPageShowEditFormInitial'; |
| 68 | +$wgHooks['ArticleViewHeader'][] = 'TasksHooks::onArticleViewHeader'; |
| 69 | +$wgHooks['SkinTemplateToolboxEnd'][] = 'TasksHooks::onSkinTemplateToolboxEnd'; |
| 70 | +$wgHooks['SpecialMovepageAfterMove'][] = 'TasksHooks::onSpecialMovepageAfterMove'; |
| 71 | +$wgHooks['ArticleDeleteComplete'][] = 'TasksHooks::onArticleDeleteComplete'; |
| 72 | +$wgHooks['ArticleInsertComplete'][] = 'TasksHooks::onArticleInsertComplete'; |
| 73 | +$wgHooks['SkinTemplatePreventOtherActiveTabs'][] = 'TasksHooks::onSkinTemplatePreventOtherActiveTabs'; |
| 74 | +$wgHooks['SkinTemplateTabs'][] = 'TasksHooks::onSkinTemplateTabs'; |
| 75 | +$wgHooks['UnknownAction'][] = 'TasksHooks::onUnknownAction'; |
| 76 | +$wgHooks['ParserTestTables'][] = 'TasksHooks::onParserTestTables'; |
| 77 | +$wgHooks['LoadExtensionSchemaUpdates'][] = 'TasksHooks::onLoadExtensionSchemaUpdates'; |
| 78 | +$wgHooks['GetPreferences'][] = 'TasksHooks::onGetPreferences'; |
108 | 79 | |
109 | | -function wfTasksAddLogName( &$names ) { # Checked for HTML and MySQL insertion attacks |
110 | | - $names['tasks'] = 'tasks_logpage'; |
111 | | - return true; |
112 | | -} |
| 80 | +# Logging |
| 81 | +$wgLogTypes[] = 'tasks'; |
| 82 | +$wgLogNames['tasks'] = 'tasks_logpage'; |
| 83 | +$wgLogHeaders['tasks'] = 'tasks_logpagetext'; |
| 84 | +$wgLogActions['tasks/tasks'] = 'tasks_logentry'; |
113 | 85 | |
114 | | -function wfTasksAddLogHeader( &$headers ) { # Checked for HTML and MySQL insertion attacks |
115 | | - $headers['tasks'] = 'tasks_logpagetext'; |
116 | | - return true; |
117 | | -} |
118 | 86 | |
119 | | -function wfTasksAddActionText( &$actions ) { # Checked for HTML and MySQL insertion attacks |
120 | | - $actions['tasks/tasks'] = 'tasks_logentry'; |
121 | | - return true; |
122 | | -} |
123 | | -# END logging functions |
124 | | - |
125 | | -/** |
126 | | - * Text adding function |
127 | | - */ |
128 | | -function wfTasksAddCache() { # Checked for HTML and MySQL insertion attacks |
129 | | - global $wgTasksAddCache, $wgDefaultUserOptions; |
130 | | - |
131 | | - if( $wgTasksAddCache ) { |
132 | | - return; |
133 | | - } |
134 | | - |
135 | | - $wgTasksAddCache = true; |
136 | | -} |
137 | | - |
138 | | -#___________________________________________________________________ |
139 | | -# Hook functions |
140 | | - |
141 | | -/** |
142 | | - * Display header on 'Task:' pages (dummy hook for edit pages) |
143 | | - * @param EditPage $editPage |
144 | | - */ |
145 | | -function wfTaskExtensionEditFormInitialHook( &$editPage ) { # Checked for HTML and MySQL insertion attacks |
146 | | - return wfTaskExtensionHeaderHook( $editPage->getArticle() ); |
147 | | -} |
148 | | - |
149 | | -/** |
150 | | - * Display header on 'Task:' pages |
151 | | - * @param Article $article |
152 | | - */ |
153 | | -function wfTaskExtensionHeaderHook( &$article ) { # Checked for HTML and MySQL insertion attacks |
154 | | - global $wgTasksNamespace, $wgOut, $wgUser, $wgTitle; |
155 | | - $title = $article->getTitle(); |
156 | | - $ns = $title->getNamespace(); |
157 | | - if( $ns != $wgTasksNamespace && $ns != $wgTasksNamespace+1 ) { |
158 | | - // Show sign, if any, then we can leave |
159 | | - wfTaskExtensionHeaderSign(); |
160 | | - return true; |
161 | | - } |
162 | | - |
163 | | - $subtitle = ''; |
164 | | - |
165 | | - if( ctype_digit( $title->getText() ) ) { |
166 | | - // Page title format 'Task:123', suggested by Rowan Collins |
167 | | - $taskid = intval( $title->getText() ); |
168 | | - } else { |
169 | | - // Invalid page title; can't extract the task id |
170 | | - return true; |
171 | | - } |
172 | | - |
173 | | - wfTasksAddCache(); |
174 | | - wfLoadExtensionMessages('Tasks'); |
175 | | - $st = new SpecialTasks(); |
176 | | - $task = ''; |
177 | | - $page_title = $st->get_title_from_task( $taskid, $task ); |
178 | | - if( $task == '' ) { |
179 | | - # No such task |
180 | | - return true; |
181 | | - } |
182 | | - |
183 | | - $sk =& $wgUser->getSkin(); |
184 | | - $returnto = $wgTitle->getFullURL(); |
185 | | - $link1 = $sk->makeLinkObj( $page_title ); |
186 | | - $link2 = $sk->makeLinkObj( $page_title, wfMsgHTML( 'tasks_here' ), 'action=tasks' ); |
187 | | - $subtitle .= '<div id="task_header">' . wfMsgForContent( 'tasks_discussion_page_for', $link1, $link2 ) . "</div>\n" ; |
188 | | - $subtitle .= '<table border="1" cellspacing="1" cellpadding="2" id="task_header_table">' . |
189 | | - '<tr>' . wfTaskExtensionGetTableHeader( false ) . "</tr>\n"; |
190 | | - $subtitle .= $st->get_task_table_row( $task, $page_title, false, $returnto ); |
191 | | - $subtitle .= "</table>\n"; |
192 | | - |
193 | | - $subtitle = $wgOut->getSubtitle() . '<br />' . $subtitle; |
194 | | - $wgOut->setSubtitle( $subtitle ); |
195 | | - return true; |
196 | | -} |
197 | | - |
198 | | -/** |
199 | | - * Display header signs for "notable" tasks |
200 | | - */ |
201 | | -function wfTaskExtensionHeaderSign() { |
202 | | - global $wgTitle, $wgOut; |
203 | | - |
204 | | - if( $wgTitle->isTalkPage() ) { |
205 | | - # No talk pages please |
206 | | - return true; |
207 | | - } |
208 | | - if( $wgTitle->getNamespace() < 0 ) { |
209 | | - # No special pages please |
210 | | - return true; |
211 | | - } |
212 | | - |
213 | | - wfTasksAddCache(); |
214 | | - wfLoadExtensionMessages('Tasks'); |
215 | | - $st = new SpecialTasks(); |
216 | | - $tasks = $st->get_open_task_list( $wgTitle, true ); |
217 | | - if( count( $tasks ) == 0 ) { |
218 | | - # No tasks |
219 | | - return true; |
220 | | - } |
221 | | - |
222 | | - $out = ''; |
223 | | - $max = 0; |
224 | | - foreach( $tasks as $task ) { |
225 | | - $ttype = $st->get_task_type( $task->task_type ); |
226 | | - $msg = wfMsgForContent( 'tasks_sign_' . $ttype ); |
227 | | - if( $msg == '' ) { |
228 | | - # No sign defined for this |
229 | | - continue; |
230 | | - } |
231 | | - |
232 | | - $order = $st->get_task_order( $ttype ); |
233 | | - if( $order > $max ) { |
234 | | - $max = $order; |
235 | | - $max_type = $ttype; |
236 | | - $max_task = $task; |
237 | | - $max_msg = $msg; |
238 | | - } |
239 | | - } |
240 | | - |
241 | | - if( $max == 0 ) { |
242 | | - # Nothing for you to see here, please move along |
243 | | - return; |
244 | | - } |
245 | | - |
246 | | - // Wiki-safe output |
247 | | - $out = $wgOut->parse( '<div id="task_sign">' . $max_msg . '</div>' ); |
248 | | - |
249 | | - $subtitle = $wgOut->getSubtitle() . '<br />' . $out; |
250 | | - $wgOut->setSubtitle( $subtitle ); |
251 | | -} |
252 | | - |
253 | | -/** |
254 | | -* Return the header |
255 | | -*/ |
256 | | -function wfTaskExtensionGetTableHeader( $with_title = false ) { |
257 | | - $s = wfMsgHTML( 'tasks_existing_table_header' ); |
258 | | - if( $with_title ) { |
259 | | - $s = wfMsgHTML( 'tasks_table_header_page' ) . '|' . $s; |
260 | | - } |
261 | | - $s = '<th>' . str_replace( '|', '</th><th>', $s ) . '</th>'; |
262 | | - return $s; |
263 | | -} |
264 | | - |
265 | | -/** |
266 | | -* Display in sidebar |
267 | | -*/ |
268 | | -function wfTasksExtensionAfterToolbox( &$tpl ) { # Checked for HTML and MySQL insertion attacks |
269 | | - global $wgTitle; |
270 | | - if( $wgTitle->isTalkPage() ) { |
271 | | - # No talk pages please |
272 | | - return true; |
273 | | - } |
274 | | - if( $wgTitle->getNamespace() < 0 ) { |
275 | | - # No special pages please |
276 | | - return true; |
277 | | - } |
278 | | - |
279 | | - wfTasksAddCache(); |
280 | | - wfLoadExtensionMessages('Tasks'); |
281 | | - $st = new SpecialTasks; |
282 | | - $tasks = $st->get_open_task_list( $wgTitle, true ); |
283 | | - if( count( $tasks ) == 0 ) { |
284 | | - # No tasks |
285 | | - return true; |
286 | | - } |
287 | | - |
288 | | -?> |
289 | | - |
290 | | - </ul> |
291 | | - </div> |
292 | | - </div> |
293 | | - <div class="portlet" id="p-tasks"> |
294 | | - <h5><?php $tpl->msg('tasks_sidebar_title') ?></h5> |
295 | | - <div class="pBody"> |
296 | | - <ul> |
297 | | -<?php |
298 | | - foreach( $tasks as $task ) { |
299 | | - $ttype = $st->get_task_type( $task->task_type ); |
300 | | -?> |
301 | | - <li id="task_sidebar_<?php echo $ttype ?>"> |
302 | | - <a href="<?php |
303 | | - $nt = $st->get_task_discussion_page( $task ); |
304 | | - echo $nt->escapeLocalURL(); |
305 | | - ?>"><?php |
306 | | - echo $st->get_type_html( $ttype ); |
307 | | - ?></a><?php |
308 | | - if( $task->task_user_assigned != 0 ) { |
309 | | - echo ' ' . wfMsgHTML( 'tasks_task_is_assigned' ); |
310 | | - } |
311 | | - ?></li> |
312 | | -<?php |
313 | | - |
314 | | - } |
315 | | - return true ; |
316 | | -} |
317 | | - |
318 | | -/** |
319 | | -* Catch page movement, fix internal task_page_title values |
320 | | -*/ |
321 | | -function wfTasksExtensionAfterMove( &$special_page, &$old_title, &$new_title ) { # Checked for HTML and MySQL insertion attacks |
322 | | - if( $new_title->isTalkPage() ) { |
323 | | - # No tasks for talk pages, no need to bother the database... |
324 | | - return false; |
325 | | - } |
326 | | - |
327 | | - wfTasksAddCache(); |
328 | | - wfLoadExtensionMessages('Tasks'); |
329 | | - |
330 | | - $st = new SpecialTasks; |
331 | | - $st->rename_tasks_page( $old_title, $new_title ); |
332 | | - return false; |
333 | | -} |
334 | | - |
335 | | - |
336 | | -/** |
337 | | -* Catch article deletion, remove all tasks |
338 | | -*/ |
339 | | -function wfTasksExtensionArticleDeleteComplete( &$article, &$user, $reason ) { # Checked for HTML and MySQL insertion attacks |
340 | | - # return false ; # Uncomment this line to prevent deletion of tasks upon deletion of article |
341 | | - wfTasksAddCache(); |
342 | | - wfLoadExtensionMessages('Tasks'); |
343 | | - $t = $article->getTitle(); |
344 | | - if( $t->isTalkPage() ) { |
345 | | - # No tasks for talk pages, no need to bother the database... |
346 | | - return false; |
347 | | - } |
348 | | - |
349 | | - $st = new SpecialTasks; |
350 | | - $st->delete_all_tasks( $t ); |
351 | | - return false; |
352 | | -} |
353 | | - |
354 | | -/** |
355 | | - * Catch article creation, to close "create" tasks, and optionally |
356 | | - * open a default task on new page creation. |
357 | | - * |
358 | | - * @param Article $article |
359 | | - * @param User $user |
360 | | - * @param string $text |
361 | | - * @param string $summary |
362 | | - * @param bool $isminor |
363 | | - * @param bool $watchthis |
364 | | - * @param mixed $something WHAT THE HELL IS THIS |
365 | | - * @return bool true to continue, false to cancel operation <- WHAT THE HELL |
366 | | - */ |
367 | | -function wfTasksExtensionArticleSaveComplete( &$article, &$user, $text, $summary, $isminor, $watchthis, $something ) { # Checked for HTML and MySQL insertion attacks |
368 | | - global $wgUser; |
369 | | - wfTasksAddCache(); |
370 | | - wfLoadExtensionMessages('Tasks'); |
371 | | - $t = $article->getTitle(); |
372 | | - if( $t->isTalkPage() ) { |
373 | | - # No tasks for talk pages, no need to bother the database... |
374 | | - return false; |
375 | | - } |
376 | | - |
377 | | - $st = new SpecialTasks; |
378 | | - $tasks = $st->get_tasks_for_page( $t, true ); |
379 | | - |
380 | | - # Mark creation tasks as closed |
381 | | - foreach( $tasks as $task ) { |
382 | | - if( !$st->is_creation_task( $task->task_type ) ) { |
383 | | - # Not a "create" task |
384 | | - continue; |
385 | | - } |
386 | | - if( $st->is_closed( $task->task_status ) ) { |
387 | | - # Not open |
388 | | - continue; |
389 | | - } |
390 | | - $st->change_task_status( $task->task_id, MW_TASK_CLOSED ); |
391 | | - $st->set_new_article_id( $t ); |
392 | | - |
393 | | - $id = $t->getArticleId(); |
394 | | - # Nothing more to do |
395 | | - break; |
396 | | - } |
397 | | - |
398 | | - # OPTIONALLY create a new task |
399 | | - $on_create = $st->get_task_num( wfMsgForContent( 'tasks_event_on_creation' ) ); |
400 | | - $on_create_anon = $st->get_task_num( wfMsgForContent( 'tasks_event_on_creation_anon' ) ); |
401 | | - $add_task = MW_TASK_INVALID; |
402 | | - if( $wgUser->isAnon() && $on_create_anon != MW_TASK_INVALID ) { |
403 | | - $add_task = $on_create_anon; |
404 | | - } elseif( $wgUser->isLoggedIn() && $on_create != MW_TASK_INVALID ) { |
405 | | - $add_task = $on_create; |
406 | | - } |
407 | | - if( $add_task != MW_TASK_INVALID ) { |
408 | | - $comment = htmlspecialchars( wfMsgForContent( 'tasks_on_creation_comment' ) ); |
409 | | - $st->add_new_task( $t, $comment, $add_task ) ; |
410 | | - } |
411 | | - |
412 | | - return false; |
413 | | -} |
414 | | - |
415 | | -/** |
416 | | -* Prevents other tabs shown as active |
417 | | -*/ |
418 | | -function wfTasksExtensionPreventOtherActiveTabs( &$skin, &$prevent_active_tabs ) { # Checked for HTML and MySQL insertion attacks |
419 | | - global $action; |
420 | | - $prevent_active_tabs = ( $action == 'tasks' ); |
421 | | - return true; |
422 | | -} |
423 | | - |
424 | | -/** |
425 | | - * Show the tab |
426 | | - * @param SkinTemplate $skin |
427 | | - * @param array $content_actions |
428 | | - * @return bool true to continue running other hooks, false to abort operation |
429 | | - */ |
430 | | -function wfTasksExtensionTab( $skin, &$content_actions ) { # Checked for HTML and MySQL insertion attacks |
431 | | - global $wgTitle, $action; |
432 | | - if( $wgTitle->isTalkPage() ) { |
433 | | - # No talk pages please |
434 | | - return true; |
435 | | - } |
436 | | - if( $wgTitle->getNamespace() < 0 ) { |
437 | | - # No special pages please |
438 | | - return true; |
439 | | - } |
440 | | - |
441 | | - wfTasksAddCache(); |
442 | | - wfLoadExtensionMessages('Tasks'); |
443 | | - $content_actions['tasks'] = array( |
444 | | - 'class' => ($action == 'tasks') ? 'selected' : false, |
445 | | - 'text' => wfMsgHTML('tasks_tab'), |
446 | | - 'href' => $wgTitle->getLocalUrl( 'action=tasks' ) |
447 | | - ); |
448 | | - return true; |
449 | | -} |
450 | | - |
451 | | -/** |
452 | | -* This is where the action is :-) |
453 | | -*/ |
454 | | -function wfTasksExtensionAction( $action, $article ) { # Checked for HTML and MySQL insertion attacks |
455 | | - if( $action != 'tasks' ) { |
456 | | - # Not my kind of action! |
457 | | - return true; |
458 | | - } |
459 | | - |
460 | | - wfTasksAddCache(); |
461 | | - wfLoadExtensionMessages('Tasks'); |
462 | | - |
463 | | - $t = new SpecialTasks; |
464 | | - $t->page_management( $article->getTitle() ); |
465 | | - |
466 | | - return false; |
467 | | -} |
468 | | - |
469 | | -#_____________________________________________________________________________ |
470 | | - |
471 | | -/** |
472 | | -* The special page |
473 | | -*/ |
474 | | -function wfTasksExtension() { # Checked for HTML and MySQL insertion attacks |
475 | | - global $IP; |
476 | | - wfTasksAddCache(); |
477 | | - wfLoadExtensionMessages('Tasks'); |
478 | | - |
479 | | - require_once $IP.'/includes/SpecialPage.php'; |
480 | | - |
481 | | - class SpecialTasks extends SpecialPage { |
482 | | - |
483 | | - var $status_types = array( |
484 | | - MW_TASK_OPEN => 'open', |
485 | | - MW_TASK_ASSIGNED => 'assigned', |
486 | | - MW_TASK_CLOSED => 'closed', |
487 | | - MW_TASK_WONTFIX => 'wontfix' |
488 | | - ); |
489 | | - var $task_types; # e.g., 0 => 'cleanup' |
490 | | - var $task_types_text; # e.g., 'cleanup' => 'Clean up' |
491 | | - var $creation_tasks; # e.g., ( 1, 2, 3 ) |
492 | | - var $task_order = array(); # e.g., 'delete' => 5 |
493 | | - var $pagemode = '' ; |
494 | | - |
495 | | - /** |
496 | | - * Constructor |
497 | | - */ |
498 | | - function SpecialTasks() { # Checked for HTML and MySQL insertion attacks |
499 | | - parent::__construct( 'Tasks' ); |
500 | | - $this->includable( true ); |
501 | | - $this->update_types(); |
502 | | - } |
503 | | - |
504 | | - /** |
505 | | - * Returns the order of the type, or 0 if not defined |
506 | | - * @param string $type key like 'create' or 'cleanup' |
507 | | - * @return int |
508 | | - */ |
509 | | - function get_task_order( $type ) { # Checked for HTML and MySQL insertion attacks |
510 | | - if( isset( $this->task_order[$type] ) ) { |
511 | | - return $this->task_order[$type]; |
512 | | - } else { |
513 | | - return 0; |
514 | | - } |
515 | | - } |
516 | | - |
517 | | - /** |
518 | | - * Returns the type constant associated with the text key, or MW_TASK_INVALID |
519 | | - * @param string $type text key |
520 | | - * @return int |
521 | | - */ |
522 | | - function get_task_num( $type ) { # Checked for HTML and MySQL insertion attacks |
523 | | - $key = array_search( trim( strtolower( $type ) ), $this->task_types ); |
524 | | - if( $key === false ) { |
525 | | - wfDebug( 'Tasks: get_task_num was passed illegal text key : ' . $type . " (out of range)\n" ); |
526 | | - return MW_TASK_INVALID; |
527 | | - } else { |
528 | | - return $key; |
529 | | - } |
530 | | - } |
531 | | - |
532 | | - /** |
533 | | - * Returns the text key for a given task type constant |
534 | | - * @param $num numeric key |
535 | | - * @return string |
536 | | - */ |
537 | | - function get_task_type( $num ) { # Checked for HTML and MySQL insertion attacks |
538 | | - if( !isset( $this->task_types[$num] ) ) { |
539 | | - wfDebug( 'Tasks: get_task_type was passed illegal num : ' . $num . " (out of range)\n" ); |
540 | | - return MW_TASK_INVALID; |
541 | | - } |
542 | | - return $this->task_types[$num]; |
543 | | - } |
544 | | - |
545 | | - /** |
546 | | - * Updates task_types and creation_tasks from wfMsg |
547 | | - * @fixme Provide localized display names for user's UI language |
548 | | - */ |
549 | | - function update_types() { # Checked for HTML and MySQL insertion attacks |
550 | | - wfTasksAddCache(); |
551 | | - wfLoadExtensionMessages('Tasks'); |
552 | | - |
553 | | - # task type numeric key, text key, localized text |
554 | | - $this->task_types = array(); |
555 | | - $s = wfMsgForContent( 'tasks_task_types' ); |
556 | | - $s = explode( '|', $s ); |
557 | | - foreach( $s as $line ) { |
558 | | - $bits = explode( ':', $line, 3 ); |
559 | | - if( count( $bits ) != 3 ) { |
560 | | - # Invalid line |
561 | | - continue; |
562 | | - } |
563 | | - $keyNum = intval( $bits[0] ); |
564 | | - $keyName = trim( $bits[1] ); |
565 | | - $localName = trim( $bits[2] ); |
566 | | - if( $keyNum < 1 ) { |
567 | | - wfDebug( 'SpecialTasks::update_types: expected positive integer for key, got ' . $keyNum . "\n" ); |
568 | | - continue; |
569 | | - } |
570 | | - $this->task_types[$keyNum] = $keyName; |
571 | | - $this->task_types_text[$keyName] = $localName; |
572 | | - } |
573 | | - |
574 | | - # List of creation-type tasks |
575 | | - $this->creation_tasks = array(); |
576 | | - $s = wfMsgForContent( 'tasks_creation_tasks' ); |
577 | | - $s = explode( ',', $s ); |
578 | | - foreach( $s as $line ) { |
579 | | - $keyNum = intval( $line ); |
580 | | - if( $keyNum < 1 ) { |
581 | | - wfDebug( 'SpecialTasks::update_types: expected positive integer for key in tasks_creation_tasks, got ' . $keyNum . "\n" ); |
582 | | - continue; |
583 | | - } |
584 | | - $this->creation_tasks[] = $keyNum; |
585 | | - } |
586 | | - |
587 | | - $this->task_order = array(); |
588 | | - $s = wfMsgForContent( 'tasks_significance_order' ); |
589 | | - $s = explode( '<' , $s ); |
590 | | - $count = 1; |
591 | | - foreach( $s as $line ) { |
592 | | - $line = trim( $line ); |
593 | | - if( $line != '' ) { |
594 | | - $this->task_order[$line] = $count++; |
595 | | - } |
596 | | - } |
597 | | - } |
598 | | - |
599 | | - /** |
600 | | - * @param string $type_key 'open', 'wontfix', etc |
601 | | - * @return string localized name as text: 'Open', 'Nefarinda', etc |
602 | | - */ |
603 | | - function get_type_text( $type_key ) { # Checked for HTML and MySQL insertion attacks |
604 | | - if( !isset( $this->task_types_text[$type_key] ) ) { |
605 | | - wfDebug( 'Tasks: get_type_text was passed illegal type_key : ' . $type_key . " (out of range)\n" ); |
606 | | - return ''; |
607 | | - } |
608 | | - return $this->task_types_text[$type_key]; |
609 | | - } |
610 | | - |
611 | | - /** |
612 | | - * @param string $type_key |
613 | | - * @return string HTML-escaped localized name |
614 | | - */ |
615 | | - function get_type_html( $type_key ) { |
616 | | - return htmlspecialchars( $this->get_type_text( $type_key ) ); |
617 | | - } |
618 | | - |
619 | | - /** |
620 | | - * @param int $task_type key |
621 | | - * @return bool |
622 | | - */ |
623 | | - function is_creation_task( $task_type ) { # Checked for HTML and MySQL insertion attacks |
624 | | - return in_array( $task_type, $this->creation_tasks ); |
625 | | - } |
626 | | - |
627 | | - /** |
628 | | - * @param int $status key |
629 | | - * @return bool |
630 | | - */ |
631 | | - function is_open( $status ) { # Checked for HTML and MySQL insertion attacks |
632 | | - return ( $status == MW_TASK_OPEN || $status == MW_TASK_ASSIGNED ); |
633 | | - } |
634 | | - |
635 | | - /** |
636 | | - * @param int $status key |
637 | | - * @return bool |
638 | | - */ |
639 | | - function is_closed( $status ) { # Checked for HTML and MySQL insertion attacks |
640 | | - return !$this->is_open( $status ); |
641 | | - } |
642 | | - |
643 | | - /** |
644 | | - * Takes a title and a list of existing tasks, and decides which new tasks can be created. |
645 | | - * There's no point in having a dozen "wikify" tasks for a single article, now is there? :-) |
646 | | - * @param Title $title |
647 | | - * @param array $tasks out-parameter, will receive the set of existing tasks |
648 | | - * @return array set of creatable tasks....? |
649 | | - */ |
650 | | - function get_valid_new_tasks( $title, &$tasks ) { # Checked for HTML and MySQL insertion attacks |
651 | | - $exists = $title->exists(); |
652 | | - $tasks = $this->get_tasks_for_page( $title ); |
653 | | - $new_tasks = array(); |
654 | | - $tg = array(); |
655 | | - |
656 | | - foreach( $tasks as $t ) { |
657 | | - # Assemble types; if multiple of one type, assemble open ones |
658 | | - if( !isset( $tg[$t->task_type] ) || $this->is_open( $t->task_status ) ) { |
659 | | - $tg[$t->task_type] = $t->task_status; |
660 | | - } |
661 | | - } |
662 | | - |
663 | | - foreach( array_keys( $this->task_types ) as $a ) { |
664 | | - if( $exists == $this->is_creation_task( $a ) ) { |
665 | | - # Creation task and existence exclude each other |
666 | | - continue; |
667 | | - } |
668 | | - if( isset( $tg[$a] ) && $this->is_open( $tg[$a] ) ) { |
669 | | - # Task exists and is not closed |
670 | | - continue; |
671 | | - } |
672 | | - $new_tasks[$a] = $this->get_task_type( $a ); |
673 | | - } |
674 | | - return $new_tasks; |
675 | | - } |
676 | | - |
677 | | - /** |
678 | | - * The form for creating a new task from a form ("tasks" tab) |
679 | | - * @return string HTML output |
680 | | - * @fixme Display error messages on invalid input |
681 | | - */ |
682 | | - function create_from_form( $title ) { # Checked for HTML and MySQL insertion attacks |
683 | | - global $wgRequest, $wgUser; |
684 | | - if( $wgRequest->getText( 'create_task', '' ) == '' ) { |
685 | | - # No form |
686 | | - return ''; |
687 | | - } |
688 | | - |
689 | | - $out = ''; |
690 | | - $tasks = array(); |
691 | | - $type = $wgRequest->getInt( 'type' ); |
692 | | - $name = $wgRequest->getText( 'username' ); |
693 | | - |
694 | | - $comment = $wgRequest->getText( 'text', '' ); # Not evaluated here; stored in database through safe database function |
695 | | - $new_tasks = $this->get_valid_new_tasks( $title, $tasks ); |
696 | | - if( !isset( $new_tasks[$type] ) ) { |
697 | | - # Trying to create a task that isn't available |
698 | | - $out .= '<p>' . wfMsgHtml('tasks_error1') . '</p>'; |
699 | | - } else { |
700 | | - $this->add_new_task( $title, $comment, $type, $name ); |
701 | | - $out .= '<p>' . wfMsgHtml( 'tasks_ok1' ) . '</p>'; |
702 | | - } |
703 | | - return $out; |
704 | | - } |
705 | | - |
706 | | - /** |
707 | | - * Adds a new task |
708 | | - */ |
709 | | - function add_new_task( $title, $comment, $type, $name ) { |
710 | | - global $wgUser; |
711 | | - $dbw = wfGetDB( DB_MASTER ); |
712 | | - $user_id = 0; |
713 | | - if ( !is_null($name) ) { |
714 | | - $user_id = $wgUser->idFromName($name); |
715 | | - if ( !ctype_digit($user_id) ) { |
716 | | - $user_id = 0; # (int)0 indicates assigned to "no one" |
717 | | - } |
718 | | - } |
719 | | - $dbw->insert( 'tasks', |
720 | | - array( |
721 | | - 'task_page_id' => $title->getArticleID(), |
722 | | - 'task_page_title' => $title->getPrefixedDBkey(), |
723 | | - 'task_user_id' => $wgUser->getID(), |
724 | | - 'task_user_text' => $wgUser->getName(), |
725 | | - 'task_user_assigned' => $user_id, |
726 | | - 'task_status' => $this->get_status_number( 'open' ), |
727 | | - 'task_comment' => $comment, |
728 | | - 'task_type' => $type, |
729 | | - 'task_timestamp' => $dbw->timestamp() |
730 | | - ) ); |
731 | | - } |
732 | | - |
733 | | - /** |
734 | | - * For a list of tasks, get a single table row |
735 | | - * This function is heavy on output! |
736 | | - */ |
737 | | - function get_task_table_row( &$task, &$title, $show_page = false, $returnto = '' ) { # Checked for HTML and MySQL insertion attacks |
738 | | - global $wgContLang, $wgUser, $wgTasksNamespace, $wgExtraNamespaces; |
739 | | - $out = ''; |
740 | | - $sk =& $wgUser->getSkin(); |
741 | | - $ct = $wgContLang->timeanddate( $task->task_timestamp ); # Time object from string of digits |
742 | | - $cu = Title::makeTitleSafe( NS_USER, $task->task_user_text ); # Safe user name |
743 | | - $comment = htmlspecialchars( $task->task_comment ); # Safe user comment, no HTML allowed |
744 | | - $comment = nl2br( $comment ); # display newlines as they were in the edit box |
745 | | - $status = $task->task_status; # Integer |
746 | | - $tid = $task->task_id; # Integer |
747 | | - $encType = $this->get_type_html( $this->get_task_type( $task->task_type ) ); # Will catch illegal types and wfDebug them |
748 | | - if( $returnto != '' ) { |
749 | | - $returnto = '&returnto=' . urlencode( $returnto ); |
750 | | - } |
751 | | - |
752 | | - $out .= '<tr>'; |
753 | | - if( $show_page ) { |
754 | | - $out .= '<td align="left" valign="top">'; |
755 | | - $out .= $sk->makeLinkObj( $title ); |
756 | | - $out .= '<br />'; |
757 | | - $out .= $sk->makeLinkObj( $title, wfMsgHTML('tasks_see_page_tasks'), 'action=tasks' ); |
758 | | - $out .= '</td>'; |
759 | | - } |
760 | | - $out .= '<td valign="top" align="left" nowrap class="tasks_status_bgcol_' . $this->status_types[$status] . '">'; |
761 | | - $out .= '<b>' . $encType . '</b><br /><i>'; |
762 | | - $out .= wfMsgForContent( 'tasks_status_' . $this->status_types[$status] ); |
763 | | - $out .= '</i><br />' ; |
764 | | - |
765 | | - # Additional info |
766 | | - $help_title = Title::makeTitleSafe( NS_HELP, wfMsgForContent('tasks_help_page') ); |
767 | | - $help_title->mFragment = $encType ; |
768 | | - $ext1 = $sk->makeLinkObj( $help_title , wfMsgForContent('tasks_help_page_link') ); |
769 | | - $more_title = SpecialPage::getTitleFor( 'Tasks' ); # This special page |
770 | | - $ext2 = $sk->makeLinkObj( $more_title , wfMsgForContent('tasks_more_like_it') , 'task_type='.$task->task_type ); |
771 | | - $out .= wfMsgForContent ( 'tasks_help_separator' , $ext1 , $ext2 ) ; |
772 | | - |
773 | | - |
774 | | - $out .= '</td>'; |
775 | | - $out .= '<td align="left" valign="top" nowrap>'; |
776 | | - $out .= wfMsgHTML( 'tasks_created_by', $sk->makeLinkObj( $cu, htmlspecialchars( $task->task_user_text ) ) ); |
777 | | - $out .= '<br />' . $ct ; |
778 | | - |
779 | | - # Closing information |
780 | | - if( $task->task_user_close != 0 && $this->is_closed( $status ) ) { |
781 | | - $user_close = new User; |
782 | | - $user_close->setID( $task->task_user_close ); |
783 | | - $uct = Title::makeTitleSafe( NS_USER, $user_close->getName() ); # Assigned user title |
784 | | - $out .= '<br />' . wfMsgHTML( 'tasks_closedby', $sk->makeLinkObj( $uct, htmlspecialchars( $user_close->getName() ) ) ); |
785 | | - if( $task->task_timestamp_closed != "" ) { |
786 | | - $out .= '<br />' . $wgContLang->timeanddate( $task->task_timestamp_closed ); # Time object from string of digits |
787 | | - } |
788 | | - } |
789 | | - $out .= '</td>'; |
790 | | - |
791 | | - $out .= '<td align="left" valign="top">' . $comment . '</td>' ; # Comment is HTML-stripped |
792 | | - $out .= '<td align="left" valign="top">'; |
793 | | - if( $task->task_user_assigned == 0 ) { |
794 | | - # Noone is assigned this task |
795 | | - $out .= '<form method="get">' // Added a form in place of old "assign to me" link |
796 | | - . wfMsgHTML( 'tasks_assign_to' ) . ' <input type="text" name="username" />' |
797 | | - . '<input type="hidden" name="action" value="tasks" />' |
798 | | - . '<input type="hidden" name="mode" value="assignto" />' |
799 | | - . '<input type="hidden" name="title" value="'.htmlspecialchars($title->getPrefixedText()).'" />' |
800 | | - . '<input type="hidden" name="taskid" value="'.$tid.'" />' |
801 | | - . '<input type="submit" name="submit" value="' . wfMsgHTML( 'ok' ) . '" />' |
802 | | - . '</form>'; |
803 | | - } else { |
804 | | - # Someone is assigned this task |
805 | | - $au = new User(); # Assigned user |
806 | | - $au->setID( $task->task_user_assigned ); |
807 | | - $aut = Title::makeTitleSafe( NS_USER, $au->getName() ); # Assigned user title |
808 | | - $out .= wfMsgHTML( 'tasks_assignedto', $sk->makeLinkObj( $aut, htmlspecialchars( $au->getName() ) ) ); |
809 | | - } |
810 | | - if( $wgUser->isLoggedIn() ) { |
811 | | - $txt = array(); |
812 | | - if( $this->is_open( $status ) ) { |
813 | | - # Assignment |
814 | | - if( $wgUser->getID() != $task->task_user_assigned ) { |
815 | | - # Assign myself |
816 | | - $txt[] = $sk->makeLinkObj( $title, |
817 | | - wfMsgHTML( 'tasks_assign_me' ), |
818 | | - "action=tasks&mode=assignme&taskid={$tid}{$returnto}" ); # tid is integer, returnto is safe |
819 | | - } else { |
820 | | - # Unassign myself |
821 | | - $txt[] = $sk->makeLinkObj( $title, |
822 | | - wfMsgHTML( 'tasks_unassign_me' ), |
823 | | - "action=tasks&mode=unassignme&taskid={$tid}{$returnto}" ); # tid is integer, returnto is safe |
824 | | - } |
825 | | - } |
826 | | - if( $this->is_open( $status ) ) { |
827 | | - # Open or assigned |
828 | | - $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_close' ), "action=tasks&mode=close&taskid={$tid}{$returnto}" ); |
829 | | - $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_wontfix' ), "action=tasks&mode=wontfix&taskid={$tid}{$returnto}" ); |
830 | | - } else if( $this->get_task_type( $task->task_type ) != 'create' ) { |
831 | | - # Closed or wontfix, can reopen (maybe) |
832 | | - $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_reopen' ), "action=tasks&mode=reopen&taskid={$tid}{$returnto}" ); |
833 | | - } |
834 | | - |
835 | | - if( $wgUser->isAllowed( 'delete' ) ) { |
836 | | - $txt[] = $sk->makeLinkObj( $title, wfMsgHTML( 'tasks_delete' ), "action=tasks&mode=delete&taskid={$tid}{$returnto}" ); |
837 | | - } |
838 | | - |
839 | | - if( count( $txt ) > 0 ) { |
840 | | - $out .= '<br />' . implode( ' - ', $txt ); |
841 | | - } |
842 | | - |
843 | | - } |
844 | | - $tdp = $this->get_task_discussion_page( $task ); |
845 | | - $out .= '<br />' . $sk->makeLinkObj( $tdp, wfMsgHTML('tasks_discussion_page_link') ); |
846 | | - $out .= '</td></tr>' ; |
847 | | - |
848 | | - # Transclude comments page, if wanted |
849 | | - if( $wgUser->getOption( 'show_task_comments' ) ) { |
850 | | - if( $this->pagemode == 'search' || $this->pagemode == 'tasks_of_page' ) { |
851 | | - $out .= $this->transclude_comments( $tdp, $show_page ? 5 : 4 ) ; |
852 | | - } |
853 | | - } |
854 | | - return $out; |
855 | | - } |
856 | | - |
857 | | - /** |
858 | | - * Returns the |
859 | | - * @param Title $title of task page to load |
860 | | - * @param int $col_compensator Number of table columns to span |
861 | | - * @return string HTML table row, or empty string |
862 | | - * @access private |
863 | | - */ |
864 | | - function transclude_comments( $title, $col_compensator ) { |
865 | | - if( !$title->exists() ) { |
866 | | - # Nothing to transclude |
867 | | - return ''; |
868 | | - } |
869 | | - |
870 | | - global $wgOut; |
871 | | - $art = new Article( $title ); |
872 | | - $ret = $art->getContent( false ); |
873 | | - $ret = $wgOut->parse( $ret ); |
874 | | - return '<tr><td id="task_transcluded_comment" colspan="' . $col_compensator . '">' . $ret . '</td></tr>' ; |
875 | | - } |
876 | | - |
877 | | - /** |
878 | | - * @param Task $task |
879 | | - * @return Title |
880 | | - */ |
881 | | - function get_task_discussion_page( &$task ) { # Checked for HTML and MySQL insertion attacks |
882 | | - global $wgTasksNamespace; |
883 | | - # Format : "Task:123" |
884 | | - return Title::makeTitle( $wgTasksNamespace, strval( $task->task_id ) ); |
885 | | - } |
886 | | - |
887 | | - /** |
888 | | - * On the "tasks" tab, show the list of existing tasks for that article |
889 | | - */ |
890 | | - function show_existing_tasks( &$title, &$tasks ) { # Checked for HTML and MySQL insertion attacks |
891 | | - $out = ''; |
892 | | - foreach( $tasks as $task ) { |
893 | | - $out .= $this->get_task_table_row( $task, $title ); # Assumed safe |
894 | | - } |
895 | | - if( $out == '' ) { |
896 | | - return ''; |
897 | | - } |
898 | | - |
899 | | - $out = "<h2>" . wfMsgHTML( 'tasks_existing_header' ) . "</h2>\n" . |
900 | | - "<table border='1' cellspacing='1' cellpadding='2'>" . |
901 | | - "<tr>" . wfTaskExtensionGetTableHeader() . "</tr>" . |
902 | | - $out . "</table>"; |
903 | | - return $out; |
904 | | - } |
905 | | - |
906 | | - /** |
907 | | - * Checks if there's a "mode" set in the URL of the current page |
908 | | - * (performs changes on tasks, like assigning or closing them) |
909 | | - * @return HTML output |
910 | | - * @fixme There is no output! Should there be? No error output either. |
911 | | - */ |
912 | | - function check_mode( $title ) { |
913 | | - |
914 | | - global $wgUser, $wgRequest; |
915 | | - |
916 | | - $mode = trim( $wgRequest->getVal( 'mode' ) ); |
917 | | - $name = trim( $wgRequest->getVal( 'username' ) ); |
918 | | - $taskid = $wgRequest->getInt( 'taskid', 0 ); |
919 | | - |
920 | | - if( $mode == '' || $taskid == 0 ) { |
921 | | - # Simple validation |
922 | | - return ''; |
923 | | - } |
924 | | - if( !$wgUser->isLoggedIn() ) { |
925 | | - # Needs to be logged in |
926 | | - return ''; |
927 | | - } |
928 | | - |
929 | | - $out = ''; |
930 | | - $fname = 'Tasks::check_mode'; |
931 | | - $dbw = wfGetDB( DB_MASTER ); |
932 | | - |
933 | | - switch( $mode ) { |
934 | | - |
935 | | - case 'assignto': |
936 | | - case 'assignme': |
937 | | - case 'unassignme': |
938 | | - |
939 | | - // Assign or unassign an existing used to this task |
940 | | - $conditions = array( 'task_id' => $taskid ); |
941 | | - $user_id = $wgUser->getId() ; # Assign |
942 | | - if( $mode == 'unassignme' ) { |
943 | | - # Unassign me; this can be invoked for every user by editing the URL! |
944 | | - $user_id = 0; |
945 | | - } else if ( $mode == 'assignto' ) { |
946 | | - $user_id = $wgUser->idFromName($name); |
947 | | - if ( empty($user_id) ) { |
948 | | - break; # break as though "mode" were undefined (no action) |
949 | | - } |
950 | | - } |
951 | | - $do_set = array( # SET |
952 | | - 'task_user_assigned' => $user_id, # Coming from $wgUser, so assumed safe |
953 | | - 'task_status' => ( $mode == 'assignme' ) |
954 | | - ? $this->get_status_number( 'assigned' ) |
955 | | - : $this->get_status_number( 'open' ), # Integer |
956 | | - ); |
957 | | - $dbw->update( 'tasks', |
958 | | - $do_set, |
959 | | - $conditions, |
960 | | - $fname ); |
961 | | - |
962 | | - $title = $this->get_title_from_task( $taskid, $task ); |
963 | | - $act = wfMsgHTML( 'tasks_assigned_myself_log', |
964 | | - $this->get_type_html( $this->get_task_type( $task->task_type ) ) ); |
965 | | - $log = new LogPage( 'tasks' ); |
966 | | - $log->addEntry( 'tasks', $title, $act ); |
967 | | - |
968 | | - break; |
969 | | - |
970 | | - case 'close': |
971 | | - case 'wontfix': |
972 | | - case 'reopen': |
973 | | - |
974 | | - # Changing task status |
975 | | - if( $mode == 'reopen' ) { |
976 | | - $mode = 'open'; |
977 | | - } |
978 | | - if( $mode == 'close' ) { |
979 | | - $mode = 'closed'; |
980 | | - } |
981 | | - $new_status = $this->get_status_number( $mode ); |
982 | | - $this->change_task_status( $taskid, $new_status ); |
983 | | - |
984 | | - break; |
985 | | - |
986 | | - case 'delete': |
987 | | - |
988 | | - # Delete this task; sysops only! |
989 | | - if( $wgUser->isAllowed( 'delete' ) ) { |
990 | | - # OK, deleting |
991 | | - $dbw->delete( 'tasks', |
992 | | - array( 'task_id' => $taskid ), |
993 | | - $fname ); |
994 | | - |
995 | | - # Log task deletion |
996 | | - $act = wfMsgForContent( 'tasks_action_delete' ); |
997 | | - $log = new LogPage( 'tasks' ); |
998 | | - $log->addEntry( 'tasks', $title, $act ); |
999 | | - $out .= wfMsgHtml( 'tasks_task_was_deleted' ); |
1000 | | - } else { |
1001 | | - # No-no! |
1002 | | - global $wgOut; |
1003 | | - $wgOut->setPageTitle( wfMsg( 'tasks_no_task_delete_title' ) ); |
1004 | | - $wgOut->addWikiText( wfMsg( 'tasks_no_task_delete_text' ) ); |
1005 | | - return ''; |
1006 | | - } |
1007 | | - |
1008 | | - break; |
1009 | | - |
1010 | | - default: |
1011 | | - # Unknown mode |
1012 | | - return ''; |
1013 | | - } // end switch() |
1014 | | - |
1015 | | - return $out; |
1016 | | - } |
1017 | | - |
1018 | | - /** |
1019 | | - * Returns the number for the status |
1020 | | - * @param string $status key |
1021 | | - * @return int |
1022 | | - */ |
1023 | | - function get_status_number( $status ) { # Checked for HTML and MySQL insertion attacks |
1024 | | - foreach( $this->status_types as $k => $v ) { |
1025 | | - if( $v == $status ) { |
1026 | | - return $k; |
1027 | | - } |
1028 | | - } |
1029 | | - # Invalid status |
1030 | | - return 0; |
1031 | | - } |
1032 | | - |
1033 | | - /** |
1034 | | - * Changes the status of a task, performs some associated cleanup, and logs the action |
1035 | | - */ |
1036 | | - function change_task_status( $taskid, $new_status ) { # Checked for HTML and MySQL insertion attacks |
1037 | | - global $wgUser; |
1038 | | - $fname = 'Tasks:change_task_status'; |
1039 | | - $dbw = wfGetDB( DB_MASTER ); |
1040 | | - |
1041 | | - if( !is_numeric( $new_status ) || !is_numeric( $taskid ) ) { |
1042 | | - # Paranoia |
1043 | | - return; |
1044 | | - } |
1045 | | - |
1046 | | - # What to change: |
1047 | | - $as = array( 'task_status' => $new_status ); |
1048 | | - # Where to change it: |
1049 | | - $aw = array( 'task_id' => $taskid ); |
1050 | | - |
1051 | | - if( $this->is_closed( $new_status ) ) { |
1052 | | - # When closing, set closing user ID, and reset assignment |
1053 | | - $as['task_user_close'] = $wgUser->getID(); |
1054 | | - $as['task_user_assigned'] = 0; |
1055 | | - $as['task_timestamp_closed'] = $dbw->timestamp(); # Assumed safe |
1056 | | - } elseif( $new_status == $this->get_status_number( 'open' ) ) { |
1057 | | - # Change to "open", no assigned user or closing user |
1058 | | - $as['task_user_assigned'] = 0; |
1059 | | - $as['task_user_close'] = 0; |
1060 | | - $as['task_timestamp_closed'] = ''; |
1061 | | - } |
1062 | | - |
1063 | | - $dbw->update( 'tasks', |
1064 | | - $as, # SET |
1065 | | - $aw, # WHERE |
1066 | | - $fname ); |
1067 | | - |
1068 | | - # Logging |
1069 | | - $title = $this->get_title_from_task( $taskid, $task ); |
1070 | | - $act = wfMsgHTML( 'tasks_action_' . $this->status_types[$new_status], |
1071 | | - $this->get_type_html( $this->get_task_type( $task->task_type ) ) ); |
1072 | | - $log = new LogPage( 'tasks' ); |
1073 | | - $log->addEntry( 'tasks', $title, $act ); |
1074 | | - } |
1075 | | - |
1076 | | - |
1077 | | - /** |
1078 | | - * Returns the list of active tasks for this page, for display in the sidebar |
1079 | | - */ |
1080 | | - function get_open_task_list( &$title, $useCache = false ) { # Checked for HTML and MySQL insertion attacks |
1081 | | - |
1082 | | - global $wgTaskExtensionTasksCachedTitle , $wgTaskExtensionTasksCache ; |
1083 | | - |
1084 | | - if( $useCache && $wgTaskExtensionTasksCachedTitle == $title->getPrefixedText() ) { |
1085 | | - # Return the cache, thus skip the query and increase shareholder value |
1086 | | - return $wgTaskExtensionTasksCache; |
1087 | | - } |
1088 | | - |
1089 | | - $tasks = $this->get_tasks_for_page( $title ); |
1090 | | - $ret = array(); |
1091 | | - foreach( $tasks as $task ) { |
1092 | | - if( $this->is_open( $task->task_status ) ) { |
1093 | | - $ret[$this->get_type_html( $this->get_task_type( $task->task_type ) )] = $task; |
1094 | | - } |
1095 | | - } |
1096 | | - ksort( $ret ); |
1097 | | - if( $useCache ) { |
1098 | | - # Store results in cache for further use |
1099 | | - $wgTaskExtensionTasksCache = $ret; |
1100 | | - $wgTaskExtensionTasksCachedTitle = $title->getPrefixedText(); |
1101 | | - } |
1102 | | - return $ret; |
1103 | | - } |
1104 | | - |
1105 | | - /** |
1106 | | - * Returns the title object for a task, and the task data through reference |
1107 | | - * If no task can be found, return '' |
1108 | | - * @param int $task_id |
1109 | | - * @param Task $task warning: out-param |
1110 | | - * @return Title |
1111 | | - */ |
1112 | | - function get_title_from_task( $task_id, &$task ) { # Checked for HTML and MySQL insertion attacks |
1113 | | - $task = $this->get_task_from_id( $task_id ); |
1114 | | - |
1115 | | - if( $task == '' ) { |
1116 | | - return ''; |
1117 | | - } elseif ( $task->task_page_id == 0 ) { # Non-existing page |
1118 | | - $title = Title::newFromDBkey( $task->task_page_title ); |
1119 | | - } else { # Existing page |
1120 | | - $title = Title::newFromID( $task->task_page_id ); |
1121 | | - } |
1122 | | - return $title; |
1123 | | - } |
1124 | | - |
1125 | | - /** |
1126 | | - * Returns a single task by its ID |
1127 | | - */ |
1128 | | - function get_task_from_id( $task_id ) { # Checked for HTML and MySQL insertion attacks |
1129 | | - if( !is_numeric( $task_id ) ) { |
1130 | | - # Paranoia |
1131 | | - return null; |
1132 | | - } |
1133 | | - $dbr = wfGetDB( DB_SLAVE ); |
1134 | | - $res = $dbr->select( |
1135 | | - /* FROM */ 'tasks', |
1136 | | - /* SELECT */ '*', |
1137 | | - /* WHERE */ array( 'task_id' => $task_id ) |
1138 | | - ); |
1139 | | - $task = $dbr->fetchObject( $res ); |
1140 | | - $dbr->freeResult( $res ); |
1141 | | - return $task; |
1142 | | - } |
1143 | | - |
1144 | | - /** |
1145 | | - * Sets the article ID (on page creation) |
1146 | | - */ |
1147 | | - function set_new_article_id( &$title ) { # Checked for HTML and MySQL insertion attacks |
1148 | | - $fname = 'Tasks:set_new_article_id'; |
1149 | | - $dbw = wfGetDB( DB_MASTER ); |
1150 | | - $dbw->update( 'tasks', |
1151 | | - array( 'task_page_id' => $title->getArticleID() ), # SET |
1152 | | - array( 'task_page_title' => $title->getPrefixedDBkey() ), # WHERE |
1153 | | - $fname ); |
1154 | | - } |
1155 | | - |
1156 | | - /** |
1157 | | - * Deletes all tasks associated with an article; done on article deletion |
1158 | | - * @fixme this is only used on page deletion at the moment; will all conditions be used? |
1159 | | - */ |
1160 | | - function delete_all_tasks( $title ) { # Checked for HTML and MySQL insertion attacks |
1161 | | - $fname = 'Tasks:delete_all_tasks'; |
1162 | | - if( $title->exists() ) { |
1163 | | - $conds = array( 'task_page_id' => $title->getArticleID() ); |
1164 | | - } else { |
1165 | | - $conds = array( 'task_page_title' => $title->getPrefixedDBkey() ); |
1166 | | - } |
1167 | | - $dbw = wfGetDB( DB_MASTER ); |
1168 | | - $dbw->delete( 'tasks', |
1169 | | - $conds, |
1170 | | - $fname ); |
1171 | | - } |
1172 | | - |
1173 | | - /** |
1174 | | - * Called for page moves |
1175 | | - */ |
1176 | | - function rename_tasks_page( $old_title, $new_title ) { # Checked for HTML and MySQL insertion attacks |
1177 | | - $fname = 'Tasks:rename_tasks_page'; |
1178 | | - $dbw = wfGetDB( DB_MASTER ); |
1179 | | - $dbw->update( 'tasks', |
1180 | | - array( 'task_page_title' => $new_title->getPrefixedDBkey() ), # SET |
1181 | | - array( 'task_page_title' => $old_title->getPrefixedDBkey() ), # WHERE |
1182 | | - $fname ); |
1183 | | - } |
1184 | | - |
1185 | | - /** |
1186 | | - * THIS IS THE MAIN FUNCTION FOR THE TAB-BASED INTERFACE |
1187 | | - */ |
1188 | | - function page_management( $title ) { # Checked for HTML and MySQL insertion attacks |
1189 | | - if( $title->isTalkPage() ) { |
1190 | | - # No tasks for talk pages, no need to bother the database... |
1191 | | - return; |
1192 | | - } |
1193 | | - |
1194 | | - global $wgOut, $action, $wgRequest, $wgUser, $wgTitle; |
1195 | | - $out = ''; |
1196 | | - $tasks = array(); |
1197 | | - $wgOut->addLink(array( |
1198 | | - 'rel' => 'stylesheet', |
1199 | | - 'type' => 'text/css', |
1200 | | - 'media' => 'screen', |
1201 | | - 'href' => TASKS_CSS, |
1202 | | - )); |
1203 | | - $wgOut->setSubtitle( wfMsgHTML( 'tasks_title', $title->getPrefixedText() ) ); |
1204 | | - $this->pagemode = 'tasks_of_page' ; |
1205 | | - |
1206 | | - # Create from form |
1207 | | - $out .= $this->create_from_form( $title ); |
1208 | | - |
1209 | | - # Check for mode |
1210 | | - $out .= $this->check_mode( $title ); |
1211 | | - |
1212 | | - # Get list of tasks that can be created |
1213 | | - $new_tasks = $this->get_valid_new_tasks( $title, $tasks ); |
1214 | | - |
1215 | | - # Show task creation form, if tasks can be created |
1216 | | - $out .= $this->generate_form( $new_tasks ); |
1217 | | - |
1218 | | - # Existing tasks |
1219 | | - $out .= $this->show_existing_tasks( $title, $tasks ); |
1220 | | - |
1221 | | - # And ... out! |
1222 | | - $returnto = $wgRequest->getVal( 'returnto' ); |
1223 | | - if( $this->isValidRedirect( $returnto ) ) { |
1224 | | - # Forward to other page |
1225 | | - $wgOut->redirect( $returnto ); |
1226 | | - |
1227 | | - $skin =& $wgUser->getSkin(); |
1228 | | - $link = $skin->makeExternalLink( $returnto, wfMsgHTML( 'tasks_here' ) ); |
1229 | | - $msg = wfMsgHTML( 'tasks_returnto', $link ); |
1230 | | - $wgOut->addHTML( $msg ); |
1231 | | - } else { |
1232 | | - $this->setHeaders(); |
1233 | | - $wgOut->addHTML( $out ); |
1234 | | - } |
1235 | | - } |
1236 | | - |
1237 | | - /** |
1238 | | - * Confirm that the given redirect page is local to this site |
1239 | | - * FIXME: this can still pass you anywhere on the domain, |
1240 | | - * or perhaps be an invalid URL altogether. |
1241 | | - * @param string $url |
1242 | | - * @return bool |
1243 | | - */ |
1244 | | - function isValidRedirect( $url ) { |
1245 | | - if( $url == '' ) { |
1246 | | - return false; |
1247 | | - } |
1248 | | - |
1249 | | - global $wgTitle; |
1250 | | - $url1 = $wgTitle->getFullURL(); |
1251 | | - $url1 = explode( '/', $url1 ); |
1252 | | - $url1 = $url1[0] . '/' . $url1[1] .'/' . $url1[2]; |
1253 | | - $url2 = explode( '/', $url ); |
1254 | | - $url2 = $url2[0] . '/' . $url2[1] .'/' . $url2[2]; |
1255 | | - |
1256 | | - return ( $url1 == $url2 ); |
1257 | | - } |
1258 | | - |
1259 | | - /** |
1260 | | - * Generates a form for creating a new task |
1261 | | - */ |
1262 | | - function generate_form( &$new_tasks ) { # Checked for HTML and MySQL insertion attacks |
1263 | | - if( count( $new_tasks ) == 0 ) { |
1264 | | - return ''; |
1265 | | - } |
1266 | | - |
1267 | | - // Heading and table heading |
1268 | | - $out = '<h2>' . wfMsgHTML( 'tasks_create_header' ) . "</h2>\n" |
1269 | | - . '<form method="post">' |
1270 | | - . '<table border="0" width="100%"><tr><td valign="top" nowrap><b>' |
1271 | | - . wfMsgHTML( 'tasks_form_new' ) |
1272 | | - . '</b></td><td width="100%" align="center">' |
1273 | | - . '<b>' . wfMsgHTML( 'tasks_form_comment' ) . '</b> ' |
1274 | | - . wfMsgHTML( 'tasks_plain_text_only' ) |
1275 | | - . '</td></tr><tr>' |
1276 | | - ; |
1277 | | - |
1278 | | - // Select possible tasks |
1279 | | - $out .= '<td valign="top" nowrap><select name="type" size="7" style="width:100%">'; |
1280 | | - $o = array(); |
1281 | | - foreach( $new_tasks as $k => $v ) { |
1282 | | - $o[$v] = '<option value="' . $k . '">' . $this->get_type_html( $v ) . '</option>'; |
1283 | | - } |
1284 | | - ksort( $o ); |
1285 | | - $out .= implode( '', $o ); |
1286 | | - $out .= '</select>'; |
1287 | | - |
1288 | | - $out .= '</td><td valign="top">' |
1289 | | - . '<textarea name="text" rows="4" cols="20" style="width:100%"></textarea><br />' |
1290 | | - . wfMsgHTML( 'tasks_assign_to' ) . ' <input type="text" name="username" />' |
1291 | | - . '<input type="submit" name="create_task" value="' . wfMsgHTML( 'ok' ) . '" />' |
1292 | | - . '</td></tr></table>' |
1293 | | - . '</form>' |
1294 | | - ; |
1295 | | - return $out; |
1296 | | - } |
1297 | | - |
1298 | | - /** |
1299 | | - * Returns the exisiting tasks for a single page |
1300 | | - */ |
1301 | | - function get_tasks_for_page( &$title, $force_dbtitle = false ) { # Checked for HTML and MySQL insertion attacks |
1302 | | - $dbr = wfGetDB( DB_SLAVE ); |
1303 | | - $id = $title->getArticleID(); |
1304 | | - |
1305 | | - if( $id == 0 || $force_dbtitle ) { |
1306 | | - $conds = array( 'task_page_title' => $title->getPrefixedDBkey() ); |
1307 | | - } else { |
1308 | | - $conds = array( 'task_page_id' => $id ); |
1309 | | - } |
1310 | | - |
1311 | | - $res = $dbr->select( |
1312 | | - /* FROM */ 'tasks', |
1313 | | - /* SELECT */ '*', |
1314 | | - /* WHERE */ $conds |
1315 | | - ); |
1316 | | - |
1317 | | - $ret = array(); |
1318 | | - while( $line = $dbr->fetchObject( $res ) ) { |
1319 | | - $ret[$line->task_timestamp.':'.$line->task_id] = $line; |
1320 | | - } |
1321 | | - $dbr->freeResult($res); |
1322 | | - krsort( $ret ); |
1323 | | - return $ret; |
1324 | | - } |
1325 | | - |
1326 | | - function get_assigned_tasks( $userid ) { # Checked for HTML and MySQL insertion attacks |
1327 | | - if( !is_numeric( $userid ) ) { |
1328 | | - # Paranoia |
1329 | | - return null; |
1330 | | - } |
1331 | | - |
1332 | | - $dbr = wfGetDB( DB_SLAVE ); |
1333 | | - |
1334 | | - $res = $dbr->select( |
1335 | | - /* FROM */ 'tasks', |
1336 | | - /* SELECT */ '*', |
1337 | | - /* WHERE */ array( 'task_user_assigned' => $userid ) |
1338 | | - ); |
1339 | | - |
1340 | | - $ret = array(); |
1341 | | - while( $line = $dbr->fetchObject( $res ) ) { |
1342 | | - $ret[$line->task_timestamp.':'.$line->task_id] = $line; |
1343 | | - } |
1344 | | - $dbr->freeResult( $res ); |
1345 | | - krsort( $ret ); |
1346 | | - return $ret; |
1347 | | - } |
1348 | | - |
1349 | | - /** |
1350 | | - * Special page main function |
1351 | | - */ |
1352 | | - function execute( $par = null ) { # Checked for HTML and MySQL insertion attacks |
1353 | | - global $wgOut, $wgRequest, $wgUser, $wgTitle, $wgLang; |
1354 | | - $fname = 'Special::Tasks:execute'; |
1355 | | - |
1356 | | - $out = ''; |
1357 | | - $mode = trim( $wgRequest->getVal( 'mode' ) ); |
1358 | | - $skin =& $wgUser->getSkin(); |
1359 | | - $dbr = wfGetDB( DB_SLAVE ); |
1360 | | - |
1361 | | - # Assignments |
1362 | | - if( $wgUser->isLoggedIn() ) { |
1363 | | - if( $mode == 'myassignments' ) { |
1364 | | - # Show my assignments |
1365 | | - $tasks = $this->get_assigned_tasks( $wgUser->getId() ); |
1366 | | - if( count( $tasks ) == 0 ) { |
1367 | | - $out .= '<p>' . wfMsgHTML( 'tasks_you_have_no_assignments' ) . '</p>'; |
1368 | | - } else { |
1369 | | - $out .= '<h2>' . wfMsgExt( 'tasks_my_assignments', array( 'escape', 'parsemag' ), count( $tasks ) ) . "</h2>\n" |
1370 | | - . '<br /><table border="1" cellspacing="1" cellpadding="2">' |
1371 | | - . '<tr>' . wfTaskExtensionGetTableHeader( true ) . '</tr>' |
1372 | | - ; |
1373 | | - foreach( $tasks as $task ) { |
1374 | | - $page_title = $this->get_title_from_task( $task->task_id, $task ); |
1375 | | - $returnto = $wgTitle->getFullURL( 'mode=myassignments' ); |
1376 | | - $out .= $this->get_task_table_row( $task, $page_title, true, $returnto ); |
1377 | | - } |
1378 | | - $out.= '</table>'; |
1379 | | - } |
1380 | | - } else { # default |
1381 | | - $res = $dbr->select( |
1382 | | - /* FROM */ 'tasks', |
1383 | | - /* SELECT */ ' COUNT(task_id) AS num', |
1384 | | - /* WHERE */ array( 'task_user_assigned' => $wgUser->getId() ), |
1385 | | - /* FNAME */ $fname |
1386 | | - ); |
1387 | | - $tasks = array(); |
1388 | | - $data = $dbr->fetchObject( $res ); |
1389 | | - $dbr->freeResult( $res ); |
1390 | | - if( !isset ( $data ) || !isset ( $data->num ) ) { |
1391 | | - # Paranoia dummy |
1392 | | - $data->num = 0; |
1393 | | - } |
1394 | | - |
1395 | | - $specialTasks = SpecialPage::getTitleFor( 'Tasks' ); |
1396 | | - $link = $skin->makeLinkObj( $specialTasks, |
1397 | | - wfMsgHTML( 'tasks_link_your_assignments' ), 'mode=myassignments' ); |
1398 | | - $out .= '<p>'; |
1399 | | - if( $data->num == 0 ) { |
1400 | | - $out .= wfMsgHTML( 'tasks_you_have_no_assignments' ) . '.' ; |
1401 | | - } else { |
1402 | | - $out .= wfMsgExt( 'tasks_see_your_assignments', |
1403 | | - array( 'escape', 'parsemag' ), |
1404 | | - $wgLang->formatNum( $data->num ), $link ) ; |
1405 | | - } |
1406 | | - $out .= '</p>'; |
1407 | | - |
1408 | | - } |
1409 | | - } |
1410 | | - |
1411 | | - # Read former form |
1412 | | - $task_type = array(); |
1413 | | - $status_type = array( 1 => 1 ) ; # Default : open tasks |
1414 | | - if( isset( $_POST['task_type'] ) ) { |
1415 | | - $task_type = $_POST['task_type']; |
1416 | | - } |
1417 | | - if( isset( $_POST['status_type'] ) ) { |
1418 | | - $status_type = $_POST['status_type']; |
1419 | | - } |
1420 | | - $ascending = $wgRequest->getCheck( 'ascending' ); |
1421 | | - |
1422 | | - $get_task_type = $wgRequest->getInt( 'task_type' , 0 ) ; |
1423 | | - if ( count ( $task_type ) == 0 && $get_task_type > 0 ) { |
1424 | | - $task_type = array() ; |
1425 | | - $task_type[$get_task_type] = 1 ; |
1426 | | - } |
1427 | | - |
1428 | | - if( !is_array( $status_type ) ) { |
1429 | | - return; |
1430 | | - } |
1431 | | - if( !is_array( $task_type ) ) { |
1432 | | - return; |
1433 | | - } |
1434 | | - |
1435 | | - $out .= '<form method="post" action="' . $wgTitle->escapeLocalURL() . '">'; |
1436 | | - |
1437 | | - # Search results |
1438 | | - if( $wgRequest->getVal( 'doit' ) . $wgRequest->getVal( 'prev' ) . $wgRequest->getVal( 'next' ) != "" || $get_task_type > 0 ) { |
1439 | | - $this->pagemode = 'search' ; |
1440 | | - $search_tasks = array_keys( $task_type ); |
1441 | | - if( count( $task_type ) == 0 ) { |
1442 | | - # No choice => search all |
1443 | | - $search_tasks = array_keys( $this->task_types ); |
1444 | | - } |
1445 | | - $search_status = array_keys( $status_type ); |
1446 | | - if( count( $search_status ) == 0 ) { |
1447 | | - # No choice => search all |
1448 | | - $search_status = array_keys( $this->status_types ); |
1449 | | - } |
1450 | | - |
1451 | | - $limit = intval( wfMsg( 'tasks_search_limit' ) ); |
1452 | | - $offset = $wgRequest->getInt( 'offset', 0 ); |
1453 | | - if( $wgRequest->getCheck( 'next' ) ) { |
1454 | | - $offset += $limit; |
1455 | | - } |
1456 | | - if( $wgRequest->getCheck( 'prev' ) && $offset >= $limit ) { |
1457 | | - $offset -= $limit; |
1458 | | - } |
1459 | | - |
1460 | | - # Search |
1461 | | - $conds = array( |
1462 | | - 'task_type' => $search_tasks, |
1463 | | - 'task_status' => $search_status, |
1464 | | - ); |
1465 | | - $options = array( |
1466 | | - 'LIMIT' => $limit, |
1467 | | - 'OFFSET' => $offset, |
1468 | | - 'ORDER BY' => 'task_timestamp' . ( $ascending == '1' ? ' DESC' : '' ), |
1469 | | - ); |
1470 | | - $res = $dbr->select( |
1471 | | - /* FROM */ 'tasks', |
1472 | | - /* SELECT */ '*', |
1473 | | - /* WHERE */ $conds, |
1474 | | - /* FNAME */ $fname, |
1475 | | - /* OPTIONS */$options |
1476 | | - ); |
1477 | | - $tasks = array(); |
1478 | | - while( $line = $dbr->fetchObject( $res ) ) { |
1479 | | - $tasks[] = $line; |
1480 | | - } |
1481 | | - $dbr->freeResult( $res ); |
1482 | | - |
1483 | | - if( count( $tasks ) > 0 ) { |
1484 | | - |
1485 | | - $out .= '<h2>' . wfMsgHTML( 'tasks_search_results' ) . "</h2>\n"; |
1486 | | - |
1487 | | - # Last/next form |
1488 | | - |
1489 | | - if( $offset >= $limit ) { |
1490 | | - $out .= '<input type="submit" name="prev" value="' |
1491 | | - . wfMsgHTML('tasks_previous') . '" /> ' ; |
1492 | | - } |
1493 | | - $out .= ($offset + 1) . ' .. ' . ($offset + count( $tasks )) . ' '; |
1494 | | - |
1495 | | - if( count( $tasks ) >= $limit ) { |
1496 | | - $out .= '<input type="submit" name="next" value="' . |
1497 | | - wfMsgHTML( 'tasks_next' ) . '" />' ; |
1498 | | - } |
1499 | | - |
1500 | | - $out .= '<input type="hidden" name="offset" value="' . $offset . '" />' |
1501 | | - . '<br /><table border="1" cellspacing="1" cellpadding="2">' |
1502 | | - . '<tr>' . wfTaskExtensionGetTableHeader( true ) . '</tr>' |
1503 | | - ; |
1504 | | - |
1505 | | - $returnto = $wgTitle->getFullURL(); # Return to this page |
1506 | | - |
1507 | | - foreach( $tasks as $task ) { |
1508 | | - $page_title = $this->get_title_from_task( $task->task_id, $task ); |
1509 | | - $out .= $this->get_task_table_row( $task, $page_title, true, $returnto ); |
1510 | | - } |
1511 | | - $out .= '</table>'; |
1512 | | - } |
1513 | | - } |
1514 | | - |
1515 | | - # Search form |
1516 | | - $out .= '<h2>' . wfMsgHTML( 'tasks_search_form_title' ) . '</h2>' |
1517 | | - . '<table border="0">' |
1518 | | - . '<tr><th align="left">' . wfMsgHTML( 'tasks_search_tasks' ) . '</th>' |
1519 | | - . '<td>' |
1520 | | - ; |
1521 | | - foreach( $this->task_types as $k => $v ) { |
1522 | | - $out .= $this->checkbox( "task_type[$k]", isset( $task_type[$k] ) ); |
1523 | | - $out .= $this->get_type_html( $v ) . " "; |
1524 | | - } |
1525 | | - $out .= wfMsgHTML( 'tasks_search_no_tasks_chosen_note' ); |
1526 | | - $out .= '</td></tr>'; |
1527 | | - |
1528 | | - $out .= '<tr><th align="left">' . wfMsgHTML( 'tasks_search_status' ) . '</th>'; |
1529 | | - $out .= '<td>'; |
1530 | | - foreach( $this->status_types as $k => $v ) { |
1531 | | - $out .= $this->checkbox( "status_type[$k]", isset( $status_type[$k] ) ); |
1532 | | - $out .= wfMsgHTML( 'tasks_status_' . $v ) . " "; |
1533 | | - } |
1534 | | - $out .= "</td></tr>\n<tr><th>"; |
1535 | | - $out .= wfMsgHTML('tasks_sort') . "</th><td>"; |
1536 | | - $out .= $this->checkbox( 'ascending', $ascending ); |
1537 | | - $out .= wfMsgHTML( 'tasks_ascending' ); |
1538 | | - $out .= "</td></tr></table>"; |
1539 | | - |
1540 | | - $out .= '<input type="submit" name="doit" value="' . wfMsgHTML( 'search' ) . '" />'; |
1541 | | - $out .= '</form>'; |
1542 | | - |
1543 | | - # and ... out! |
1544 | | - $wgOut->addLink(array( |
1545 | | - 'rel' => 'stylesheet', |
1546 | | - 'type' => 'text/css', |
1547 | | - 'media' => 'screen', |
1548 | | - 'href' => TASKS_CSS, |
1549 | | - )); |
1550 | | - $this->setHeaders(); |
1551 | | - $wgOut->addHTML( $out ); |
1552 | | - } |
1553 | | - |
1554 | | - /** |
1555 | | - * Format an HTML checkbox |
1556 | | - * @param string $name |
1557 | | - * @param bool $checked |
1558 | | - * @return string |
1559 | | - * @access private |
1560 | | - */ |
1561 | | - function checkbox( $name, $checked ) { |
1562 | | - $attribs = array( |
1563 | | - 'type' => 'checkbox', |
1564 | | - 'name' => $name, |
1565 | | - 'value' => '1' ); |
1566 | | - if( $checked ) { |
1567 | | - $attribs['checked'] = 'checked'; |
1568 | | - } |
1569 | | - return Xml::element( 'input', $attribs ); |
1570 | | - } |
1571 | | - } # end of class |
1572 | | - |
1573 | | - SpecialPage::addPage( new SpecialTasks ); |
1574 | | -} |
1575 | | - |
1576 | | -/** |
1577 | | - * Ensure the parser tests don't die; the table must |
1578 | | - * be duplicated to let the save hooks work. |
1579 | | - */ |
1580 | | -function wfTasksTestTables( &$tables ) { |
1581 | | - $tables[] = 'tasks'; |
1582 | | - return true; |
1583 | | -} |
1584 | | - |
1585 | | -/** |
1586 | | - * Hook for user preferences |
1587 | | - */ |
1588 | | -function wfTasksOnGetPreferences( $user, &$preferences ) { |
1589 | | - $preferences['show_task_comments'] = |
1590 | | - array( |
1591 | | - 'type' => 'toggle', |
1592 | | - 'section' => 'misc', |
1593 | | - 'label-message' => 'tasks-pref-showtaskcomments', |
1594 | | - ); |
1595 | | - |
1596 | | - return true; |
1597 | | -} |
Index: trunk/extensions/Tasks/Tasks.hooks.php |
— | — | @@ -0,0 +1,346 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class TasksHooks { |
| 5 | + |
| 6 | + /** |
| 7 | + * Display header on 'Task:' pages (dummy hook for edit pages) |
| 8 | + * @param EditPage $editPage |
| 9 | + */ |
| 10 | + public static function onEditPageShowEditFormInitial( &$editPage ) { # Checked for HTML and MySQL insertion attacks |
| 11 | + return self::onArticleViewHeader( $editPage->getArticle() ); |
| 12 | + } |
| 13 | + |
| 14 | + /** |
| 15 | + * Display header on 'Task:' pages |
| 16 | + * @param Article $article |
| 17 | + */ |
| 18 | + public static function onArticleViewHeader( &$article ) { # Checked for HTML and MySQL insertion attacks |
| 19 | + global $wgTasksNamespace, $wgOut, $wgUser; |
| 20 | + |
| 21 | + $title = $article->getTitle(); |
| 22 | + $ns = $title->getNamespace(); |
| 23 | + if( $ns != $wgTasksNamespace && $ns != $wgTasksNamespace+1 ) { |
| 24 | + // Show sign, if any, then we can leave |
| 25 | + self::headerSign( $title ); |
| 26 | + return true; |
| 27 | + } |
| 28 | + |
| 29 | + $subtitle = ''; |
| 30 | + |
| 31 | + if( ctype_digit( $title->getText() ) ) { |
| 32 | + // Page title format 'Task:123', suggested by Rowan Collins |
| 33 | + $taskid = intval( $title->getText() ); |
| 34 | + } else { |
| 35 | + // Invalid page title; can't extract the task id |
| 36 | + return true; |
| 37 | + } |
| 38 | + |
| 39 | + wfLoadExtensionMessages('Tasks'); |
| 40 | + $st = new SpecialTasks(); |
| 41 | + $task = ''; |
| 42 | + $page_title = $st->get_title_from_task( $taskid, $task ); |
| 43 | + if( $task == '' ) { |
| 44 | + # No such task |
| 45 | + return true; |
| 46 | + } |
| 47 | + |
| 48 | + $sk = $wgUser->getSkin(); |
| 49 | + $returnto = $title->getFullURL(); |
| 50 | + $link1 = $sk->makeLinkObj( $page_title ); |
| 51 | + $link2 = $sk->makeLinkObj( $page_title, wfMsgHTML( 'tasks_here' ), 'action=tasks' ); |
| 52 | + $subtitle .= '<div id="task_header">' . wfMsgForContent( 'tasks_discussion_page_for', $link1, $link2 ) . "</div>\n" ; |
| 53 | + $subtitle .= '<table border="1" cellspacing="1" cellpadding="2" id="task_header_table">' . |
| 54 | + '<tr>' . SpecialTasks::getTableHeader( false ) . "</tr>\n"; |
| 55 | + $subtitle .= $st->get_task_table_row( $task, $page_title, false, $returnto ); |
| 56 | + $subtitle .= "</table>\n"; |
| 57 | + |
| 58 | + $subtitle = $wgOut->getSubtitle() . '<br />' . $subtitle; |
| 59 | + $wgOut->setSubtitle( $subtitle ); |
| 60 | + return true; |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * Display header signs for "notable" tasks |
| 65 | + */ |
| 66 | + private static function headerSign( $title ) { |
| 67 | + global $wgOut; |
| 68 | + |
| 69 | + if( $title->isTalkPage() ) { |
| 70 | + # No talk pages please |
| 71 | + return true; |
| 72 | + } |
| 73 | + if( $title->getNamespace() < 0 ) { |
| 74 | + # No special pages please |
| 75 | + return true; |
| 76 | + } |
| 77 | + |
| 78 | + wfLoadExtensionMessages('Tasks'); |
| 79 | + $st = new SpecialTasks(); |
| 80 | + $tasks = $st->get_open_task_list( $title, true ); |
| 81 | + if( count( $tasks ) == 0 ) { |
| 82 | + # No tasks |
| 83 | + return true; |
| 84 | + } |
| 85 | + |
| 86 | + $out = ''; |
| 87 | + $max = 0; |
| 88 | + foreach( $tasks as $task ) { |
| 89 | + $ttype = $st->get_task_type( $task->task_type ); |
| 90 | + $msg = wfMsgForContent( 'tasks_sign_' . $ttype ); |
| 91 | + if( $msg == '' ) { |
| 92 | + # No sign defined for this |
| 93 | + continue; |
| 94 | + } |
| 95 | + |
| 96 | + $order = $st->get_task_order( $ttype ); |
| 97 | + if( $order > $max ) { |
| 98 | + $max = $order; |
| 99 | + $max_type = $ttype; |
| 100 | + $max_task = $task; |
| 101 | + $max_msg = $msg; |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + if( $max == 0 ) { |
| 106 | + # Nothing for you to see here, please move along |
| 107 | + return; |
| 108 | + } |
| 109 | + |
| 110 | + // Wiki-safe output |
| 111 | + $out = $wgOut->parse( '<div id="task_sign">' . $max_msg . '</div>' ); |
| 112 | + |
| 113 | + $subtitle = $wgOut->getSubtitle() . '<br />' . $out; |
| 114 | + $wgOut->setSubtitle( $subtitle ); |
| 115 | + } |
| 116 | + |
| 117 | + /** |
| 118 | + * Display in sidebar |
| 119 | + */ |
| 120 | + public static function onSkinTemplateToolboxEnd( &$tpl ) { # Checked for HTML and MySQL insertion attacks |
| 121 | + global $wgTitle; |
| 122 | + if( $wgTitle->isTalkPage() ) { |
| 123 | + # No talk pages please |
| 124 | + return true; |
| 125 | + } |
| 126 | + if( $wgTitle->getNamespace() < 0 ) { |
| 127 | + # No special pages please |
| 128 | + return true; |
| 129 | + } |
| 130 | + |
| 131 | + wfLoadExtensionMessages('Tasks'); |
| 132 | + $st = new SpecialTasks; |
| 133 | + $tasks = $st->get_open_task_list( $wgTitle, true ); |
| 134 | + if( count( $tasks ) == 0 ) { |
| 135 | + # No tasks |
| 136 | + return true; |
| 137 | + } |
| 138 | + |
| 139 | +?> |
| 140 | + |
| 141 | + </ul> |
| 142 | + </div> |
| 143 | + </div> |
| 144 | + <div class="portlet" id="p-tasks"> |
| 145 | + <h5><?php $tpl->msg('tasks_sidebar_title') ?></h5> |
| 146 | + <div class="pBody"> |
| 147 | + <ul> |
| 148 | +<?php |
| 149 | + foreach( $tasks as $task ) { |
| 150 | + $ttype = $st->get_task_type( $task->task_type ); |
| 151 | +?> |
| 152 | + <li id="task_sidebar_<?php echo $ttype ?>"> |
| 153 | + <a href="<?php |
| 154 | + $nt = $st->get_task_discussion_page( $task ); |
| 155 | + echo $nt->escapeLocalURL(); |
| 156 | + ?>"><?php |
| 157 | + echo $st->get_type_html( $ttype ); |
| 158 | + ?></a><?php |
| 159 | + if( $task->task_user_assigned != 0 ) { |
| 160 | + echo ' ' . wfMsgHTML( 'tasks_task_is_assigned' ); |
| 161 | + } |
| 162 | + ?></li> |
| 163 | +<?php |
| 164 | + |
| 165 | + } |
| 166 | + return true; |
| 167 | + } |
| 168 | + |
| 169 | + /** |
| 170 | + * Catch page movement, fix internal task_page_title values |
| 171 | + */ |
| 172 | + public static function onSpecialMovepageAfterMove( &$special_page, &$old_title, &$new_title ) { # Checked for HTML and MySQL insertion attacks |
| 173 | + if( $new_title->isTalkPage() ) { |
| 174 | + # No tasks for talk pages, no need to bother the database... |
| 175 | + return false; |
| 176 | + } |
| 177 | + |
| 178 | + wfLoadExtensionMessages('Tasks'); |
| 179 | + |
| 180 | + $st = new SpecialTasks; |
| 181 | + $st->rename_tasks_page( $old_title, $new_title ); |
| 182 | + return false; |
| 183 | + } |
| 184 | + |
| 185 | + |
| 186 | + /** |
| 187 | + * Catch article deletion, remove all tasks |
| 188 | + */ |
| 189 | + public static function onArticleDeleteComplete( &$article, &$user, $reason ) { # Checked for HTML and MySQL insertion attacks |
| 190 | + # return false ; # Uncomment this line to prevent deletion of tasks upon deletion of article |
| 191 | + wfLoadExtensionMessages('Tasks'); |
| 192 | + $t = $article->getTitle(); |
| 193 | + if( $t->isTalkPage() ) { |
| 194 | + # No tasks for talk pages, no need to bother the database... |
| 195 | + return false; |
| 196 | + } |
| 197 | + |
| 198 | + $st = new SpecialTasks; |
| 199 | + $st->delete_all_tasks( $t ); |
| 200 | + return false; |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * Catch article creation, to close "create" tasks, and optionally |
| 205 | + * open a default task on new page creation. |
| 206 | + * |
| 207 | + * @param Article $article |
| 208 | + * @param User $user |
| 209 | + * @param string $text |
| 210 | + * @param string $summary |
| 211 | + * @param bool $isminor |
| 212 | + * @param bool $watchthis |
| 213 | + * @param mixed $something WHAT THE HELL IS THIS |
| 214 | + * @return bool true to continue, false to cancel operation <- WHAT THE HELL |
| 215 | + */ |
| 216 | + public static function onArticleInsertComplete( &$article, &$user, $text, $summary, $isminor, $watchthis, $something ) { # Checked for HTML and MySQL insertion attacks |
| 217 | + global $wgUser; |
| 218 | + wfLoadExtensionMessages('Tasks'); |
| 219 | + $t = $article->getTitle(); |
| 220 | + if( $t->isTalkPage() ) { |
| 221 | + # No tasks for talk pages, no need to bother the database... |
| 222 | + return false; |
| 223 | + } |
| 224 | + |
| 225 | + $st = new SpecialTasks; |
| 226 | + $tasks = $st->get_tasks_for_page( $t, true ); |
| 227 | + |
| 228 | + # Mark creation tasks as closed |
| 229 | + foreach( $tasks as $task ) { |
| 230 | + if( !$st->is_creation_task( $task->task_type ) ) { |
| 231 | + # Not a "create" task |
| 232 | + continue; |
| 233 | + } |
| 234 | + if( $st->is_closed( $task->task_status ) ) { |
| 235 | + # Not open |
| 236 | + continue; |
| 237 | + } |
| 238 | + $st->change_task_status( $task->task_id, MW_TASK_CLOSED ); |
| 239 | + $st->set_new_article_id( $t ); |
| 240 | + |
| 241 | + $id = $t->getArticleId(); |
| 242 | + # Nothing more to do |
| 243 | + break; |
| 244 | + } |
| 245 | + |
| 246 | + # OPTIONALLY create a new task |
| 247 | + $on_create = $st->get_task_num( wfMsgForContent( 'tasks_event_on_creation' ) ); |
| 248 | + $on_create_anon = $st->get_task_num( wfMsgForContent( 'tasks_event_on_creation_anon' ) ); |
| 249 | + $add_task = MW_TASK_INVALID; |
| 250 | + if( $wgUser->isAnon() && $on_create_anon != MW_TASK_INVALID ) { |
| 251 | + $add_task = $on_create_anon; |
| 252 | + } elseif( $wgUser->isLoggedIn() && $on_create != MW_TASK_INVALID ) { |
| 253 | + $add_task = $on_create; |
| 254 | + } |
| 255 | + if( $add_task != MW_TASK_INVALID ) { |
| 256 | + $comment = htmlspecialchars( wfMsgForContent( 'tasks_on_creation_comment' ) ); |
| 257 | + $st->add_new_task( $t, $comment, $add_task ) ; |
| 258 | + } |
| 259 | + |
| 260 | + return false; |
| 261 | + } |
| 262 | + |
| 263 | + /** |
| 264 | + * Prevents other tabs shown as active |
| 265 | + */ |
| 266 | + public static function onSkinTemplatePreventOtherActiveTabs( &$skin, &$prevent_active_tabs ) { # Checked for HTML and MySQL insertion attacks |
| 267 | + global $wgRequest; |
| 268 | + |
| 269 | + $prevent_active_tabs = ( $wgRequest->getVal( 'action', 'view' ) == 'tasks' ); |
| 270 | + return true; |
| 271 | + } |
| 272 | + |
| 273 | + /** |
| 274 | + * Show the tab |
| 275 | + * @param SkinTemplate $skin |
| 276 | + * @param array $content_actions |
| 277 | + * @return bool true to continue running other hooks, false to abort operation |
| 278 | + */ |
| 279 | + public static function onSkinTemplateTabs( $skin, &$content_actions ) { # Checked for HTML and MySQL insertion attacks |
| 280 | + global $wgTitle, $wgRequest; |
| 281 | + |
| 282 | + if( $wgTitle->isTalkPage() ) { |
| 283 | + # No talk pages please |
| 284 | + return true; |
| 285 | + } |
| 286 | + if( $wgTitle->getNamespace() < 0 ) { |
| 287 | + # No special pages please |
| 288 | + return true; |
| 289 | + } |
| 290 | + |
| 291 | + wfLoadExtensionMessages('Tasks'); |
| 292 | + $content_actions['tasks'] = array( |
| 293 | + 'class' => ($wgRequest->getVal( 'action', 'view' ) == 'tasks') ? 'selected' : false, |
| 294 | + 'text' => wfMsgHTML('tasks_tab'), |
| 295 | + 'href' => $wgTitle->getLocalUrl( 'action=tasks' ) |
| 296 | + ); |
| 297 | + return true; |
| 298 | + } |
| 299 | + |
| 300 | + /** |
| 301 | + * This is where the action is :-) |
| 302 | + */ |
| 303 | + public static function onUnknownAction( $action, $article ) { # Checked for HTML and MySQL insertion attacks |
| 304 | + if( $action != 'tasks' ) { |
| 305 | + # Not my kind of action! |
| 306 | + return true; |
| 307 | + } |
| 308 | + |
| 309 | + wfLoadExtensionMessages('Tasks'); |
| 310 | + |
| 311 | + $t = new SpecialTasks; |
| 312 | + return $t->page_management( $article->getTitle() ); |
| 313 | + } |
| 314 | + |
| 315 | + /** |
| 316 | + * Ensure the parser tests don't die; the table must |
| 317 | + * be duplicated to let the save hooks work. |
| 318 | + */ |
| 319 | + public static function onParserTestTables( &$tables ) { |
| 320 | + $tables[] = 'tasks'; |
| 321 | + return true; |
| 322 | + } |
| 323 | + |
| 324 | + /** |
| 325 | + * Ensure the parser tests don't die; the table must |
| 326 | + * be duplicated to let the save hooks work. |
| 327 | + */ |
| 328 | + public static function onLoadExtensionSchemaUpdates( $updater ) { |
| 329 | + $updater->addExtensionTable( 'tasks', dirname( __FILE__ ) . '/tasks.sql' ); |
| 330 | + return true; |
| 331 | + } |
| 332 | + |
| 333 | + /** |
| 334 | + * Hook for user preferences |
| 335 | + */ |
| 336 | + public static function onGetPreferences( $user, &$preferences ) { |
| 337 | + $preferences['show_task_comments'] = |
| 338 | + array( |
| 339 | + 'type' => 'toggle', |
| 340 | + 'section' => 'misc', |
| 341 | + 'label-message' => 'tasks-pref-showtaskcomments', |
| 342 | + ); |
| 343 | + |
| 344 | + return true; |
| 345 | + } |
| 346 | + |
| 347 | +} |
Property changes on: trunk/extensions/Tasks/Tasks.hooks.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 348 | + native |