Completed
Pull Request — master (#28)
by Peter
13:00 queued 03:02
created

SemanticTasksMailer::mailAssigneesUpdatedTask()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
cc 4
nc 3
nop 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
Unused Code introduced by
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...
Unused Code introduced by
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...
Unused Code introduced by
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
		$assignedToParserOutput = self::getAssignedUsersFromParserOutput($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
	public static function getAssignedUsersFromParserOutput(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
Bug introduced by
The class ParserOutput does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
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,
0 ignored issues
show
Best Practice introduced by
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...
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 ) {
0 ignored issues
show
Best Practice introduced by
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...
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) {
0 ignored issues
show
Best Practice introduced by
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...
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
Best Practice introduced by
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...
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() {
0 ignored issues
show
Best Practice introduced by
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...
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 ) {
0 ignored issues
show
Best Practice introduced by
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...
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