Completed
Pull Request — master (#28)
by Peter
02:06
created

src/SemanticTasksMailer.php (3 issues)

Severity

Upgrade to new PHP Analysis Engine

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
The parameter $summary is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $watchthis is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $sectionanchor is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
73
		return self::mailAssignees( $article, $text, $current_user, $status, $assignees, $revision );
74
	}
75
76
	/**
77
	 *
78
	 * @param WikiPage $article
79
	 * @param Content $content
80
	 * @param User $user
81
	 * @param integer $status
82
	 * @param Assignees $assignees
83
	 * @return boolean
84
	 * @throws ComplexityException
85
	 * @throws MWException
86
	 * @global boolean $wgSemanticTasksNotifyIfUnassigned
87
	 */
88
	static function mailAssignees( WikiPage $article, Content $content, User $user, $status, Assignees $assignees,
89
								   $revision ) {
90
		$text = ContentHandler::getContentText( $content );
91
		$title = $article->getTitle();
92
93
		// Notify those unassigned from this task
94
		global $wgSemanticTasksNotifyIfUnassigned;
95
		if ( $wgSemanticTasksNotifyIfUnassigned ) {
96
			$removedAssignees = $assignees->getRemovedAssignees( $article, $revision );
97
			$removedAssignees = Assignees::getAssigneeAddresses( $removedAssignees );
98
			self::mailNotification( $removedAssignees, $text, $title, $user, ST_UNASSIGNED );
99
		}
100
101
		// Send notification of an assigned task to assignees
102
		// Treat task as new
103
		$newAssignees = $assignees->getNewAssignees( $article, $revision );
104
		$newAssignees = Assignees::getAssigneeAddresses( $newAssignees );
105
		self::mailNotification( $newAssignees, $text, $title, $user, ST_ASSIGNED );
106
107
		$copies = $assignees->getCurrentCarbonCopy( $article, $revision );
108
		$currentStatus = $assignees->getCurrentStatus( $article, $revision );
109
		$oldStatus = $assignees->getSavedStatus();
110
		if ( $currentStatus === "Closed" && $oldStatus !== "Closed" ) {
111
			$close_mailto = Assignees::getAssigneeAddresses( $copies );
112
			self::mailNotification( $close_mailto, $text, $title, $user, ST_CLOSED );
113
		}
114
115
		$currentAssignees = $assignees->getCurrentAssignees( $article, $revision );
116
117
		// Only send group notifications on new tasks
118
		$groups = array();
119
		if ( $status === ST_NEWTASK ) {
120
			$groups = $assignees->getGroupAssignees( $article );
121
		}
122
123
		$mailto = array_merge( $currentAssignees, $copies, $groups );
124
		$mailto = array_unique( $mailto );
125
		$mailto = Assignees::getAssigneeAddresses( $mailto );
126
127
		// Send notifications to assignees, ccs, and groups
128
		self::mailNotification( $mailto, $text, $title, $user, $status );
129
130
		return true;
131
	}
132
133
	/**
134
	 * Sends mail notifications
135
	 *
136
	 * @param array $assignees
137
	 * @param string $text
138
	 * @param Title $title
139
	 * @param User $user
140
	 * @param integer $status
141
	 * @throws MWException
142
	 * @throws ComplexityException
143
	 * @global string $wgSitename
144
	 */
145
	static function mailNotification( array $assignees, $text, Title $title, User $user, $status ) {
146
		global $wgSitename;
147
148
		if ( empty( $assignees ) ) {
149
			return;
150
		}
151
		$title_text = $title->getFullText();
152
		$from = new \MailAddress( $user->getEmail(), $user->getName() );
153
		$link = htmlspecialchars( $title->getFullURL() );
154
155
		/** @todo This should probably be refactored */
156
		if ( $status == ST_NEWTASK ) {
157
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-newtask' )->text() . ' ' .
158
				$title_text;
159
			$message = 'semantictasks-newtask-msg';
160
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
161
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
162
		} elseif ( $status == ST_UPDATE ) {
163
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskupdated' )->text() . ' ' .
164
				$title_text;
165
			$message = 'semantictasks-updatedtoyou-msg2';
166
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
167
			$body .= "\n \n" . wfMessage( 'semantictasks-diff-message' )->text() . "\n" .
168
				self::generateDiffBodyTxt( $title );
169
		} elseif ( $status == ST_CLOSED ) {
170
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskclosed' )->text() . ' ' .
171
				$title_text;
172
			$message = 'semantictasks-taskclosed-msg';
173
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
174
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
175
		} elseif ( $status == ST_UNASSIGNED ) {
176
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskunassigned' )->text() . ' ' .
177
				$title_text;
178
			$message = 'semantictasks-unassignedtoyou-msg2';
179
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
180
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
181
		} else {
182
			// status == ASSIGNED
183
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskassigned' )->text() . ' ' .
184
				$title_text;
185
			$message = 'semantictasks-assignedtoyou-msg2';
186
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
187
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
188
		}
189
190
		if (!self::$user_mailer) {
191
			self::$user_mailer = new \ST\UserMailer(new \UserMailer());
192
		}
193
194
		self::$user_mailer->send( $assignees, $from, $subject, $body );
195
	}
196
197
	static function setUserMailer(\ST\UserMailer $user_mailer) {
198
		self::$user_mailer = $user_mailer;
199
	}
