r79656 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r79655‎ | r79656 | r79657 >
Date:19:18, 5 January 2011
Author:ialex
Status:deferred
Tags:
Comment:
Big code cleanup:
* Moved special page class, hooks and sql to their own files
* Use $wgSpecialPages to register the special page, not the pre-1.6 method
* Use the new method tho add log type and action
* Added a LoadExtensionSchemaUpdates hook to add the table
* And some other stuff
Modified paths:
  • /trunk/extensions/Tasks/Tasks.body.php (added) (history)
  • /trunk/extensions/Tasks/Tasks.hooks.php (added) (history)
  • /trunk/extensions/Tasks/Tasks.php (modified) (history)
  • /trunk/extensions/Tasks/tasks.sql (added) (history)

Diff [purge]

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
119 + 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
11094 + native
Index: trunk/extensions/Tasks/Tasks.php
@@ -1,9 +1,8 @@
22 <?php
3 -// FIXME: needs to be split in different files (Special page, possibly others like hooks).
43 /*
54 To activate, put something like this in your LocalSettings.php:
65 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" );
87 $wgTasksNamespace = 200;
98 $wgExtraNamespaces[$wgTasksNamespace] = "Task";
109 $wgExtraNamespaces[$wgTasksNamespace+1] = "Task_Talk";
@@ -11,27 +10,6 @@
1211 The TASKS_CSS define is only needed if you use a 'non-standard' extensions
1312 directory.
1413
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 -
3614 Known bugs:
3715 * FIXME: sidebar task list for Monobook only?
3816
@@ -64,8 +42,8 @@
6543 /**
6644 * Global variable to cache tasks for a page, so sidebar and header check only have to read them once
6745 */
68 -$wgTaskExtensionTasksCache = array () ;
69 -$wgTaskExtensionTasksCachedTitle = '' ;
 46+$wgTaskExtensionTasksCache = array ();
 47+$wgTaskExtensionTasksCachedTitle = '';
7048
7149 # Integrating into the MediaWiki environment
7250 $wgExtensionCredits['Tasks'][] = array(
@@ -76,1521 +54,32 @@
7755 'url' => 'http://www.mediawiki.org/wiki/Extension:Tasks',
7856 );
7957
80 -$wgExtensionFunctions[] = 'wfTasksExtension';
8158 $wgExtensionMessagesFiles['Tasks'] = dirname( __FILE__ ) . '/Tasks.i18n.php';
8259
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';
9562
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';
10165
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';
10879
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';
11385
114 -function wfTasksAddLogHeader( &$headers ) { # Checked for HTML and MySQL insertion attacks
115 - $headers['tasks'] = 'tasks_logpagetext';
116 - return true;
117 -}
11886
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
1348 + native

Status & tagging log