1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Model for messages stored in database. |
4
|
|
|
* |
5
|
|
|
* @package ThreemaGateway |
6
|
|
|
* @author rugk |
7
|
|
|
* @copyright Copyright (c) 2015-2016 rugk |
8
|
|
|
* @license MIT |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
class ThreemaGateway_Model_Messages extends XenForo_Model |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* @var string database table (prefix) for messages |
15
|
|
|
*/ |
16
|
|
|
const DB_TABLE_MESSAGES = 'xf_threemagw_messages'; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var string database table for files |
20
|
|
|
*/ |
21
|
|
|
const DB_TABLE_FILES = 'xf_threemagw_files'; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var string database table for acknowledged messages/delivery receipts |
25
|
|
|
*/ |
26
|
|
|
const DB_TABLE_DELIVERY_RECEIPT = 'xf_threemagw_ackmsgs'; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var int constant for type code |
30
|
|
|
*/ |
31
|
|
|
const TYPE_DELIVERY_MESSAGE = 0x80; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var int constant for type code |
35
|
|
|
*/ |
36
|
|
|
const TYPE_FILE_MESSAGE = 0x17; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var int constant for type code |
40
|
|
|
*/ |
41
|
|
|
const TYPE_IMAGE_MESSAGE = 0x02; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var int constant for type code |
45
|
|
|
*/ |
46
|
|
|
const TYPE_TEXT_MESSAGE = 0x01; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var array constant for type code |
50
|
|
|
*/ |
51
|
|
|
const ORDER_CHOICE = [ |
52
|
|
|
'id' => 'message_id', |
53
|
|
|
'date_send' => 'date_send', |
54
|
|
|
'date_received' => 'date_received', |
55
|
|
|
'delivery_state' => 'message.receipt_type', |
56
|
|
|
]; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var array data used when querying |
60
|
|
|
*/ |
61
|
|
|
protected $fetchOptions = [ |
62
|
|
|
'where' => [], |
63
|
|
|
'params' => [] |
64
|
|
|
]; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Execute this before any query. |
68
|
|
|
* |
69
|
|
|
* Sets internal values necessary for a correct connection to the database. |
70
|
|
|
*/ |
71
|
|
|
public function preQuery() |
72
|
|
|
{ |
73
|
|
|
// set correct character encoding |
74
|
|
|
$this->_getDb()->query('SET NAMES utf8mb4'); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Inject or modify a fetch option manually. |
79
|
|
|
* |
80
|
|
|
* Sets internal values necessary for a correct connection to the database. |
81
|
|
|
* This should best be avoided, when it is not really necessary to change |
82
|
|
|
* the value directly. |
83
|
|
|
* It can e.g. be used to reset data. When you e.g. want to reset the where |
84
|
|
|
* option call it this way: injectFetchOption('where', []). |
85
|
|
|
* |
86
|
|
|
* @param string $option The option name to inject |
87
|
|
|
* @param string $value The value of the option to set. |
88
|
|
|
* @param bool $append If set to true, the value is not overriden, but |
89
|
|
|
* just appended as an array. (default: false) |
90
|
|
|
*/ |
91
|
|
|
public function injectFetchOption($option, $value, $append = false) |
92
|
|
|
{ |
93
|
|
|
if ($append) { |
94
|
|
|
$this->fetchOptions[$option][] = $value; |
95
|
|
|
} else { |
96
|
|
|
$this->fetchOptions[$option] = $value; |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Rests all fetch options. This is useful to prevent incorrect or |
102
|
|
|
* unexpected results when using one model for multiple queries (not |
103
|
|
|
* recommend) or for e.g. resetting the options before calling |
104
|
|
|
* {@link fetchAll()};. |
105
|
|
|
*/ |
106
|
|
|
public function resetFetchOptions() |
107
|
|
|
{ |
108
|
|
|
$this->fetchOptions = []; |
109
|
|
|
// set empty data, which is required to prevent failing |
110
|
|
|
$this->fetchOptions['where'] = []; |
111
|
|
|
$this->fetchOptions['params'] = []; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Sets the message ID(s) for the query. |
116
|
|
|
* |
117
|
|
|
* @param string|array $messageIds one (string) or more (array) message IDs |
118
|
|
|
* @param string $tablePrefix The table prefix (optional) |
119
|
|
|
*/ |
120
|
|
|
public function setMessageId($messageIds, $tablePrefix = null) |
121
|
|
|
{ |
122
|
|
|
return $this->appendMixedCondition( |
123
|
|
|
($tablePrefix ? $tablePrefix . '.' : '') . 'message_id', |
124
|
|
|
$messageIds |
125
|
|
|
); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Sets the sender Threema ID(s) for querying it/them. |
130
|
|
|
* |
131
|
|
|
* @param string $threemaIds one (string) or more (array) Threema IDs |
132
|
|
|
*/ |
133
|
|
|
public function setSenderId($threemaIds) |
134
|
|
|
{ |
135
|
|
|
return $this->appendMixedCondition( |
136
|
|
|
'metamessage.sender_threema_id', |
137
|
|
|
$threemaIds |
138
|
|
|
); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Sets the type code(s) for querying only one (or a few) type. |
143
|
|
|
* |
144
|
|
|
* Please use the TYPE_* constants for specifying the type code(s). |
145
|
|
|
* You should avoid using this and rather use {@link getMessageDataByType()} |
146
|
|
|
* directly if you know the type code. |
147
|
|
|
* If you want to limit the types you want to query this method would be a |
148
|
|
|
* good way for you to use. |
149
|
|
|
* |
150
|
|
|
* @param string $typeCodes one (string) or more (array) type codes(s) |
151
|
|
|
*/ |
152
|
|
|
public function setTypeCode($typeCodes) |
153
|
|
|
{ |
154
|
|
|
return $this->appendMixedCondition( |
155
|
|
|
'metamessage.message_type_code', |
156
|
|
|
$typeCodes |
157
|
|
|
); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Sets a string to look for when querying text messages. |
162
|
|
|
* |
163
|
|
|
* The string is processed by MySQL via the `LIKE` command and may |
164
|
|
|
* therefore contain some wildcards: % for none or any character and |
165
|
|
|
* _ for exactly one character. |
166
|
|
|
* Attention: This is only possible when using the text message type! |
167
|
|
|
* Otherwise your query will fail. |
168
|
|
|
* |
169
|
|
|
* @param string $keyword a keyword to look for |
170
|
|
|
*/ |
171
|
|
|
public function setKeyword($keyword) |
172
|
|
|
{ |
173
|
|
|
$this->fetchOptions['where'][] = 'message.text LIKE ?'; |
174
|
|
|
$this->fetchOptions['params'][] = $keyword; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Sets the a time limit for what messages should be queried. |
179
|
|
|
* |
180
|
|
|
* @param int|null $dateMin oldest date of messages (optional) |
181
|
|
|
* @param int|null $dateMax latest date of messages (optional) |
182
|
|
|
* @param string $attribute Set the atttribute to apply this to. |
183
|
|
|
*/ |
184
|
|
|
public function setTimeLimit($dateMin = null, $dateMax = null, $attribute = 'metamessage.date_send') |
185
|
|
|
{ |
186
|
|
|
if ($dateMin) { |
|
|
|
|
187
|
|
|
$this->fetchOptions['where'][] = $attribute . ' >= ?'; |
188
|
|
|
$this->fetchOptions['params'][] = $dateMin; |
189
|
|
|
} |
190
|
|
|
if ($dateMax) { |
|
|
|
|
191
|
|
|
$this->fetchOptions['where'][] = $attribute . ' <= ?'; |
192
|
|
|
$this->fetchOptions['params'][] = $dateMax; |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Limit the result to a number of datasets. |
198
|
|
|
* |
199
|
|
|
* @param int $limit oldest date of messages |
200
|
|
|
*/ |
201
|
|
|
public function setResultLimit($limit) |
202
|
|
|
{ |
203
|
|
|
$this->fetchOptions['limit'] = $limit; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Sets an order for the query. |
208
|
|
|
* |
209
|
|
|
* This function overwrites previous values if they were set as ordering by |
210
|
|
|
* multiple columns is not possible. |
211
|
|
|
* |
212
|
|
|
* @param int $column the column to order by (see {@link OrderChoice} for valid values) |
213
|
|
|
* @param string $direction asc or desc |
214
|
|
|
*/ |
215
|
|
|
public function setOrder($column, $direction = 'asc') |
216
|
|
|
{ |
217
|
|
|
$this->fetchOptions['order'] = $column; |
218
|
|
|
$this->fetchOptions['direction'] = $direction; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Queries all available data from a list of message IDs. |
223
|
|
|
* |
224
|
|
|
* Note that this requires one to have the meta data of the messages already |
225
|
|
|
* and therefore you have to run {@link getMessageMetaData()} before and |
226
|
|
|
* submit it as the first parameter. |
227
|
|
|
* This method also resets the conditions of the where clause |
228
|
|
|
* ($fetchOptions['where']) and the params ($fetchOptions['params']) based |
229
|
|
|
* on the results included in the meta data. Other fetch options however |
230
|
|
|
* remain and are still applied, so if you want to avoid this, use |
231
|
|
|
* {@link resetFetchOptions()}. |
232
|
|
|
* Note that the ordering values of different message types will not work as |
233
|
|
|
* this function internally needs to handle each message type differently. |
234
|
|
|
* |
235
|
|
|
* @param array[string] $metaData The message meta data from |
|
|
|
|
236
|
|
|
* {@link getMessageMetaData()} |
237
|
|
|
* (without grouping) |
238
|
|
|
* @param bool $groupByMessageType Set to true to group the |
239
|
|
|
* return value via message |
240
|
|
|
* types. (default: false) |
241
|
|
|
* @throws XenForo_Exception |
242
|
|
|
* @return null|array |
243
|
|
|
*/ |
244
|
|
|
public function getAllMessageData(array $metaData, $groupByMessageType = false) |
245
|
|
|
{ |
246
|
|
|
// get grouped messages by type |
247
|
|
|
$messageTypes = $this->groupArray($metaData, 'message_type_code'); |
248
|
|
|
// we always need to do this (regardless of message_type_code) as each |
249
|
|
|
// message type needs to be handled individually |
250
|
|
|
|
251
|
|
|
// query message types individually |
252
|
|
|
$output = null; |
253
|
|
|
foreach ($messageTypes as $messageType => $messages) { |
254
|
|
|
// get messages of current data type in groups |
255
|
|
|
$groupedMessages = $this->groupArray($messages, 'message_id', true); |
256
|
|
|
|
257
|
|
|
// overwrite conditions with message IDs we already know |
258
|
|
|
$this->fetchOptions['params'] = []; |
259
|
|
|
$this->fetchOptions['where'] = []; |
260
|
|
|
$this->setMessageId($this->getMessageIdsFromResult($messages), 'message'); |
261
|
|
|
|
262
|
|
|
// query data |
263
|
|
|
$groupedResult = $this->getMessageDataByType($messageType, false); |
264
|
|
|
// skip processing if there are no results (most likely all |
265
|
|
|
// messages of this type have been deleted) |
266
|
|
|
if (!is_array($groupedResult)) { |
267
|
|
|
continue; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
// go through each message to merge result with meta data |
271
|
|
|
foreach ($groupedMessages as $msgId => $msgMetaData) { |
272
|
|
|
// ignore non-exisiting key (might be deleted messages) |
273
|
|
|
if (!array_key_exists($msgId, $groupedResult)) { |
274
|
|
|
continue; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
// merge arrays |
278
|
|
|
$mergedArrays = $msgMetaData + $groupedResult[$msgId]; |
279
|
|
|
|
280
|
|
|
// remove unnecessary message_id (the ID is already the key) |
281
|
|
|
if (array_key_exists('message_id', $mergedArrays)) { |
282
|
|
|
unset($mergedArrays['message_id']); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
// save as output |
286
|
|
|
if ($groupByMessageType) { |
287
|
|
|
// remove unnecessary message_type_code (as it is already |
288
|
|
|
// grouped by it) |
289
|
|
|
if (array_key_exists('message_type_code', $mergedArrays)) { |
290
|
|
|
unset($mergedArrays['message_type_code']); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
$output[$messageType][$msgId] = $mergedArrays; |
294
|
|
|
} else { |
295
|
|
|
$output[$msgId] = $mergedArrays; |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
return $output; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Queries all available data for a message type. |
305
|
|
|
* |
306
|
|
|
* The return value should be an array in the same format as the one |
307
|
|
|
* returned by {@link getAllMessageData()} when $groupByMessageType is set |
308
|
|
|
* to false. Of course, however, only one message type is returned here. |
309
|
|
|
* |
310
|
|
|
* @param int $messageType The message type the messages belong to |
311
|
|
|
* @param bool $includeMetaData Set to true to also include the main |
312
|
|
|
* message table in your query. If you do so you |
313
|
|
|
* will also get the meta data of the message. |
314
|
|
|
* (default: true) |
315
|
|
|
* @throws XenForo_Exception |
316
|
|
|
* @return null|array |
317
|
|
|
*/ |
318
|
|
|
public function getMessageDataByType($messageType, $includeMetaData = true) |
319
|
|
|
{ |
320
|
|
|
/** @var array $output */ |
321
|
|
|
$output = []; |
322
|
|
|
|
323
|
|
|
// prepare query |
324
|
|
|
/** @var array $limitOptions */ |
325
|
|
|
$limitOptions = $this->prepareLimitFetchOptions($this->fetchOptions); |
326
|
|
|
|
327
|
|
|
// built query |
328
|
|
|
switch ($messageType) { |
329
|
|
|
case self::TYPE_DELIVERY_MESSAGE: |
330
|
|
|
/** @var Zend_Db_Table_Select $select */ |
331
|
|
|
$select = $this->_getDb()->select() |
332
|
|
|
->from(['message' => self::DB_TABLE_MESSAGES . '_delivery_receipt']) |
333
|
|
|
->joinInner( |
334
|
|
|
['ack_messages' => self::DB_TABLE_DELIVERY_RECEIPT], |
335
|
|
|
'message.message_id = ack_messages.message_id' |
336
|
|
|
); |
337
|
|
|
|
338
|
|
|
/** @var string $resultIndex index to use for additional data from query */ |
339
|
|
|
$resultIndex = 'ackmsgs'; |
340
|
|
|
break; |
341
|
|
|
|
342
|
|
View Code Duplication |
case self::TYPE_FILE_MESSAGE: |
|
|
|
|
343
|
|
|
/** @var Zend_Db_Table_Select $select */ |
344
|
|
|
$select = $this->_getDb()->select() |
345
|
|
|
->from(['message' => self::DB_TABLE_MESSAGES . '_file']) |
346
|
|
|
->joinInner( |
347
|
|
|
['filelist' => self::DB_TABLE_FILES], |
348
|
|
|
'filelist.message_id = message.message_id' |
349
|
|
|
); |
350
|
|
|
|
351
|
|
|
/** @var string $resultIndex index to use for additional data from query */ |
352
|
|
|
$resultIndex = 'files'; |
353
|
|
|
break; |
354
|
|
|
|
355
|
|
View Code Duplication |
case self::TYPE_IMAGE_MESSAGE: |
|
|
|
|
356
|
|
|
/** @var Zend_Db_Table_Select $select */ |
357
|
|
|
$select = $this->_getDb()->select() |
358
|
|
|
->from(['message' => self::DB_TABLE_MESSAGES . '_image']) |
359
|
|
|
->joinInner( |
360
|
|
|
['filelist' => self::DB_TABLE_FILES], |
361
|
|
|
'filelist.message_id = message.message_id' |
362
|
|
|
); |
363
|
|
|
|
364
|
|
|
/** @var string $resultIndex index to use for additional data from query */ |
365
|
|
|
$resultIndex = 'files'; |
366
|
|
|
break; |
367
|
|
|
|
368
|
|
|
case self::TYPE_TEXT_MESSAGE: |
369
|
|
|
/** @var Zend_Db_Table_Select $select */ |
370
|
|
|
$select = $this->_getDb()->select() |
371
|
|
|
->from(['message' => self::DB_TABLE_MESSAGES . '_text']); |
372
|
|
|
|
373
|
|
|
// although this is not strictly necessary, to ease the |
374
|
|
|
// processing the data later, we also index this |
375
|
|
|
/** @var string $resultIndex index to use for additional data from query */ |
376
|
|
|
$resultIndex = 'text'; |
377
|
|
|
break; |
378
|
|
|
|
379
|
|
|
default: |
380
|
|
|
throw new XenForo_Exception(new XenForo_Phrase('threemagw_unknown_message_type')); |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
// add table if necessary |
384
|
|
|
if ($includeMetaData) { |
385
|
|
|
$select->joinInner( |
386
|
|
|
['metamessage' => self::DB_TABLE_MESSAGES], |
387
|
|
|
'message.message_id = metamessage.message_id' |
388
|
|
|
); |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
// general options for query |
392
|
|
|
$select |
393
|
|
|
->where($this->getConditionsForClause($this->fetchOptions['where'])) |
394
|
|
|
->order($this->getOrderByClause(self::ORDER_CHOICE, $this->fetchOptions)); |
395
|
|
|
|
396
|
|
|
// execute query |
397
|
|
|
/** @var array|null $result database query result */ |
398
|
|
|
$result = $this->_getDb()->fetchAll( |
399
|
|
|
$this->limitQueryResults( |
400
|
|
|
$select, |
401
|
|
|
$limitOptions['limit'], $limitOptions['offset']), |
402
|
|
|
$this->fetchOptions['params']); |
403
|
|
|
|
404
|
|
|
// throw error if data is missing |
405
|
|
|
if (!is_array($result)) { |
406
|
|
|
throw new XenForo_Exception(new XenForo_Phrase('threemagw_missing_database_data')); |
407
|
|
|
} |
408
|
|
|
// if there is no result, just return null |
409
|
|
|
if (empty($result)) { |
410
|
|
|
return null; |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
// group array by message ID |
414
|
|
|
$result = $this->groupArray($result, 'message_id'); |
415
|
|
|
|
416
|
|
|
// attributes to remove/push |
417
|
|
|
$removeAttributes = [ |
418
|
|
|
'message_id', |
419
|
|
|
'file_name', |
420
|
|
|
'mime_type', |
421
|
|
|
'file_size' |
422
|
|
|
]; |
423
|
|
|
if ($includeMetaData) { |
424
|
|
|
$removeAttributes = array_merge($removeAttributes, [ |
425
|
|
|
'message_type_code', |
426
|
|
|
'sender_threema_id', |
427
|
|
|
'date_send', |
428
|
|
|
'date_received' |
429
|
|
|
]); |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
// push general attributes one array up |
433
|
|
|
if (!$resultIndex) { |
434
|
|
|
throw new XenForo_Exception(new XenForo_Phrase('threemagw_unknown_message_type')); |
435
|
|
|
break; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
// go through each message |
439
|
|
|
foreach ($result as $msgId => $resultForId) { |
440
|
|
|
$output[$msgId] = []; |
441
|
|
|
$output[$msgId] = $this->pushArrayKeys($output[$msgId], |
442
|
|
|
$resultForId, |
443
|
|
|
$removeAttributes); |
444
|
|
|
$output[$msgId][$resultIndex] = $resultForId; |
445
|
|
|
|
446
|
|
|
// remove unnecessary message_id (the ID is already the key) |
447
|
|
|
if (array_key_exists('message_id', $output[$msgId])) { |
448
|
|
|
unset($output[$msgId]['message_id']); |
449
|
|
|
} |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
if (!$output) { |
|
|
|
|
453
|
|
|
return null; |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
return $output; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
/** |
460
|
|
|
* Returns only the meta data of one or more messages not depending on the |
461
|
|
|
* type of the message. |
462
|
|
|
* |
463
|
|
|
* @param bool $groupById When true groups the data by the message ID (default: false) |
464
|
|
|
* @param bool $ignoreInvalid When true removes data sets where the message content may be deleted (default: true) |
465
|
|
|
* @return null|array |
466
|
|
|
*/ |
467
|
|
|
public function getMessageMetaData($groupById = false, $ignoreInvalid = true) |
468
|
|
|
{ |
469
|
|
|
/** @var array $limitOptions */ |
470
|
|
|
$limitOptions = $this->prepareLimitFetchOptions($this->fetchOptions); |
471
|
|
|
|
472
|
|
|
/** @var array $result query result */ |
473
|
|
|
$result = $this->_getDb()->fetchAll( |
474
|
|
|
$this->limitQueryResults( |
475
|
|
|
$this->_getDb()->select() |
476
|
|
|
->from(['metamessage' => self::DB_TABLE_MESSAGES]) |
477
|
|
|
->where($this->getConditionsForClause($this->fetchOptions['where'])) |
478
|
|
|
->order($this->getOrderByClause(self::ORDER_CHOICE, $this->fetchOptions)), |
479
|
|
|
$limitOptions['limit'], $limitOptions['offset']), |
480
|
|
|
$this->fetchOptions['params']); |
481
|
|
|
|
482
|
|
|
// fail if there is no data |
483
|
|
|
if (!is_array($result) || !$result) { |
|
|
|
|
484
|
|
|
return null; |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
// remove invalid data sets (where message might be deleted) |
488
|
|
|
if ($ignoreInvalid) { |
489
|
|
|
foreach ($result as $i => $msgData) { |
490
|
|
|
if (!array_key_exists('message_type_code', $msgData) || |
491
|
|
|
!$msgData['message_type_code']) { |
492
|
|
|
unset($result[$i]); |
493
|
|
|
} |
494
|
|
|
} |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
// group array by message ID if wanted |
498
|
|
|
if ($groupById) { |
499
|
|
|
$result = $this->groupArray($result, 'message_id'); |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
return $result; |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* Removes entries or meta data fields from the meta data message table. |
507
|
|
|
* |
508
|
|
|
* Note that if the message(s) has/have other data saved in the database |
509
|
|
|
* the full deletion will fail. |
510
|
|
|
* Note: The number of where and params-options must be equal. You can |
511
|
|
|
* submit additional conditions with the first parameter. |
512
|
|
|
* Attention: This ignores the limit/offset clause for simplicity. |
513
|
|
|
* |
514
|
|
|
* @param string[] $additionalConditions Add additional where conditions if |
515
|
|
|
* neccessary. |
516
|
|
|
* @param string[] $removeOnlyField When set only the passed fields are |
517
|
|
|
* updated to "null" rather than deleting |
518
|
|
|
* the whole record. |
519
|
|
|
*/ |
520
|
|
|
public function removeMetaData(array $additionalConditions = [], array $removeOnlyField = []) |
521
|
|
|
{ |
522
|
|
|
if (!empty($removeOnlyField)) { |
523
|
|
|
$this->_getDb()->update( |
524
|
|
|
self::DB_TABLE_MESSAGES, |
525
|
|
|
array_fill_keys($removeOnlyField, null), |
526
|
|
|
array_merge( |
527
|
|
|
array_combine($this->fetchOptions['where'], $this->fetchOptions['params']), |
528
|
|
|
$additionalConditions |
529
|
|
|
) |
530
|
|
|
); |
531
|
|
|
} else { |
532
|
|
|
$this->_getDb()->delete( |
533
|
|
|
self::DB_TABLE_MESSAGES, |
534
|
|
|
array_merge( |
535
|
|
|
array_combine($this->fetchOptions['where'], $this->fetchOptions['params']), |
536
|
|
|
$additionalConditions |
537
|
|
|
) |
538
|
|
|
); |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
/** |
543
|
|
|
* Returns all available data from a list of message IDs. |
544
|
|
|
* |
545
|
|
|
* @param array[string] $messages The message result |
|
|
|
|
546
|
|
|
* @throws XenForo_Exception |
547
|
|
|
* @return null|array |
548
|
|
|
*/ |
549
|
|
|
protected function getMessageIdsFromResult(array $messages) |
550
|
|
|
{ |
551
|
|
|
// use PHP function if available (>= PHP 5.5.0) |
552
|
|
|
if (function_exists('array_column')) { |
553
|
|
|
return array_column($messages, 'message_id'); |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
// manually extract message_id from array |
557
|
|
|
$output = []; |
558
|
|
|
foreach ($messages as $message) { |
559
|
|
|
$output[] = $message['message_id']; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
return $output; |
563
|
|
|
} |
564
|
|
|
|
565
|
|
|
/** |
566
|
|
|
* Removes the specified keys from the second array and pushes them into |
567
|
|
|
* the first base array. |
568
|
|
|
* The subarray must be indexed by integers, where each index contains an |
569
|
|
|
* associative array with the keys to remove. |
570
|
|
|
* It assumes that the 0-index of $subArray is there, including the data, |
571
|
|
|
* which should be pushed to $baseArray. |
572
|
|
|
* |
573
|
|
|
* @param array $baseArray the main array, where the key/value pairs get to |
574
|
|
|
* @param array $subArray the array, which keys should be removed |
575
|
|
|
* @param string[] $removeKeys an array of keys, which should be removed |
576
|
|
|
* |
577
|
|
|
* @throws XenForo_Exception |
578
|
|
|
* @return false|array |
579
|
|
|
*/ |
580
|
|
|
protected function pushArrayKeys(array &$baseArray, array &$subArray, array $removeKeys) |
581
|
|
|
{ |
582
|
|
|
foreach ($removeKeys as $key) { |
583
|
|
|
// skip invalid keys |
584
|
|
|
if (!array_key_exists($key, $subArray[0])) { |
585
|
|
|
continue; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
// move value from subarray to base array |
589
|
|
|
$baseArray[$key] = $subArray[0][$key]; |
590
|
|
|
|
591
|
|
|
// then delete it from sub array |
592
|
|
|
/** @var int $subArrayCount */ |
593
|
|
|
$subArrayCount = count($subArray); |
594
|
|
|
for ($i = 0; $i < $subArrayCount; $i++) { |
595
|
|
|
unset($subArray[$i][$key]); |
596
|
|
|
} |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
return $baseArray; |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* Groups an array by using the value of a specific index in it. |
604
|
|
|
* |
605
|
|
|
* @param array $array the array, which is sued as the base |
606
|
|
|
* @param string|int $indexKey the value of the key, which should be used |
607
|
|
|
* for indexing |
608
|
|
|
* @param bool $ignoreIndex Set to true to ignore multiple values in |
609
|
|
|
* $array. If activated only the last key of |
610
|
|
|
* $array will be placed into the group and |
611
|
|
|
* it will be the only key. This is only |
612
|
|
|
* useful if you know for sure that only one |
613
|
|
|
* key is available. |
614
|
|
|
* |
615
|
|
|
* @return array |
616
|
|
|
*/ |
617
|
|
|
public function groupArray(array $array, $indexKey, $ignoreIndex = false) |
618
|
|
|
{ |
619
|
|
|
/** @var array $output */ |
620
|
|
|
$output = []; |
621
|
|
|
foreach ($array as $i => $value) { |
622
|
|
|
if ($ignoreIndex) { |
623
|
|
|
$output[$value[$indexKey]] = $value; |
624
|
|
|
} else { |
625
|
|
|
$output[$value[$indexKey]][] = $value; |
626
|
|
|
} |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
return $output; |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
/** |
633
|
|
|
* Appends a WHERE condition for either a string or an array. |
634
|
|
|
* |
635
|
|
|
* It automatically chooses between a simple `this = ?` or a more complex |
636
|
|
|
* `this IN (?, ?, ...)`. |
637
|
|
|
* |
638
|
|
|
* @param string $attName the name of the required attribut |
639
|
|
|
* @param string|array $attValue the value, which should be required |
640
|
|
|
* |
641
|
|
|
* @return array |
|
|
|
|
642
|
|
|
*/ |
643
|
|
|
protected function appendMixedCondition($attName, $attValue) |
644
|
|
|
{ |
645
|
|
|
// convert arrays with only one value |
646
|
|
|
if (is_array($attValue) && count($attValue) == 1) { |
647
|
|
|
$attValue = $attValue[0]; |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
if (!is_array($attValue)) { |
651
|
|
|
$this->fetchOptions['where'][] = $attName . ' = ?'; |
652
|
|
|
$this->fetchOptions['params'][] = $attValue; |
653
|
|
|
} else { |
654
|
|
|
$this->fetchOptions['where'][] = $attName . ' IN (' . implode(', ', array_fill(0, count($attValue), '?')) . ')'; |
655
|
|
|
$this->fetchOptions['params'] += $attValue; |
656
|
|
|
} |
657
|
|
|
} |
658
|
|
|
} |
659
|
|
|
|
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: