]> git.mxchange.org Git - friendica.git/blob - mod/settings.php
8eef254e4b871c604721e094bb955809d4b77a4d
[friendica.git] / mod / settings.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 use Friendica\App;
23 use Friendica\BaseModule;
24 use Friendica\Content\Feature;
25 use Friendica\Content\Nav;
26 use Friendica\Core\ACL;
27 use Friendica\Core\Hook;
28 use Friendica\Core\Logger;
29 use Friendica\Core\Renderer;
30 use Friendica\Core\Session;
31 use Friendica\Core\Theme;
32 use Friendica\Core\Worker;
33 use Friendica\Database\DBA;
34 use Friendica\DI;
35 use Friendica\Model\Contact;
36 use Friendica\Model\GContact;
37 use Friendica\Model\Group;
38 use Friendica\Model\Notify\Type;
39 use Friendica\Model\User;
40 use Friendica\Module\Security\Login;
41 use Friendica\Protocol\Email;
42 use Friendica\Util\Strings;
43 use Friendica\Util\Temporal;
44 use Friendica\Worker\Delivery;
45
46 function get_theme_config_file($theme)
47 {
48         $theme = Strings::sanitizeFilePathItem($theme);
49
50         $a = DI::app();
51         $base_theme = $a->theme_info['extends'] ?? '';
52
53         if (file_exists("view/theme/$theme/config.php")) {
54                 return "view/theme/$theme/config.php";
55         }
56         if ($base_theme && file_exists("view/theme/$base_theme/config.php")) {
57                 return "view/theme/$base_theme/config.php";
58         }
59         return null;
60 }
61
62 function settings_init(App $a)
63 {
64         if (!local_user()) {
65                 notice(DI::l10n()->t('Permission denied.') . EOL);
66                 return;
67         }
68
69         // These lines provide the javascript needed by the acl selector
70
71         $tpl = Renderer::getMarkupTemplate('settings/head.tpl');
72         DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
73                 '$ispublic' => DI::l10n()->t('everybody')
74         ]);
75
76         $tabs = [
77                 [
78                         'label' => DI::l10n()->t('Account'),
79                         'url'   => 'settings',
80                         'selected'      =>  (($a->argc == 1) && ($a->argv[0] === 'settings')?'active':''),
81                         'accesskey' => 'o',
82                 ],
83         ];
84
85         $tabs[] = [
86                 'label' => DI::l10n()->t('Two-factor authentication'),
87                 'url' => 'settings/2fa',
88                 'selected' => (($a->argc > 1) && ($a->argv[1] === '2fa') ? 'active' : ''),
89                 'accesskey' => 'o',
90         ];
91
92         $tabs[] =       [
93                 'label' => DI::l10n()->t('Profile'),
94                 'url'   => 'settings/profile',
95                 'selected'      => (($a->argc > 1) && ($a->argv[1] === 'profile')?'active':''),
96                 'accesskey' => 'p',
97         ];
98
99         if (Feature::get()) {
100                 $tabs[] =       [
101                                         'label' => DI::l10n()->t('Additional features'),
102                                         'url'   => 'settings/features',
103                                         'selected'      => (($a->argc > 1) && ($a->argv[1] === 'features') ? 'active' : ''),
104                                         'accesskey' => 't',
105                                 ];
106         }
107
108         $tabs[] =       [
109                 'label' => DI::l10n()->t('Display'),
110                 'url'   => 'settings/display',
111                 'selected'      => (($a->argc > 1) && ($a->argv[1] === 'display')?'active':''),
112                 'accesskey' => 'i',
113         ];
114
115         $tabs[] =       [
116                 'label' => DI::l10n()->t('Social Networks'),
117                 'url'   => 'settings/connectors',
118                 'selected'      => (($a->argc > 1) && ($a->argv[1] === 'connectors')?'active':''),
119                 'accesskey' => 'w',
120         ];
121
122         $tabs[] =       [
123                 'label' => DI::l10n()->t('Addons'),
124                 'url'   => 'settings/addon',
125                 'selected'      => (($a->argc > 1) && ($a->argv[1] === 'addon')?'active':''),
126                 'accesskey' => 'l',
127         ];
128
129         $tabs[] =       [
130                 'label' => DI::l10n()->t('Manage Accounts'),
131                 'url'   => 'settings/delegation',
132                 'selected'      => (($a->argc > 1) && ($a->argv[1] === 'delegation')?'active':''),
133                 'accesskey' => 'd',
134         ];
135
136         $tabs[] =       [
137                 'label' => DI::l10n()->t('Connected apps'),
138                 'url' => 'settings/oauth',
139                 'selected' => (($a->argc > 1) && ($a->argv[1] === 'oauth')?'active':''),
140                 'accesskey' => 'b',
141         ];
142
143         $tabs[] =       [
144                 'label' => DI::l10n()->t('Export personal data'),
145                 'url' => 'settings/userexport',
146                 'selected' => (($a->argc > 1) && ($a->argv[1] === 'userexport')?'active':''),
147                 'accesskey' => 'e',
148         ];
149
150         $tabs[] =       [
151                 'label' => DI::l10n()->t('Remove account'),
152                 'url' => 'removeme',
153                 'selected' => (($a->argc == 1) && ($a->argv[0] === 'removeme')?'active':''),
154                 'accesskey' => 'r',
155         ];
156
157
158         $tabtpl = Renderer::getMarkupTemplate("generic_links_widget.tpl");
159         DI::page()['aside'] = Renderer::replaceMacros($tabtpl, [
160                 '$title' => DI::l10n()->t('Settings'),
161                 '$class' => 'settings-widget',
162                 '$items' => $tabs,
163         ]);
164
165 }
166
167 function settings_post(App $a)
168 {
169         if (!local_user()) {
170                 return;
171         }
172
173         if (!empty($_SESSION['submanage'])) {
174                 return;
175         }
176
177         if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) {
178                 notice(DI::l10n()->t('Permission denied.') . EOL);
179                 return;
180         }
181
182         $old_page_flags = $a->user['page-flags'];
183
184         if (($a->argc > 1) && ($a->argv[1] === 'oauth') && !empty($_POST['remove'])) {
185                 BaseModule::checkFormSecurityTokenRedirectOnError('/settings/oauth', 'settings_oauth');
186
187                 $key = $_POST['remove'];
188                 DBA::delete('tokens', ['id' => $key, 'uid' => local_user()]);
189                 DI::baseUrl()->redirect('settings/oauth/', true);
190                 return;
191         }
192
193         if (($a->argc > 2) && ($a->argv[1] === 'oauth')  && ($a->argv[2] === 'edit'||($a->argv[2] === 'add')) && !empty($_POST['submit'])) {
194                 BaseModule::checkFormSecurityTokenRedirectOnError('/settings/oauth', 'settings_oauth');
195
196                 $name     = $_POST['name']     ?? '';
197                 $key      = $_POST['key']      ?? '';
198                 $secret   = $_POST['secret']   ?? '';
199                 $redirect = $_POST['redirect'] ?? '';
200                 $icon     = $_POST['icon']     ?? '';
201
202                 if ($name == "" || $key == "" || $secret == "") {
203                         notice(DI::l10n()->t("Missing some important data!"));
204                 } else {
205                         if ($_POST['submit'] == DI::l10n()->t("Update")) {
206                                 q("UPDATE clients SET
207                                                         client_id='%s',
208                                                         pw='%s',
209                                                         name='%s',
210                                                         redirect_uri='%s',
211                                                         icon='%s',
212                                                         uid=%d
213                                                 WHERE client_id='%s'",
214                                         DBA::escape($key),
215                                         DBA::escape($secret),
216                                         DBA::escape($name),
217                                         DBA::escape($redirect),
218                                         DBA::escape($icon),
219                                         local_user(),
220                                         DBA::escape($key)
221                                 );
222                         } else {
223                                 q("INSERT INTO clients
224                                                         (client_id, pw, name, redirect_uri, icon, uid)
225                                                 VALUES ('%s', '%s', '%s', '%s', '%s',%d)",
226                                         DBA::escape($key),
227                                         DBA::escape($secret),
228                                         DBA::escape($name),
229                                         DBA::escape($redirect),
230                                         DBA::escape($icon),
231                                         local_user()
232                                 );
233                         }
234                 }
235                 DI::baseUrl()->redirect('settings/oauth/', true);
236                 return;
237         }
238
239         if (($a->argc > 1) && ($a->argv[1] == 'addon')) {
240                 BaseModule::checkFormSecurityTokenRedirectOnError('/settings/addon', 'settings_addon');
241
242                 Hook::callAll('addon_settings_post', $_POST);
243                 return;
244         }
245
246         if (($a->argc > 1) && ($a->argv[1] == 'connectors')) {
247                 BaseModule::checkFormSecurityTokenRedirectOnError('/settings/connectors', 'settings_connectors');
248
249                 if (!empty($_POST['general-submit'])) {
250                         DI::pConfig()->set(local_user(), 'system', 'accept_only_sharer', intval($_POST['accept_only_sharer']));
251                         DI::pConfig()->set(local_user(), 'system', 'disable_cw', intval($_POST['disable_cw']));
252                         DI::pConfig()->set(local_user(), 'system', 'no_intelligent_shortening', intval($_POST['no_intelligent_shortening']));
253                         DI::pConfig()->set(local_user(), 'system', 'attach_link_title', intval($_POST['attach_link_title']));
254                         DI::pConfig()->set(local_user(), 'system', 'ostatus_autofriend', intval($_POST['snautofollow']));
255                         DI::pConfig()->set(local_user(), 'ostatus', 'default_group', $_POST['group-selection']);
256                         DI::pConfig()->set(local_user(), 'ostatus', 'legacy_contact', $_POST['legacy_contact']);
257                 } elseif (!empty($_POST['imap-submit'])) {
258                         $mail_server       =                 $_POST['mail_server']       ?? '';
259                         $mail_port         =                 $_POST['mail_port']         ?? '';
260                         $mail_ssl          = strtolower(trim($_POST['mail_ssl']          ?? ''));
261                         $mail_user         =                 $_POST['mail_user']         ?? '';
262                         $mail_pass         =            trim($_POST['mail_pass']         ?? '');
263                         $mail_action       =            trim($_POST['mail_action']       ?? '');
264                         $mail_movetofolder =            trim($_POST['mail_movetofolder'] ?? '');
265                         $mail_replyto      =                 $_POST['mail_replyto']      ?? '';
266                         $mail_pubmail      =                 $_POST['mail_pubmail']      ?? '';
267
268                         if (
269                                 !DI::config()->get('system', 'dfrn_only')
270                                 && function_exists('imap_open')
271                                 && !DI::config()->get('system', 'imap_disabled')
272                         ) {
273                                 $failed = false;
274                                 $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
275                                         intval(local_user())
276                                 );
277                                 if (!DBA::isResult($r)) {
278                                         DBA::insert('mailacct', ['uid' => local_user()]);
279                                 }
280                                 if (strlen($mail_pass)) {
281                                         $pass = '';
282                                         openssl_public_encrypt($mail_pass, $pass, $a->user['pubkey']);
283                                         DBA::update('mailacct', ['pass' => bin2hex($pass)], ['uid' => local_user()]);
284                                 }
285                                 $r = q("UPDATE `mailacct` SET `server` = '%s', `port` = %d, `ssltype` = '%s', `user` = '%s',
286                                         `action` = %d, `movetofolder` = '%s',
287                                         `mailbox` = 'INBOX', `reply_to` = '%s', `pubmail` = %d WHERE `uid` = %d",
288                                         DBA::escape($mail_server),
289                                         intval($mail_port),
290                                         DBA::escape($mail_ssl),
291                                         DBA::escape($mail_user),
292                                         intval($mail_action),
293                                         DBA::escape($mail_movetofolder),
294                                         DBA::escape($mail_replyto),
295                                         intval($mail_pubmail),
296                                         intval(local_user())
297                                 );
298                                 Logger::log("mail: updating mailaccount. Response: ".print_r($r, true));
299                                 $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
300                                         intval(local_user())
301                                 );
302                                 if (DBA::isResult($r)) {
303                                         $eacct = $r[0];
304                                         $mb = Email::constructMailboxName($eacct);
305
306                                         if (strlen($eacct['server'])) {
307                                                 $dcrpass = '';
308                                                 openssl_private_decrypt(hex2bin($eacct['pass']), $dcrpass, $a->user['prvkey']);
309                                                 $mbox = Email::connect($mb, $mail_user, $dcrpass);
310                                                 unset($dcrpass);
311                                                 if (!$mbox) {
312                                                         $failed = true;
313                                                         notice(DI::l10n()->t('Failed to connect with email account using the settings provided.') . EOL);
314                                                 }
315                                         }
316                                 }
317                                 if (!$failed) {
318                                         info(DI::l10n()->t('Email settings updated.') . EOL);
319                                 }
320                         }
321                 }
322
323                 Hook::callAll('connector_settings_post', $_POST);
324                 return;
325         }
326
327         if (($a->argc > 1) && ($a->argv[1] === 'features')) {
328                 BaseModule::checkFormSecurityTokenRedirectOnError('/settings/features', 'settings_features');
329                 foreach ($_POST as $k => $v) {
330                         if (strpos($k, 'feature_') === 0) {
331                                 DI::pConfig()->set(local_user(), 'feature', substr($k, 8), ((intval($v)) ? 1 : 0));
332                         }
333                 }
334                 info(DI::l10n()->t('Features updated') . EOL);
335                 return;
336         }
337
338         if (($a->argc > 1) && ($a->argv[1] === 'display')) {
339                 BaseModule::checkFormSecurityTokenRedirectOnError('/settings/display', 'settings_display');
340
341                 $theme              = !empty($_POST['theme'])              ? Strings::escapeTags(trim($_POST['theme']))        : $a->user['theme'];
342                 $mobile_theme       = !empty($_POST['mobile_theme'])       ? Strings::escapeTags(trim($_POST['mobile_theme'])) : '';
343                 $nosmile            = !empty($_POST['nosmile'])            ? intval($_POST['nosmile'])            : 0;
344                 $first_day_of_week  = !empty($_POST['first_day_of_week'])  ? intval($_POST['first_day_of_week'])  : 0;
345                 $infinite_scroll    = !empty($_POST['infinite_scroll'])    ? intval($_POST['infinite_scroll'])    : 0;
346                 $no_auto_update     = !empty($_POST['no_auto_update'])     ? intval($_POST['no_auto_update'])     : 0;
347                 $no_smart_threading = !empty($_POST['no_smart_threading']) ? intval($_POST['no_smart_threading']) : 0;
348                 $browser_update     = !empty($_POST['browser_update'])     ? intval($_POST['browser_update'])     : 0;
349                 if ($browser_update != -1) {
350                         $browser_update = $browser_update * 1000;
351                         if ($browser_update < 10000) {
352                                 $browser_update = 10000;
353                         }
354                 }
355
356                 $itemspage_network = !empty($_POST['itemspage_network']) ?
357                         intval($_POST['itemspage_network']) :
358                         DI::config()->get('system', 'itemspage_network');
359                 if ($itemspage_network > 100) {
360                         $itemspage_network = 100;
361                 }
362                 $itemspage_mobile_network = !empty($_POST['itemspage_mobile_network']) ?
363                         intval($_POST['itemspage_mobile_network']) :
364                         DI::config()->get('system', 'itemspage_network_mobile');
365                 if ($itemspage_mobile_network > 100) {
366                         $itemspage_mobile_network = 100;
367                 }
368
369                 if ($mobile_theme !== '') {
370                         DI::pConfig()->set(local_user(), 'system', 'mobile_theme', $mobile_theme);
371                 }
372
373                 DI::pConfig()->set(local_user(), 'system', 'update_interval'         , $browser_update);
374                 DI::pConfig()->set(local_user(), 'system', 'itemspage_network'       , $itemspage_network);
375                 DI::pConfig()->set(local_user(), 'system', 'itemspage_mobile_network', $itemspage_mobile_network);
376                 DI::pConfig()->set(local_user(), 'system', 'no_smilies'              , $nosmile);
377                 DI::pConfig()->set(local_user(), 'system', 'first_day_of_week'       , $first_day_of_week);
378                 DI::pConfig()->set(local_user(), 'system', 'infinite_scroll'         , $infinite_scroll);
379                 DI::pConfig()->set(local_user(), 'system', 'no_auto_update'          , $no_auto_update);
380                 DI::pConfig()->set(local_user(), 'system', 'no_smart_threading'      , $no_smart_threading);
381
382                 if (in_array($theme, Theme::getAllowedList())) {
383                         if ($theme == $a->user['theme']) {
384                                 // call theme_post only if theme has not been changed
385                                 if (($themeconfigfile = get_theme_config_file($theme)) !== null) {
386                                         require_once $themeconfigfile;
387                                         theme_post($a);
388                                 }
389                         } else {
390                                 DBA::update('user', ['theme' => $theme], ['uid' => local_user()]);
391                         }
392                 } else {
393                         notice(DI::l10n()->t('The theme you chose isn\'t available.'));
394                 }
395
396                 Hook::callAll('display_settings_post', $_POST);
397                 DI::baseUrl()->redirect('settings/display');
398                 return; // NOTREACHED
399         }
400
401         BaseModule::checkFormSecurityTokenRedirectOnError('/settings', 'settings');
402
403         // Import Contacts from CSV file
404         if (!empty($_POST['importcontact-submit'])) {
405                 if (isset($_FILES['importcontact-filename'])) {
406                         // was there an error
407                         if ($_FILES['importcontact-filename']['error'] > 0) {
408                                 Logger::notice('Contact CSV file upload error');
409                                 info(DI::l10n()->t('Contact CSV file upload error'));
410                         } else {
411                                 $csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name']));
412                                 // import contacts
413                                 foreach ($csvArray as $csvRow) {
414                                         // The 1st row may, or may not contain the headers of the table
415                                         // We expect the 1st field of the row to contain either the URL
416                                         // or the handle of the account, therefore we check for either
417                                         // "http" or "@" to be present in the string.
418                                         // All other fields from the row will be ignored
419                                         if ((strpos($csvRow[0],'@') !== false) || (strpos($csvRow[0],'http') !== false)) {
420                                                 Worker::add(PRIORITY_LOW, 'AddContact', $_SESSION['uid'], $csvRow[0]);
421                                         }
422                                 }
423
424                                 info(DI::l10n()->t('Importing Contacts done'));
425                                 // delete temp file
426                                 unlink($_FILES['importcontact-filename']['tmp_name']);
427                         }
428                 }
429         }
430
431         if (!empty($_POST['resend_relocate'])) {
432                 Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, local_user());
433                 info(DI::l10n()->t("Relocate message has been send to your contacts"));
434                 DI::baseUrl()->redirect('settings');
435         }
436
437         Hook::callAll('settings_post', $_POST);
438
439         if (!empty($_POST['password']) || !empty($_POST['confirm'])) {
440                 $newpass = $_POST['password'];
441                 $confirm = $_POST['confirm'];
442
443                 try {
444                         if ($newpass != $confirm) {
445                                 throw new Exception(DI::l10n()->t('Passwords do not match.'));
446                         }
447
448                         //  check if the old password was supplied correctly before changing it to the new value
449                         User::getIdFromPasswordAuthentication(local_user(), $_POST['opassword']);
450
451                         $result = User::updatePassword(local_user(), $newpass);
452                         if (!DBA::isResult($result)) {
453                                 throw new Exception(DI::l10n()->t('Password update failed. Please try again.'));
454                         }
455
456                         info(DI::l10n()->t('Password changed.'));
457                 } catch (Exception $e) {
458                         notice($e->getMessage());
459                         notice(DI::l10n()->t('Password unchanged.'));
460                 }
461         }
462
463         $username         = (!empty($_POST['username'])   ? Strings::escapeTags(trim($_POST['username']))     : '');
464         $email            = (!empty($_POST['email'])      ? Strings::escapeTags(trim($_POST['email']))        : '');
465         $timezone         = (!empty($_POST['timezone'])   ? Strings::escapeTags(trim($_POST['timezone']))     : '');
466         $language         = (!empty($_POST['language'])   ? Strings::escapeTags(trim($_POST['language']))     : '');
467
468         $defloc           = (!empty($_POST['defloc'])     ? Strings::escapeTags(trim($_POST['defloc']))       : '');
469         $maxreq           = (!empty($_POST['maxreq'])     ? intval($_POST['maxreq'])             : 0);
470         $expire           = (!empty($_POST['expire'])     ? intval($_POST['expire'])             : 0);
471         $def_gid          = (!empty($_POST['group-selection']) ? intval($_POST['group-selection']) : 0);
472
473
474         $expire_items     = (!empty($_POST['expire_items']) ? intval($_POST['expire_items'])     : 0);
475         $expire_notes     = (!empty($_POST['expire_notes']) ? intval($_POST['expire_notes'])     : 0);
476         $expire_starred   = (!empty($_POST['expire_starred']) ? intval($_POST['expire_starred']) : 0);
477         $expire_photos    = (!empty($_POST['expire_photos'])? intval($_POST['expire_photos'])    : 0);
478         $expire_network_only    = (!empty($_POST['expire_network_only'])? intval($_POST['expire_network_only'])  : 0);
479
480         $delete_openid    = ((!empty($_POST['delete_openid']) && (intval($_POST['delete_openid']) == 1)) ? 1: 0);
481
482         $allow_location   = ((!empty($_POST['allow_location']) && (intval($_POST['allow_location']) == 1)) ? 1: 0);
483         $publish          = ((!empty($_POST['profile_in_directory']) && (intval($_POST['profile_in_directory']) == 1)) ? 1: 0);
484         $net_publish      = ((!empty($_POST['profile_in_netdirectory']) && (intval($_POST['profile_in_netdirectory']) == 1)) ? 1: 0);
485         $old_visibility   = ((!empty($_POST['visibility']) && (intval($_POST['visibility']) == 1)) ? 1 : 0);
486         $account_type     = ((!empty($_POST['account-type']) && (intval($_POST['account-type']))) ? intval($_POST['account-type']) : 0);
487         $page_flags       = ((!empty($_POST['page-flags']) && (intval($_POST['page-flags']))) ? intval($_POST['page-flags']) : 0);
488         $blockwall        = ((!empty($_POST['blockwall']) && (intval($_POST['blockwall']) == 1)) ? 0: 1); // this setting is inverted!
489         $blocktags        = ((!empty($_POST['blocktags']) && (intval($_POST['blocktags']) == 1)) ? 0: 1); // this setting is inverted!
490         $unkmail          = ((!empty($_POST['unkmail']) && (intval($_POST['unkmail']) == 1)) ? 1: 0);
491         $cntunkmail       = (!empty($_POST['cntunkmail']) ? intval($_POST['cntunkmail']) : 0);
492         $hide_friends     = (($_POST['hide-friends'] == 1) ? 1: 0);
493         $hidewall         = (($_POST['hidewall'] == 1) ? 1: 0);
494
495         $email_textonly   = (($_POST['email_textonly'] == 1) ? 1 : 0);
496         $detailed_notif   = (($_POST['detailed_notif'] == 1) ? 1 : 0);
497
498         $notify = 0;
499
500         if (!empty($_POST['notify1'])) {
501                 $notify += intval($_POST['notify1']);
502         }
503         if (!empty($_POST['notify2'])) {
504                 $notify += intval($_POST['notify2']);
505         }
506         if (!empty($_POST['notify3'])) {
507                 $notify += intval($_POST['notify3']);
508         }
509         if (!empty($_POST['notify4'])) {
510                 $notify += intval($_POST['notify4']);
511         }
512         if (!empty($_POST['notify5'])) {
513                 $notify += intval($_POST['notify5']);
514         }
515         if (!empty($_POST['notify6'])) {
516                 $notify += intval($_POST['notify6']);
517         }
518         if (!empty($_POST['notify7'])) {
519                 $notify += intval($_POST['notify7']);
520         }
521         if (!empty($_POST['notify8'])) {
522                 $notify += intval($_POST['notify8']);
523         }
524
525         // Adjust the page flag if the account type doesn't fit to the page flag.
526         if (($account_type == User::ACCOUNT_TYPE_PERSON) && !in_array($page_flags, [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_SOAPBOX, User::PAGE_FLAGS_FREELOVE])) {
527                 $page_flags = User::PAGE_FLAGS_NORMAL;
528         } elseif (($account_type == User::ACCOUNT_TYPE_ORGANISATION) && !in_array($page_flags, [User::PAGE_FLAGS_SOAPBOX])) {
529                 $page_flags = User::PAGE_FLAGS_SOAPBOX;
530         } elseif (($account_type == User::ACCOUNT_TYPE_NEWS) && !in_array($page_flags, [User::PAGE_FLAGS_SOAPBOX])) {
531                 $page_flags = User::PAGE_FLAGS_SOAPBOX;
532         } elseif (($account_type == User::ACCOUNT_TYPE_COMMUNITY) && !in_array($page_flags, [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
533                 $page_flags = User::PAGE_FLAGS_COMMUNITY;
534         }
535
536         $err = '';
537
538         if ($username != $a->user['username']) {
539                 if (strlen($username) > 40) {
540                         $err .= DI::l10n()->t(' Please use a shorter name.');
541                 }
542                 if (strlen($username) < 3) {
543                         $err .= DI::l10n()->t(' Name too short.');
544                 }
545         }
546
547         if ($email != $a->user['email']) {
548                 //  check for the correct password
549                 if (!User::authenticate(intval(local_user()), $_POST['mpassword'])) {
550                         $err .= DI::l10n()->t('Wrong Password') . EOL;
551                         $email = $a->user['email'];
552                 }
553                 //  check the email is valid
554                 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
555                         $err .= DI::l10n()->t('Invalid email.');
556                 }
557                 //  ensure new email is not the admin mail
558                 if (DI::config()->get('config', 'admin_email')) {
559                         $adminlist = explode(",", str_replace(" ", "", strtolower(DI::config()->get('config', 'admin_email'))));
560                         if (in_array(strtolower($email), $adminlist)) {
561                                 $err .= DI::l10n()->t('Cannot change to that email.');
562                                 $email = $a->user['email'];
563                         }
564                 }
565         }
566
567         if (strlen($err)) {
568                 notice($err . EOL);
569                 return;
570         }
571
572         if (($timezone != $a->user['timezone']) && strlen($timezone)) {
573                 date_default_timezone_set($timezone);
574         }
575
576         $aclFormatter = DI::aclFormatter();
577
578         $str_group_allow   = !empty($_POST['group_allow'])   ? $aclFormatter->toString($_POST['group_allow'])   : '';
579         $str_contact_allow = !empty($_POST['contact_allow']) ? $aclFormatter->toString($_POST['contact_allow']) : '';
580         $str_group_deny    = !empty($_POST['group_deny'])    ? $aclFormatter->toString($_POST['group_deny'])    : '';
581         $str_contact_deny  = !empty($_POST['contact_deny'])  ? $aclFormatter->toString($_POST['contact_deny'])  : '';
582
583         DI::pConfig()->set(local_user(), 'expire', 'items', $expire_items);
584         DI::pConfig()->set(local_user(), 'expire', 'notes', $expire_notes);
585         DI::pConfig()->set(local_user(), 'expire', 'starred', $expire_starred);
586         DI::pConfig()->set(local_user(), 'expire', 'photos', $expire_photos);
587         DI::pConfig()->set(local_user(), 'expire', 'network_only', $expire_network_only);
588
589         DI::pConfig()->set(local_user(), 'system', 'email_textonly', $email_textonly);
590         DI::pConfig()->set(local_user(), 'system', 'detailed_notif', $detailed_notif);
591
592         if ($page_flags == User::PAGE_FLAGS_PRVGROUP) {
593                 $hidewall = 1;
594                 if (!$str_contact_allow && !$str_group_allow && !$str_contact_deny && !$str_group_deny) {
595                         if ($def_gid) {
596                                 info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.'). EOL);
597                                 $str_group_allow = '<' . $def_gid . '>';
598                         } else {
599                                 notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.') . EOL);
600                         }
601                 }
602         }
603
604         $fields = ['username' => $username, 'email' => $email, 'timezone' => $timezone,
605                 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_group_allow, 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_group_deny,
606                 'notify-flags' => $notify, 'page-flags' => $page_flags, 'account-type' => $account_type, 'default-location' => $defloc,
607                 'allow_location' => $allow_location, 'maxreq' => $maxreq, 'expire' => $expire, 'def_gid' => $def_gid, 'blockwall' => $blockwall,
608                 'hidewall' => $hidewall, 'blocktags' => $blocktags, 'unkmail' => $unkmail, 'cntunkmail' => $cntunkmail, 'language' => $language];
609
610         if ($delete_openid) {
611                 $fields['openid'] = '';
612                 $fields['openidserver'] = '';
613         }
614
615         if (DBA::update('user', $fields, ['uid' => local_user()])) {
616                 info(DI::l10n()->t('Settings updated.') . EOL);
617         }
618
619         // clear session language
620         unset($_SESSION['language']);
621
622         q("UPDATE `profile`
623                 SET `publish` = %d,
624                 `name` = '%s',
625                 `net-publish` = %d,
626                 `hide-friends` = %d
627                 WHERE `uid` = %d",
628                 intval($publish),
629                 DBA::escape($username),
630                 intval($net_publish),
631                 intval($hide_friends),
632                 intval(local_user())
633         );
634
635         Contact::updateSelfFromUserID(local_user());
636
637         if (($old_visibility != $net_publish) || ($page_flags != $old_page_flags)) {
638                 // Update global directory in background
639                 $url = $_SESSION['my_url'];
640                 if ($url && strlen(DI::config()->get('system', 'directory'))) {
641                         Worker::add(PRIORITY_LOW, "Directory", $url);
642                 }
643         }
644
645         Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
646
647         // Update the global contact for the user
648         GContact::updateForUser(local_user());
649
650         DI::baseUrl()->redirect('settings');
651         return; // NOTREACHED
652 }
653
654
655 function settings_content(App $a)
656 {
657         $o = '';
658         Nav::setSelected('settings');
659
660         if (!local_user()) {
661                 //notice(DI::l10n()->t('Permission denied.') . EOL);
662                 return Login::form();
663         }
664
665         if (!empty($_SESSION['submanage'])) {
666                 notice(DI::l10n()->t('Permission denied.') . EOL);
667                 return;
668         }
669
670         if (($a->argc > 1) && ($a->argv[1] === 'oauth')) {
671                 if (($a->argc > 2) && ($a->argv[2] === 'add')) {
672                         $tpl = Renderer::getMarkupTemplate('settings/oauth_edit.tpl');
673                         $o .= Renderer::replaceMacros($tpl, [
674                                 '$form_security_token' => BaseModule::getFormSecurityToken("settings_oauth"),
675                                 '$title'        => DI::l10n()->t('Add application'),
676                                 '$submit'       => DI::l10n()->t('Save Settings'),
677                                 '$cancel'       => DI::l10n()->t('Cancel'),
678                                 '$name'         => ['name', DI::l10n()->t('Name'), '', ''],
679                                 '$key'          => ['key', DI::l10n()->t('Consumer Key'), '', ''],
680                                 '$secret'       => ['secret', DI::l10n()->t('Consumer Secret'), '', ''],
681                                 '$redirect'     => ['redirect', DI::l10n()->t('Redirect'), '', ''],
682                                 '$icon'         => ['icon', DI::l10n()->t('Icon url'), '', ''],
683                         ]);
684                         return $o;
685                 }
686
687                 if (($a->argc > 3) && ($a->argv[2] === 'edit')) {
688                         $r = q("SELECT * FROM clients WHERE client_id='%s' AND uid=%d",
689                                         DBA::escape($a->argv[3]),
690                                         local_user());
691
692                         if (!DBA::isResult($r)) {
693                                 notice(DI::l10n()->t("You can't edit this application."));
694                                 return;
695                         }
696                         $app = $r[0];
697
698                         $tpl = Renderer::getMarkupTemplate('settings/oauth_edit.tpl');
699                         $o .= Renderer::replaceMacros($tpl, [
700                                 '$form_security_token' => BaseModule::getFormSecurityToken("settings_oauth"),
701                                 '$title'        => DI::l10n()->t('Add application'),
702                                 '$submit'       => DI::l10n()->t('Update'),
703                                 '$cancel'       => DI::l10n()->t('Cancel'),
704                                 '$name'         => ['name', DI::l10n()->t('Name'), $app['name'] , ''],
705                                 '$key'          => ['key', DI::l10n()->t('Consumer Key'), $app['client_id'], ''],
706                                 '$secret'       => ['secret', DI::l10n()->t('Consumer Secret'), $app['pw'], ''],
707                                 '$redirect'     => ['redirect', DI::l10n()->t('Redirect'), $app['redirect_uri'], ''],
708                                 '$icon'         => ['icon', DI::l10n()->t('Icon url'), $app['icon'], ''],
709                         ]);
710                         return $o;
711                 }
712
713                 if (($a->argc > 3) && ($a->argv[2] === 'delete')) {
714                         BaseModule::checkFormSecurityTokenRedirectOnError('/settings/oauth', 'settings_oauth', 't');
715
716                         DBA::delete('clients', ['client_id' => $a->argv[3], 'uid' => local_user()]);
717                         DI::baseUrl()->redirect('settings/oauth/', true);
718                         return;
719                 }
720
721                 /// @TODO validate result with DBA::isResult()
722                 $r = q("SELECT clients.*, tokens.id as oauth_token, (clients.uid=%d) AS my
723                                 FROM clients
724                                 LEFT JOIN tokens ON clients.client_id=tokens.client_id
725                                 WHERE clients.uid IN (%d, 0)",
726                                 local_user(),
727                                 local_user());
728
729
730                 $tpl = Renderer::getMarkupTemplate('settings/oauth.tpl');
731                 $o .= Renderer::replaceMacros($tpl, [
732                         '$form_security_token' => BaseModule::getFormSecurityToken("settings_oauth"),
733                         '$baseurl'      => DI::baseUrl()->get(true),
734                         '$title'        => DI::l10n()->t('Connected Apps'),
735                         '$add'          => DI::l10n()->t('Add application'),
736                         '$edit'         => DI::l10n()->t('Edit'),
737                         '$delete'               => DI::l10n()->t('Delete'),
738                         '$consumerkey' => DI::l10n()->t('Client key starts with'),
739                         '$noname'       => DI::l10n()->t('No name'),
740                         '$remove'       => DI::l10n()->t('Remove authorization'),
741                         '$apps'         => $r,
742                 ]);
743                 return $o;
744         }
745
746         if (($a->argc > 1) && ($a->argv[1] === 'addon')) {
747                 $settings_addons = "";
748
749                 $r = q("SELECT * FROM `hook` WHERE `hook` = 'addon_settings' ");
750                 if (!DBA::isResult($r)) {
751                         $settings_addons = DI::l10n()->t('No Addon settings configured');
752                 }
753
754                 Hook::callAll('addon_settings', $settings_addons);
755
756
757                 $tpl = Renderer::getMarkupTemplate('settings/addons.tpl');
758                 $o .= Renderer::replaceMacros($tpl, [
759                         '$form_security_token' => BaseModule::getFormSecurityToken("settings_addon"),
760                         '$title'        => DI::l10n()->t('Addon Settings'),
761                         '$settings_addons' => $settings_addons
762                 ]);
763                 return $o;
764         }
765
766         if (($a->argc > 1) && ($a->argv[1] === 'features')) {
767
768                 $arr = [];
769                 $features = Feature::get();
770                 foreach ($features as $fname => $fdata) {
771                         $arr[$fname] = [];
772                         $arr[$fname][0] = $fdata[0];
773                         foreach (array_slice($fdata,1) as $f) {
774                                 $arr[$fname][1][] = ['feature_' .$f[0], $f[1],((intval(Feature::isEnabled(local_user(), $f[0]))) ? "1" : ''), $f[2],[DI::l10n()->t('Off'), DI::l10n()->t('On')]];
775                         }
776                 }
777
778                 $tpl = Renderer::getMarkupTemplate('settings/features.tpl');
779                 $o .= Renderer::replaceMacros($tpl, [
780                         '$form_security_token' => BaseModule::getFormSecurityToken("settings_features"),
781                         '$title'               => DI::l10n()->t('Additional Features'),
782                         '$features'            => $arr,
783                         '$submit'              => DI::l10n()->t('Save Settings'),
784                 ]);
785                 return $o;
786         }
787
788         if (($a->argc > 1) && ($a->argv[1] === 'connectors')) {
789                 $accept_only_sharer        = intval(DI::pConfig()->get(local_user(), 'system', 'accept_only_sharer'));
790                 $disable_cw                = intval(DI::pConfig()->get(local_user(), 'system', 'disable_cw'));
791                 $no_intelligent_shortening = intval(DI::pConfig()->get(local_user(), 'system', 'no_intelligent_shortening'));
792                 $attach_link_title         = intval(DI::pConfig()->get(local_user(), 'system', 'attach_link_title'));
793                 $ostatus_autofriend        = intval(DI::pConfig()->get(local_user(), 'system', 'ostatus_autofriend'));
794                 $default_group             = DI::pConfig()->get(local_user(), 'ostatus', 'default_group');
795                 $legacy_contact            = DI::pConfig()->get(local_user(), 'ostatus', 'legacy_contact');
796
797                 if (!empty($legacy_contact)) {
798                         /// @todo Isn't it supposed to be a $a->internalRedirect() call?
799                         DI::page()['htmlhead'] = '<meta http-equiv="refresh" content="0; URL=' . DI::baseUrl().'/ostatus_subscribe?url=' . urlencode($legacy_contact) . '">';
800                 }
801
802                 $settings_connectors = '';
803                 Hook::callAll('connector_settings', $settings_connectors);
804
805                 if (is_site_admin()) {
806                         $diasp_enabled = DI::l10n()->t('Built-in support for %s connectivity is %s', DI::l10n()->t('Diaspora (Socialhome, Hubzilla)'), ((DI::config()->get('system', 'diaspora_enabled')) ? DI::l10n()->t('enabled') : DI::l10n()->t('disabled')));
807                         $ostat_enabled = DI::l10n()->t('Built-in support for %s connectivity is %s', DI::l10n()->t('OStatus (GNU Social)'), ((DI::config()->get('system', 'ostatus_disabled')) ? DI::l10n()->t('disabled') : DI::l10n()->t('enabled')));
808                 } else {
809                         $diasp_enabled = "";
810                         $ostat_enabled = "";
811                 }
812
813                 $mail_disabled = ((function_exists('imap_open') && (!DI::config()->get('system', 'imap_disabled'))) ? 0 : 1);
814                 if (DI::config()->get('system', 'dfrn_only')) {
815                         $mail_disabled = 1;
816                 }
817                 if (!$mail_disabled) {
818                         $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
819                                 local_user()
820                         );
821                 } else {
822                         $r = null;
823                 }
824
825                 $mail_server       = ((DBA::isResult($r)) ? $r[0]['server'] : '');
826                 $mail_port         = ((DBA::isResult($r) && intval($r[0]['port'])) ? intval($r[0]['port']) : '');
827                 $mail_ssl          = ((DBA::isResult($r)) ? $r[0]['ssltype'] : '');
828                 $mail_user         = ((DBA::isResult($r)) ? $r[0]['user'] : '');
829                 $mail_replyto      = ((DBA::isResult($r)) ? $r[0]['reply_to'] : '');
830                 $mail_pubmail      = ((DBA::isResult($r)) ? $r[0]['pubmail'] : 0);
831                 $mail_action       = ((DBA::isResult($r)) ? $r[0]['action'] : 0);
832                 $mail_movetofolder = ((DBA::isResult($r)) ? $r[0]['movetofolder'] : '');
833                 $mail_chk          = ((DBA::isResult($r)) ? $r[0]['last_check'] : DBA::NULL_DATETIME);
834
835
836                 $tpl = Renderer::getMarkupTemplate('settings/connectors.tpl');
837
838                 $mail_disabled_message = ($mail_disabled ? DI::l10n()->t('Email access is disabled on this site.') : '');
839
840                 $ssl_options = ['TLS' => 'TLS', 'SSL' => 'SSL'];
841
842                 if (DI::config()->get('system', 'insecure_imap')) {
843                         $ssl_options['notls'] = DI::l10n()->t('None');
844                 }
845
846                 $o .= Renderer::replaceMacros($tpl, [
847                         '$form_security_token' => BaseModule::getFormSecurityToken("settings_connectors"),
848
849                         '$title'        => DI::l10n()->t('Social Networks'),
850
851                         '$diasp_enabled' => $diasp_enabled,
852                         '$ostat_enabled' => $ostat_enabled,
853
854                         '$general_settings' => DI::l10n()->t('General Social Media Settings'),
855                         '$accept_only_sharer' => ['accept_only_sharer', DI::l10n()->t('Accept only top level posts by contacts you follow'), $accept_only_sharer, DI::l10n()->t('The system does an auto completion of threads when a comment arrives. This has got the side effect that you can receive posts that had been started by a non-follower but had been commented by someone you follow. This setting deactivates this behaviour. When activated, you strictly only will receive posts from people you really do follow.')],
856                         '$disable_cw' => ['disable_cw', DI::l10n()->t('Disable Content Warning'), $disable_cw, DI::l10n()->t('Users on networks like Mastodon or Pleroma are able to set a content warning field which collapse their post by default. This disables the automatic collapsing and sets the content warning as the post title. Doesn\'t affect any other content filtering you eventually set up.')],
857                         '$no_intelligent_shortening' => ['no_intelligent_shortening', DI::l10n()->t('Disable intelligent shortening'), $no_intelligent_shortening, DI::l10n()->t('Normally the system tries to find the best link to add to shortened posts. If this option is enabled then every shortened post will always point to the original friendica post.')],
858                         '$attach_link_title' => ['attach_link_title', DI::l10n()->t('Attach the link title'), $attach_link_title, DI::l10n()->t('When activated, the title of the attached link will be added as a title on posts to Diaspora. This is mostly helpful with "remote-self" contacts that share feed content.')],
859                         '$ostatus_autofriend' => ['snautofollow', DI::l10n()->t("Automatically follow any GNU Social \x28OStatus\x29 followers/mentioners"), $ostatus_autofriend, DI::l10n()->t('If you receive a message from an unknown OStatus user, this option decides what to do. If it is checked, a new contact will be created for every unknown user.')],
860                         '$default_group' => Group::displayGroupSelection(local_user(), $default_group, DI::l10n()->t("Default group for OStatus contacts")),
861                         '$legacy_contact' => ['legacy_contact', DI::l10n()->t('Your legacy GNU Social account'), $legacy_contact, DI::l10n()->t("If you enter your old GNU Social/Statusnet account name here \x28in the format user@domain.tld\x29, your contacts will be added automatically. The field will be emptied when done.")],
862
863                         '$repair_ostatus_url' => DI::baseUrl() . '/repair_ostatus',
864                         '$repair_ostatus_text' => DI::l10n()->t('Repair OStatus subscriptions'),
865
866                         '$settings_connectors' => $settings_connectors,
867
868                         '$h_imap' => DI::l10n()->t('Email/Mailbox Setup'),
869                         '$imap_desc' => DI::l10n()->t("If you wish to communicate with email contacts using this service \x28optional\x29, please specify how to connect to your mailbox."),
870                         '$imap_lastcheck' => ['imap_lastcheck', DI::l10n()->t('Last successful email check:'), $mail_chk, ''],
871                         '$mail_disabled' => $mail_disabled_message,
872                         '$mail_server'  => ['mail_server',      DI::l10n()->t('IMAP server name:'), $mail_server, ''],
873                         '$mail_port'    => ['mail_port',        DI::l10n()->t('IMAP port:'), $mail_port, ''],
874                         '$mail_ssl'     => ['mail_ssl',         DI::l10n()->t('Security:'), strtoupper($mail_ssl), '', $ssl_options],
875                         '$mail_user'    => ['mail_user',        DI::l10n()->t('Email login name:'), $mail_user, ''],
876                         '$mail_pass'    => ['mail_pass',        DI::l10n()->t('Email password:'), '', ''],
877                         '$mail_replyto' => ['mail_replyto',     DI::l10n()->t('Reply-to address:'), $mail_replyto, 'Optional'],
878                         '$mail_pubmail' => ['mail_pubmail',     DI::l10n()->t('Send public posts to all email contacts:'), $mail_pubmail, ''],
879                         '$mail_action'  => ['mail_action',      DI::l10n()->t('Action after import:'), $mail_action, '', [0 => DI::l10n()->t('None'), 1 => DI::l10n()->t('Delete'), 2 => DI::l10n()->t('Mark as seen'), 3 => DI::l10n()->t('Move to folder')]],
880                         '$mail_movetofolder' => ['mail_movetofolder', DI::l10n()->t('Move to folder:'), $mail_movetofolder, ''],
881                         '$submit' => DI::l10n()->t('Save Settings'),
882                 ]);
883
884                 Hook::callAll('display_settings', $o);
885                 return $o;
886         }
887
888         /*
889          * DISPLAY SETTINGS
890          */
891         if (($a->argc > 1) && ($a->argv[1] === 'display')) {
892                 $default_theme = DI::config()->get('system', 'theme');
893                 if (!$default_theme) {
894                         $default_theme = 'default';
895                 }
896                 $default_mobile_theme = DI::config()->get('system', 'mobile-theme');
897                 if (!$default_mobile_theme) {
898                         $default_mobile_theme = 'none';
899                 }
900
901                 $allowed_themes = Theme::getAllowedList();
902
903                 $themes = [];
904                 $mobile_themes = ["---" => DI::l10n()->t('No special theme for mobile devices')];
905                 foreach ($allowed_themes as $theme) {
906                         $is_experimental = file_exists('view/theme/' . $theme . '/experimental');
907                         $is_unsupported  = file_exists('view/theme/' . $theme . '/unsupported');
908                         $is_mobile       = file_exists('view/theme/' . $theme . '/mobile');
909                         if (!$is_experimental || ($is_experimental && (DI::config()->get('experimentals', 'exp_themes')==1 || is_null(DI::config()->get('experimentals', 'exp_themes'))))) {
910                                 $theme_name = ucfirst($theme);
911                                 if ($is_unsupported) {
912                                         $theme_name = DI::l10n()->t('%s - (Unsupported)', $theme_name);
913                                 } elseif ($is_experimental) {
914                                         $theme_name = DI::l10n()->t('%s - (Experimental)', $theme_name);
915                                 }
916
917                                 if ($is_mobile) {
918                                         $mobile_themes[$theme] = $theme_name;
919                                 } else {
920                                         $themes[$theme] = $theme_name;
921                                 }
922                         }
923                 }
924
925                 $theme_selected        = $a->user['theme'] ?: $default_theme;
926                 $mobile_theme_selected = Session::get('mobile-theme', $default_mobile_theme);
927
928                 $browser_update = intval(DI::pConfig()->get(local_user(), 'system', 'update_interval'));
929                 if (intval($browser_update) != -1) {
930                         $browser_update = (($browser_update == 0) ? 40 : $browser_update / 1000); // default if not set: 40 seconds
931                 }
932
933                 $itemspage_network = intval(DI::pConfig()->get(local_user(), 'system', 'itemspage_network'));
934                 $itemspage_network = (($itemspage_network > 0 && $itemspage_network < 101) ? $itemspage_network : DI::config()->get('system', 'itemspage_network'));
935                 $itemspage_mobile_network = intval(DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network'));
936                 $itemspage_mobile_network = (($itemspage_mobile_network > 0 && $itemspage_mobile_network < 101) ? $itemspage_mobile_network : DI::config()->get('system', 'itemspage_network_mobile'));
937
938                 $nosmile = DI::pConfig()->get(local_user(), 'system', 'no_smilies', 0);
939                 $first_day_of_week = DI::pConfig()->get(local_user(), 'system', 'first_day_of_week', 0);
940                 $weekdays = [0 => DI::l10n()->t("Sunday"), 1 => DI::l10n()->t("Monday")];
941
942                 $infinite_scroll = DI::pConfig()->get(local_user(), 'system', 'infinite_scroll', 0);
943                 $no_auto_update = DI::pConfig()->get(local_user(), 'system', 'no_auto_update', 0);
944                 $no_smart_threading = DI::pConfig()->get(local_user(), 'system', 'no_smart_threading', 0);
945
946                 $theme_config = "";
947                 if (($themeconfigfile = get_theme_config_file($theme_selected)) !== null) {
948                         require_once $themeconfigfile;
949                         $theme_config = theme_content($a);
950                 }
951
952                 $tpl = Renderer::getMarkupTemplate('settings/display.tpl');
953                 $o = Renderer::replaceMacros($tpl, [
954                         '$ptitle'       => DI::l10n()->t('Display Settings'),
955                         '$form_security_token' => BaseModule::getFormSecurityToken("settings_display"),
956                         '$submit'       => DI::l10n()->t('Save Settings'),
957                         '$baseurl' => DI::baseUrl()->get(true),
958                         '$uid' => local_user(),
959
960                         '$theme'        => ['theme', DI::l10n()->t('Display Theme:'), $theme_selected, '', $themes, true],
961                         '$mobile_theme' => ['mobile_theme', DI::l10n()->t('Mobile Theme:'), $mobile_theme_selected, '', $mobile_themes, false],
962                         '$ajaxint'   => ['browser_update',  DI::l10n()->t("Update browser every xx seconds"), $browser_update, DI::l10n()->t('Minimum of 10 seconds. Enter -1 to disable it.')],
963                         '$itemspage_network'   => ['itemspage_network',  DI::l10n()->t("Number of items to display per page:"), $itemspage_network, DI::l10n()->t('Maximum of 100 items')],
964                         '$itemspage_mobile_network'   => ['itemspage_mobile_network',  DI::l10n()->t("Number of items to display per page when viewed from mobile device:"), $itemspage_mobile_network, DI::l10n()->t('Maximum of 100 items')],
965                         '$nosmile'      => ['nosmile', DI::l10n()->t("Don't show emoticons"), $nosmile, DI::l10n()->t('Normally emoticons are replaced with matching symbols. This setting disables this behaviour.')],
966                         '$calendar_title' => DI::l10n()->t('Calendar'),
967                         '$first_day_of_week'    => ['first_day_of_week', DI::l10n()->t('Beginning of week:'), $first_day_of_week, '', $weekdays, false],
968                         '$infinite_scroll'      => ['infinite_scroll', DI::l10n()->t("Infinite scroll"), $infinite_scroll, DI::l10n()->t('Automatic add new items when reaching the page end.')],
969                         '$no_auto_update'       => ['no_auto_update', DI::l10n()->t("Automatic updates only at the top of the network page"), $no_auto_update, DI::l10n()->t('When disabled, the network page is updated all the time, which could be confusing while reading.')],
970                         '$no_smart_threading' => ['no_smart_threading', DI::l10n()->t('Disable Smart Threading'), $no_smart_threading, DI::l10n()->t('Disable the automatic suppression of extraneous thread indentation.')],
971
972                         '$d_tset' => DI::l10n()->t('General Theme Settings'),
973                         '$d_ctset' => DI::l10n()->t('Custom Theme Settings'),
974                         '$d_cset' => DI::l10n()->t('Content Settings'),
975                         'stitle' => DI::l10n()->t('Theme settings'),
976                         '$theme_config' => $theme_config,
977                 ]);
978
979                 return $o;
980         }
981
982
983         /*
984          * ACCOUNT SETTINGS
985          */
986
987         $profile = DBA::selectFirst('profile', [], ['uid' => local_user()]);
988         if (!DBA::isResult($profile)) {
989                 notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.') . EOL);
990                 return;
991         }
992
993         $username   = $a->user['username'];
994         $email      = $a->user['email'];
995         $nickname   = $a->user['nickname'];
996         $timezone   = $a->user['timezone'];
997         $language   = $a->user['language'];
998         $notify     = $a->user['notify-flags'];
999         $defloc     = $a->user['default-location'];
1000         $openid     = $a->user['openid'];
1001         $maxreq     = $a->user['maxreq'];
1002         $expire     = ((intval($a->user['expire'])) ? $a->user['expire'] : '');
1003         $unkmail    = $a->user['unkmail'];
1004         $cntunkmail = $a->user['cntunkmail'];
1005
1006         $expire_items = DI::pConfig()->get(local_user(), 'expire', 'items', true);
1007         $expire_notes = DI::pConfig()->get(local_user(), 'expire', 'notes', true);
1008         $expire_starred = DI::pConfig()->get(local_user(), 'expire', 'starred', true);
1009         $expire_photos = DI::pConfig()->get(local_user(), 'expire', 'photos', false);
1010         $expire_network_only = DI::pConfig()->get(local_user(), 'expire', 'network_only', false);
1011
1012         if (!strlen($a->user['timezone'])) {
1013                 $timezone = date_default_timezone_get();
1014         }
1015
1016         // Set the account type to "Community" when the page is a community page but the account type doesn't fit
1017         // This is only happening on the first visit after the update
1018         if (in_array($a->user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]) &&
1019                 ($a->user['account-type'] != User::ACCOUNT_TYPE_COMMUNITY))
1020                 $a->user['account-type'] = User::ACCOUNT_TYPE_COMMUNITY;
1021
1022         $pageset_tpl = Renderer::getMarkupTemplate('settings/pagetypes.tpl');
1023
1024         $pagetype = Renderer::replaceMacros($pageset_tpl, [
1025                 '$account_types'        => DI::l10n()->t("Account Types"),
1026                 '$user'                 => DI::l10n()->t("Personal Page Subtypes"),
1027                 '$community'            => DI::l10n()->t("Community Forum Subtypes"),
1028                 '$account_type'         => $a->user['account-type'],
1029                 '$type_person'          => User::ACCOUNT_TYPE_PERSON,
1030                 '$type_organisation'    => User::ACCOUNT_TYPE_ORGANISATION,
1031                 '$type_news'            => User::ACCOUNT_TYPE_NEWS,
1032                 '$type_community'       => User::ACCOUNT_TYPE_COMMUNITY,
1033
1034                 '$account_person'       => ['account-type', DI::l10n()->t('Personal Page'), User::ACCOUNT_TYPE_PERSON,
1035                                                                         DI::l10n()->t('Account for a personal profile.'),
1036                                                                         ($a->user['account-type'] == User::ACCOUNT_TYPE_PERSON)],
1037
1038                 '$account_organisation' => ['account-type', DI::l10n()->t('Organisation Page'), User::ACCOUNT_TYPE_ORGANISATION,
1039                                                                         DI::l10n()->t('Account for an organisation that automatically approves contact requests as "Followers".'),
1040                                                                         ($a->user['account-type'] == User::ACCOUNT_TYPE_ORGANISATION)],
1041
1042                 '$account_news'         => ['account-type', DI::l10n()->t('News Page'), User::ACCOUNT_TYPE_NEWS,
1043                                                                         DI::l10n()->t('Account for a news reflector that automatically approves contact requests as "Followers".'),
1044                                                                         ($a->user['account-type'] == User::ACCOUNT_TYPE_NEWS)],
1045
1046                 '$account_community'    => ['account-type', DI::l10n()->t('Community Forum'), User::ACCOUNT_TYPE_COMMUNITY,
1047                                                                         DI::l10n()->t('Account for community discussions.'),
1048                                                                         ($a->user['account-type'] == User::ACCOUNT_TYPE_COMMUNITY)],
1049
1050                 '$page_normal'          => ['page-flags', DI::l10n()->t('Normal Account Page'), User::PAGE_FLAGS_NORMAL,
1051                                                                         DI::l10n()->t('Account for a regular personal profile that requires manual approval of "Friends" and "Followers".'),
1052                                                                         ($a->user['page-flags'] == User::PAGE_FLAGS_NORMAL)],
1053
1054                 '$page_soapbox'         => ['page-flags', DI::l10n()->t('Soapbox Page'), User::PAGE_FLAGS_SOAPBOX,
1055                                                                         DI::l10n()->t('Account for a public profile that automatically approves contact requests as "Followers".'),
1056                                                                         ($a->user['page-flags'] == User::PAGE_FLAGS_SOAPBOX)],
1057
1058                 '$page_community'       => ['page-flags', DI::l10n()->t('Public Forum'), User::PAGE_FLAGS_COMMUNITY,
1059                                                                         DI::l10n()->t('Automatically approves all contact requests.'),
1060                                                                         ($a->user['page-flags'] == User::PAGE_FLAGS_COMMUNITY)],
1061
1062                 '$page_freelove'        => ['page-flags', DI::l10n()->t('Automatic Friend Page'), User::PAGE_FLAGS_FREELOVE,
1063                                                                         DI::l10n()->t('Account for a popular profile that automatically approves contact requests as "Friends".'),
1064                                                                         ($a->user['page-flags'] == User::PAGE_FLAGS_FREELOVE)],
1065
1066                 '$page_prvgroup'        => ['page-flags', DI::l10n()->t('Private Forum [Experimental]'), User::PAGE_FLAGS_PRVGROUP,
1067                                                                         DI::l10n()->t('Requires manual approval of contact requests.'),
1068                                                                         ($a->user['page-flags'] == User::PAGE_FLAGS_PRVGROUP)],
1069
1070
1071         ]);
1072
1073         $noid = DI::config()->get('system', 'no_openid');
1074
1075         if ($noid) {
1076                 $openid_field = false;
1077         } else {
1078                 $openid_field = ['openid_url', DI::l10n()->t('OpenID:'), $openid, DI::l10n()->t("\x28Optional\x29 Allow this OpenID to login to this account."), "", "readonly", "url"];
1079         }
1080
1081         $opt_tpl = Renderer::getMarkupTemplate("field_checkbox.tpl");
1082         if (DI::config()->get('system', 'publish_all')) {
1083                 $profile_in_dir = '<input type="hidden" name="profile_in_directory" value="1" />';
1084         } else {
1085                 $profile_in_dir = Renderer::replaceMacros($opt_tpl, [
1086                         '$field' => ['profile_in_directory', DI::l10n()->t('Publish your profile in your local site directory?'), $profile['publish'], DI::l10n()->t('Your profile will be published in this node\'s <a href="%s">local directory</a>. Your profile details may be publicly visible depending on the system settings.', DI::baseUrl().'/directory')]
1087                 ]);
1088         }
1089
1090         if (strlen(DI::config()->get('system', 'directory'))) {
1091                 $net_pub_desc = ' ' . DI::l10n()->t('Your profile will also be published in the global friendica directories (e.g. <a href="%s">%s</a>).', DI::config()->get('system', 'directory'), DI::config()->get('system', 'directory'));
1092         } else {
1093                 $net_pub_desc = '';
1094         }
1095
1096         $profile_in_net_dir = Renderer::replaceMacros($opt_tpl, [
1097                 '$field' => ['profile_in_netdirectory', DI::l10n()->t('Allow your profile to be searchable globally?'), $profile['net-publish'], DI::l10n()->t("Activate this setting if you want others to easily find and follow you. Your profile will be searchable on remote systems. This setting also determines whether Friendica will inform search engines that your profile should be indexed or not.") . $net_pub_desc]
1098         ]);
1099
1100         $hide_friends = Renderer::replaceMacros($opt_tpl, [
1101                 '$field' => ['hide-friends', DI::l10n()->t('Hide your contact/friend list from viewers of your default profile?'), $profile['hide-friends'], DI::l10n()->t('Your contact list won\'t be shown in your default profile page. You can decide to show your contact list separately for each additional profile you create')],
1102         ]);
1103
1104         $hide_wall = Renderer::replaceMacros($opt_tpl, [
1105                 '$field' => ['hidewall', DI::l10n()->t('Hide your profile details from anonymous viewers?'), $a->user['hidewall'], DI::l10n()->t('Anonymous visitors will only see your profile picture, your display name and the nickname you are using on your profile page. Your public posts and replies will still be accessible by other means.')],
1106         ]);
1107
1108         $blockwall = Renderer::replaceMacros($opt_tpl, [
1109                 '$field' => ['blockwall', DI::l10n()->t('Allow friends to post to your profile page?'), (intval($a->user['blockwall']) ? '0' : '1'), DI::l10n()->t('Your contacts may write posts on your profile wall. These posts will be distributed to your contacts')],
1110         ]);
1111
1112         $blocktags = Renderer::replaceMacros($opt_tpl, [
1113                 '$field' => ['blocktags', DI::l10n()->t('Allow friends to tag your posts?'), (intval($a->user['blocktags']) ? '0' : '1'), DI::l10n()->t('Your contacts can add additional tags to your posts.')],
1114         ]);
1115
1116         $unkmail = Renderer::replaceMacros($opt_tpl, [
1117                 '$field' => ['unkmail', DI::l10n()->t('Permit unknown people to send you private mail?'), $unkmail, DI::l10n()->t('Friendica network users may send you private messages even if they are not in your contact list.')],
1118         ]);
1119
1120         $tpl_addr = Renderer::getMarkupTemplate('settings/nick_set.tpl');
1121
1122         $prof_addr = Renderer::replaceMacros($tpl_addr,[
1123                 '$desc' => DI::l10n()->t("Your Identity Address is <strong>'%s'</strong> or '%s'.", $nickname . '@' . DI::baseUrl()->getHostname() . DI::baseUrl()->getUrlPath(), DI::baseUrl() . '/profile/' . $nickname),
1124                 '$basepath' => DI::baseUrl()->getHostname()
1125         ]);
1126
1127         $stpl = Renderer::getMarkupTemplate('settings/settings.tpl');
1128
1129         $expire_arr = [
1130                 'days' => ['expire',  DI::l10n()->t("Automatically expire posts after this many days:"), $expire, DI::l10n()->t('If empty, posts will not expire. Expired posts will be deleted')],
1131                 'label' => DI::l10n()->t('Expiration settings'),
1132                 'items' => ['expire_items', DI::l10n()->t('Expire posts'), $expire_items, DI::l10n()->t('When activated, posts and comments will be expired.')],
1133                 'notes' => ['expire_notes', DI::l10n()->t('Expire personal notes'), $expire_notes, DI::l10n()->t('When activated, the personal notes on your profile page will be expired.')],
1134                 'starred' => ['expire_starred', DI::l10n()->t('Expire starred posts'), $expire_starred, DI::l10n()->t('Starring posts keeps them from being expired. That behaviour is overwritten by this setting.')],
1135                 'photos' => ['expire_photos', DI::l10n()->t('Expire photos'), $expire_photos, DI::l10n()->t('When activated, photos will be expired.')],
1136                 'network_only' => ['expire_network_only', DI::l10n()->t('Only expire posts by others'), $expire_network_only, DI::l10n()->t('When activated, your own posts never expire. Then the settings above are only valid for posts you received.')],
1137         ];
1138
1139         $group_select = Group::displayGroupSelection(local_user(), $a->user['def_gid']);
1140
1141         // Private/public post links for the non-JS ACL form
1142         $private_post = 1;
1143         if (!empty($_REQUEST['public']) && !$_REQUEST['public']) {
1144                 $private_post = 0;
1145         }
1146
1147         $query_str = DI::args()->getQueryString();
1148         if (strpos($query_str, 'public=1') !== false) {
1149                 $query_str = str_replace(['?public=1', '&public=1'], ['', ''], $query_str);
1150         }
1151
1152         // I think $a->query_string may never have ? in it, but I could be wrong
1153         // It looks like it's from the index.php?q=[etc] rewrite that the web
1154         // server does, which converts any ? to &, e.g. suggest&ignore=61 for suggest?ignore=61
1155         if (strpos($query_str, '?') === false) {
1156                 $public_post_link = '?public=1';
1157         } else {
1158                 $public_post_link = '&public=1';
1159         }
1160
1161         /* Installed langs */
1162         $lang_choices = DI::l10n()->getAvailableLanguages();
1163
1164         /// @TODO Fix indending (or so)
1165         $o .= Renderer::replaceMacros($stpl, [
1166                 '$ptitle'       => DI::l10n()->t('Account Settings'),
1167
1168                 '$submit'       => DI::l10n()->t('Save Settings'),
1169                 '$baseurl' => DI::baseUrl()->get(true),
1170                 '$uid' => local_user(),
1171                 '$form_security_token' => BaseModule::getFormSecurityToken("settings"),
1172                 '$nickname_block' => $prof_addr,
1173
1174                 '$h_pass'       => DI::l10n()->t('Password Settings'),
1175                 '$password1'=> ['password', DI::l10n()->t('New Password:'), '', DI::l10n()->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces, accentuated letters and colon (:).')],
1176                 '$password2'=> ['confirm', DI::l10n()->t('Confirm:'), '', DI::l10n()->t('Leave password fields blank unless changing')],
1177                 '$password3'=> ['opassword', DI::l10n()->t('Current Password:'), '', DI::l10n()->t('Your current password to confirm the changes')],
1178                 '$password4'=> ['mpassword', DI::l10n()->t('Password:'), '', DI::l10n()->t('Your current password to confirm the changes')],
1179                 '$oid_enable' => (!DI::config()->get('system', 'no_openid')),
1180                 '$openid'       => $openid_field,
1181                 '$delete_openid' => ['delete_openid', DI::l10n()->t('Delete OpenID URL'), false, ''],
1182
1183                 '$h_basic'      => DI::l10n()->t('Basic Settings'),
1184                 '$username' => ['username',  DI::l10n()->t('Full Name:'), $username, ''],
1185                 '$email'        => ['email', DI::l10n()->t('Email Address:'), $email, '', '', '', 'email'],
1186                 '$timezone' => ['timezone_select' , DI::l10n()->t('Your Timezone:'), Temporal::getTimezoneSelect($timezone), ''],
1187                 '$language' => ['language', DI::l10n()->t('Your Language:'), $language, DI::l10n()->t('Set the language we use to show you friendica interface and to send you emails'), $lang_choices],
1188                 '$defloc'       => ['defloc', DI::l10n()->t('Default Post Location:'), $defloc, ''],
1189                 '$allowloc' => ['allow_location', DI::l10n()->t('Use Browser Location:'), ($a->user['allow_location'] == 1), ''],
1190
1191
1192                 '$h_prv'        => DI::l10n()->t('Security and Privacy Settings'),
1193
1194                 '$maxreq'       => ['maxreq', DI::l10n()->t('Maximum Friend Requests/Day:'), $maxreq , DI::l10n()->t("\x28to prevent spam abuse\x29")],
1195                 '$permissions' => DI::l10n()->t('Default Post Permissions'),
1196                 '$permdesc' => DI::l10n()->t("\x28click to open/close\x29"),
1197                 '$visibility' => $profile['net-publish'],
1198                 '$aclselect' => ACL::getFullSelectorHTML(DI::page(), $a->user),
1199                 '$blockwall'=> $blockwall, // array('blockwall', DI::l10n()->t('Allow friends to post to your profile page:'), !$blockwall, ''),
1200                 '$blocktags'=> $blocktags, // array('blocktags', DI::l10n()->t('Allow friends to tag your posts:'), !$blocktags, ''),
1201
1202                 // ACL permissions box
1203                 '$group_perms' => DI::l10n()->t('Show to Groups'),
1204                 '$contact_perms' => DI::l10n()->t('Show to Contacts'),
1205                 '$private' => DI::l10n()->t('Default Private Post'),
1206                 '$public' => DI::l10n()->t('Default Public Post'),
1207                 '$is_private' => $private_post,
1208                 '$return_path' => $query_str,
1209                 '$public_link' => $public_post_link,
1210                 '$settings_perms' => DI::l10n()->t('Default Permissions for New Posts'),
1211
1212                 '$group_select' => $group_select,
1213
1214
1215                 '$expire'       => $expire_arr,
1216
1217                 '$profile_in_dir' => $profile_in_dir,
1218                 '$profile_in_net_dir' => $profile_in_net_dir,
1219                 '$hide_friends' => $hide_friends,
1220                 '$hide_wall' => $hide_wall,
1221                 '$unkmail' => $unkmail,
1222                 '$cntunkmail'   => ['cntunkmail', DI::l10n()->t('Maximum private messages per day from unknown people:'), $cntunkmail , DI::l10n()->t("\x28to prevent spam abuse\x29")],
1223
1224
1225                 '$h_not'        => DI::l10n()->t('Notification Settings'),
1226                 '$lbl_not'      => DI::l10n()->t('Send a notification email when:'),
1227                 '$notify1'      => ['notify1', DI::l10n()->t('You receive an introduction'), ($notify & Type::INTRO), Type::INTRO, ''],
1228                 '$notify2'      => ['notify2', DI::l10n()->t('Your introductions are confirmed'), ($notify & Type::CONFIRM), Type::CONFIRM, ''],
1229                 '$notify3'      => ['notify3', DI::l10n()->t('Someone writes on your profile wall'), ($notify & Type::WALL), Type::WALL, ''],
1230                 '$notify4'      => ['notify4', DI::l10n()->t('Someone writes a followup comment'), ($notify & Type::COMMENT), Type::COMMENT, ''],
1231                 '$notify5'      => ['notify5', DI::l10n()->t('You receive a private message'), ($notify & Type::MAIL), Type::MAIL, ''],
1232                 '$notify6'  => ['notify6', DI::l10n()->t('You receive a friend suggestion'), ($notify & Type::SUGGEST), Type::SUGGEST, ''],
1233                 '$notify7'  => ['notify7', DI::l10n()->t('You are tagged in a post'), ($notify & Type::TAG_SELF), Type::TAG_SELF, ''],
1234                 '$notify8'  => ['notify8', DI::l10n()->t('You are poked/prodded/etc. in a post'), ($notify & Type::POKE), Type::POKE, ''],
1235
1236                 '$desktop_notifications' => ['desktop_notifications', DI::l10n()->t('Activate desktop notifications') , false, DI::l10n()->t('Show desktop popup on new notifications')],
1237
1238                 '$email_textonly' => ['email_textonly', DI::l10n()->t('Text-only notification emails'),
1239                                                                         DI::pConfig()->get(local_user(), 'system', 'email_textonly'),
1240                                                                         DI::l10n()->t('Send text only notification emails, without the html part')],
1241
1242                 '$detailed_notif' => ['detailed_notif', DI::l10n()->t('Show detailled notifications'),
1243                                                                         DI::pConfig()->get(local_user(), 'system', 'detailed_notif'),
1244                                                                         DI::l10n()->t('Per default, notifications are condensed to a single notification per item. When enabled every notification is displayed.')],
1245
1246                 '$h_advn' => DI::l10n()->t('Advanced Account/Page Type Settings'),
1247                 '$h_descadvn' => DI::l10n()->t('Change the behaviour of this account for special situations'),
1248                 '$pagetype' => $pagetype,
1249
1250                 '$importcontact' => DI::l10n()->t('Import Contacts'),
1251                 '$importcontact_text' => DI::l10n()->t('Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'),
1252                 '$importcontact_button' => DI::l10n()->t('Upload File'),
1253                 '$importcontact_maxsize' => DI::config()->get('system', 'max_csv_file_size', 30720), 
1254                 '$relocate' => DI::l10n()->t('Relocate'),
1255                 '$relocate_text' => DI::l10n()->t("If you have moved this profile from another server, and some of your contacts don't receive your updates, try pushing this button."),
1256                 '$relocate_button' => DI::l10n()->t("Resend relocate message to contacts"),
1257
1258         ]);
1259
1260         Hook::callAll('settings_form', $o);
1261
1262         $o .= '</form>' . "\r\n";
1263
1264         return $o;
1265
1266 }