]> git.mxchange.org Git - friendica.git/blob - src/Database/PostUpdate.php
Add missing Friendica\DI namespace to files (PHPStorm inspection)
[friendica.git] / src / Database / PostUpdate.php
1 <?php
2 /**
3  * @file src/Database/PostUpdate.php
4  */
5 namespace Friendica\Database;
6
7 use Friendica\Core\Config;
8 use Friendica\Core\Logger;
9 use Friendica\Core\Protocol;
10 use Friendica\DI;
11 use Friendica\Model\Contact;
12 use Friendica\Model\Item;
13 use Friendica\Model\ItemURI;
14 use Friendica\Model\UserItem;
15 use Friendica\Model\PermissionSet;
16
17 /**
18  * Post update functions
19  */
20 class PostUpdate
21 {
22         /**
23          * Calls the post update functions
24          */
25         public static function update()
26         {
27                 if (!self::update1194()) {
28                         return false;
29                 }
30                 if (!self::update1206()) {
31                         return false;
32                 }
33                 if (!self::update1279()) {
34                         return false;
35                 }
36                 if (!self::update1281()) {
37                         return false;
38                 }
39                 if (!self::update1297()) {
40                         return false;
41                 }
42                 if (!self::update1322()) {
43                         return false;
44                 }
45                 if (!self::update1329()) {
46                         return false;
47                 }
48
49                 return true;
50         }
51
52         /**
53          * Updates the "global" field in the item table
54          *
55          * @return bool "true" when the job is done
56          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
57          */
58         private static function update1194()
59         {
60                 // Was the script completed?
61                 if (DI::config()->get("system", "post_update_version") >= 1194) {
62                         return true;
63                 }
64
65                 Logger::log("Start", Logger::DEBUG);
66
67                 $end_id = DI::config()->get("system", "post_update_1194_end");
68                 if (!$end_id) {
69                         $r = q("SELECT `id` FROM `item` WHERE `uid` != 0 ORDER BY `id` DESC LIMIT 1");
70                         if ($r) {
71                                 DI::config()->set("system", "post_update_1194_end", $r[0]["id"]);
72                                 $end_id = DI::config()->get("system", "post_update_1194_end");
73                         }
74                 }
75
76                 Logger::log("End ID: ".$end_id, Logger::DEBUG);
77
78                 $start_id = DI::config()->get("system", "post_update_1194_start");
79
80                 $query1 = "SELECT `item`.`id` FROM `item` ";
81
82                 $query2 = "INNER JOIN `item` AS `shadow` ON `item`.`uri` = `shadow`.`uri` AND `shadow`.`uid` = 0 ";
83
84                 $query3 = "WHERE `item`.`uid` != 0 AND `item`.`id` >= %d AND `item`.`id` <= %d
85                                 AND `item`.`visible` AND NOT `item`.`private`
86                                 AND NOT `item`.`deleted` AND NOT `item`.`moderated`
87                                 AND `item`.`network` IN ('%s', '%s', '%s', '')
88                                 AND NOT `item`.`global`";
89
90                 $r = q($query1.$query2.$query3."  ORDER BY `item`.`id` LIMIT 1",
91                         intval($start_id), intval($end_id),
92                         DBA::escape(Protocol::DFRN), DBA::escape(Protocol::DIASPORA), DBA::escape(Protocol::OSTATUS));
93                 if (!$r) {
94                         DI::config()->set("system", "post_update_version", 1194);
95                         Logger::log("Update is done", Logger::DEBUG);
96                         return true;
97                 } else {
98                         DI::config()->set("system", "post_update_1194_start", $r[0]["id"]);
99                         $start_id = DI::config()->get("system", "post_update_1194_start");
100                 }
101
102                 Logger::log("Start ID: ".$start_id, Logger::DEBUG);
103
104                 $r = q($query1.$query2.$query3."  ORDER BY `item`.`id` LIMIT 1000,1",
105                         intval($start_id), intval($end_id),
106                         DBA::escape(Protocol::DFRN), DBA::escape(Protocol::DIASPORA), DBA::escape(Protocol::OSTATUS));
107                 if ($r) {
108                         $pos_id = $r[0]["id"];
109                 } else {
110                         $pos_id = $end_id;
111                 }
112                 Logger::log("Progress: Start: ".$start_id." position: ".$pos_id." end: ".$end_id, Logger::DEBUG);
113
114                 q("UPDATE `item` ".$query2." SET `item`.`global` = 1 ".$query3,
115                         intval($start_id), intval($pos_id),
116                         DBA::escape(Protocol::DFRN), DBA::escape(Protocol::DIASPORA), DBA::escape(Protocol::OSTATUS));
117
118                 Logger::log("Done", Logger::DEBUG);
119         }
120
121         /**
122          * update the "last-item" field in the "self" contact
123          *
124          * This field avoids cost intensive calls in the admin panel and in "nodeinfo"
125          *
126          * @return bool "true" when the job is done
127          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
128          */
129         private static function update1206()
130         {
131                 // Was the script completed?
132                 if (DI::config()->get("system", "post_update_version") >= 1206) {
133                         return true;
134                 }
135
136                 Logger::log("Start", Logger::DEBUG);
137                 $r = q("SELECT `contact`.`id`, `contact`.`last-item`,
138                         (SELECT MAX(`changed`) FROM `item` USE INDEX (`uid_wall_changed`) WHERE `wall` AND `uid` = `user`.`uid`) AS `lastitem_date`
139                         FROM `user`
140                         INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`");
141
142                 if (!DBA::isResult($r)) {
143                         return false;
144                 }
145                 foreach ($r as $user) {
146                         if (!empty($user["lastitem_date"]) && ($user["lastitem_date"] > $user["last-item"])) {
147                                 DBA::update('contact', ['last-item' => $user['lastitem_date']], ['id' => $user['id']]);
148                         }
149                 }
150
151                 DI::config()->set("system", "post_update_version", 1206);
152                 Logger::log("Done", Logger::DEBUG);
153                 return true;
154         }
155
156         /**
157          * update the item related tables
158          *
159          * @return bool "true" when the job is done
160          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
161          * @throws \ImagickException
162          */
163         private static function update1279()
164         {
165                 // Was the script completed?
166                 if (DI::config()->get("system", "post_update_version") >= 1279) {
167                         return true;
168                 }
169
170                 $id = DI::config()->get("system", "post_update_version_1279_id", 0);
171
172                 Logger::log("Start from item " . $id, Logger::DEBUG);
173
174                 $fields = array_merge(Item::MIXED_CONTENT_FIELDLIST, ['network', 'author-id', 'owner-id', 'tag', 'file',
175                         'author-name', 'author-avatar', 'author-link', 'owner-name', 'owner-avatar', 'owner-link', 'id',
176                         'uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'psid', 'post-type', 'bookmark', 'type',
177                         'inform', 'postopts', 'icid']);
178
179                 $start_id = $id;
180                 $rows = 0;
181                 $condition = ["`id` > ?", $id];
182                 $params = ['order' => ['id'], 'limit' => 10000];
183                 $items = Item::select($fields, $condition, $params);
184
185                 if (DBA::errorNo() != 0) {
186                         Logger::log('Database error ' . DBA::errorNo() . ':' . DBA::errorMessage());
187                         return false;
188                 }
189
190                 while ($item = Item::fetch($items)) {
191                         $id = $item['id'];
192
193                         if (empty($item['author-id'])) {
194                                 $default = ['url' => $item['author-link'], 'name' => $item['author-name'],
195                                         'photo' => $item['author-avatar'], 'network' => $item['network']];
196
197                                 $item['author-id'] = Contact::getIdForURL($item["author-link"], 0, false, $default);
198                         }
199
200                         if (empty($item['owner-id'])) {
201                                 $default = ['url' => $item['owner-link'], 'name' => $item['owner-name'],
202                                         'photo' => $item['owner-avatar'], 'network' => $item['network']];
203
204                                 $item['owner-id'] = Contact::getIdForURL($item["owner-link"], 0, false, $default);
205                         }
206
207                         if (empty($item['psid'])) {
208                                 $item['psid'] = PermissionSet::fetchIDForPost($item);
209                         } else {
210                                 $item['allow_cid'] = null;
211                                 $item['allow_gid'] = null;
212                                 $item['deny_cid'] = null;
213                                 $item['deny_gid'] = null;
214                         }
215
216                         if ($item['post-type'] == 0) {
217                                 if (!empty($item['type']) && ($item['type'] == 'note')) {
218                                         $item['post-type'] = Item::PT_PERSONAL_NOTE;
219                                 } elseif (!empty($item['type']) && ($item['type'] == 'photo')) {
220                                         $item['post-type'] = Item::PT_IMAGE;
221                                 } elseif (!empty($item['bookmark']) && $item['bookmark']) {
222                                         $item['post-type'] = Item::PT_PAGE;
223                                 }
224                         }
225
226                         self::createLanguage($item);
227
228                         if (!empty($item['icid']) && !empty($item['language'])) {
229                                 DBA::update('item-content', ['language' => $item['language']], ['id' => $item['icid']]);
230                         }
231                         unset($item['language']);
232
233                         Item::update($item, ['id' => $id]);
234
235                         ++$rows;
236                 }
237                 DBA::close($items);
238
239                 DI::config()->set("system", "post_update_version_1279_id", $id);
240
241                 Logger::log("Processed rows: " . $rows . " - last processed item:  " . $id, Logger::DEBUG);
242
243                 if ($start_id == $id) {
244                         // Set all deprecated fields to "null" if they contain an empty string
245                         $nullfields = ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'postopts', 'inform', 'type',
246                                 'bookmark', 'file', 'location', 'coord', 'tag', 'plink', 'title', 'content-warning',
247                                 'body', 'app', 'verb', 'object-type', 'object', 'target-type', 'target',
248                                 'author-name', 'author-link', 'author-avatar', 'owner-name', 'owner-link', 'owner-avatar',
249                                 'rendered-hash', 'rendered-html'];
250                         foreach ($nullfields as $field) {
251                                 $fields = [$field => null];
252                                 $condition = [$field => ''];
253                                 Logger::log("Setting '" . $field . "' to null if empty.", Logger::DEBUG);
254                                 // Important: This has to be a "DBA::update", not a "Item::update"
255                                 DBA::update('item', $fields, $condition);
256                         }
257
258                         DI::config()->set("system", "post_update_version", 1279);
259                         Logger::log("Done", Logger::DEBUG);
260                         return true;
261                 }
262
263                 return false;
264         }
265
266         private static function createLanguage(&$item)
267         {
268                 if (empty($item['postopts'])) {
269                         return;
270                 }
271
272                 $opts = explode(',', $item['postopts']);
273
274                 $postopts = [];
275
276                 foreach ($opts as $opt) {
277                         if (strstr($opt, 'lang=')) {
278                                 $language = substr($opt, 5);
279                         } else {
280                                 $postopts[] = $opt;
281                         }
282                 }
283
284                 if (empty($language)) {
285                         return;
286                 }
287
288                 if (!empty($postopts)) {
289                         $item['postopts'] = implode(',', $postopts);
290                 } else {
291                         $item['postopts'] = null;
292                 }
293
294                 $lang_pairs = explode(':', $language);
295
296                 $lang_arr = [];
297
298                 foreach ($lang_pairs as $pair) {
299                         $lang_pair_arr = explode(';', $pair);
300                         if (count($lang_pair_arr) == 2) {
301                                 $lang_arr[$lang_pair_arr[0]] = $lang_pair_arr[1];
302                         }
303                 }
304
305                 $item['language'] = json_encode($lang_arr);
306         }
307
308         /**
309          * update item-uri data. Prerequisite for the next item structure update.
310          *
311          * @return bool "true" when the job is done
312          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
313          */
314         private static function update1281()
315         {
316                 // Was the script completed?
317                 if (DI::config()->get("system", "post_update_version") >= 1281) {
318                         return true;
319                 }
320
321                 $id = DI::config()->get("system", "post_update_version_1281_id", 0);
322
323                 Logger::log("Start from item " . $id, Logger::DEBUG);
324
325                 $fields = ['id', 'guid', 'uri', 'uri-id', 'parent-uri', 'parent-uri-id', 'thr-parent', 'thr-parent-id'];
326
327                 $start_id = $id;
328                 $rows = 0;
329                 $condition = ["`id` > ?", $id];
330                 $params = ['order' => ['id'], 'limit' => 10000];
331                 $items = DBA::select('item', $fields, $condition, $params);
332
333                 if (DBA::errorNo() != 0) {
334                         Logger::log('Database error ' . DBA::errorNo() . ':' . DBA::errorMessage());
335                         return false;
336                 }
337
338                 while ($item = DBA::fetch($items)) {
339                         $id = $item['id'];
340
341                         if (empty($item['uri'])) {
342                                 // Should not happen
343                                 continue;
344                         } elseif (empty($item['uri-id'])) {
345                                 $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
346                         }
347
348                         if (empty($item['parent-uri'])) {
349                                 $item['parent-uri-id'] = $item['uri-id'];
350                         } elseif (empty($item['parent-uri-id'])) {
351                                 $item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
352                         }
353
354                         // Very old items don't have this field
355                         if (empty($item['thr-parent'])) {
356                                 $item['thr-parent-id'] = $item['parent-uri-id'];
357                         } elseif (empty($item['thr-parent-id'])) {
358                                 $item['thr-parent-id'] = ItemURI::getIdByURI($item['thr-parent']);
359                         }
360
361                         unset($item['id']);
362                         unset($item['guid']);
363                         unset($item['uri']);
364                         unset($item['parent-uri']);
365                         unset($item['thr-parent']);
366
367                         DBA::update('item', $item, ['id' => $id]);
368
369                         ++$rows;
370                 }
371                 DBA::close($items);
372
373                 DI::config()->set("system", "post_update_version_1281_id", $id);
374
375                 Logger::log("Processed rows: " . $rows . " - last processed item:  " . $id, Logger::DEBUG);
376
377                 if ($start_id == $id) {
378                         Logger::log("Updating item-uri in item-activity", Logger::DEBUG);
379                         DBA::e("UPDATE `item-activity` INNER JOIN `item-uri` ON `item-uri`.`uri` = `item-activity`.`uri` SET `item-activity`.`uri-id` = `item-uri`.`id` WHERE `item-activity`.`uri-id` IS NULL");
380
381                         Logger::log("Updating item-uri in item-content", Logger::DEBUG);
382                         DBA::e("UPDATE `item-content` INNER JOIN `item-uri` ON `item-uri`.`uri` = `item-content`.`uri` SET `item-content`.`uri-id` = `item-uri`.`id` WHERE `item-content`.`uri-id` IS NULL");
383
384                         DI::config()->set("system", "post_update_version", 1281);
385                         Logger::log("Done", Logger::DEBUG);
386                         return true;
387                 }
388
389                 return false;
390         }
391
392         /**
393          * Set the delivery queue count to a negative value for all items preceding the feature.
394          *
395          * @return bool "true" when the job is done
396          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
397          */
398         private static function update1297()
399         {
400                 // Was the script completed?
401                 if (DI::config()->get('system', 'post_update_version') >= 1297) {
402                         return true;
403                 }
404
405                 $max_item_delivery_data = DBA::selectFirst('item-delivery-data', ['iid'], ['queue_count > 0 OR queue_done > 0'], ['order' => ['iid']]);
406                 $max_iid = $max_item_delivery_data['iid'];
407
408                 Logger::info('Start update1297 with max iid: ' . $max_iid);
409
410                 $condition = ['`queue_count` = 0 AND `iid` < ?', $max_iid];
411
412                 DBA::update('item-delivery-data', ['queue_count' => -1], $condition);
413
414                 if (DBA::errorNo() != 0) {
415                         Logger::error('Database error ' . DBA::errorNo() . ':' . DBA::errorMessage());
416                         return false;
417                 }
418
419                 Logger::info('Processed rows: ' . DBA::affectedRows());
420
421                 DI::config()->set('system', 'post_update_version', 1297);
422
423                 Logger::info('Done');
424
425                 return true;
426         }
427         /**
428          * Remove contact duplicates
429          *
430          * @return bool "true" when the job is done
431          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
432          */
433         private static function update1322()
434         {
435                 // Was the script completed?
436                 if (DI::config()->get('system', 'post_update_version') >= 1322) {
437                         return true;
438                 }
439
440                 Logger::info('Start');
441
442                 $contacts = DBA::p("SELECT `nurl`, `uid` FROM `contact`
443                         WHERE EXISTS (SELECT `nurl` FROM `contact` AS `c2`
444                                 WHERE `c2`.`nurl` = `contact`.`nurl` AND `c2`.`id` != `contact`.`id` AND `c2`.`uid` = `contact`.`uid` AND `c2`.`network` IN (?, ?, ?) AND NOT `deleted`)
445                         AND (`network` IN (?, ?, ?) OR (`uid` = ?)) AND NOT `deleted` GROUP BY `nurl`, `uid`",
446                         Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB,
447                         Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 0);
448
449                 while ($contact = DBA::fetch($contacts)) {
450                         Logger::info('Remove duplicates', ['nurl' => $contact['nurl'], 'uid' => $contact['uid']]);
451                         Contact::removeDuplicates($contact['nurl'], $contact['uid']);
452                 }
453
454                 DBA::close($contact);
455                 DI::config()->set('system', 'post_update_version', 1322);
456
457                 Logger::info('Done');
458
459                 return true;
460         }
461
462         /**
463          * update user-item data with notifications
464          *
465          * @return bool "true" when the job is done
466          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
467          */
468         private static function update1329()
469         {
470                 // Was the script completed?
471                 if (DI::config()->get('system', 'post_update_version') >= 1329) {
472                         return true;
473                 }
474
475                 $id = DI::config()->get('system', 'post_update_version_1329_id', 0);
476
477                 Logger::info('Start', ['item' => $id]);
478
479                 $start_id = $id;
480                 $rows = 0;
481                 $condition = ["`id` > ?", $id];
482                 $params = ['order' => ['id'], 'limit' => 10000];
483                 $items = DBA::select('item', ['id'], $condition, $params);
484
485                 if (DBA::errorNo() != 0) {
486                         Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
487                         return false;
488                 }
489
490                 while ($item = DBA::fetch($items)) {
491                         $id = $item['id'];
492
493                         UserItem::setNotification($item['id']);
494
495                         ++$rows;
496                 }
497                 DBA::close($items);
498
499                 DI::config()->set('system', 'post_update_version_1329_id', $id);
500
501                 Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
502
503                 if ($start_id == $id) {
504                         DI::config()->set('system', 'post_update_version', 1329);
505                         Logger::info('Done');
506                         return true;
507                 }
508
509                 return false;
510         }
511 }