Completed
Pull Request — master (#27)
by Peter
11:07 queued 01:04
created

src/SemanticTasksMailer.php (1 issue)

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 SMWPrintRequest;
13
use Title;
14
use User;
15
use WikiPage;
16
17
if ( !defined( 'MEDIAWIKI' ) ) {
18
	echo 'Not a valid entry point';
19
	exit( 1 );
20
}
21
22
if ( !defined( 'SMW_VERSION' ) ) {
23
	echo 'This extension requires Semantic MediaWiki to be installed.';
24
	exit( 1 );
25
}
26
27
// constants for message type
28
if ( !defined( 'ST_NEWTASK' ) ) {
29
	define( 'ST_NEWTASK', 0 );
30
	define( 'ST_UPDATE', 1 );
31
	define( 'ST_ASSIGNED', 2 );
32
	define( 'ST_CLOSED', 3 );
33
	define( 'ST_UNASSIGNED', 4 );
34
}
35
36
/**
37
 * This class handles the creation and sending of notification emails.
38
 */
39
class SemanticTasksMailer {
40
41
	private static $user_mailer;
42
43
	/**
44
	 * Mails the assignees when the task is modified
45
	 *
46
	 * @param Assignees $assignees
47
	 * @param WikiPage $article
48
	 * @param User $current_user
49
	 * @param Content $text
50
	 * @param string $summary Unused
51
	 * @param bool $minoredit
52
	 * @param null $watchthis Unused
53
	 * @param null $sectionanchor Unused
54
	 * @param $flags
55
	 * @return boolean
56
	 * @throws ComplexityException
57
	 * @throws MWException
58
	 */
59
	public static function mailAssigneesUpdatedTask( Assignees $assignees, WikiPage $article, User $current_user, $text,
60
			$summary, $minoredit, $watchthis, $sectionanchor, $flags ) {
61
		if ( $minoredit ) {
62
			return true;
63
		}
64
		$status = ST_UPDATE;
65
		if ( ( $flags & EDIT_NEW ) && !$article->getTitle()->isTalkPage() ) {
66
			$status = ST_NEWTASK;
67
		}
68
		return self::mailAssignees( $article, $text, $current_user, $status, $assignees );
69
	}
70
71
	/**
72
	 *
73
	 * @param WikiPage $article
74
	 * @param Content $content
75
	 * @param User $user
76
	 * @param integer $status
77
	 * @param Assignees $assignees
78
	 * @return boolean
79
	 * @throws ComplexityException
80
	 * @throws MWException
81
	 * @global boolean $wgSemanticTasksNotifyIfUnassigned
82
	 */
83
	static function mailAssignees( WikiPage $article, Content $content, User $user, $status, Assignees $assignees ) {
84
		$text = ContentHandler::getContentText( $content );
85
		$title = $article->getTitle();
86
87
		// Notify those unassigned from this task
88
		global $wgSemanticTasksNotifyIfUnassigned;
89
		if ( $wgSemanticTasksNotifyIfUnassigned ) {
90
			$removedAssignees = $assignees->getRemovedAssignees( $article );
91
			$removedAssignees = $assignees->getAssigneeAddresses( $removedAssignees );
92
			self::mailNotification( $removedAssignees, $text, $title, $user, ST_UNASSIGNED );
93
		}
94
95
		// Send notification of an assigned task to assignees
96
		// Treat task as new
97
		$newAssignees = $assignees->getNewAssignees( $article );
98
		$newAssignees = $assignees->getAssigneeAddresses( $newAssignees );
99
		self::mailNotification( $newAssignees, $text, $title, $user, ST_ASSIGNED );
100
101
		$copies = $assignees->getCurrentCarbonCopy( $article );
102
		$currentStatus = $assignees->getCurrentStatus( $article );
103
		$oldStatus = $assignees->getSavedStatus();
104
		if ( $currentStatus === "Closed" && $oldStatus !== "Closed" ) {
105
			$close_mailto = $assignees->getAssigneeAddresses( $copies );
106
			self::mailNotification( $close_mailto, $text, $title, $user, ST_CLOSED );
107
		}
108
109
		$currentAssignees = $assignees->getCurrentAssignees( $article );
110
111
		// Only send group notifications on new tasks
112
		$groups = array();
113
		if ( $status === ST_NEWTASK ) {
114
			$groups = $assignees->getGroupAssignees( $article );
115
		}
116
117
		$mailto = array_merge( $currentAssignees, $copies, $groups );
118
		$mailto = array_unique( $mailto );
119
		$mailto = $assignees->getAssigneeAddresses( $mailto );
120
121
		// Send notifications to assignees, ccs, and groups
122
		self::mailNotification( $mailto, $text, $title, $user, $status );
123
124
		return true;
125
	}
126
127
	/**
128
	 * Sends mail notifications
129
	 *
130
	 * @param array $assignees
131
	 * @param string $text
132
	 * @param Title $title
133
	 * @param User $user
134
	 * @param integer $status
135
	 * @throws MWException
136
	 * @throws ComplexityException
137
	 * @global string $wgSitename
138
	 */
139
	static function mailNotification( array $assignees, $text, Title $title, User $user, $status ) {
140
		global $wgSitename;
141
142
		if ( empty( $assignees ) ) {
143
			return;
144
		}
145
		$title_text = $title->getFullText();
146
		$from = new \MailAddress( $user->getEmail(), $user->getName() );
147
		$link = htmlspecialchars( $title->getFullURL() );
148
149
		/** @todo This should probably be refactored */
150
		if ( $status == ST_NEWTASK ) {
151
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-newtask' )->text() . ' ' .
152
				$title_text;
153
			$message = 'semantictasks-newtask-msg';
154
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
155
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
156
		} elseif ( $status == ST_UPDATE ) {
157
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskupdated' )->text() . ' ' .
158
				$title_text;
159
			$message = 'semantictasks-updatedtoyou-msg2';
160
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
161
			$body .= "\n \n" . wfMessage( 'semantictasks-diff-message' )->text() . "\n" .
162
				self::generateDiffBodyTxt( $title );
163
		} elseif ( $status == ST_CLOSED ) {
164
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskclosed' )->text() . ' ' .
165
				$title_text;
166
			$message = 'semantictasks-taskclosed-msg';
167
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
168
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
169
		} elseif ( $status == ST_UNASSIGNED ) {
170
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskunassigned' )->text() . ' ' .
171
				$title_text;
172
			$message = 'semantictasks-unassignedtoyou-msg2';
173
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
174
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
175
		} else {
176
			// status == ASSIGNED
177
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-taskassigned' )->text() . ' ' .
178
				$title_text;
179
			$message = 'semantictasks-assignedtoyou-msg2';
180
			$body = wfMessage( $message, $title_text )->text() . " " . $link;
181
			$body .= "\n \n" . wfMessage( 'semantictasks-text-message' )->text() . "\n" . $text;
182
		}
183
184
		if (!self::$user_mailer) {
185
			self::$user_mailer = new \ST\UserMailer(new \UserMailer());
186
		}
187
188
		self::$user_mailer->send( $assignees, $from, $subject, $body );
189
	}
190
191
	static function setUserMailer(\ST\UserMailer $user_mailer) {
192
		self::$user_mailer = $user_mailer;
193
	}
194
195
	/**
196
	 * Generates a diff txt
197
	 *
198
	 * Code is similar to DifferenceEngine::generateTextDiffBody
199
	 * @param Title $title
200
	 * @param IContextSource $context
201
	 * @return string
202
	 * @throws ComplexityException
203
	 * @throws MWException
204
	 */
205
	static function generateDiffBodyTxt( Title $title, IContextSource $context = null) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
206
		$revision = \Revision::newFromTitle( $title, 0 );
207
		if ($revision === null) {
208
			return '';
209
		}
210
		/** @todo The first parameter should be a Context. */
211
		$diff = new \DifferenceEngine( $context, $revision->getId(), 'prev' );
212
		// The DifferenceEngine::getDiffBody() method generates html,
213
		// so let's generate the txt diff manually:
214
		global $wgContLang;
215
		$diff->loadText();
216
		if ( version_compare( MW_VERSION, '1.32', '<' ) ) {
217
			$otext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mOldContent ) );
218
			$ntext = str_replace( "\r\n", "\n", \ContentHandler::getContentText( $diff->mNewContent ) );
219
		} else {
220
			$otext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getOldRevision()->getContent( 'main' ) ) );
