SemanticMediaWiki /
SemanticTasks
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace ST; |
||
| 4 | |||
| 5 | use Content; |
||
| 6 | use ContentHandler; |
||
| 7 | use Exception; |
||
| 8 | use IContextSource; |
||
| 9 | use Language; |
||
| 10 | use MediaWiki\Diff\ComplexityException; |
||
| 11 | use MWException; |
||
| 12 | use ParserOutput; |
||
| 13 | use SMW\ApplicationFactory; |
||
| 14 | use SMW\DIWikiPage; |
||
| 15 | use SMWDataItem; |
||
| 16 | use SMWPrintRequest; |
||
| 17 | use Title; |
||
| 18 | use User; |
||
| 19 | use WikiPage; |
||
| 20 | |||
| 21 | if ( !defined( 'MEDIAWIKI' ) ) { |
||
| 22 | echo 'Not a valid entry point'; |
||
| 23 | exit( 1 ); |
||
| 24 | } |
||
| 25 | |||
| 26 | if ( !defined( 'SMW_VERSION' ) ) { |
||
| 27 | echo 'This extension requires Semantic MediaWiki to be installed.'; |
||
| 28 | exit( 1 ); |
||
| 29 | } |
||
| 30 | |||
| 31 | // constants for message type |
||
| 32 | if ( !defined( 'ST_NEWTASK' ) ) { |
||
| 33 | define( 'ST_NEWTASK', 0 ); |
||
| 34 | define( 'ST_UPDATE', 1 ); |
||
| 35 | define( 'ST_ASSIGNED', 2 ); |
||
| 36 | define( 'ST_CLOSED', 3 ); |
||
| 37 | define( 'ST_UNASSIGNED', 4 ); |
||
| 38 | } |
||
| 39 | |||
| 40 | /** |
||
| 41 | * This class handles the creation and sending of notification emails. |
||
| 42 | */ |
||
| 43 | class SemanticTasksMailer { |
||
| 44 | |||
| 45 | private static $user_mailer; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * Mails the assignees when the task is modified |
||
| 49 | * |
||
| 50 | * @param Assignees $assignees |
||
| 51 | * @param WikiPage $article |
||
| 52 | * @param User $current_user |
||
| 53 | * @param Content $text |
||
| 54 | * @param string $summary Unused |
||
| 55 | * @param bool $minoredit |
||
| 56 | * @param null $watchthis Unused |
||
| 57 | * @param null $sectionanchor Unused |
||
| 58 | * @param $flags |
||
| 59 | * @return boolean |
||
| 60 | * @throws ComplexityException |
||
| 61 | * @throws MWException |
||
| 62 | */ |
||
| 63 | public static function mailAssigneesUpdatedTask( Assignees $assignees, WikiPage $article, User $current_user, $text, |
||
| 64 | $summary, $minoredit, $watchthis, $sectionanchor, $flags, $revision ) { |
||
|
0 ignored issues
–
show
|
|||
| 65 | if ( $minoredit ) { |
||
| 66 | return true; |
||
| 67 | } |
||
| 68 | $status = ST_UPDATE; |
||
| 69 | if ( ( $flags & EDIT_NEW ) && !$article->getTitle()->isTalkPage() ) { |
||
| 70 | $status = ST_NEWTASK; |
||
| 71 | } |
||
| 72 | $assignedToParserOutput = self::getAssignedUserFromParserOutput($article, $revision, $current_user); |
||
| 73 | |||
| 74 | return self::mailAssignees( $article, $text, $current_user, $status, $assignees, $assignedToParserOutput ); |
||
| 75 | } |
||
| 76 | |||
| 77 | // todo: this could replace Assignees->getAssignees(...). |
||
| 78 | private static function getAssignedUserFromParserOutput(WikiPage $article, $revision, User $current_user) { |
||
| 79 | $smwFactory = ApplicationFactory::getInstance(); |
||
| 80 | $mwCollaboratorFactory = $smwFactory->newMwCollaboratorFactory(); |
||
| 81 | $editInfo = $mwCollaboratorFactory->newEditInfo( |
||
| 82 | $article, |
||
| 83 | $revision, |
||
| 84 | $current_user |
||
| 85 | ); |
||
| 86 | $editInfo->fetchEditInfo(); |
||
| 87 | $parserOutput = $editInfo->getOutput(); |
||
| 88 | |||
| 89 | if ( !$parserOutput instanceof ParserOutput ) { |
||
|
0 ignored issues
–
show
|
|||
| 90 | return []; |
||
| 91 | } |
||
| 92 | |||
| 93 | global $stgPropertyAssignedTo; |
||
| 94 | $stgPropertyAssignedToString = str_replace(' ', '_', $stgPropertyAssignedTo); |
||
| 95 | $property = new \SMW\DIProperty($stgPropertyAssignedToString, false); |
||
| 96 | |||
| 97 | /** @var $semanticData \SMW\SemanticData */ |
||
| 98 | $semanticData = $parserOutput->getExtensionData('smwdata'); |
||
| 99 | $assigneesPropValues = $semanticData->getPropertyValues($property); |
||
| 100 | // todo: maybe there should be a check if these pages are userpages. |
||
| 101 | $assigneeList = array_map(function(DIWikiPage $assignePropVal) { |
||
| 102 | return $assignePropVal->getTitle()->getText(); |
||
| 103 | }, $assigneesPropValues); |
||
| 104 | |||
| 105 | |||
| 106 | return $assigneeList; |
||
| 107 | } |
||
| 108 | |||
| 109 | /** |
||
| 110 | * |
||
| 111 | * @param WikiPage $article |
||
| 112 | * @param Content $content |
||
| 113 | * @param User $user |
||
| 114 | * @param integer $status |
||
| 115 | * @param Assignees $assignees |
||
| 116 | * @return boolean |
||
| 117 | * @throws ComplexityException |
||
| 118 | * @throws MWException |
||
| 119 | * @global boolean $wgSemanticTasksNotifyIfUnassigned |
||
| 120 | */ |
||
| 121 | static function mailAssignees( WikiPage $article, Content $content, User $user, $status, Assignees $assignees, |
||
| 122 | $assignedToParserOutput ) { |
||
| 123 | $text = ContentHandler::getContentText( $content ); |
||
| 124 | $title = $article->getTitle(); |
||
| 125 | |||
| 126 | // Notify those unassigned from this task |
||
| 127 | global $wgSemanticTasksNotifyIfUnassigned; |
||
| 128 | if ( $wgSemanticTasksNotifyIfUnassigned ) { |
||
| 129 | $removedAssignees = $assignees->getRemovedAssignees( $article ); |
||
| 130 | $removedAssignees = Assignees::getAssigneeAddresses( $removedAssignees ); |
||
| 131 | self::mailNotification( $removedAssignees, $text, $title, $user, ST_UNASSIGNED ); |
||
| 132 | } |
||
| 133 | |||
| 134 | // Send notification of an assigned task to assignees |
||
| 135 | // Treat task as new |
||
| 136 | $newAssignees = $assignees->getNewAssignees( $article ); |
||
| 137 | $newAssignees = Assignees::getAssigneeAddresses( $newAssignees ); |
||
| 138 | self::mailNotification( $newAssignees, $text, $title, $user, ST_ASSIGNED ); |
||
| 139 | |||
| 140 | $copies = $assignees->getCurrentCarbonCopy( $article ); |
||
| 141 | $currentStatus = $assignees->getCurrentStatus( $article ); |
||
| 142 | $oldStatus = $assignees->getSavedStatus(); |
||
| 143 | if ( $currentStatus === "Closed" && $oldStatus !== "Closed" ) { |
||
| 144 | $close_mailto = Assignees::getAssigneeAddresses( $copies ); |
||
| 145 | self::mailNotification( $close_mailto, $text, $title, $user, ST_CLOSED ); |
||
| 146 | } |
||
| 147 | |||
| 148 | $currentAssignees = $assignees->getCurrentAssignees( $article ); |
||
| 149 | |||
| 150 | // Only send group notifications on new tasks |
||
| 151 | $groups = array(); |
||
| 152 | if ( $status === ST_NEWTASK ) { |
||
| 153 | $groups = $assignees->getGroupAssignees( $article ); |
||
| 154 | |||
| 155 | // if this is a new task and there are no $currentAssignees but there are $assignedToParserOutput |
||
| 156 | // then this is probably a new page and sending ST_ASSIGNED notifications didn't work and needs to be retried. |
||
| 157 | if ( empty( $currentAssignees ) ) { |
||
| 158 | $mails = Assignees::getAssigneeAddresses( $assignedToParserOutput ); |
||
| 159 | self::mailNotification( $mails, $text, $title, $user, ST_ASSIGNED ); |
||
| 160 | } |
||
| 161 | } |
||
| 162 | |||
| 163 | $mailto = array_merge( $currentAssignees, $copies, $groups ); |
||
| 164 | $mailto = array_unique( $mailto ); |
||
| 165 | $mailto = Assignees::getAssigneeAddresses( $mailto ); |
||
| 166 | |||
| 167 | // Send notifications to assignees, ccs, and groups |
||
| 168 | self::mailNotification( $mailto, $text, $title, $user, $status ); |
||
| 169 | |||
| 170 | return true; |
||
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Sends mail notifications |
||
| 175 | * |
||
| 176 | * @param array $assignees |
||
| 177 | * @param string $text |
||
| 178 | * @param Title $title |
||
| 179 | * @param User $user |
||
| 180 | * @param integer $status |
||
| 181 | * @throws MWException |
||
| 182 | * @throws ComplexityException |
||
| 183 | * @global string $wgSitename |
||
| 184 | */ |
||
| 185 | static function mailNotification( array $assignees, $text, Title $title, User $user, $status ) { |
||
| 186 | global $wgSitename; |
||
| 187 | |||
| 188 | if ( empty( $assignees ) ) { |
||
| 189 | return; |
||
| 190 | } |
||
| 191 | $title_text = $title->getFullText(); |
||
| 192 | $from = new \MailAddress( $user->getEmail(), $user->getName() ); |
||
| 193 | $link = htmlspecialchars( $title->getFullURL() ); |
||
| 194 | |||
| 195 | /** @todo This should probably be refactored */ |
||
| 196 | if ( $status == ST_NEWTASK ) { |
||
| 197 | $subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-newtask' )->text() . ' ' . |
||
| 198 | $title_text; |
||
| 199 | $message = 'semantictasks-newtask-msg'; |
||
| 200 | $body = wfMessage( $message, $title_text )->text() . " " . $link; |
||
| 201 | $body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text; |
||
| 202 | } elseif ( $status == ST_UPDATE ) { |
||
| 203 | $subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskupdated' )->text() . ' ' . |
||
| 204 | $title_text; |
||
| 205 | $message = 'semantictasks-updatedtoyou-msg2'; |
||
| 206 | $body = wfMessage( $message, $title_text )->text() . " " . $link; |
||
| 207 | $body .= "\n \n" . wfMessage( 'semantictasks-diff-message' )->text() . "\n" . |
||
| 208 | self::generateDiffBodyTxt( $title ); |
||
| 209 | } elseif ( $status == ST_CLOSED ) { |
||
| 210 | $subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskclosed' )->text() . ' ' . |
||
| 211 | $title_text; |
||
| 212 | $message = 'semantictasks-taskclosed-msg'; |
||
| 213 | $body = wfMessage( $message, $title_text )->text() . " " . $link; |
||
| 214 | $body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text; |
||
| 215 | } elseif ( $status == ST_UNASSIGNED ) { |
||
| 216 | $subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskunassigned' )->text() . ' ' . |
||
| 217 | $title_text; |
||
| 218 | $message = 'semantictasks-unassignedtoyou-msg2'; |
||
| 219 | $body = wfMessage( $message, $title_text )->text() . " " . $link; |
||
| 220 | $body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text; |
||
| 221 | } else { |
||
| 222 | // status == ASSIGNED |
||
| 223 | $subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskassigned' )->text() . ' ' . |
||
| 224 | $title_text; |
||
| 225 | $message = 'semantictasks-assignedtoyou-msg2'; |
||
| 226 | $body = wfMessage( $message, $title_text )->text() . " " . $link; |
||
| 227 | $body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text; |
||
| 228 | } |
||
| 229 | |||
| 230 | if (!self::$user_mailer) { |
||
| 231 | self::$user_mailer = new \ST\UserMailer(new \UserMailer()); |
||
| 232 | } |
||
| 233 | |||
| 234 | self::$user_mailer->send( $assignees, $from, $subject, $body ); |
||
| 235 | } |
||
| 236 | |||
| 237 | static function setUserMailer(\ST\UserMailer $user_mailer) { |
||
| 238 | self::$user_mailer = $user_mailer; |
||
| 239 | } |
||
| 240 | |||
| 241 | /** |
||
| 242 | * Generates a diff txt |
||
| 243 | * |
||
| 244 | * Code is similar to DifferenceEngine::generateTextDiffBody |
||
| 245 | * @param Title $title |
||
| 246 | * @param IContextSource $context |
||
| 247 | * @return string |
||
| 248 | * @throws ComplexityException |
||
| 249 | * @throws MWException |
||
| 250 | */ |
||
| 251 | static function generateDiffBodyTxt( Title $title, IContextSource $context = null) { |
||
|
0 ignored issues
–
show
|
|||
| 252 | $revision = \Revision::newFromTitle( $title, 0 ); |
||
| 253 | if ($revision === null) { |
||
| 254 | return ''; |
||
| 255 | } |
||
| 256 | /** @todo The first parameter should be a Context. */ |
||
| 257 | $diff = new \DifferenceEngine( $context, $revision->getId(), 'prev' ); |
||
| 258 | // The DifferenceEngine::getDiffBody() method generates html, |
||
| 259 | // so let's generate the txt diff manually: |
||
| 260 | global $wgContLang; |
||
| 261 | $diff->loadText(); |
||
| 262 | $otext = ''; |
||
| 263 | $ntext = ''; |
||
| 264 | if ( version_compare( MW_VERSION, '1.32', '<' ) ) { |
||
| 265 | $otext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mOldContent ) ); |
||
| 266 | $ntext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mNewContent ) ); |
||
| 267 | } else { |
||
| 268 | if ($diff->getOldRevision()) { |
||
| 269 | $otext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getOldRevision()->getContent( 'main' ) ) ); |
||
| 270 | } |
||
| 271 | if ($diff->getNewRevision()) { |
||
| 272 | $ntext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getNewRevision()->getContent( 'main' ) ) ); |
||
| 273 | } |
||
| 274 | } |
||
| 275 | $ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) ); |
||
| 276 | $nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) ); |
||
| 277 | // We use here the php diff engine included in MediaWiki |
||
| 278 | $diffs = new \Diff( $ota, $nta ); |
||
| 279 | // And we ask for a txt formatted diff |
||
| 280 | $formatter = new \UnifiedDiffFormatter(); |
||
| 281 | $diff_text = $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) ); |
||
| 282 | return $diff_text; |
||
| 283 | } |
||
| 284 | |||
| 285 | /** |
||
| 286 | * Run by the maintenance script to remind the assignees |
||
| 287 | * |
||
| 288 | * @return boolean |
||
| 289 | * @throws Exception |
||
| 290 | * @global string $wgSitename |
||
| 291 | * @global Language $wgLang |
||
| 292 | */ |
||
| 293 | static function remindAssignees() { |
||
| 294 | global $wgSitename; |
||
| 295 | global $stgPropertyReminderAt; |
||
| 296 | global $stgPropertyAssignedTo; |
||
| 297 | global $stgPropertyTargetDate; |
||
| 298 | global $stgPropertyStatus; |
||
| 299 | |||
| 300 | # Make this equal to midnight. Rational is that if users set today as the Target date with |
||
| 301 | # reminders set to "0" so that the reminder happens on the deadline, the reminders will go |
||
| 302 | # out even though now it is after the beginning of today and technically past the |
||
| 303 | # target date. |
||
| 304 | $today = wfTimestamp( TS_ISO_8601, strtotime( 'today midnight' ) ); |
||
| 305 | |||
| 306 | # Get tasks where a reminder is called for, whose status is either new or in progress, and |
||
| 307 | # whose target date is in the future. |
||
| 308 | $query_string = "[[$stgPropertyReminderAt::+]][[$stgPropertyStatus::New||In Progress]][[$stgPropertyTargetDate::≥ $today]]"; |
||
| 309 | $properties_to_display = array( $stgPropertyReminderAt, $stgPropertyAssignedTo, $stgPropertyTargetDate ); |
||
| 310 | |||
| 311 | $results = Query::getQueryResults( $query_string, $properties_to_display, true ); |
||
| 312 | if ( empty( $results ) ) { |
||
| 313 | return false; |
||
| 314 | } |
||
| 315 | |||
| 316 | while ( $row = $results->getNext() ) { |
||
| 317 | $task_name = $row[0]->getNextObject()->getTitle(); |
||
| 318 | $subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-reminder' )->text() . $task_name; |
||
| 319 | // The following doesn't work, maybe because we use a cron job. |
||
| 320 | // $link = $task_name->getFullURL(); |
||
| 321 | // So let's do it manually |
||
| 322 | //$link = $wiki_url . $task_name->getPartialURL(); |
||
| 323 | // You know what? Let's try it again. |
||
| 324 | $link = $task_name->getFullURL(); |
||
| 325 | |||
| 326 | $target_date = $row[3]->getNextObject(); |
||
| 327 | $tg_date = new DateTime( $target_date->getShortHTMLText() ); |
||
| 328 | |||
| 329 | while ( $reminder = $row[1]->getNextObject() ) { |
||
| 330 | $remind_me_in = $reminder->getShortHTMLText(); |
||
| 331 | $date = new DateTime( 'today midnight' ); |
||
| 332 | $date->modify( "+$remind_me_in day" ); |
||
| 333 | |||
| 334 | if ( $tg_date === $date ) { |
||
| 335 | global $wgLang; |
||
| 336 | while ( $task_assignee = $row[2]->getNextObject() ) { |
||
| 337 | $assignee_username = $task_assignee->getTitle()->getText(); |
||
| 338 | $assignee = User::newFromName( $assignee_username ); |
||
| 339 | |||
| 340 | $body = wfMessage( 'semantictasks-reminder-message2', $task_name, |
||
| 341 | $wgLang->formatNum( $remind_me_in ), $link )->text(); |
||
| 342 | $assignee->sendMail( $subject, $body ); |
||
| 343 | } |
||
| 344 | } |
||
| 345 | } |
||
| 346 | } |
||
| 347 | return true; |
||
| 348 | } |
||
| 349 | |||
| 350 | /** |
||
| 351 | * Prints debugging information. $debugText is what you want to print, $debugVal |
||
| 352 | * is the level at which you want to print the information. |
||
| 353 | * |
||
| 354 | * @global boolean $wgSemanticTasksDebug |
||
| 355 | * @param string $debugText |
||
| 356 | * @param string $debugArr |
||
| 357 | * @access private |
||
| 358 | */ |
||
| 359 | static function printDebug( $debugText, $debugArr = null ) { |
||
| 360 | global $wgSemanticTasksDebug; |
||
| 361 | |||
| 362 | if ( $wgSemanticTasksDebug ) { |
||
| 363 | if ( isset( $debugArr ) ) { |
||
| 364 | $text = $debugText . ' ' . implode( '::', $debugArr ); |
||
| 365 | wfDebugLog( 'semantic-tasks', $text, false ); |
||
| 366 | } else { |
||
| 367 | wfDebugLog( 'semantic-tasks', $debugText, false ); |
||
| 368 | } |
||
| 369 | } |
||
| 370 | } |
||
| 371 | |||
| 372 | } |
||
| 373 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.