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 ) { |
|
|
|
|
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
|
|
|
|
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.