]> git.mxchange.org Git - friendica.git/blob - src/Database/PostUpdate.php
Merge pull request #9901 from annando/post-thread-user
[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                 $id = DI::config()->get('system', 'post_update_version_1329_id', 0);
183
184                 Logger::info('Start', ['item' => $id]);
185
186                 $start_id = $id;
187                 $rows = 0;
188                 $condition = ["`id` > ?", $id];
189                 $params = ['order' => ['id'], 'limit' => 10000];
190                 $items = DBA::select('item', ['id', 'uri-id', 'uid'], $condition, $params);
191
192                 if (DBA::errorNo() != 0) {
193                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
194                         return false;
195                 }
196
197                 while ($item = DBA::fetch($items)) {
198                         $id = $item['id'];
199
200                         Post\UserNotification::setNotification($item['uri-id'], $item['uid']);
201
202                         ++$rows;
203                 }
204                 DBA::close($items);
205
206                 DI::config()->set('system', 'post_update_version_1329_id', $id);
207
208                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
209
210                 if ($start_id == $id) {
211                         DI::config()->set('system', 'post_update_version', 1329);
212                         Logger::info('Done');
213                         return true;
214                 }
215
216                 return false;
217         }
218
219         /**
220          * Fill the "tag" table with tags and mentions from the body
221          *
222          * @return bool "true" when the job is done
223          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
224          */
225         private static function update1341()
226         {
227                 // Was the script completed?
228                 if (DI::config()->get('system', 'post_update_version') >= 1341) {
229                         return true;
230                 }
231
232                 if (!DBStructure::existsTable('item-content')) {
233                         DI::config()->set('system', 'post_update_version', 1342);
234                         return true;
235                 }
236
237                 $id = DI::config()->get('system', 'post_update_version_1341_id', 0);
238
239                 Logger::info('Start', ['item' => $id]);
240
241                 $rows = 0;
242
243                 $items = DBA::p("SELECT `uri-id`,`body` FROM `item-content` WHERE
244                         (`body` LIKE ? OR `body` LIKE ? OR `body` LIKE ?) AND `uri-id` >= ?
245                         ORDER BY `uri-id` LIMIT 100000", '%#%', '%@%', '%!%', $id);
246
247                 if (DBA::errorNo() != 0) {
248                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
249                         return false;
250                 }
251
252                 while ($item = DBA::fetch($items)) {
253                         Tag::storeFromBody($item['uri-id'], $item['body'], '#!@', false);
254                         $id = $item['uri-id'];
255                         ++$rows;
256                         if ($rows % 1000 == 0) {
257                                 DI::config()->set('system', 'post_update_version_1341_id', $id);
258                         }
259                 }
260                 DBA::close($items);
261
262                 DI::config()->set('system', 'post_update_version_1341_id', $id);
263
264                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
265
266                 // When there are less than 1,000 items processed this means that we reached the end
267                 // The other entries will then be processed with the regular functionality
268                 if ($rows < 1000) {
269                         DI::config()->set('system', 'post_update_version', 1341);
270                         Logger::info('Done');
271                         return true;
272                 }
273
274                 return false;
275         }
276
277         /**
278          * Fill the "tag" table with tags and mentions from the "term" table
279          *
280          * @return bool "true" when the job is done
281          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
282          */
283         private static function update1342()
284         {
285                 // Was the script completed?
286                 if (DI::config()->get('system', 'post_update_version') >= 1342) {
287                         return true;
288                 }
289
290                 if (!DBStructure::existsTable('term') || !DBStructure::existsTable('item-content')) {
291                         DI::config()->set('system', 'post_update_version', 1342);
292                         return true;
293                 }
294
295                 $id = DI::config()->get('system', 'post_update_version_1342_id', 0);
296
297                 Logger::info('Start', ['item' => $id]);
298
299                 $rows = 0;
300
301                 $terms = DBA::p("SELECT `term`.`tid`, `item`.`uri-id`, `term`.`type`, `term`.`term`, `term`.`url`, `item-content`.`body`
302                         FROM `term`
303                         INNER JOIN `item` ON `item`.`id` = `term`.`oid`
304                         INNER JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
305                         WHERE term.type IN (?, ?, ?, ?) AND `tid` >= ? ORDER BY `tid` LIMIT 100000",
306                         Tag::HASHTAG, Tag::MENTION, Tag::EXCLUSIVE_MENTION, Tag::IMPLICIT_MENTION, $id);
307
308                 if (DBA::errorNo() != 0) {
309                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
310                         return false;
311                 }
312
313                 while ($term = DBA::fetch($terms)) {
314                         if (($term['type'] == Tag::MENTION) && !empty($term['url']) && !strstr($term['body'], $term['url'])) {
315                 $condition = ['nurl' => Strings::normaliseLink($term['url']), 'uid' => 0, 'deleted' => false];
316                 $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
317                 if (!DBA::isResult($contact)) {
318                         $ssl_url = str_replace('http://', 'https://', $term['url']);
319                         $condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $term['url'], Strings::normaliseLink($term['url']), $ssl_url, 0];
320                         $contact = DBA::selectFirst('contact', ['url', 'alias'], $condition, ['order' => ['id']]);
321                 }
322
323                 if (DBA::isResult($contact) && (!strstr($term['body'], $contact['url']) && (empty($contact['alias']) || !strstr($term['body'], $contact['alias'])))) {
324                         $term['type'] = Tag::IMPLICIT_MENTION;
325                 }
326                         }
327
328                         Tag::store($term['uri-id'], $term['type'], $term['term'], $term['url'], false);
329
330                         $id = $term['tid'];
331                         ++$rows;
332                         if ($rows % 1000 == 0) {
333                                 DI::config()->set('system', 'post_update_version_1342_id', $id);
334                         }
335                 }
336                 DBA::close($terms);
337
338                 DI::config()->set('system', 'post_update_version_1342_id', $id);
339
340                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
341
342                 // When there are less than 1,000 items processed this means that we reached the end
343                 // The other entries will then be processed with the regular functionality
344                 if ($rows < 1000) {
345                         DI::config()->set('system', 'post_update_version', 1342);
346                         Logger::info('Done');
347                         return true;
348                 }
349
350                 return false;
351         }
352
353         /**
354          * Fill the "post-delivery-data" table with data from the "item-delivery-data" table
355          *
356          * @return bool "true" when the job is done
357          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
358          */
359         private static function update1345()
360         {
361                 // Was the script completed?
362                 if (DI::config()->get('system', 'post_update_version') >= 1345) {
363                         return true;
364                 }
365
366                 if (!DBStructure::existsTable('item-delivery-data')) {
367                         DI::config()->set('system', 'post_update_version', 1345);
368                         return true;
369                 }
370
371                 $id = DI::config()->get('system', 'post_update_version_1345_id', 0);
372
373                 Logger::info('Start', ['item' => $id]);
374
375                 $rows = 0;
376
377                 $deliveries = DBA::p("SELECT `uri-id`, `iid`, `item-delivery-data`.`postopts`, `item-delivery-data`.`inform`,
378                         `queue_count`, `queue_done`, `activitypub`, `dfrn`, `diaspora`, `ostatus`, `legacy_dfrn`, `queue_failed`
379                         FROM `item-delivery-data`
380                         INNER JOIN `item` ON `item`.`id` = `item-delivery-data`.`iid`
381                         WHERE `iid` >= ? ORDER BY `iid` LIMIT 10000", $id);
382
383                 if (DBA::errorNo() != 0) {
384                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
385                         return false;
386                 }
387
388                 while ($delivery = DBA::fetch($deliveries)) {
389                         $id = $delivery['iid'];
390                         unset($delivery['iid']);
391                         DBA::insert('post-delivery-data', $delivery, Database::INSERT_UPDATE);
392                         ++$rows;
393                 }
394                 DBA::close($deliveries);
395
396                 DI::config()->set('system', 'post_update_version_1345_id', $id);
397
398                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
399
400                 // When there are less than 100 items processed this means that we reached the end
401                 // The other entries will then be processed with the regular functionality
402                 if ($rows < 100) {
403                         DI::config()->set('system', 'post_update_version', 1345);
404                         Logger::info('Done');
405                         return true;
406                 }
407
408                 return false;
409         }
410
411         /**
412          * Generates the legacy item.file field string from an item ID.
413          * Includes only file and category terms.
414          *
415          * @param int $item_id
416          * @return string
417          * @throws \Exception
418          */
419         private static function fileTextFromItemId($item_id)
420         {
421                 $file_text = '';
422
423                 $condition = ['otype' => self::OBJECT_TYPE_POST, 'oid' => $item_id, 'type' => [Category::FILE, Category::CATEGORY]];
424                 $tags = DBA::selectToArray('term', ['type', 'term', 'url'], $condition);
425                 foreach ($tags as $tag) {
426                         if ($tag['type'] == Category::CATEGORY) {
427                                 $file_text .= '<' . $tag['term'] . '>';
428                         } else {
429                                 $file_text .= '[' . $tag['term'] . ']';
430                         }
431                 }
432
433                 return $file_text;
434         }
435
436         /**
437          * Fill the "tag" table with tags and mentions from the "term" table
438          *
439          * @return bool "true" when the job is done
440          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
441          */
442         private static function update1346()
443         {
444                 // Was the script completed?
445                 if (DI::config()->get('system', 'post_update_version') >= 1346) {
446                         return true;
447                 }
448
449                 if (!DBStructure::existsTable('term')) {
450                         DI::config()->set('system', 'post_update_version', 1346);
451                         return true;
452                 }
453
454                 $id = DI::config()->get('system', 'post_update_version_1346_id', 0);
455
456                 Logger::info('Start', ['item' => $id]);
457
458                 $rows = 0;
459
460                 $terms = DBA::select('term', ['oid'],
461                         ["`type` IN (?, ?) AND `oid` >= ?", Category::CATEGORY, Category::FILE, $id],
462                         ['order' => ['oid'], 'limit' => 1000, 'group_by' => ['oid']]);
463
464                 if (DBA::errorNo() != 0) {
465                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
466                         return false;
467                 }
468
469                 while ($term = DBA::fetch($terms)) {
470                         $item = Post::selectFirst(['uri-id', 'uid'], ['id' => $term['oid']]);
471                         if (!DBA::isResult($item)) {
472                                 continue;
473                         }
474
475                         $file = self::fileTextFromItemId($term['oid']);
476                         if (!empty($file)) {
477                                 Category::storeTextByURIId($item['uri-id'], $item['uid'], $file);
478                         }
479
480                         $id = $term['oid'];
481                         ++$rows;
482                         if ($rows % 100 == 0) {
483                                 DI::config()->set('system', 'post_update_version_1346_id', $id);
484                         }
485                 }
486                 DBA::close($terms);
487
488                 DI::config()->set('system', 'post_update_version_1346_id', $id);
489
490                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
491
492                 // When there are less than 10 items processed this means that we reached the end
493                 // The other entries will then be processed with the regular functionality
494                 if ($rows < 10) {
495                         DI::config()->set('system', 'post_update_version', 1346);
496                         Logger::info('Done');
497                         return true;
498                 }
499
500                 return false;
501         }
502
503         /**
504          * update the "vid" (verb) field in the item table
505          *
506          * @return bool "true" when the job is done
507          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
508          * @throws \ImagickException
509          */
510         private static function update1347()
511         {
512                 // Was the script completed?
513                 if (DI::config()->get("system", "post_update_version") >= 1347) {
514                         return true;
515                 }
516
517                 if (!DBStructure::existsTable('item-activity')) {
518                         DI::config()->set('system', 'post_update_version', 1347);
519                         return true;
520                 }
521
522                 $id = DI::config()->get("system", "post_update_version_1347_id", 0);
523
524                 Logger::info('Start', ['item' => $id]);
525
526                 $start_id = $id;
527                 $rows = 0;
528
529                 $items = DBA::p("SELECT `item`.`id`, `item`.`verb` AS `item-verb`, `item-content`.`verb`, `item-activity`.`activity`
530                         FROM `item` LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`
531                         LEFT JOIN `item-activity` ON `item-activity`.`uri-id` = `item`.`uri-id` AND `item`.`gravity` = ?
532                         WHERE `item`.`id` >= ? AND `item`.`vid` IS NULL ORDER BY `item`.`id` LIMIT 10000", GRAVITY_ACTIVITY, $id);
533
534                 if (DBA::errorNo() != 0) {
535                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
536                         return false;
537                 }
538
539                 while ($item = DBA::fetch($items)) {
540                         $id = $item['id'];
541                         $verb = $item['item-verb'];
542                         if (empty($verb)) {
543                                 $verb = $item['verb'];
544                         }
545                         if (empty($verb) && is_int($item['activity'])) {
546                                 $verb = Item::ACTIVITIES[$item['activity']];
547                         }
548                         if (empty($verb)) {
549                                 continue;
550                         }
551
552                         DBA::update('item', ['vid' => Verb::getID($verb)], ['id' => $item['id']]);
553                         ++$rows;
554                 }
555                 DBA::close($items);
556
557                 DI::config()->set("system", "post_update_version_1347_id", $id);
558
559                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
560
561                 if ($start_id == $id) {
562                         DI::config()->set("system", "post_update_version", 1347);
563                         Logger::info('Done');
564                         return true;
565                 }
566
567                 return false;
568         }
569
570         /**
571          * update the "gsid" (global server id) field in the contact table
572          *
573          * @return bool "true" when the job is done
574          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
575          * @throws \ImagickException
576          */
577         private static function update1348()
578         {
579                 // Was the script completed?
580                 if (DI::config()->get("system", "post_update_version") >= 1348) {
581                         return true;
582                 }
583
584                 $id = DI::config()->get("system", "post_update_version_1348_id", 0);
585
586                 Logger::info('Start', ['contact' => $id]);
587
588                 $start_id = $id;
589                 $rows = 0;
590                 $condition = ["`id` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
591                 $params = ['order' => ['id'], 'limit' => 10000];
592                 $contacts = DBA::select('contact', ['id', 'baseurl'], $condition, $params);
593
594                 if (DBA::errorNo() != 0) {
595                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
596                         return false;
597                 }
598
599                 while ($contact = DBA::fetch($contacts)) {
600                         $id = $contact['id'];
601
602                         DBA::update('contact',
603                                 ['gsid' => GServer::getID($contact['baseurl'], true), 'baseurl' => GServer::cleanURL($contact['baseurl'])],
604                                 ['id' => $contact['id']]);
605
606                         ++$rows;
607                 }
608                 DBA::close($contacts);
609
610                 DI::config()->set("system", "post_update_version_1348_id", $id);
611
612                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
613
614                 if ($start_id == $id) {
615                         DI::config()->set("system", "post_update_version", 1348);
616                         Logger::info('Done');
617                         return true;
618                 }
619
620                 return false;
621         }
622
623         /**
624          * update the "gsid" (global server id) field in the apcontact table
625          *
626          * @return bool "true" when the job is done
627          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
628          * @throws \ImagickException
629          */
630         private static function update1349()
631         {
632                 // Was the script completed?
633                 if (DI::config()->get("system", "post_update_version") >= 1349) {
634                         return true;
635                 }
636
637                 $id = DI::config()->get("system", "post_update_version_1349_id", '');
638
639                 Logger::info('Start', ['apcontact' => $id]);
640
641                 $start_id = $id;
642                 $rows = 0;
643                 $condition = ["`url` > ? AND `gsid` IS NULL AND `baseurl` != '' AND NOT `baseurl` IS NULL", $id];
644                 $params = ['order' => ['url'], 'limit' => 10000];
645                 $apcontacts = DBA::select('apcontact', ['url', 'baseurl'], $condition, $params);
646
647                 if (DBA::errorNo() != 0) {
648                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
649                         return false;
650                 }
651
652                 while ($apcontact = DBA::fetch($apcontacts)) {
653                         $id = $apcontact['url'];
654
655                         DBA::update('apcontact',
656                                 ['gsid' => GServer::getID($apcontact['baseurl'], true), 'baseurl' => GServer::cleanURL($apcontact['baseurl'])],
657                                 ['url' => $apcontact['url']]);
658
659                         ++$rows;
660                 }
661                 DBA::close($apcontacts);
662
663                 DI::config()->set("system", "post_update_version_1349_id", $id);
664
665                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
666
667                 if ($start_id == $id) {
668                         DI::config()->set("system", "post_update_version", 1349);
669                         Logger::info('Done');
670                         return true;
671                 }
672
673                 return false;
674         }
675
676         /**
677          * Remove orphaned photo entries
678          *
679          * @return bool "true" when the job is done
680          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
681          * @throws \ImagickException
682          */
683         private static function update1383()
684         {
685                 // Was the script completed?
686                 if (DI::config()->get("system", "post_update_version") >= 1383) {
687                         return true;
688                 }
689
690                 Logger::info('Start');
691
692                 $deleted = 0;
693                 $avatar = [4 => 'photo', 5 => 'thumb', 6 => 'micro'];
694
695                 $photos = DBA::select('photo', ['id', 'contact-id', 'resource-id', 'scale'], ["`contact-id` != ? AND `album` = ?", 0, Photo::CONTACT_PHOTOS]);
696                 while ($photo = DBA::fetch($photos)) {
697                         $delete = !in_array($photo['scale'], [4, 5, 6]);
698
699                         if (!$delete) {
700                                 // Check if there is a contact entry with that photo
701                                 $delete = !DBA::exists('contact', ["`id` = ? AND `" . $avatar[$photo['scale']] . "` LIKE ?",
702                                         $photo['contact-id'], '%' . $photo['resource-id'] . '%']);
703                         }
704
705                         if ($delete) {
706                                 Photo::delete(['id' => $photo['id']]);
707                                 $deleted++;
708                         }
709                 }
710                 DBA::close($photos);
711
712                 DI::config()->set("system", "post_update_version", 1383);
713                 Logger::info('Done', ['deleted' => $deleted]);
714                 return true;
715         }
716
717         /**
718          * update the "hash" field in the photo table
719          *
720          * @return bool "true" when the job is done
721          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
722          * @throws \ImagickException
723          */
724         private static function update1384()
725         {
726                 // Was the script completed?
727                 if (DI::config()->get("system", "post_update_version") >= 1384) {
728                         return true;
729                 }
730
731                 $condition = ["`hash` IS NULL"];
732                 Logger::info('Start', ['rest' => DBA::count('photo', $condition)]);
733
734                 $rows = 0;
735                 $photos = DBA::select('photo', [], $condition, ['limit' => 10000]);
736
737                 if (DBA::errorNo() != 0) {
738                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
739                         return false;
740                 }
741
742                 while ($photo = DBA::fetch($photos)) {
743                         $img = Photo::getImageForPhoto($photo);
744                         if (!empty($img)) {
745                                 $md5 = md5($img->asString());
746                         } else {
747                                 $md5 = '';
748                         }
749                         DBA::update('photo', ['hash' => $md5], ['id' => $photo['id']]);
750                         ++$rows;
751                 }
752                 DBA::close($photos);
753
754                 Logger::info('Processed', ['rows' => $rows]);
755
756                 if ($rows <= 100) {
757                         DI::config()->set("system", "post_update_version", 1384);
758                         Logger::info('Done');
759                         return true;
760                 }
761
762                 return false;
763         }
764 }