]> git.mxchange.org Git - friendica.git/blob - src/Database/PostUpdate.php
Check the edit date before storing history
[friendica.git] / src / Database / PostUpdate.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Database;
23
24 use Friendica\Core\Logger;
25 use Friendica\Core\Protocol;
26 use Friendica\DI;
27 use Friendica\Model\Contact;
28 use Friendica\Model\Conversation;
29 use Friendica\Model\GServer;
30 use Friendica\Model\Item;
31 use Friendica\Model\ItemURI;
32 use Friendica\Model\Photo;
33 use Friendica\Model\Post;
34 use Friendica\Model\Post\Category;
35 use Friendica\Model\Tag;
36 use Friendica\Model\Verb;
37 use Friendica\Protocol\ActivityPub\Processor;
38 use Friendica\Protocol\ActivityPub\Receiver;
39 use Friendica\Util\JsonLD;
40 use Friendica\Util\Strings;
41
42 /**
43  * These database-intensive post update routines are meant to be executed in the background by the cronjob.
44  *
45  * If there is a need for a intensive migration after a database structure change, update this file
46  * by adding a new method at the end with the number of the new DB_UPDATE_VERSION.
47  */
48 class PostUpdate
49 {
50         // Needed for the helper function to read from the legacy term table
51         const OBJECT_TYPE_POST  = 1;
52
53         const VERSION = 1452;
54
55         /**
56          * Calls the post update functions
57          */
58         public static function update()
59         {
60                 if (!self::update1297()) {
61                         return false;
62                 }
63                 if (!self::update1322()) {
64                         return false;
65                 }
66                 if (!self::update1329()) {
67                         return false;
68                 }
69                 if (!self::update1341()) {
70                         return false;
71                 }
72                 if (!self::update1342()) {
73                         return false;
74                 }
75                 if (!self::update1345()) {
76                         return false;
77                 }
78                 if (!self::update1346()) {
79                         return false;
80                 }
81                 if (!self::update1347()) {
82                         return false;
83                 }
84                 if (!self::update1348()) {
85                         return false;
86                 }
87                 if (!self::update1349()) {
88                         return false;
89                 }
90                 if (!self::update1383()) {
91                         return false;
92                 }
93                 if (!self::update1384()) {
94                         return false;
95                 }
96                 if (!self::update1400()) {
97                         return false;
98                 }
99                 if (!self::update1424()) {
100                         return false;
101                 }
102                 if (!self::update1425()) {
103                         return false;
104                 }
105                 if (!self::update1426()) {
106                         return false;
107                 }
108                 if (!self::update1427()) {
109                         return false;
110                 }
111                 if (!self::update1452()) {
112                         return false;
113                 }
114                 return true;
115         }
116
117         /**
118          * Set the delivery queue count to a negative value for all items preceding the feature.
119          *
120          * @return bool "true" when the job is done
121          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
122          */
123         private static function update1297()
124         {
125                 // Was the script completed?
126                 if (DI::config()->get('system', 'post_update_version') >= 1297) {
127                         return true;
128                 }
129
130                 if (!DBStructure::existsTable('item-delivery-data')) {
131                         DI::config()->set('system', 'post_update_version', 1297);
132                         return true;
133                 }
134
135                 $max_item_delivery_data = DBA::selectFirst('item-delivery-data', ['iid'], ['queue_count > 0 OR queue_done > 0'], ['order' => ['iid']]);
136                 $max_iid = $max_item_delivery_data['iid'];
137
138                 Logger::info('Start update1297 with max iid: ' . $max_iid);
139
140                 $condition = ['`queue_count` = 0 AND `iid` < ?', $max_iid];
141
142                 DBA::update('item-delivery-data', ['queue_count' => -1], $condition);
143
144                 if (DBA::errorNo() != 0) {
145                         Logger::error('Database error ' . DBA::errorNo() . ':' . DBA::errorMessage());
146                         return false;
147                 }
148
149                 Logger::info('Processed rows: ' . DBA::affectedRows());
150
151                 DI::config()->set('system', 'post_update_version', 1297);
152
153                 Logger::info('Done');
154
155                 return true;
156         }
157         /**
158          * Remove contact duplicates
159          *
160          * @return bool "true" when the job is done
161          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
162          */
163         private static function update1322()
164         {
165                 // Was the script completed?
166                 if (DI::config()->get('system', 'post_update_version') >= 1322) {
167                         return true;
168                 }
169
170                 Logger::info('Start');
171
172                 $contacts = DBA::p("SELECT `nurl`, `uid` FROM `contact`
173                         WHERE EXISTS (SELECT `nurl` FROM `contact` AS `c2`
174                                 WHERE `c2`.`nurl` = `contact`.`nurl` AND `c2`.`id` != `contact`.`id` AND `c2`.`uid` = `contact`.`uid` AND `c2`.`network` IN (?, ?, ?) AND NOT `deleted`)
175                         AND (`network` IN (?, ?, ?) OR (`uid` = ?)) AND NOT `deleted` GROUP BY `nurl`, `uid`",
176                         Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB,
177                         Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 0);
178
179                 while ($contact = DBA::fetch($contacts)) {
180                         Logger::info('Remove duplicates', ['nurl' => $contact['nurl'], 'uid' => $contact['uid']]);
181                         Contact::removeDuplicates($contact['nurl'], $contact['uid']);
182                 }
183
184                 DBA::close($contact);
185                 DI::config()->set('system', 'post_update_version', 1322);
186
187                 Logger::info('Done');
188
189                 return true;
190         }
191
192         /**
193          * update user notification data
194          *
195          * @return bool "true" when the job is done
196          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
197          */
198         private static function update1329()
199         {
200                 // Was the script completed?
201                 if (DI::config()->get('system', 'post_update_version') >= 1329) {
202                         return true;
203                 }
204
205                 if (!DBStructure::existsTable('item')) {
206                         DI::config()->set('system', 'post_update_version', 1329);
207                         return true;
208                 }
209
210                 $id = DI::config()->get('system', 'post_update_version_1329_id', 0);
211
212                 Logger::info('Start', ['item' => $id]);
213
214                 $start_id = $id;
215                 $rows = 0;
216                 $condition = ["`id` > ?", $id];
217                 $params = ['order' => ['id'], 'limit' => 10000];
218                 $items = DBA::select('item', ['id', 'uri-id', 'uid'], $condition, $params);
219
220                 if (DBA::errorNo() != 0) {
221                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
222                         return false;
223                 }
224
225                 while ($item = DBA::fetch($items)) {
226                         $id = $item['id'];
227
228                         Post\UserNotification::setNotification($item['uri-id'], $item['uid']);
229
230                         ++$rows;
231                 }
232                 DBA::close($items);
233
234                 DI::config()->set('system', 'post_update_version_1329_id', $id);
235
236                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
237
238                 if ($start_id == $id) {
239                         DI::config()->set('system', 'post_update_version', 1329);
240                         Logger::info('Done');
241                         return true;
242                 }
243
244                 return false;
245         }
246
247         /**
248          * Fill the "tag" table with tags and mentions from the body
249          *
250          * @return bool "true" when the job is done
251          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
252          */
253         private static function update1341()
254         {
255                 // Was the script completed?
256                 if (DI::config()->get('system', 'post_update_version') >= 1341) {
257                         return true;
258                 }
259
260                 if (!DBStructure::existsTable('item-content')) {
261                         DI::config()->set('system', 'post_update_version', 1342);
262                         return true;
263                 }
264
265                 $id = DI::config()->get('system', 'post_update_version_1341_id', 0);
266
267                 Logger::info('Start', ['item' => $id]);
268
269                 $rows = 0;
270
271                 $items = DBA::p("SELECT `uri-id`,`body` FROM `item-content` WHERE
272                         (`body` LIKE ? OR `body` LIKE ? OR `body` LIKE ?) AND `uri-id` >= ?
273                         ORDER BY `uri-id` LIMIT 100000", '%#%', '%@%', '%!%', $id);
274
275                 if (DBA::errorNo() != 0) {
276                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
277                         return false;
278                 }
279
280                 while ($item = DBA::fetch($items)) {
281                         Tag::storeFromBody($item['uri-id'], $item['body'], '#!@', false);
282                         $id = $item['uri-id'];
283                         ++$rows;
284                         if ($rows % 1000 == 0) {
285                                 DI::config()->set('system', 'post_update_version_1341_id', $id);
286                         }
287                 }
288                 DBA::close($items);
289
290                 DI::config()->set('system', 'post_update_version_1341_id', $id);
291
292                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
293
294                 // When there are less than 1,000 items processed this means that we reached the end
295                 // The other entries will then be processed with the regular functionality
296                 if ($rows < 1000) {
297                         DI::config()->set('system', 'post_update_version', 1341);
298                         Logger::info('Done');
299                         return true;
300                 }
301
302                 return false;
303         }
304
305         /**
306          * Fill the "tag" table with tags and mentions from the "term" table
307          *
308          * @return bool "true" when the job is done
309          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
310          */
311         private static function update1342()
312         {
313                 // Was the script completed?
314                 if (DI::config()->get('system', 'post_update_version') >= 1342) {
315                         return true;
316                 }
317
318                 if (!DBStructure::existsTable('term') || !DBStructure::existsTable('item-content')) {
319                         DI::config()->set('system', 'post_update_version', 1342);
320                         return true;
321                 }
322
323                 $id = DI::config()->get('system', 'post_update_version_1342_id', 0);
324
325                 Logger::info('Start', ['item' => $id]);
326
327                 $rows = 0;
328
329                 $terms = DBA::p("SELECT `term`.`tid`, `item`.`uri-id`, `term`.`type`, `term`.`term`, `term`.`url`, `item-content`.`body`
330                         FROM `term`
331                         INNER JOIN `item` ON `item`.`id` = `term`.`oid`
332                         INNER JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
333                         WHERE term.type IN (?, ?, ?, ?) AND `tid` >= ? ORDER BY `tid` LIMIT 100000",
334                         Tag::HASHTAG, Tag::MENTION, Tag::EXCLUSIVE_MENTION, Tag::IMPLICIT_MENTION, $id);
335
336                 if (DBA::errorNo() != 0) {
337                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
338                         return false;
339                 }
340
341                 while ($term = DBA::fetch($terms)) {
342                         if (($term['type'] == Tag::MENTION) && !empty($term['url']) && !strstr($term['body'], $term['url'])) {
343                 $condition = ['nurl' => Strings::normaliseLink($term['url']), 'uid' => 0, 'deleted' => false];
344                 $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
345                 if (!DBA::isResult($contact)) {
346                         $ssl_url = str_replace('http://', 'https://', $term['url']);
347                         $condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $term['url'], Strings::normaliseLink($term['url']), $ssl_url, 0];
348                         $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
349                 }
350
351                 if (DBA::isResult($contact) && (!strstr($term['body'], $contact['url']) && (empty($contact['alias']) || !strstr($term['body'], $contact['alias'])))) {
352                         $term['type'] = Tag::IMPLICIT_MENTION;
353                 }
354                         }
355
356                         Tag::store($term['uri-id'], $term['type'], $term['term'], $term['url'], false);
357
358                         $id = $term['tid'];
359                         ++$rows;
360                         if ($rows % 1000 == 0) {
361                                 DI::config()->set('system', 'post_update_version_1342_id', $id);
362                         }
363                 }
364                 DBA::close($terms);
365
366                 DI::config()->set('system', 'post_update_version_1342_id', $id);
367
368                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
369
370                 // When there are less than 1,000 items processed this means that we reached the end
371                 // The other entries will then be processed with the regular functionality
372                 if ($rows < 1000) {
373                         DI::config()->set('system', 'post_update_version', 1342);
374                         Logger::info('Done');
375                         return true;
376                 }
377
378                 return false;
379         }
380
381         /**
382          * Fill the "post-delivery-data" table with data from the "item-delivery-data" table
383          *
384          * @return bool "true" when the job is done
385          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
386          */
387         private static function update1345()
388         {
389                 // Was the script completed?
390                 if (DI::config()->get('system', 'post_update_version') >= 1345) {
391                         return true;
392                 }
393
394                 if (!DBStructure::existsTable('item-delivery-data')) {
395                         DI::config()->set('system', 'post_update_version', 1345);
396                         return true;
397                 }
398
399                 $id = DI::config()->get('system', 'post_update_version_1345_id', 0);
400
401                 Logger::info('Start', ['item' => $id]);
402
403                 $rows = 0;
404
405                 $deliveries = DBA::p("SELECT `uri-id`, `iid`, `item-delivery-data`.`postopts`, `item-delivery-data`.`inform`,
406                         `queue_count`, `queue_done`, `activitypub`, `dfrn`, `diaspora`, `ostatus`, `legacy_dfrn`, `queue_failed`
407                         FROM `item-delivery-data`
408                         INNER JOIN `item` ON `item`.`id` = `item-delivery-data`.`iid`
409                         WHERE `iid` >= ? ORDER BY `iid` LIMIT 10000", $id);
410
411                 if (DBA::errorNo() != 0) {
412                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
413                         return false;
414                 }
415
416                 while ($delivery = DBA::fetch($deliveries)) {
417                         $id = $delivery['iid'];
418                         unset($delivery['iid']);
419                         DBA::insert('post-delivery-data', $delivery, Database::INSERT_UPDATE);
420                         ++$rows;
421                 }
422                 DBA::close($deliveries);
423
424                 DI::config()->set('system', 'post_update_version_1345_id', $id);
425
426                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
427
428                 // When there are less than 100 items processed this means that we reached the end
429                 // The other entries will then be processed with the regular functionality
430                 if ($rows < 100) {
431                         DI::config()->set('system', 'post_update_version', 1345);
432                         Logger::info('Done');
433                         return true;
434                 }
435
436                 return false;
437         }
438
439         /**
440          * Generates the legacy item.file field string from an item ID.
441          * Includes only file and category terms.
442          *
443          * @param int $item_id
444          * @return string
445          * @throws \Exception
446          */
447         private static function fileTextFromItemId($item_id)
448         {
449                 $file_text = '';
450
451                 $condition = ['otype' => self::OBJECT_TYPE_POST, 'oid' => $item_id, 'type' => [Category::FILE, Category::CATEGORY]];
452                 $tags = DBA::selectToArray('term', ['type', 'term', 'url'], $condition);
453                 foreach ($tags as $tag) {
454                         if ($tag['type'] == Category::CATEGORY) {
455                                 $file_text .= '<' . $tag['term'] . '>';
456                         } else {
457                                 $file_text .= '[' . $tag['term'] . ']';
458                         }
459                 }
460
461                 return $file_text;
462         }
463
464         /**
465          * Fill the "tag" table with tags and mentions from the "term" table
466          *
467          * @return bool "true" when the job is done
468          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
469          */
470         private static function update1346()
471         {
472                 // Was the script completed?
473                 if (DI::config()->get('system', 'post_update_version') >= 1346) {
474                         return true;
475                 }
476
477                 if (!DBStructure::existsTable('term')) {
478                         DI::config()->set('system', 'post_update_version', 1346);
479                         return true;
480                 }
481
482                 $id = DI::config()->get('system', 'post_update_version_1346_id', 0);
483
484                 Logger::info('Start', ['item' => $id]);
485
486                 $rows = 0;
487
488                 $terms = DBA::select('term', ['oid'],
489                         ["`type` IN (?, ?) AND `oid` >= ?", Category::CATEGORY, Category::FILE, $id],
490                         ['order' => ['oid'], 'limit' => 1000, 'group_by' => ['oid']]);
491
492                 if (DBA::errorNo() != 0) {
493                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
494                         return false;
495                 }
496
497                 while ($term = DBA::fetch($terms)) {
498                         $item = Post::selectFirst(['uri-id', 'uid'], ['id' => $term['oid']]);
499                         if (!DBA::isResult($item)) {
500                                 continue;
501                         }
502
503                         $file = self::fileTextFromItemId($term['oid']);
504                         if (!empty($file)) {
505                                 Category::storeTextByURIId($item['uri-id'], $item['uid'], $file);
506                         }
507
508                         $id = $term['oid'];
509                         ++$rows;
510                         if ($rows % 100 == 0) {
511                                 DI::config()->set('system', 'post_update_version_1346_id', $id);
512                         }
513                 }
514                 DBA::close($terms);
515
516                 DI::config()->set('system', 'post_update_version_1346_id', $id);
517
518                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
519
520                 // When there are less than 10 items processed this means that we reached the end
521                 // The other entries will then be processed with the regular functionality
522                 if ($rows < 10) {
523                         DI::config()->set('system', 'post_update_version', 1346);
524                         Logger::info('Done');
525                         return true;
526                 }
527
528                 return false;
529         }
530
531         /**
532          * update the "vid" (verb) field in the item table
533          *
534          * @return bool "true" when the job is done
535          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
536          * @throws \ImagickException
537          */
538         private static function update1347()
539         {
540                 // Was the script completed?
541                 if (DI::config()->get("system", "post_update_version") >= 1347) {
542                         return true;
543                 }
544
545                 if (!DBStructure::existsTable('item-activity') || !DBStructure::existsTable('item')) {
546                         DI::config()->set('system', 'post_update_version', 1347);
547                         return true;
548                 }
549
550                 $id = DI::config()->get("system", "post_update_version_1347_id", 0);
551
552                 Logger::info('Start', ['item' => $id]);
553
554                 $start_id = $id;
555                 $rows = 0;
556
557                 $items = DBA::p("SELECT `item`.`id`, `item`.`verb` AS `item-verb`, `item-content`.`verb`, `item-activity`.`activity`
558                         FROM `item` LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
559                         LEFT JOIN `item-activity` ON `item-activity`.`uri-id` = `item`.`uri-id` AND `item`.`gravity` = ?
560                         WHERE `item`.`id` >= ? AND `item`.`vid` IS NULL ORDER BY `item`.`id` LIMIT 10000", GRAVITY_ACTIVITY, $id);
561
562                 if (DBA::errorNo() != 0) {
563                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
564                         return false;
565                 }
566
567                 while ($item = DBA::fetch($items)) {
568                         $id = $item['id'];
569                         $verb = $item['item-verb'];
570                         if (empty($verb)) {
571                                 $verb = $item['verb'];
572                         }
573                         if (empty($verb) && is_int($item['activity'])) {
574                                 $verb = Item::ACTIVITIES[$item['activity']];
575                         }
576                         if (empty($verb)) {
577                                 continue;
578                         }
579
580                         DBA::update('item', ['vid' => Verb::getID($verb)], ['id' => $item['id']]);
581                         ++$rows;
582                 }
583                 DBA::close($items);
584
585                 DI::config()->set("system", "post_update_version_1347_id", $id);
586
587                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
588
589                 if ($start_id == $id) {
590                         DI::config()->set("system", "post_update_version", 1347);
591                         Logger::info('Done');
592                         return true;
593                 }
594
595                 return false;
596         }
597
598         /**
599          * update the "gsid" (global server id) field in the contact table
600          *
601          * @return bool "true" when the job is done
602          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
603          * @throws \ImagickException
604          */
605         private static function update1348()
606         {
607                 // Was the script completed?
608                 if (DI::config()->get("system", "post_update_version") >= 1348) {
609                         return true;
610                 }
611
612                 $id = DI::config()->get("system", "post_update_version_1348_id", 0);
613
614                 Logger::info('Start', ['contact' => $id]);
615
616                 $start_id = $id;
617                 $rows = 0;
618                 $condition = ["`id` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
619                 $params = ['order' => ['id'], 'limit' => 10000];
620                 $contacts = DBA::select('contact', ['id', 'baseurl'], $condition, $params);
621
622                 if (DBA::errorNo() != 0) {
623                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
624                         return false;
625                 }
626
627                 while ($contact = DBA::fetch($contacts)) {
628                         $id = $contact['id'];
629
630                         DBA::update('contact',
631                                 ['gsid' => GServer::getID($contact['baseurl'], true), 'baseurl' => GServer::cleanURL($contact['baseurl'])],
632                                 ['id' => $contact['id']]);
633
634                         ++$rows;
635                 }
636                 DBA::close($contacts);
637
638                 DI::config()->set("system", "post_update_version_1348_id", $id);
639
640                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
641
642                 if ($start_id == $id) {
643                         DI::config()->set("system", "post_update_version", 1348);
644                         Logger::info('Done');
645                         return true;
646                 }
647
648                 return false;
649         }
650
651         /**
652          * update the "gsid" (global server id) field in the apcontact table
653          *
654          * @return bool "true" when the job is done
655          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
656          * @throws \ImagickException
657          */
658         private static function update1349()
659         {
660                 // Was the script completed?
661                 if (DI::config()->get("system", "post_update_version") >= 1349) {
662                         return true;
663                 }
664
665                 $id = DI::config()->get("system", "post_update_version_1349_id", '');
666
667                 Logger::info('Start', ['apcontact' => $id]);
668
669                 $start_id = $id;
670                 $rows = 0;
671                 $condition = ["`url` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
672                 $params = ['order' => ['url'], 'limit' => 10000];
673                 $apcontacts = DBA::select('apcontact', ['url', 'baseurl'], $condition, $params);
674
675                 if (DBA::errorNo() != 0) {
676                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
677                         return false;
678                 }
679
680                 while ($apcontact = DBA::fetch($apcontacts)) {
681                         $id = $apcontact['url'];
682
683                         DBA::update('apcontact',
684                                 ['gsid' => GServer::getID($apcontact['baseurl'], true), 'baseurl' => GServer::cleanURL($apcontact['baseurl'])],
685                                 ['url' => $apcontact['url']]);
686
687                         ++$rows;
688                 }
689                 DBA::close($apcontacts);
690
691                 DI::config()->set("system", "post_update_version_1349_id", $id);
692
693                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
694
695                 if ($start_id == $id) {
696                         DI::config()->set("system", "post_update_version", 1349);
697                         Logger::info('Done');
698                         return true;
699                 }
700
701                 return false;
702         }
703
704         /**
705          * Remove orphaned photo entries
706          *
707          * @return bool "true" when the job is done
708          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
709          * @throws \ImagickException
710          */
711         private static function update1383()
712         {
713                 // Was the script completed?
714                 if (DI::config()->get("system", "post_update_version") >= 1383) {
715                         return true;
716                 }
717
718                 Logger::info('Start');
719
720                 $deleted = 0;
721                 $avatar = [4 => 'photo', 5 => 'thumb', 6 => 'micro'];
722
723                 $photos = DBA::select('photo', ['id', 'contact-id', 'resource-id', 'scale'], ["`contact-id` != ? AND `album` = ?", 0, Photo::CONTACT_PHOTOS]);
724                 while ($photo = DBA::fetch($photos)) {
725                         $delete = !in_array($photo['scale'], [4, 5, 6]);
726
727                         if (!$delete) {
728                                 // Check if there is a contact entry with that photo
729                                 $delete = !DBA::exists('contact', ["`id` = ? AND `" . $avatar[$photo['scale']] . "` LIKE ?",
730                                         $photo['contact-id'], '%' . $photo['resource-id'] . '%']);
731                         }
732
733                         if ($delete) {
734                                 Photo::delete(['id' => $photo['id']]);
735                                 $deleted++;
736                         }
737                 }
738                 DBA::close($photos);
739
740                 DI::config()->set("system", "post_update_version", 1383);
741                 Logger::info('Done', ['deleted' => $deleted]);
742                 return true;
743         }
744
745         /**
746          * update the "hash" field in the photo table
747          *
748          * @return bool "true" when the job is done
749          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
750          * @throws \ImagickException
751          */
752         private static function update1384()
753         {
754                 // Was the script completed?
755                 if (DI::config()->get("system", "post_update_version") >= 1384) {
756                         return true;
757                 }
758
759                 $condition = ["`hash` IS NULL"];
760                 Logger::info('Start', ['rest' => DBA::count('photo', $condition)]);
761
762                 $rows = 0;
763                 $photos = DBA::select('photo', [], $condition, ['limit' => 100]);
764
765                 if (DBA::errorNo() != 0) {
766                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
767                         return false;
768                 }
769
770                 while ($photo = DBA::fetch($photos)) {
771                         $img = Photo::getImageForPhoto($photo);
772                         if (!empty($img)) {
773                                 $md5 = md5($img->asString());
774                         } else {
775                                 $md5 = '';
776                         }
777                         DBA::update('photo', ['hash' => $md5], ['id' => $photo['id']]);
778                         ++$rows;
779                 }
780                 DBA::close($photos);
781
782                 Logger::info('Processed', ['rows' => $rows]);
783
784                 if ($rows <= 100) {
785                         DI::config()->set("system", "post_update_version", 1384);
786                         Logger::info('Done');
787                         return true;
788                 }
789
790                 return false;
791         }
792
793         /**
794          * update the "external-id" field in the post table
795          *
796          * @return bool "true" when the job is done
797          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
798          * @throws \ImagickException
799          */
800         private static function update1400()
801         {
802                 // Was the script completed?
803                 if (DI::config()->get("system", "post_update_version") >= 1400) {
804                         return true;
805                 }
806
807                 if (!DBStructure::existsTable('item')) {
808                         DI::config()->set("system", "post_update_version", 1400);
809                         return true;
810                 }
811
812                 $condition = ["`extid` != ? AND EXISTS(SELECT `id` FROM `post-user` WHERE `uri-id` = `item`.`uri-id` AND `uid` = `item`.`uid` AND `external-id` IS NULL)", ''];
813                 Logger::info('Start', ['rest' => DBA::count('item', $condition)]);
814
815                 $rows = 0;
816                 $items = DBA::select('item', ['uri-id', 'uid', 'extid'], $condition, ['order' => ['id'], 'limit' => 10000]);
817
818                 if (DBA::errorNo() != 0) {
819                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
820                         return false;
821                 }
822
823                 while ($item = DBA::fetch($items)) {
824                         Post::update(['external-id' => ItemURI::getIdByURI($item['extid'])], ['uri-id' => $item['uri-id'], 'uid' => $item['uid']]);
825                         ++$rows;
826                 }
827                 DBA::close($items);
828
829                 Logger::info('Processed', ['rows' => $rows]);
830
831                 if ($rows <= 100) {
832                         DI::config()->set("system", "post_update_version", 1400);
833                         Logger::info('Done');
834                         return true;
835                 }
836
837                 return false;
838         }
839
840         /**
841          * update the "uri-id" field in the contact table
842          *
843          * @return bool "true" when the job is done
844          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
845          * @throws \ImagickException
846          */
847         private static function update1424()
848         {
849                 // Was the script completed?
850                 if (DI::config()->get("system", "post_update_version") >= 1424) {
851                         return true;
852                 }
853
854                 $condition = ["`uri-id` IS NULL"];
855                 Logger::info('Start', ['rest' => DBA::count('contact', $condition)]);
856
857                 $rows = 0;
858                 $contacts = DBA::select('contact', ['id', 'url'], $condition, ['limit' => 1000]);
859
860                 if (DBA::errorNo() != 0) {
861                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
862                         return false;
863                 }
864
865                 while ($contact = DBA::fetch($contacts)) {
866                         DBA::update('contact', ['uri-id' => ItemURI::getIdByURI($contact['url'])], ['id' => $contact['id']]);
867                         ++$rows;
868                 }
869                 DBA::close($contacts);
870
871                 Logger::info('Processed', ['rows' => $rows]);
872
873                 if ($rows <= 100) {
874                         DI::config()->set("system", "post_update_version", 1424);
875                         Logger::info('Done');
876                         return true;
877                 }
878
879                 return false;
880         }
881
882         /**
883          * update the "uri-id" field in the fcontact table
884          *
885          * @return bool "true" when the job is done
886          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
887          * @throws \ImagickException
888          */
889         private static function update1425()
890         {
891                 // Was the script completed?
892                 if (DI::config()->get("system", "post_update_version") >= 1425) {
893                         return true;
894                 }
895
896                 $condition = ["`uri-id` IS NULL"];
897                 Logger::info('Start', ['rest' => DBA::count('fcontact', $condition)]);
898
899                 $rows = 0;
900                 $fcontacts = DBA::select('fcontact', ['id', 'url', 'guid'], $condition, ['limit' => 1000]);
901
902                 if (DBA::errorNo() != 0) {
903                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
904                         return false;
905                 }
906
907                 while ($fcontact = DBA::fetch($fcontacts)) {
908                         if (!empty($fcontact['guid'])) {
909                                 $uriid = ItemURI::insert(['uri' => $fcontact['url'], 'guid' => $fcontact['guid']]);
910                         } else {
911                                 $uriid = ItemURI::getIdByURI($fcontact['url']);
912                         }
913                         DBA::update('fcontact', ['uri-id' => $uriid], ['id' => $fcontact['id']]);
914                         ++$rows;
915                 }
916                 DBA::close($fcontacts);
917
918                 Logger::info('Processed', ['rows' => $rows]);
919
920                 if ($rows <= 100) {
921                         DI::config()->set("system", "post_update_version", 1425);
922                         Logger::info('Done');
923                         return true;
924                 }
925
926                 return false;
927         }
928
929         /**
930          * update the "uri-id" field in the apcontact table
931          *
932          * @return bool "true" when the job is done
933          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
934          * @throws \ImagickException
935          */
936         private static function update1426()
937         {
938                 // Was the script completed?
939                 if (DI::config()->get("system", "post_update_version") >= 1426) {
940                         return true;
941                 }
942
943                 $condition = ["`uri-id` IS NULL"];
944                 Logger::info('Start', ['rest' => DBA::count('apcontact', $condition)]);
945
946                 $rows = 0;
947                 $apcontacts = DBA::select('apcontact', ['url', 'uuid'], $condition, ['limit' => 1000]);
948
949                 if (DBA::errorNo() != 0) {
950                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
951                         return false;
952                 }
953
954                 while ($apcontact = DBA::fetch($apcontacts)) {
955                         if (!empty($apcontact['uuid'])) {
956                                 $uriid = ItemURI::insert(['uri' => $apcontact['url'], 'guid' => $apcontact['uuid']]);
957                         } else {
958                                 $uriid = ItemURI::getIdByURI($apcontact['url']);
959                         }
960                         DBA::update('apcontact', ['uri-id' => $uriid], ['url' => $apcontact['url']]);
961                         ++$rows;
962                 }
963                 DBA::close($apcontacts);
964
965                 Logger::info('Processed', ['rows' => $rows]);
966
967                 if ($rows <= 100) {
968                         DI::config()->set("system", "post_update_version", 1426);
969                         Logger::info('Done');
970                         return true;
971                 }
972
973                 return false;
974         }
975
976         /**
977          * update the "uri-id" field in the event table
978          *
979          * @return bool "true" when the job is done
980          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
981          * @throws \ImagickException
982          */
983         private static function update1427()
984         {
985                 // Was the script completed?
986                 if (DI::config()->get("system", "post_update_version") >= 1427) {
987                         return true;
988                 }
989
990                 $condition = ["`uri-id` IS NULL"];
991                 Logger::info('Start', ['rest' => DBA::count('event', $condition)]);
992
993                 $rows = 0;
994                 $events = DBA::select('event', ['id', 'uri', 'guid'], $condition, ['limit' => 1000]);
995
996                 if (DBA::errorNo() != 0) {
997                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
998                         return false;
999                 }
1000
1001                 while ($event = DBA::fetch($events)) {
1002                         if (!empty($event['guid'])) {
1003                                 $uriid = ItemURI::insert(['uri' => $event['uri'], 'guid' => $event['guid']]);
1004                         } else {
1005                                 $uriid = ItemURI::getIdByURI($event['uri']);
1006                         }
1007                         DBA::update('event', ['uri-id' => $uriid], ['id' => $event['id']]);
1008                         ++$rows;
1009                 }
1010                 DBA::close($events);
1011
1012                 Logger::info('Processed', ['rows' => $rows]);
1013
1014                 if ($rows <= 100) {
1015                         DI::config()->set("system", "post_update_version", 1427);
1016                         Logger::info('Done');
1017                         return true;
1018                 }
1019
1020                 return false;
1021         }
1022
1023         /**
1024          * Fill the receivers of the post via the raw source
1025          *
1026          * @return bool "true" when the job is done
1027          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
1028          * @throws \ImagickException
1029          */
1030         private static function update1452()
1031         {
1032                 // Was the script completed?
1033                 if (DI::config()->get('system', 'post_update_version') >= 1452) {
1034                         return true;
1035                 }
1036
1037                 $id = DI::config()->get('system', 'post_update_version_1452_id', 0);
1038
1039                 Logger::info('Start', ['uri-id' => $id]);
1040
1041                 $rows     = 0;
1042                 $received = '';
1043
1044                 $conversations = DBA::p("SELECT `post-view`.`uri-id`, `conversation`.`source`, `conversation`.`received` FROM `conversation`
1045                         INNER JOIN `post-view` ON `post-view`.`uri` = `conversation`.`item-uri`
1046                         WHERE NOT `source` IS NULL AND `conversation`.`protocol` = ? AND `uri-id` > ? LIMIT ?",
1047                         Conversation::PARCEL_ACTIVITYPUB, $id, 1000);
1048
1049                 if (DBA::errorNo() != 0) {
1050                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
1051                         return false;
1052                 }
1053
1054                 while ($conversation = DBA::fetch($conversations)) {
1055                         $id       = $conversation['uri-id'];
1056                         $received = $conversation['received'];
1057
1058                         $raw = json_decode($conversation['source'], true);
1059                         if (empty($raw)) {
1060                                 continue;
1061                         }
1062                         $activity = JsonLD::compact($raw);
1063
1064                         $urls = Receiver::getReceiverURL($activity);
1065                         Processor::storeReceivers($conversation['uri-id'], $urls);
1066
1067                         if (!empty($activity['as:object'])) {
1068                                 $urls = array_merge($urls, Receiver::getReceiverURL($activity['as:object']));
1069                                 Processor::storeReceivers($conversation['uri-id'], $urls);
1070                         }
1071                         ++$rows;
1072                 }
1073
1074                 DBA::close($conversations);
1075
1076                 DI::config()->set('system', 'post_update_version_1452_id', $id);
1077
1078                 Logger::info('Processed', ['rows' => $rows, 'last' => $id, 'last-received' => $received]);
1079
1080                 if ($rows <= 100) {
1081                         DI::config()->set('system', 'post_update_version', 1452);
1082                         Logger::info('Done');
1083                         return true;
1084                 }
1085
1086                 return false;
1087         }
1088 }