ReminderListModule   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 299
Duplicated Lines 0 %

Importance

Changes 5
Bugs 2 Features 1
Metric Value
eloc 143
c 5
b 2
f 1
dl 0
loc 299
rs 9.92
wmc 31

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A execute() 0 15 4
A getReminderFolderEntryId() 0 6 1
F createReminderFolder() 0 121 15
C getReminders() 0 133 10
1
<?php
2
3
/**
4
 * Reminder Module.
5
 *
6
 * TODO: add description
7
 */
8
class ReminderListModule extends ListModule {
9
	private $reminderEntryId;
10
11
	/**
12
	 * Constructor.
13
	 *
14
	 * @param int   $id   unique id
15
	 * @param array $data list of all actions
16
	 */
17
	public function __construct($id, $data) {
18
		parent::__construct($id, $data);
19
20
		$this->properties = $GLOBALS["properties"]->getReminderProperties();
21
	}
22
23
	#[Override]
24
	public function execute() {
25
		foreach ($this->data as $actionType => $action) {
26
			$store = $GLOBALS["mapisession"]->getDefaultMessageStore();
27
			$this->reminderEntryId = $this->getReminderFolderEntryId($store);
28
29
			if (isset($actionType)) {
30
				try {
31
					match ($actionType) {
32
						"list" => $this->getReminders(),
33
						default => $this->handleUnknownActionType($actionType),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->handleUnknownActionType($actionType) targeting Module::handleUnknownActionType() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
34
					};
35
				}
36
				catch (MAPIException $e) {
37
					$this->processException($e, $actionType, $store, null, null, $action);
38
				}
39
			}
40
		}
41
	}
42
43
	public function getReminderFolderEntryId($store) {
44
		$root = mapi_msgstore_openentry($store);
45
		$rootProps = mapi_getprops($root, [PR_REM_ONLINE_ENTRYID]);
46
47
		// Reminder folder didn't exist, create one
48
		return $rootProps[PR_REM_ONLINE_ENTRYID] ?? $this->createReminderFolder($store);
49
	}
50
51
	public function createReminderFolder($store) {
52
		$storeProps = mapi_getprops($store, [PR_IPM_OUTBOX_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_IPM_SUBTREE_ENTRYID]);
53
		$root = mapi_msgstore_openentry($store);
54
		$rootProps = mapi_getprops($root, [PR_ADDITIONAL_REN_ENTRYIDS, PR_IPM_DRAFTS_ENTRYID]);
55
56
		$folders = [];
57
		/* The list of forbidden folders (MS-OXORMDR v14 §1.1) */
58
		if (isset($storeProps[PR_IPM_WASTEBASKET_ENTRYID])) {
59
			$folders[] = $storeProps[PR_IPM_WASTEBASKET_ENTRYID];
60
		}
61
		if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS]) && !empty($rootProps[PR_ADDITIONAL_REN_ENTRYIDS][4])) {
62
			$folders[] = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS][4];
63
		} // junk mail
64
		if (isset($rootProps[PR_IPM_DRAFTS_ENTRYID])) {
65
			$folders[] = $rootProps[PR_IPM_DRAFTS_ENTRYID];
66
		}
67
		if (isset($storeProps[PR_IPM_OUTBOX_ENTRYID])) {
68
			$folders[] = $storeProps[PR_IPM_OUTBOX_ENTRYID];
69
		}
70
		if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS]) && !empty($rootProps[PR_ADDITIONAL_REN_ENTRYIDS][0])) {
71
			$folders[] = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS][0];
72
		} // conflicts
73
		if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS]) && !empty($rootProps[PR_ADDITIONAL_REN_ENTRYIDS][2])) {
74
			$folders[] = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS][2];
75
		} // local failures
76
		if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS]) && !empty($rootProps[PR_ADDITIONAL_REN_ENTRYIDS][3])) {
77
			$folders[] = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS][3];
78
		} // server failures
79
		if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS]) && !empty($rootProps[PR_ADDITIONAL_REN_ENTRYIDS][1])) {
80
			$folders[] = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS][1];
81
		} // sync issues
82
83
		$folderRestriction = [];
84
		foreach ($folders as $folder) {
85
			$folderRestriction[] = [RES_PROPERTY,
86
				[
87
					RELOP => RELOP_NE,
88
					ULPROPTAG => $this->properties["parent_entryid"],
89
					VALUE => [$this->properties["parent_entryid"] => $folder],
90
				],
91
			];
92
		}
