]> git.mxchange.org Git - friendica.git/blob - src/Database/PostUpdate.php
a58281e48b3ed62aadaf07c782df16e857d44355
[friendica.git] / src / Database / PostUpdate.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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\GServer;
29 use Friendica\Model\Item;
30 use Friendica\Model\Photo;
31 use Friendica\Model\Post;
32 use Friendica\Model\Post\Category;
33 use Friendica\Model\Tag;
34 use Friendica\Model\Verb;
35 use Friendica\Util\Strings;
36
37 /**
38  * These database-intensive post update routines are meant to be executed in the background by the cronjob.
39  *
40  * If there is a need for a intensive migration after a database structure change, update this file
41  * by adding a new method at the end with the number of the new DB_UPDATE_VERSION.
42  */
43 class PostUpdate
44 {
45         // Needed for the helper function to read from the legacy term table
46         const OBJECT_TYPE_POST  = 1;
47         const VERSION = 1384;
48
49         /**
50          * Calls the post update functions
51          */
52         public static function update()
53         {
54                 if (!self::update1297()) {
55                         return false;
56                 }
57                 if (!self::update1322()) {
58                         return false;
59                 }
60                 if (!self::update1329()) {
61                         return false;
62                 }
63                 if (!self::update1341()) {
64                         return false;
65                 }
66                 if (!self::update1342()) {
67                         return false;
68                 }
69                 if (!self::update1345()) {
70                         return false;
71                 }
72                 if (!self::update1346()) {
73                         return false;
74                 }
75                 if (!self::update1347()) {
76                         return false;
77                 }
78                 if (!self::update1348()) {
79                         return false;
80                 }
81                 if (!self::update1349()) {
82                         return false;
83                 }
84                 if (!self::update1383()) {
85                         return false;
86                 }
87                 if (!self::update1384()) {
88                         return false;
89                 }
90
91                 return true;
92         }
93
94         /**
95          * Set the delivery queue count to a negative value for all items preceding the feature.
96          *
97          * @return bool "true" when the job is done
98          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
99          */
100         private static function update1297()
101         {
102                 // Was the script completed?
103                 if (DI::config()->get('system', 'post_update_version') >= 1297) {
104                         return true;
105                 }
106
107                 if (!DBStructure::existsTable('item-delivery-data')) {
108                         DI::config()->set('system', 'post_update_version', 1297);
109                         return true;
110                 }
111
112                 $max_item_delivery_data = DBA::selectFirst('item-delivery-data', ['iid'], ['queue_count > 0 OR queue_done > 0'], ['order' => ['iid']]);
113                 $max_iid = $max_item_delivery_data['iid'];
114
115                 Logger::info('Start update1297 with max iid: ' . $max_iid);
116
117                 $condition = ['`queue_count` = 0 AND `iid` < ?', $max_iid];
118
119                 DBA::update('item-delivery-data', ['queue_count' => -1], $condition);
120
121                 if (DBA::errorNo() != 0) {
122                         Logger::error('Database error ' . DBA::errorNo() . ':' . DBA::errorMessage());
123                         return false;
124                 }
125
126                 Logger::info('Processed rows: ' . DBA::affectedRows());
127
128                 DI::config()->set('system', 'post_update_version', 1297);
129
130                 Logger::info('Done');
131
132                 return true;
133         }
134         /**
135          * Remove contact duplicates
136          *
137          * @return bool "true" when the job is done
138          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
139          */
140         private static function update1322()
141         {
142                 // Was the script completed?
143                 if (DI::config()->get('system', 'post_update_version') >= 1322) {
144                         return true;
145                 }
146
147                 Logger::info('Start');
148
149                 $contacts = DBA::p("SELECT `nurl`, `uid` FROM `contact`
150                         WHERE EXISTS (SELECT `nurl` FROM `contact` AS `c2`
151                                 WHERE `c2`.`nurl` = `contact`.`nurl` AND `c2`.`id` != `contact`.`id` AND `c2`.`uid` = `contact`.`uid` AND `c2`.`network` IN (?, ?, ?) AND NOT `deleted`)
152                         AND (`network` IN (?, ?, ?) OR (`uid` = ?)) AND NOT `deleted` GROUP BY `nurl`, `uid`",
153                         Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB,
154                         Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 0);
155
156                 while ($contact = DBA::fetch($contacts)) {
157                         Logger::info('Remove duplicates', ['nurl' => $contact['nurl'], 'uid' => $contact['uid']]);
158                         Contact::removeDuplicates($contact['nurl'], $contact['uid']);
159                 }
160
161                 DBA::close($contact);
162                 DI::config()->set('system', 'post_update_version', 1322);
163
164                 Logger::info('Done');
165
166                 return true;
167         }
168
169         /**
170          * update user notification data
171          *
172          * @return bool "true" when the job is done
173          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
174          */
175         private static function update1329()
176         {
177                 // Was the script completed?
178                 if (DI::config()->get('system', 'post_update_version') >= 1329) {
179                         return true;
180                 }
181
182                 if (!DBStructure::existsTable('item')) {
183                         DI::config()->set('system', 'post_update_version', 1329);
184                         return true;
185                 }
186
187                 $id = DI::config()->get('system', 'post_update_version_1329_id', 0);
188
189                 Logger::info('Start', ['item' => $id]);
190
191                 $start_id = $id;
192                 $rows = 0;
193                 $condition = ["`id` > ?", $id];
194                 $params = ['order' => ['id'], 'limit' => 10000];
195                 $items = DBA::select('item', ['id', 'uri-id', 'uid'], $condition, $params);
196
197                 if (DBA::errorNo() != 0) {
198                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
199                         return false;
200                 }
201
202                 while ($item = DBA::fetch($items)) {
203                         $id = $item['id'];
204
205                         Post\UserNotification::setNotification($item['uri-id'], $item['uid']);
206
207                         ++$rows;
208                 }
209                 DBA::close($items);
210
211                 DI::config()->set('system', 'post_update_version_1329_id', $id);
212
213                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
214
215                 if ($start_id == $id) {
216                         DI::config()->set('system', 'post_update_version', 1329);
217                         Logger::info('Done');
218                         return true;
219                 }
220
221                 return false;
222         }
223
224         /**
225          * Fill the "tag" table with tags and mentions from the body
226          *
227          * @return bool "true" when the job is done
228          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
229          */
230         private static function update1341()
231         {
232                 // Was the script completed?
233                 if (DI::config()->get('system', 'post_update_version') >= 1341) {
234                         return true;
235                 }
236
237                 if (!DBStructure::existsTable('item-content')) {
238                         DI::config()->set('system', 'post_update_version', 1342);
239                         return true;
240                 }
241
242                 $id = DI::config()->get('system', 'post_update_version_1341_id', 0);
243
244                 Logger::info('Start', ['item' => $id]);
245
246                 $rows = 0;
247
248                 $items = DBA::p("SELECT `uri-id`,`body` FROM `item-content` WHERE
249                         (`body` LIKE ? OR `body` LIKE ? OR `body` LIKE ?) AND `uri-id` >= ?
250                         ORDER BY `uri-id` LIMIT 100000", '%#%', '%@%', '%!%', $id);
251
252                 if (DBA::errorNo() != 0) {
253                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
254                         return false;
255                 }
256
257                 while ($item = DBA::fetch($items)) {
258                         Tag::storeFromBody($item['uri-id'], $item['body'], '#!@', false);
259                         $id = $item['uri-id'];
260                         ++$rows;
261                         if ($rows % 1000 == 0) {
262                                 DI::config()->set('system', 'post_update_version_1341_id', $id);
263                         }
264                 }
265                 DBA::close($items);
266
267                 DI::config()->set('system', 'post_update_version_1341_id', $id);
268
269                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
270
271                 // When there are less than 1,000 items processed this means that we reached the end
272                 // The other entries will then be processed with the regular functionality
273                 if ($rows < 1000) {
274                         DI::config()->set('system', 'post_update_version', 1341);
275                         Logger::info('Done');
276                         return true;
277                 }
278
279                 return false;
280         }
281
282         /**
283          * Fill the "tag" table with tags and mentions from the "term" table
284          *
285          * @return bool "true" when the job is done
286          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
287          */
288         private static function update1342()
289         {
290                 // Was the script completed?
291                 if (DI::config()->get('system', 'post_update_version') >= 1342) {
292                         return true;
293                 }
294
295                 if (!DBStructure::existsTable('term') || !DBStructure::existsTable('item-content')) {
296                         DI::config()->set('system', 'post_update_version', 1342);
297                         return true;
298                 }
299
300                 $id = DI::config()->get('system', 'post_update_version_1342_id', 0);
301
302                 Logger::info('Start', ['item' => $id]);
303
304                 $rows = 0;
305
306                 $terms = DBA::p("SELECT `term`.`tid`, `item`.`uri-id`, `term`.`type`, `term`.`term`, `term`.`url`, `item-content`.`body`
307                         FROM `term`
308                         INNER JOIN `item` ON `item`.`id` = `term`.`oid`
309                         INNER JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
310                         WHERE term.type IN (?, ?, ?, ?) AND `tid` >= ? ORDER BY `tid` LIMIT 100000",
311                         Tag::HASHTAG, Tag::MENTION, Tag::EXCLUSIVE_MENTION, Tag::IMPLICIT_MENTION, $id);
312
313                 if (DBA::errorNo() != 0) {
314                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
315                         return false;
316                 }
317
318                 while ($term = DBA::fetch($terms)) {
319                         if (($term['type'] == Tag::MENTION) && !empty($term['url']) && !strstr($term['body'], $term['url'])) {
320                 $condition = ['nurl' => Strings::normaliseLink($term['url']), 'uid' => 0, 'deleted' => false];
321                 $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
322                 if (!DBA::isResult($contact)) {
323                         $ssl_url = str_replace('http://', 'https://', $term['url']);
324                         $condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $term['url'], Strings::normaliseLink($term['url']), $ssl_url, 0];
325                         $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
326                 }
327
328                 if (DBA::isResult($contact) && (!strstr($term['body'], $contact['url']) && (empty($contact['alias']) || !strstr($term['body'], $contact['alias'])))) {
329                         $term['type'] = Tag::IMPLICIT_MENTION;
330                 }
331                         }
332
333                         Tag::store($term['uri-id'], $term['type'], $term['term'], $term['url'], false);
334
335                         $id = $term['tid'];
336                         ++$rows;
337                         if ($rows % 1000 == 0) {
338                                 DI::config()->set('system', 'post_update_version_1342_id', $id);
339                         }
340                 }
341                 DBA::close($terms);
342
343                 DI::config()->set('system', 'post_update_version_1342_id', $id);
344
345                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
346
347                 // When there are less than 1,000 items processed this means that we reached the end
348                 // The other entries will then be processed with the regular functionality
349                 if ($rows < 1000) {
350                         DI::config()->set('system', 'post_update_version', 1342);
351                         Logger::info('Done');
352                         return true;
353                 }
354
355                 return false;
356         }
357
358         /**
359          * Fill the "post-delivery-data" table with data from the "item-delivery-data" table
360          *
361          * @return bool "true" when the job is done
362          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
363          */
364         private static function update1345()
365         {
366                 // Was the script completed?
367                 if (DI::config()->get('system', 'post_update_version') >= 1345) {
368                         return true;
369                 }
370
371                 if (!DBStructure::existsTable('item-delivery-data')) {
372                         DI::config()->set('system', 'post_update_version', 1345);
373                         return true;
374                 }
375
376                 $id = DI::config()->get('system', 'post_update_version_1345_id', 0);
377
378                 Logger::info('Start', ['item' => $id]);
379
380                 $rows = 0;
381
382                 $deliveries = DBA::p("SELECT `uri-id`, `iid`, `item-delivery-data`.`postopts`, `item-delivery-data`.`inform`,
383                         `queue_count`, `queue_done`, `activitypub`, `dfrn`, `diaspora`, `ostatus`, `legacy_dfrn`, `queue_failed`
384                         FROM `item-delivery-data`
385                         INNER JOIN `item` ON `item`.`id` = `item-delivery-data`.`iid`
386                         WHERE `iid` >= ? ORDER BY `iid` LIMIT 10000", $id);
387
388                 if (DBA::errorNo() != 0) {
389                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
390                         return false;
391                 }
392
393                 while ($delivery = DBA::fetch($deliveries)) {
394                         $id = $delivery['iid'];
395                         unset($delivery['iid']);
396                         DBA::insert('post-delivery-data', $delivery, Database::INSERT_UPDATE);
397                         ++$rows;
398                 }
399                 DBA::close($deliveries);
400
401                 DI::config()->set('system', 'post_update_version_1345_id', $id);
402
403                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
404
405                 // When there are less than 100 items processed this means that we reached the end
406                 // The other entries will then be processed with the regular functionality
407                 if ($rows < 100) {
408                         DI::config()->set('system', 'post_update_version', 1345);
409                         Logger::info('Done');
410                         return true;
411                 }
412
413                 return false;
414         }
415
416         /**
417          * Generates the legacy item.file field string from an item ID.
418          * Includes only file and category terms.
419          *
420          * @param int $item_id
421          * @return string
422          * @throws \Exception
423          */
424         private static function fileTextFromItemId($item_id)
425         {
426                 $file_text = '';
427
428                 $condition = ['otype' => self::OBJECT_TYPE_POST, 'oid' => $item_id, 'type' => [Category::FILE, Category::CATEGORY]];
429                 $tags = DBA::selectToArray('term', ['type', 'term', 'url'], $condition);
430                 foreach ($tags as $tag) {
431                         if ($tag['type'] == Category::CATEGORY) {
432                                 $file_text .= '<' . $tag['term'] . '>';
433                         } else {
434                                 $file_text .= '[' . $tag['term'] . ']';
435                         }
436                 }
437
438                 return $file_text;
439         }
440
441         /**
442          * Fill the "tag" table with tags and mentions from the "term" table
443          *
444          * @return bool "true" when the job is done
445          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
446          */
447         private static function update1346()
448         {
449                 // Was the script completed?
450                 if (DI::config()->get('system', 'post_update_version') >= 1346) {
451                         return true;
452                 }
453
454                 if (!DBStructure::existsTable('term')) {
455                         DI::config()->set('system', 'post_update_version', 1346);
456                         return true;
457                 }
458
459                 $id = DI::config()->get('system', 'post_update_version_1346_id', 0);
460
461                 Logger::info('Start', ['item' => $id]);
462
463                 $rows = 0;
464
465                 $terms = DBA::select('term', ['oid'],
466                         ["`type` IN (?, ?) AND `oid` >= ?", Category::CATEGORY, Category::FILE, $id],
467                         ['order' => ['oid'], 'limit' => 1000, 'group_by' => ['oid']]);
468
469                 if (DBA::errorNo() != 0) {
470                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
471                         return false;
472                 }
473
474                 while ($term = DBA::fetch($terms)) {
475                         $item = Post::selectFirst(['uri-id', 'uid'], ['id' => $term['oid']]);
476                         if (!DBA::isResult($item)) {
477                                 continue;
478                         }
479
480                         $file = self::fileTextFromItemId($term['oid']);
481                         if (!empty($file)) {
482                                 Category::storeTextByURIId($item['uri-id'], $item['uid'], $file);
483                         }
484
485                         $id = $term['oid'];
486                         ++$rows;
487                         if ($rows % 100 == 0) {
488                                 DI::config()->set('system', 'post_update_version_1346_id', $id);
489                         }
490                 }
491                 DBA::close($terms);
492
493                 DI::config()->set('system', 'post_update_version_1346_id', $id);
494
495                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
496
497                 // When there are less than 10 items processed this means that we reached the end
498                 // The other entries will then be processed with the regular functionality
499                 if ($rows < 10) {
500                         DI::config()->set('system', 'post_update_version', 1346);
501                         Logger::info('Done');
502                         return true;
503                 }
504
505                 return false;
506         }
507
508         /**
509          * update the "vid" (verb) field in the item table
510          *
511          * @return bool "true" when the job is done
512          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
513          * @throws \ImagickException
514          */
515         private static function update1347()
516         {
517                 // Was the script completed?
518                 if (DI::config()->get("system", "post_update_version") >= 1347) {
519                         return true;
520                 }
521
522                 if (!DBStructure::existsTable('item-activity') || !DBStructure::existsTable('item')) {
523                         DI::config()->set('system', 'post_update_version', 1347);
524                         return true;
525                 }
526
527                 $id = DI::config()->get("system", "post_update_version_1347_id", 0);
528
529                 Logger::info('Start', ['item' => $id]);
530
531                 $start_id = $id;
532                 $rows = 0;
533
534                 $items = DBA::p("SELECT `item`.`id`, `item`.`verb` AS `item-verb`, `item-content`.`verb`, `item-activity`.`activity`
535                         FROM `item` LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
536                         LEFT JOIN `item-activity` ON `item-activity`.`uri-id` = `item`.`uri-id` AND `item`.`gravity` = ?
537                         WHERE `item`.`id` >= ? AND `item`.`vid` IS NULL ORDER BY `item`.`id` LIMIT 10000", GRAVITY_ACTIVITY, $id);
538
539                 if (DBA::errorNo() != 0) {
540                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
541                         return false;
542                 }
543
544                 while ($item = DBA::fetch($items)) {
545                         $id = $item['id'];
546                         $verb = $item['item-verb'];
547                         if (empty($verb)) {
548                                 $verb = $item['verb'];
549                         }
550                         if (empty($verb) && is_int($item['activity'])) {
551                                 $verb = Item::ACTIVITIES[$item['activity']];
552                         }
553                         if (empty($verb)) {
554                                 continue;
555                         }
556
557                         DBA::update('item', ['vid' => Verb::getID($verb)], ['id' => $item['id']]);
558                         ++$rows;
559                 }
560                 DBA::close($items);
561
562                 DI::config()->set("system", "post_update_version_1347_id", $id);
563
564                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
565
566                 if ($start_id == $id) {
567                         DI::config()->set("system", "post_update_version", 1347);
568                         Logger::info('Done');
569                         return true;
570                 }
571
572                 return false;
573         }
574
575         /**
576          * update the "gsid" (global server id) field in the contact table
577          *
578          * @return bool "true" when the job is done
579          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
580          * @throws \ImagickException
581          */
582         private static function update1348()
583         {
584                 // Was the script completed?
585                 if (DI::config()->get("system", "post_update_version") >= 1348) {
586                         return true;
587                 }
588
589                 $id = DI::config()->get("system", "post_update_version_1348_id", 0);
590
591                 Logger::info('Start', ['contact' => $id]);
592
593                 $start_id = $id;
594                 $rows = 0;
595                 $condition = ["`id` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
596                 $params = ['order' => ['id'], 'limit' => 10000];
597                 $contacts = DBA::select('contact', ['id', 'baseurl'], $condition, $params);
598
599                 if (DBA::errorNo() != 0) {
600                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
601                         return false;
602                 }
603
604                 while ($contact = DBA::fetch($contacts)) {
605                         $id = $contact['id'];
606
607                         DBA::update('contact',
608                                 ['gsid' => GServer::getID($contact['baseurl'], true), 'baseurl' => GServer::cleanURL($contact['baseurl'])],
609                                 ['id' => $contact['id']]);
610
611                         ++$rows;
612                 }
613                 DBA::close($contacts);
614
615                 DI::config()->set("system", "post_update_version_1348_id", $id);
616
617                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
618
619                 if ($start_id == $id) {
620                         DI::config()->set("system", "post_update_version", 1348);
621                         Logger::info('Done');
622                         return true;
623                 }
624
625                 return false;
626         }
627
628         /**
629          * update the "gsid" (global server id) field in the apcontact table
630          *
631          * @return bool "true" when the job is done
632          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
633          * @throws \ImagickException
634          */
635         private static function update1349()
636         {
637                 // Was the script completed?
638                 if (DI::config()->get("system", "post_update_version") >= 1349) {
639                         return true;
640                 }
641
642                 $id = DI::config()->get("system", "post_update_version_1349_id", '');
643
644                 Logger::info('Start', ['apcontact' => $id]);
645
646                 $start_id = $id;
647                 $rows = 0;
648                 $condition = ["`url` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
649                 $params = ['order' => ['url'], 'limit' => 10000];
650                 $apcontacts = DBA::select('apcontact', ['url', 'baseurl'], $condition, $params);
651
652                 if (DBA::errorNo() != 0) {
653                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
654                         return false;
655                 }
656
657                 while ($apcontact = DBA::fetch($apcontacts)) {
658                         $id = $apcontact['url'];
659
660                         DBA::update('apcontact',
661                                 ['gsid' => GServer::getID($apcontact['baseurl'], true), 'baseurl' => GServer::cleanURL($apcontact['baseurl'])],
662                                 ['url' => $apcontact['url']]);
663
664                         ++$rows;
665                 }
666                 DBA::close($apcontacts);
667
668                 DI::config()->set("system", "post_update_version_1349_id", $id);
669
670                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
671
672                 if ($start_id == $id) {
673                         DI::config()->set("system", "post_update_version", 1349);
674                         Logger::info('Done');
675                         return true;
676                 }
677
678                 return false;
679         }
680
681         /**
682          * Remove orphaned photo entries
683          *
684          * @return bool "true" when the job is done
685          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
686          * @throws \ImagickException
687          */
688         private static function update1383()
689         {
690                 // Was the script completed?
691                 if (DI::config()->get("system", "post_update_version") >= 1383) {
692                         return true;
693                 }
694
695                 Logger::info('Start');
696
697                 $deleted = 0;
698                 $avatar = [4 => 'photo', 5 => 'thumb', 6 => 'micro'];
699
700                 $photos = DBA::select('photo', ['id', 'contact-id', 'resource-id', 'scale'], ["`contact-id` != ? AND `album` = ?", 0, Photo::CONTACT_PHOTOS]);
701                 while ($photo = DBA::fetch($photos)) {
702                         $delete = !in_array($photo['scale'], [4, 5, 6]);
703
704                         if (!$delete) {
705                                 // Check if there is a contact entry with that photo
706                                 $delete = !DBA::exists('contact', ["`id` = ? AND `" . $avatar[$photo['scale']] . "` LIKE ?",
707                                         $photo['contact-id'], '%' . $photo['resource-id'] . '%']);
708                         }
709
710                         if ($delete) {
711                                 Photo::delete(['id' => $photo['id']]);
712                                 $deleted++;
713                         }
714                 }
715                 DBA::close($photos);
716
717                 DI::config()->set("system", "post_update_version", 1383);
718                 Logger::info('Done', ['deleted' => $deleted]);
719                 return true;
720         }
721
722         /**
723          * update the "hash" field in the photo table
724          *
725          * @return bool "true" when the job is done
726          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
727          * @throws \ImagickException
728          */
729         private static function update1384()
730         {
731                 // Was the script completed?
732                 if (DI::config()->get("system", "post_update_version") >= 1384) {
733                         return true;
734                 }
735
736                 $condition = ["`hash` IS NULL"];
737                 Logger::info('Start', ['rest' => DBA::count('photo', $condition)]);
738
739                 $rows = 0;
740                 $photos = DBA::select('photo', [], $condition, ['limit' => 10000]);
741
742                 if (DBA::errorNo() != 0) {
743                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
744                         return false;
745                 }
746
747                 while ($photo = DBA::fetch($photos)) {
748                         $img = Photo::getImageForPhoto($photo);
749                         if (!empty($img)) {
750                                 $md5 = md5($img->asString());
751                         } else {
752                                 $md5 = '';
753                         }
754                         DBA::update('photo', ['hash' => $md5], ['id' => $photo['id']]);
755                         ++$rows;
756                 }
757                 DBA::close($photos);
758
759                 Logger::info('Processed', ['rows' => $rows]);
760
761                 if ($rows <= 100) {
762                         DI::config()->set("system", "post_update_version", 1384);
763                         Logger::info('Done');
764                         return true;
765                 }
766
767                 return false;
768         }
769 }