221
			$ntext = str_replace( "\r\n", "\n", ContentHandler::getContentText( $diff->getNewRevision()->getContent( 'main' ) ) );
222
		}
223
		$ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
224
		$nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
225
		// We use here the php diff engine included in MediaWiki
226
		$diffs = new \Diff( $ota, $nta );
227
		// And we ask for a txt formatted diff
228
		$formatter = new \UnifiedDiffFormatter();
229
		$diff_text = $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) );
230
		return $diff_text;
231
	}
232
233
	/**
234
	 * Run by the maintenance script to remind the assignees
235
	 *
236
	 * @return boolean
237
	 * @throws Exception
238
	 * @global string $wgSitename
239
	 * @global Language $wgLang
240
	 */
241
	static function remindAssignees() {
242
		global $wgSitename;
243
		global $stgPropertyReminderAt;
244
		global $stgPropertyAssignedTo;
245
		global $stgPropertyTargetDate;
246
		global $stgPropertyStatus;
247
248
		# Make this equal to midnight. Rational is that if users set today as the Target date with
249
		# reminders set to "0" so that the reminder happens on the deadline, the reminders will go
250
		# out even though now it is after the beginning of today and technically past the
251
		# target date.
252
		$today = wfTimestamp( TS_ISO_8601, strtotime( 'today midnight' ) );
253
254
		# Get tasks where a reminder is called for, whose status is either new or in progress, and
255
		# whose target date is in the future.
256
		$query_string = "[[$stgPropertyReminderAt::+]][[$stgPropertyStatus::New||In Progress]][[$stgPropertyTargetDate::≥ $today]]";
257
		$properties_to_display = array( $stgPropertyReminderAt, $stgPropertyAssignedTo, $stgPropertyTargetDate );
258
259
		$results = Query::getQueryResults( $query_string, $properties_to_display, true );
260
		if ( empty( $results ) ) {
261
			return false;
262
		}
263
264
		while ( $row = $results->getNext() ) {
265
			$task_name = $row[0]->getNextObject()->getTitle();
266
			$subject = '[' . $wgSitename . '] ' . wfMessage( 'semantictasks-reminder' )->text() . $task_name;
267
			// The following doesn't work, maybe because we use a cron job.
268
			// $link = $task_name->getFullURL();
269
			// So let's do it manually
270
			//$link = $wiki_url . $task_name->getPartialURL();
271
			// You know what? Let's try it again.
272
			$link = $task_name->getFullURL();
273
274
			$target_date = $row[3]->getNextObject();
275
			$tg_date = new DateTime( $target_date->getShortHTMLText() );
276
277
			while ( $reminder = $row[1]->getNextObject() ) {
278
				$remind_me_in = $reminder->getShortHTMLText();
279
				$date = new DateTime( 'today midnight' );
280
				$date->modify( "+$remind_me_in day" );
281
282
				if ( $tg_date === $date ) {
283
					global $wgLang;
284
					while ( $task_assignee = $row[2]->getNextObject() ) {
285
						$assignee_username = $task_assignee->getTitle()->getText();
286
						$assignee = User::newFromName( $assignee_username );
287
288
						$body = wfMessage( 'semantictasks-reminder-message2', $task_name,
289
							$wgLang->formatNum( $remind_me_in ), $link )->text();
290
						$assignee->sendMail( $subject, $body );
291
					}
292
				}
293
			}
294
		}
295
		return true;
296
	}
297
298
	/**
299
	 * Prints debugging information. $debugText is what you want to print, $debugVal
300
	 * is the level at which you want to print the information.
301
	 *
302
	 * @global boolean $wgSemanticTasksDebug
303
	 * @param string $debugText
304
	 * @param string $debugArr
305
	 * @access private
306
	 */
307
	static function printDebug( $debugText, $debugArr = null ) {
308
		global $wgSemanticTasksDebug;
309
310
		if ( $wgSemanticTasksDebug ) {
311
			if ( isset( $debugArr ) ) {
312
				$text = $debugText . ' ' . implode( '::', $debugArr );
313
				wfDebugLog( 'semantic-tasks', $text, false );
314
			} else {
315
				wfDebugLog( 'semantic-tasks', $debugText, false );
316
			}
317
		}
318
	}
319
320
}
321