93
94
		$res =
95
			[RES_AND,
96
				[
97
					[RES_AND,
98
						$folderRestriction,
99
					],
100
					[RES_AND,
101
						[
102
							[RES_NOT,
103
								[
104
									[RES_AND,
105
										[
106
											[RES_EXIST,
107
												[
108
													ULPROPTAG => $this->properties["message_class"],
109
												],
110
											],
111
											[RES_CONTENT,
112
												[
113
													FUZZYLEVEL => FL_PREFIX,
114
													ULPROPTAG => $this->properties["message_class"],
115
													VALUE => [$this->properties["message_class"] => "IPM.Schedule"],
116
												],
117
											],
118
										],
119
									],
120
								],
121
							],
122
							[RES_BITMASK,
123
								[
124
									ULTYPE => BMR_EQZ,
125
									ULPROPTAG => $this->properties["message_flags"],
126
									ULMASK => MSGFLAG_SUBMIT,
127
								],
128
							],
129
							[RES_OR,
130
								[
131
									[RES_PROPERTY,
132
										[
133
											RELOP => RELOP_EQ,
134
											ULPROPTAG => $this->properties["reminder"],
135
											VALUE => [$this->properties["reminder"] => true],
136
										],
137
									],
138
									[RES_AND,
139
										[
140
											[RES_EXIST,
141
												[
142
													ULPROPTAG => $this->properties["recurring"],
143
												],
144
											],
145
											[RES_PROPERTY,
146
												[
147
													RELOP => RELOP_EQ,
148
													ULPROPTAG => $this->properties["recurring"],
149
													VALUE => [$this->properties["recurring"] => true],
150
												],
151
											],
152
										],
153
									],
154
								],
155
							],
156
						],
157
					],
158
				],
159
			];
160
161
		$folder = mapi_folder_createfolder($root, _("Reminders"), "", OPEN_IF_EXISTS, FOLDER_SEARCH);
162
		mapi_setprops($folder, [PR_CONTAINER_CLASS => "Outlook.Reminder"]);
163
		mapi_savechanges($folder);
164
165
		mapi_folder_setsearchcriteria($folder, $res, [$storeProps[PR_IPM_SUBTREE_ENTRYID]], RECURSIVE_SEARCH);
166
		$folderProps = mapi_getprops($folder, [PR_ENTRYID]);
167
168
		mapi_setprops($root, [PR_REM_ONLINE_ENTRYID => $folderProps[PR_ENTRYID]]);
169
		mapi_savechanges($root);
170
171
		return $folderProps[PR_ENTRYID];
172
	}