200
201
	/**
202
	 * Generates a diff txt
203
	 *
204
	 * Code is similar to DifferenceEngine::generateTextDiffBody
205
	 * @param Title $title
206
	 * @param IContextSource $context
207
	 * @return string
208
	 * @throws ComplexityException
209
	 * @throws MWException
210
	 */
211
	static function generateDiffBodyTxt( Title $title, IContextSource $context = null) {
212
		$revision = \Revision::newFromTitle( $title, 0 );
213
		if ($revision === null) {
214
			return '';
215
		}
216
		/** @todo The first parameter should be a Context. */
217
		$diff = new \DifferenceEngine( $context, $revision->getId(), 'prev' );
218
		// The DifferenceEngine::getDiffBody() method generates html,
219
		// so let's generate the txt diff manually:
220
		global $wgContLang;
221
		$diff->loadText();
222
		$otext = '';
223
		$ntext = '';
224
		if ( version_compare( MW_VERSION, '1.32', '<' ) ) {
225
			$otext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mOldContent ) );
226
			$ntext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mNewContent ) );
227
		} else {
228
			if ($diff->getOldRevision()) {
229
				$otext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getOldRevision()->getContent( 'main' ) ) );
230
			}
231
			if ($diff->getNewRevision()) {
232
				$ntext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getNewRevision()->getContent( 'main' ) ) );
233
			}
234
		}
235
		$ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
236
		$nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
237
		// We use here the php diff engine included in MediaWiki
238
		$diffs = new \Diff( $ota, $nta );
239
		// And we ask for a txt formatted diff
240
		$formatter = new \UnifiedDiffFormatter();
241
		$diff_text = $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) );
242
		return $diff_text;
243
	}
244
245
	/**
246
	 * Run by the maintenance script to remind the assignees
247
	 *
248
	 * @return boolean
249
	 * @throws Exception
250
	 * @global string $wgSitename
251
	 * @global Language $wgLang
252
	 */
253
	static function remindAssignees() {
254
		global $wgSitename;
255
		global $stgPropertyReminderAt;
256
		global $stgPropertyAssignedTo;
257
		global $stgPropertyTargetDate;
258
		global $stgPropertyStatus;
259
260
		# Make this equal to midnight. Rational is that if users set today as the Target date with
261
		# reminders set to "0" so that the reminder happens on the deadline, the reminders will go
262
		# out even though now it is after the beginning of today and technically past the
263
		# target date.
264
		$today = wfTimestamp( TS_ISO_8601, strtotime( 'today midnight' ) );
265
266
		# Get tasks where a reminder is called for, whose status is either new or in progress, and
267
		# whose target date is in the future.
268
		$query_string = "[[$stgPropertyReminderAt::+]][[$stgPropertyStatus::New||In Progress]][[$stgPropertyTargetDate::≥ $today]]";
269
		$properties_to_display = array( $stgPropertyReminderAt, $stgPropertyAssignedTo, $stgPropertyTargetDate );
270
271
		$results = Query::getQueryResults( $query_string, $properties_to_display, true );
272
		if ( empty( $results ) ) {
273
			return false;
274
		}
275
276
		while ( $row = $results->getNext() ) {
277
			$task_name = $row[0]->getNextObject()->getTitle();
278
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-reminder' )->text() . $task_name;
279
			// The following doesn't work, maybe because we use a cron job.
280
			// $link = $task_name->getFullURL();
281
			// So let's do it manually
282
			//$link = $wiki_url . $task_name->getPartialURL();
283
			// You know what? Let's try it again.
284
			$link = $task_name->getFullURL();
285
286
			$target_date = $row[3]->getNextObject();
287
			$tg_date = new DateTime( $target_date->getShortHTMLText() );
288
289
			while ( $reminder = $row[1]->getNextObject() ) {
290
				$remind_me_in = $reminder->getShortHTMLText();
291
				$date = new DateTime( 'today midnight' );
292
				$date->modify( "+$remind_me_in day" );
293
294
				if ( $tg_date === $date ) {
295
					global $wgLang;
296
					while ( $task_assignee = $row[2]->getNextObject() ) {
297
						$assignee_username = $task_assignee->getTitle()->getText();
298
						$assignee = User::newFromName( $assignee_username );
299
300
						$body = wfMessage( 'semantictasks-reminder-message2', $task_name,
301
							$wgLang->formatNum( $remind_me_in ), $link )->text();
302
						$assignee->sendMail( $subject, $body );
303
					}
304
				}
305
			}
306
		}
307
		return true;
308
	}
309
310
	/**
311
	 * Prints debugging information. $debugText is what you want to print, $debugVal
312
	 * is the level at which you want to print the information.
313
	 *
314
	 * @global boolean $wgSemanticTasksDebug
315
	 * @param string $debugText
316
	 * @param string $debugArr
317
	 * @access private
318
	 */
319
	static function printDebug( $debugText, $debugArr = null ) {
320
		global $wgSemanticTasksDebug;
321
322
		if ( $wgSemanticTasksDebug ) {
323
			if ( isset( $debugArr ) ) {
324
				$text = $debugText . ' ' . implode( '::', $debugArr );
325
				wfDebugLog( 'semantic-tasks', $text, false );
326
			} else {
327
				wfDebugLog( 'semantic-tasks', $debugText, false );
328
			}
329
		}
330
	}
331
332
}
333