]> git.mxchange.org Git - friendica.git/blob - update.php
Merge pull request #8263 from annando/remote-follow
[friendica.git] / update.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  * Automatic post-databse structure change updates
21  *
22  * These functions are responsible for doing critical post update changes to the data (not the structure) in the database.
23  *
24  * Database structure changes are done in static/dbstructure.config.php
25  *
26  * For non-critical database migrations, please add a method in the Database\PostUpdate class
27  *
28  * If there is a need for a post update to a structure change, update this file
29  * by adding a new function at the end with the number of the new DB_UPDATE_VERSION.
30  *
31  * The numbered script in this file has to be exactly like the DB_UPDATE_VERSION
32  *
33  * Example:
34  * You are currently on version 4711 and you are preparing changes that demand an update script.
35  *
36  * 1. Create a function "update_4712()" here in the update.php
37  * 2. Apply the needed structural changes in static/dbStructure.php
38  * 3. Set DB_UPDATE_VERSION in static/dbstructure.config.php to 4712.
39  *
40  * If you need to run a script before the database update, name the function "pre_update_4712()"
41  */
42
43 use Friendica\Core\Addon;
44 use Friendica\Core\Logger;
45 use Friendica\Core\Update;
46 use Friendica\Core\Worker;
47 use Friendica\Database\DBA;
48 use Friendica\DI;
49 use Friendica\Model\Contact;
50 use Friendica\Model\GContact;
51 use Friendica\Model\Item;
52 use Friendica\Model\User;
53 use Friendica\Model\Storage;
54 use Friendica\Util\DateTimeFormat;
55 use Friendica\Worker\Delivery;
56
57 function update_1178()
58 {
59         require_once 'mod/profiles.php';
60
61         $profiles = q("SELECT `uid`, `about`, `locality`, `pub_keywords`, `gender` FROM `profile` WHERE `is-default`");
62
63         foreach ($profiles as $profile) {
64                 if ($profile["about"].$profile["locality"].$profile["pub_keywords"].$profile["gender"] == "") {
65                         continue;
66                 }
67
68                 $profile["pub_keywords"] = profile_clean_keywords($profile["pub_keywords"]);
69
70                 $r = q("UPDATE `contact` SET `about` = '%s', `location` = '%s', `keywords` = '%s', `gender` = '%s' WHERE `self` AND `uid` = %d",
71                         DBA::escape($profile["about"]),
72                         DBA::escape($profile["locality"]),
73                         DBA::escape($profile["pub_keywords"]),
74                         DBA::escape($profile["gender"]),
75                         intval($profile["uid"])
76                 );
77         }
78 }
79
80 function update_1179()
81 {
82         if (DI::config()->get('system', 'no_community_page')) {
83                 DI::config()->set('system', 'community_page_style', CP_NO_COMMUNITY_PAGE);
84         }
85
86         // Update the central item storage with uid=0
87         Worker::add(PRIORITY_LOW, "threadupdate");
88
89         return Update::SUCCESS;
90 }
91
92 function update_1181()
93 {
94
95         // Fill the new fields in the term table.
96         Worker::add(PRIORITY_LOW, "TagUpdate");
97
98         return Update::SUCCESS;
99 }
100
101 function update_1189()
102 {
103
104         if (strlen(DI::config()->get('system', 'directory_submit_url')) &&
105                 !strlen(DI::config()->get('system', 'directory'))) {
106                 DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url')));
107                 DI::config()->delete('system', 'directory_submit_url');
108         }
109
110         return Update::SUCCESS;
111 }
112
113 function update_1191()
114 {
115         DI::config()->set('system', 'maintenance', 1);
116
117         if (Addon::isEnabled('forumlist')) {
118                 $addon = 'forumlist';
119                 $addons = DI::config()->get('system', 'addon');
120                 $addons_arr = [];
121
122                 if ($addons) {
123                         $addons_arr = explode(",", str_replace(" ", "", $addons));
124
125                         $idx = array_search($addon, $addons_arr);
126                         if ($idx !== false) {
127                                 unset($addons_arr[$idx]);
128                                 //delete forumlist manually from addon and hook table
129                                 // since Addon::uninstall() don't work here
130                                 q("DELETE FROM `addon` WHERE `name` = 'forumlist' ");
131                                 q("DELETE FROM `hook` WHERE `file` = 'addon/forumlist/forumlist.php' ");
132                                 DI::config()->set('system', 'addon', implode(", ", $addons_arr));
133                         }
134                 }
135         }
136
137         // select old formlist addon entries
138         $r = q("SELECT `uid`, `cat`, `k`, `v` FROM `pconfig` WHERE `cat` = '%s' ",
139                 DBA::escape('forumlist')
140         );
141
142         // convert old forumlist addon entries in new config entries
143         if (DBA::isResult($r)) {
144                 foreach ($r as $rr) {
145                         $uid = $rr['uid'];
146                         $family = $rr['cat'];
147                         $key = $rr['k'];
148                         $value = $rr['v'];
149
150                         if ($key === 'randomise') {
151                                 DI::pConfig()->delete($uid, $family, $key);
152                         }
153
154                         if ($key === 'show_on_profile') {
155                                 if ($value) {
156                                         DI::pConfig()->set($uid, feature, forumlist_profile, $value);
157                                 }
158
159                                 DI::pConfig()->delete($uid, $family, $key);
160                         }
161
162                         if ($key === 'show_on_network') {
163                                 if ($value) {
164                                         DI::pConfig()->set($uid, feature, forumlist_widget, $value);
165                                 }
166
167                                 DI::pConfig()->delete($uid, $family, $key);
168                         }
169                 }
170         }
171
172         DI::config()->set('system', 'maintenance', 0);
173
174         return Update::SUCCESS;
175 }
176
177 function update_1203()
178 {
179         $r = q("UPDATE `user` SET `account-type` = %d WHERE `page-flags` IN (%d, %d)",
180                 DBA::escape(User::ACCOUNT_TYPE_COMMUNITY),
181                 DBA::escape(User::PAGE_FLAGS_COMMUNITY),
182                 DBA::escape(User::PAGE_FLAGS_PRVGROUP)
183         );
184 }
185
186 function update_1244()
187 {
188         // Sets legacy_password for all legacy hashes
189         DBA::update('user', ['legacy_password' => true], ['SUBSTR(password, 1, 4) != "$2y$"']);
190
191         // All legacy hashes are re-hashed using the new secure hashing function
192         $stmt = DBA::select('user', ['uid', 'password'], ['legacy_password' => true]);
193         while ($user = DBA::fetch($stmt)) {
194                 DBA::update('user', ['password' => User::hashPassword($user['password'])], ['uid' => $user['uid']]);
195         }
196
197         // Logged in users are forcibly logged out
198         DBA::delete('session', ['1 = 1']);
199
200         return Update::SUCCESS;
201 }
202
203 function update_1245()
204 {
205         $rino = DI::config()->get('system', 'rino_encrypt');
206
207         if (!$rino) {
208                 return Update::SUCCESS;
209         }
210
211         DI::config()->set('system', 'rino_encrypt', 1);
212
213         return Update::SUCCESS;
214 }
215
216 function update_1247()
217 {
218         // Removing hooks with the old name
219         DBA::e("DELETE FROM `hook`
220 WHERE `hook` LIKE 'plugin_%'");
221
222         // Make sure we install the new renamed ones
223         Addon::reload();
224 }
225
226 function update_1260()
227 {
228         DI::config()->set('system', 'maintenance', 1);
229         DI::config()->set(
230                 'system',
231                 'maintenance_reason',
232                 DI::l10n()->t(
233                         '%s: Updating author-id and owner-id in item and thread table. ',
234                         DateTimeFormat::utcNow().' '.date('e')
235                 )
236         );
237
238         $items = DBA::p("SELECT `id`, `owner-link`, `owner-name`, `owner-avatar`, `network` FROM `item`
239                 WHERE `owner-id` = 0 AND `owner-link` != ''");
240         while ($item = DBA::fetch($items)) {
241                 $contact = ['url' => $item['owner-link'], 'name' => $item['owner-name'],
242                         'photo' => $item['owner-avatar'], 'network' => $item['network']];
243                 $cid = Contact::getIdForURL($item['owner-link'], 0, false, $contact);
244                 if (empty($cid)) {
245                         continue;
246                 }
247                 Item::update(['owner-id' => $cid], ['id' => $item['id']]);
248         }
249         DBA::close($items);
250
251         DBA::e("UPDATE `thread` INNER JOIN `item` ON `thread`.`iid` = `item`.`id`
252                 SET `thread`.`owner-id` = `item`.`owner-id` WHERE `thread`.`owner-id` = 0");
253
254         $items = DBA::p("SELECT `id`, `author-link`, `author-name`, `author-avatar`, `network` FROM `item`
255                 WHERE `author-id` = 0 AND `author-link` != ''");
256         while ($item = DBA::fetch($items)) {
257                 $contact = ['url' => $item['author-link'], 'name' => $item['author-name'],
258                         'photo' => $item['author-avatar'], 'network' => $item['network']];
259                 $cid = Contact::getIdForURL($item['author-link'], 0, false, $contact);
260                 if (empty($cid)) {
261                         continue;
262                 }
263                 Item::update(['author-id' => $cid], ['id' => $item['id']]);
264         }
265         DBA::close($items);
266
267         DBA::e("UPDATE `thread` INNER JOIN `item` ON `thread`.`iid` = `item`.`id`
268                 SET `thread`.`author-id` = `item`.`author-id` WHERE `thread`.`author-id` = 0");
269
270         DI::config()->set('system', 'maintenance', 0);
271         return Update::SUCCESS;
272 }
273
274 function update_1261()
275 {
276         // This fixes the results of an issue in the develop branch of 2018-05.
277         DBA::update('contact', ['blocked' => false, 'pending' => false], ['uid' => 0, 'blocked' => true, 'pending' => true]);
278         return Update::SUCCESS;
279 }
280
281 function update_1278()
282 {
283         DI::config()->set('system', 'maintenance', 1);
284         DI::config()->set(
285                 'system',
286                 'maintenance_reason',
287                 DI::l10n()->t(
288                         '%s: Updating post-type.',
289                         DateTimeFormat::utcNow().' '.date('e')
290                 )
291         );
292
293         Item::update(['post-type' => Item::PT_PAGE], ['bookmark' => true]);
294         Item::update(['post-type' => Item::PT_PERSONAL_NOTE], ['type' => 'note']);
295
296         DI::config()->set('system', 'maintenance', 0);
297
298         return Update::SUCCESS;
299 }
300
301 function update_1288()
302 {
303         // Updates missing `uri-id` values
304
305         DBA::e("UPDATE `item-activity` INNER JOIN `item` ON `item`.`iaid` = `item-activity`.`id` SET `item-activity`.`uri-id` = `item`.`uri-id` WHERE `item-activity`.`uri-id` IS NULL OR `item-activity`.`uri-id` = 0");
306         DBA::e("UPDATE `item-content` INNER JOIN `item` ON `item`.`icid` = `item-content`.`id` SET `item-content`.`uri-id` = `item`.`uri-id` WHERE `item-content`.`uri-id` IS NULL OR `item-content`.`uri-id` = 0");
307
308         return Update::SUCCESS;
309 }
310
311 // Post-update script of PR 5751
312 function update_1298()
313 {
314         $keys = ['gender', 'marital', 'sexual'];
315         foreach ($keys as $translateKey) {
316                 $allData = DBA::select('profile', ['id', $translateKey]);
317                 $allLangs = DI::l10n()->getAvailableLanguages();
318                 $success = 0;
319                 $fail = 0;
320                 foreach ($allData as $key => $data) {
321                         $toTranslate = $data[$translateKey];
322                         if ($toTranslate != '') {
323                                 foreach ($allLangs as $key => $lang) {
324                                         $a = new \stdClass();
325                                         $a->strings = [];
326
327                                         // First we get the the localizations
328                                         if (file_exists("view/lang/$lang/strings.php")) {
329                                                 include "view/lang/$lang/strings.php";
330                                         }
331                                         if (file_exists("addon/morechoice/lang/$lang/strings.php")) {
332                                                 include "addon/morechoice/lang/$lang/strings.php";
333                                         }
334
335                                         $localizedStrings = $a->strings;
336                                         unset($a);
337
338                                         $key = array_search($toTranslate, $localizedStrings);
339                                         if ($key !== false) {
340                                                 break;
341                                         }
342
343                                         // defaulting to empty string
344                                         $key = '';
345                                 }
346
347                                 if ($key == '') {
348                                         $fail++;
349                                 } else {
350                                         DBA::update('profile', [$translateKey => $key], ['id' => $data['id']]);
351                                         Logger::notice('Updated contact', ['action' => 'update', 'contact' => $data['id'], "$translateKey" => $key,
352                                                 'was' => $data[$translateKey]]);
353                                         Worker::add(PRIORITY_LOW, 'ProfileUpdate', $data['id']);
354                                         Contact::updateSelfFromUserID($data['id']);
355                                         GContact::updateForUser($data['id']);
356                                         $success++;
357                                 }
358                         }
359                 }
360
361                 Logger::notice($translateKey . " fix completed", ['action' => 'update', 'translateKey' => $translateKey, 'Success' => $success, 'Fail' => $fail ]);
362         }
363         return Update::SUCCESS;
364 }
365
366 function update_1309()
367 {
368         $queue = DBA::select('queue', ['id', 'cid', 'guid']);
369         while ($entry = DBA::fetch($queue)) {
370                 $contact = DBA::selectFirst('contact', ['uid'], ['id' => $entry['cid']]);
371                 if (!DBA::isResult($contact)) {
372                         continue;
373                 }
374
375                 $item = Item::selectFirst(['id', 'gravity'], ['uid' => $contact['uid'], 'guid' => $entry['guid']]);
376                 if (!DBA::isResult($item)) {
377                         continue;
378                 }
379
380                 $deliver_options = ['priority' => PRIORITY_MEDIUM, 'dont_fork' => true];
381                 Worker::add($deliver_options, 'Delivery', Delivery::POST, $item['id'], $entry['cid']);
382                 Logger::info('Added delivery worker', ['command' => $cmd, 'item' => $item['id'], 'contact' => $entry['cid']]);
383                 DBA::delete('queue', ['id' => $entry['id']]);
384         }
385         return Update::SUCCESS;
386 }
387
388 function update_1315()
389 {
390         DBA::delete('item-delivery-data', ['postopts' => '', 'inform' => '', 'queue_count' => 0, 'queue_done' => 0]);
391         return Update::SUCCESS;
392 }
393
394 function update_1318()
395 {
396         DBA::update('profile', ['marital' => "In a relation"], ['marital' => "Unavailable"]);
397         DBA::update('profile', ['marital' => "Single"], ['marital' => "Available"]);
398
399         Worker::add(PRIORITY_LOW, 'ProfileUpdate');
400         return Update::SUCCESS;
401 }
402
403 function update_1323()
404 {
405         $users = DBA::select('user', ['uid']);
406         while ($user = DBA::fetch($users)) {
407                 Contact::updateSelfFromUserID($user['uid']);
408         }
409         DBA::close($users);
410
411         return Update::SUCCESS;
412 }
413
414 function update_1327()
415 {
416         $contacts = DBA::select('contact', ['uid', 'id', 'blocked', 'readonly'], ["`uid` != ? AND (`blocked` OR `readonly`) AND NOT `pending`", 0]);
417         while ($contact = DBA::fetch($contacts)) {
418                 Contact::setBlockedForUser($contact['id'], $contact['uid'], $contact['blocked']);
419                 Contact::setIgnoredForUser($contact['id'], $contact['uid'], $contact['readonly']);
420         }
421         DBA::close($contacts);
422
423         return Update::SUCCESS;
424 }
425
426 function update_1330()
427 {
428         $currStorage = DI::config()->get('storage', 'class', '');
429
430         // set the name of the storage instead of the classpath as config
431         if (!empty($currStorage)) {
432                 /** @var Storage\IStorage $currStorage */
433                 if (!DI::config()->set('storage', 'name', $currStorage::getName())) {
434                         return Update::FAILED;
435                 }
436
437                 // try to delete the class since it isn't needed. This won't work with config files
438                 DI::config()->delete('storage', 'class');
439         }
440
441         // Update attachments and photos
442         if (!DBA::p("UPDATE `photo` SET `photo`.`backend-class` = SUBSTR(`photo`.`backend-class`, 25) WHERE `photo`.`backend-class` LIKE 'Friendica\\\Model\\\Storage\\\%' ESCAPE '|'") ||
443             !DBA::p("UPDATE `attach` SET `attach`.`backend-class` = SUBSTR(`attach`.`backend-class`, 25) WHERE `attach`.`backend-class` LIKE 'Friendica\\\Model\\\Storage\\\%' ESCAPE '|'")) {
444                 return Update::FAILED;
445         };
446
447         return Update::SUCCESS;
448 }
449
450 function update_1332()
451 {
452         $condition = ["`is-default` IS NOT NULL"];
453         $profiles = DBA::select('profile', [], $condition);
454
455         while ($profile = DBA::fetch($profiles)) {
456                 DI::profileField()->migrateFromLegacyProfile($profile);
457         }
458         DBA::close($profiles);
459
460         DBA::update('contact', ['profile-id' => null], ['`profile-id` IS NOT NULL']);
461
462         return Update::SUCCESS;
463 }