173
174
	public function getReminders() {
175
		$data = [];
176
177
		$store = $GLOBALS["mapisession"]->getDefaultMessageStore();
178
179
		$restriction = [RES_AND,
180
			[
181
				[RES_PROPERTY,
182
					[
183
						RELOP => RELOP_LT,
184
						ULPROPTAG => $this->properties["flagdueby"],
185
						VALUE => [$this->properties["flagdueby"] => time()],
186
					],
187
				],
188
				[RES_PROPERTY,
189
					[
190
						RELOP => RELOP_EQ,
191
						ULPROPTAG => $this->properties["reminder"],
192
						VALUE => true,
193
					],
194
				],
195
				[RES_PROPERTY,
196
					[
197
						RELOP => RELOP_NE,
198
						ULPROPTAG => $this->properties["message_class"],
199
						VALUE => "IPM.TaskRequest",
200
					],
201
				],
202
				[RES_PROPERTY,
203
					[
204
						RELOP => RELOP_NE,
205
						ULPROPTAG => $this->properties["message_class"],
206
						VALUE => "IPM.TaskRequest.Cancel",
207
					],
208
				],
209
				[RES_PROPERTY,
210
					[
211
						RELOP => RELOP_NE,
212
						ULPROPTAG => $this->properties["message_class"],
213
						VALUE => "IPM.TaskRequest.Accept",
214
					],
215
				],
216
				[RES_PROPERTY,
217
					[
218
						RELOP => RELOP_NE,
219
						ULPROPTAG => $this->properties["message_class"],
220
						VALUE => "IPM.TaskRequest.Update",
221
					],
222
				],
223
				[RES_PROPERTY,
224
					[
225
						RELOP => RELOP_NE,
226
						ULPROPTAG => $this->properties["message_class"],
227
						VALUE => "IPM.TaskRequest.Complete",
228
					],
229
				],
230
			],
231
		];
232
233
		try {
234
			$reminderfolder = mapi_msgstore_openentry($store, $this->reminderEntryId);
235
		}
236
		catch (MAPIException $e) {
237
			// if the reminder folder does not exist, try to recreate it.
238
			if ($e->getCode() == MAPI_E_NOT_FOUND) {
239
				$e->setHandled();
240
241
				$this->reminderEntryId = $this->createReminderFolder($store);
242
				$reminderfolder = mapi_msgstore_openentry($store, $this->reminderEntryId);
243
			}
244
		}
245
246
		$remindertable = mapi_folder_getcontentstable($reminderfolder, MAPI_DEFERRED_ERRORS);
247
		if (!$remindertable) {
248
			return false;
249
		}
250
251
		mapi_table_restrict($remindertable, $restriction, TBL_BATCH);
252
		mapi_table_sort($remindertable, [$this->properties["flagdueby"] => TABLE_SORT_DESCEND], TBL_BATCH);
253
254
		// reminder store hold only 99 records as
255
		// we show 99 notification on client side.
256
		$rows = mapi_table_queryrows($remindertable, $this->properties, 0, MAX_NUM_REMINDERS);
257
		$data["item"] = [];
258
259
		foreach ($rows as $row) {
260
			if (isset($row[$this->properties["appointment_recurring"]]) && $row[$this->properties["appointment_recurring"]]) {
261
				$recur = new Recurrence($store, $row);
0 ignored issues
show
Bug introduced by
The type Recurrence was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
262
263
				/**
264
				 * FlagDueBy == PidLidReminderSignalTime.
265
				 * FlagDueBy handles whether we should be showing the item; if now() is after FlagDueBy, then we should show a reminder
266
				 * for this recurrence. However, the item we will show is either the last passed occurrence (overdue), or the next occurrence, depending
267
				 * on whether we have reached the next occurrence yet (the reminder_time of the next item is ignored).
268
				 *
269
				 * The way we handle this is to get all occurrences between the 'flagdueby' moment and the current time. This will
270
				 * yield N items (may be a lot of it was not dismissed for a long time). We can then take the last item in this list, and this is the item
271
				 * we will show to the user. The idea here is:
272
				 *
273
				 * The item we want to show is the last item in that list (new occurrences that have started uptil now should override old ones)
274
				 *
275
				 * Add the reminder_minutes (default 15 minutes for calendar, 0 for tasks) to check over the gap between FlagDueBy and the start time of the
276
				 * occurrence, if "now" would be in between these values.
277
				 */
278
				$remindertimeinseconds = $row[$this->properties["reminder_minutes"]] * 60;
279
				$occurrences = $recur->getItems($row[$this->properties["flagdueby"]], time() + $remindertimeinseconds, 0, true);
280
281
				if (empty($occurrences)) {
282
					continue;
283
				}
284
285
				// More than one occurrence, use the last one instead of the first one after flagdueby
286
				$occ = $occurrences[count($occurrences) - 1];
287
288
				// Bydefault, on occurrence reminder is true but if reminder value is set to false then we don't send popup reminder for this occurrence
289
				if (!(isset($occ[$this->properties['reminder']]) && $occ[$this->properties['reminder']] == 0)) {
290
					$row[$this->properties["reminder_time"]] = $occ[$this->properties["appointment_startdate"]];
291
					$row[$this->properties["appointment_startdate"]] = $occ[$this->properties["appointment_startdate"]];
292
					$row[$this->properties["appointment_enddate"]] = $occ[$this->properties["appointment_startdate"]];
293
				}
294
			}
295
296
			// Add the non-bogus rows
297
			array_push($data["item"], Conversion::mapMAPI2XML($this->properties, $row));
298
		}
299
300
		$this->addActionData("list", $data);
301
		$GLOBALS["bus"]->addData($this->getResponseData());
302
303
		// Trigger the newmailnotifier
304
		$GLOBALS["bus"]->notify(REQUEST_ENTRYID, HIERARCHY_UPDATE, ['', '']);
305
306
		return true;
307
	}
308
}
309