]> git.mxchange.org Git - friendica.git/blob - mod/admin.php
added subpage for blocklist management to the admin panel
[friendica.git] / mod / admin.php
1 <?php
2
3  /**
4  * @file mod/admin.php
5  *
6  * @brief Friendica admin
7  */
8
9 use \Friendica\Core\Config;
10
11 require_once("include/enotify.php");
12 require_once("include/text.php");
13
14 /**
15  * @brief Process send data from the admin panels subpages
16  *
17  * This function acts as relais for processing the data send from the subpages
18  * of the admin panel. Depending on the 1st parameter of the url (argv[1])
19  * specialized functions are called to process the data from the subpages.
20  *
21  * The function itself does not return anything, but the subsequencely function
22  * return the HTML for the pages of the admin panel.
23  *
24  * @param App $a
25  *
26  */
27 function admin_post(App $a) {
28
29
30         if (!is_site_admin()) {
31                 return;
32         }
33
34         // do not allow a page manager to access the admin panel at all.
35
36         if (x($_SESSION,'submanage') && intval($_SESSION['submanage'])) {
37                 return;
38         }
39
40         // urls
41         if ($a->argc > 1) {
42                 switch ($a->argv[1]) {
43                         case 'site':
44                                 admin_page_site_post($a);
45                                 break;
46                         case 'users':
47                                 admin_page_users_post($a);
48                                 break;
49                         case 'plugins':
50                                 if ($a->argc > 2 &&
51                                         is_file("addon/".$a->argv[2]."/".$a->argv[2].".php")) {
52                                                 @include_once("addon/".$a->argv[2]."/".$a->argv[2].".php");
53                                                 if (function_exists($a->argv[2].'_plugin_admin_post')) {
54                                                         $func = $a->argv[2].'_plugin_admin_post';
55                                                         $func($a);
56                                                 }
57                                 }
58                                 goaway('admin/plugins/'.$a->argv[2]);
59                                 return; // NOTREACHED
60                                 break;
61                         case 'themes':
62                                 if ($a->argc < 2) {
63                                         if (is_ajax()) {
64                                                 return;
65                                         }
66                                         goaway('admin/');
67                                         return;
68                                 }
69
70                                 $theme = $a->argv[2];
71                                 if (is_file("view/theme/$theme/config.php")) {
72                                         function __call_theme_admin_post(App $a, $theme) {
73                                                 $orig_theme = $a->theme;
74                                                 $orig_page = $a->page;
75                                                 $orig_session_theme = $_SESSION['theme'];
76                                                 require_once("view/theme/$theme/theme.php");
77                                                 require_once("view/theme/$theme/config.php");
78                                                 $_SESSION['theme'] = $theme;
79
80
81                                                 $init = $theme."_init";
82                                                 if (function_exists($init)) {
83                                                         $init($a);
84                                                 }
85                                                 if (function_exists("theme_admin_post")) {
86                                                         $admin_form = theme_admin_post($a);
87                                                 }
88
89                                                 $_SESSION['theme'] = $orig_session_theme;
90                                                 $a->theme = $orig_theme;
91                                                 $a->page = $orig_page;
92                                                 return $admin_form;
93                                         }
94                                         __call_theme_admin_post($a, $theme);
95                                 }
96                                 info(t('Theme settings updated.'));
97                                 if (is_ajax()) {
98                                         return;
99                                 }
100                                 goaway('admin/themes/'.$theme);
101                                 return;
102                                 break;
103                         case 'features':
104                                 admin_page_features_post($a);
105                                 break;
106                         case 'logs':
107                                 admin_page_logs_post($a);
108                                 break;
109                         case 'dbsync':
110                                 admin_page_dbsync_post($a);
111                                 break;
112                         case 'blocklist':
113                                 admin_page_blocklist_post($a);
114                                 break;
115                 }
116         }
117
118         goaway('admin');
119         return; // NOTREACHED
120 }
121
122 /**
123  * @brief Generates content of the admin panel pages
124  *
125  * This function generates the content for the admin panel. It consists of the
126  * aside menu (same for the entire admin panel) and the code for the soecified
127  * subpage of the panel.
128  *
129  * The structure of the adress is: /admin/subpage/details though "details" is
130  * only necessary for some subpages, like themes or addons where it is the name
131  * of one theme resp. addon from which the details should be shown. Content for
132  * the subpages is generated in separate functions for each of the subpages.
133  *
134  * The returned string hold the generated HTML code of the page.
135  *
136  * @param App $a
137  * @return string
138  */
139 function admin_content(App $a) {
140
141         if (!is_site_admin()) {
142                 return login(false);
143         }
144
145         if (x($_SESSION,'submanage') && intval($_SESSION['submanage'])) {
146                 return "";
147         }
148
149         // APC deactivated, since there are problems with PHP 5.5
150         //if (function_exists("apc_delete")) {
151         //      $toDelete = new APCIterator('user', APC_ITER_VALUE);
152         //      apc_delete($toDelete);
153         //}
154
155         // Header stuff
156         $a->page['htmlhead'] .= replace_macros(get_markup_template('admin_settings_head.tpl'), array());
157
158         /*
159          * Side bar links
160          */
161         $aside_tools = array();
162         // array(url, name, extra css classes)
163         // not part of $aside to make the template more adjustable
164         $aside_sub = array(
165                 'site'   =>     array("admin/site/", t("Site") , "site"),
166                 'users'  =>     array("admin/users/", t("Users") , "users"),
167                 'plugins'=>     array("admin/plugins/", t("Plugins") , "plugins"),
168                 'themes' =>     array("admin/themes/", t("Themes") , "themes"),
169                 'features' =>   array("admin/features/", t("Additional features") , "features"),
170                 'dbsync' =>     array("admin/dbsync/", t('DB updates'), "dbsync"),
171                 'queue'  =>     array("admin/queue/", t('Inspect Queue'), "queue"),
172                 'blocklist' => array("admin/blocklist/", t('Server Blocklist'), "blocklist"),
173                 'federation' => array("admin/federation/", t('Federation Statistics'), "federation"),
174         );
175
176         /* get plugins admin page */
177
178         $r = q("SELECT `name` FROM `addon` WHERE `plugin_admin` = 1 ORDER BY `name`");
179         $aside_tools['plugins_admin']=array();
180         foreach ($r as $h) {
181                 $plugin =$h['name'];
182                 $aside_tools['plugins_admin'][] = array("admin/plugins/".$plugin, $plugin, "plugin");
183                 // temp plugins with admin
184                 $a->plugins_admin[] = $plugin;
185         }
186
187         $aside_tools['logs'] = array("admin/logs/", t("Logs"), "logs");
188         $aside_tools['viewlogs'] = array("admin/viewlogs/", t("View Logs"), 'viewlogs');
189         $aside_tools['diagnostics_probe'] = array('probe/', t('probe address'), 'probe');
190         $aside_tools['diagnostics_webfinger'] = array('webfinger/', t('check webfinger'), 'webfinger');
191
192         $t = get_markup_template("admin_aside.tpl");
193         $a->page['aside'] .= replace_macros($t, array(
194                 '$admin' => $aside_tools,
195                 '$subpages' => $aside_sub,
196                 '$admtxt' => t('Admin'),
197                 '$plugadmtxt' => t('Plugin Features'),
198                 '$logtxt' => t('Logs'),
199                 '$diagnosticstxt' => t('diagnostics'),
200                 '$h_pending' => t('User registrations waiting for confirmation'),
201                 '$admurl'=> "admin/"
202         ));
203
204
205
206         /*
207          * Page content
208          */
209         $o = '';
210         // urls
211         if ($a->argc > 1) {
212                 switch ($a->argv[1]) {
213                         case 'site':
214                                 $o = admin_page_site($a);
215                                 break;
216                         case 'users':
217                                 $o = admin_page_users($a);
218                                 break;
219                         case 'plugins':
220                                 $o = admin_page_plugins($a);
221                                 break;
222                         case 'themes':
223                                 $o = admin_page_themes($a);
224                                 break;
225                         case 'features':
226                                 $o = admin_page_features($a);
227                                 break;
228                         case 'logs':
229                                 $o = admin_page_logs($a);
230                                 break;
231                         case 'viewlogs':
232                                 $o = admin_page_viewlogs($a);
233                                 break;
234                         case 'dbsync':
235                                 $o = admin_page_dbsync($a);
236                                 break;
237                         case 'queue':
238                                 $o = admin_page_queue($a);
239                                 break;
240                         case 'federation':
241                                 $o = admin_page_federation($a);
242                                 break;
243                         case 'blocklist':
244                                 $o = admin_page_blocklist($a);
245                                 break;
246                         default:
247                                 notice(t("Item not found."));
248                 }
249         } else {
250                 $o = admin_page_summary($a);
251         }
252
253         if (is_ajax()) {
254                 echo $o;
255                 killme();
256                 return '';
257         } else {
258                 return $o;
259         }
260 }
261
262 /**
263  * @brief Subpage to modify the server wide block list via the admin panel.
264  *
265  * This function generates the subpage of the admin panel to allow the
266  * modification of the node wide block/black list to block entire
267  * remote servers from communication with this node.
268  *
269  * @param App $a
270  * @return string
271  */
272 function admin_page_blocklist(App $a) {
273         $blocklist = Config::get('system', 'blocklist');
274         $blocklistform = array();
275         if (is_array($blocklist)) {
276                 foreach($blocklist as $id =>$b) {
277                         $blocklistform[] = Array(
278                                 'url' => Array("url[$id]", t('Blocked URL'), $b['URL'], '', t('The blocked URL'), 'required', '', ''),
279                                 'reason' => Array("reason[$id]", t("Reason for the block"), $b['reason'], t('The reason why you blocked this URL.').'('.$b['URL'].')', 'required', '', ''),
280                                 'delete' => Array("delete[$id]", t("Delete UFL").' ('.$b['URL'].')', False , "Check to delete this entry from the blocklist")
281                         );
282                 }
283         }
284         $t = get_markup_template("admin_blocklist.tpl");
285         return replace_macros($t, array(
286                 '$title' => t('Administration'),
287                 '$page' => t('Server Blocklist'),
288                 '$intro' => t('This page can be used to define a black list of servers from the federated network that are not allowed to interact with your node. For all entered URLs you should also give a reason, why you have blocked the remote server.'),
289                 '$public' => t('The list of blocked servers will be made publically available on the /friendica page so that your users and people investigating communication problems can find the reason easily.'),
290                 '$addtitle' => t('Add new entry to block list'),
291                 '$newurl' => Array('newentry_url', t('Server URL'), '', t('The URL of the new server to add to the block list. Do not include the protocol to the URL.'), 'required', '', ''),
292                 '$newreason' => Array('newentry_reason', t('Block reason'), '', t('The reason why you blocked this URL.'), 'required', '', ''),
293                 '$submit' => t('Add Entry'),
294                 '$savechanges' => t('Save changes to the blocklist'),
295                 '$currenttitle' => t('Current Entries in the Blocklist'),
296                 '$thurl' => t('Blocked URL'),
297                 '$threason' => t('Reason for the block'),
298                 '$delentry' => t('Delete entry from blocklist'),
299                 '$entries' => $blocklistform,
300                 '$baseurl' => App::get_baseurl(true),
301                 '$confirm_delete' => t('Delete entriy from blocklist?'),
302                 '$form_security_token'  => get_form_security_token("admin_blocklist")
303         ));
304 }
305
306 /**
307  * @brief Process send data from Admin Blocklist Page
308  *
309  * @param App $a
310  */
311 function admin_page_blocklist_post(App $a) {
312         if (!x($_POST,"page_blocklist_save") && (!x($_POST['page_blocklist_edit']))) {
313                 return;
314         }
315
316         check_form_security_token_redirectOnErr('/admin/blocklist', 'admin_blocklist');
317
318         if (x($_POST['page_blocklist_save'])) {
319                 //  Add new item to blocklist
320                 $blocklist = get_config('system', 'blocklist');
321                 $blocklist[] = Array(
322                         'URL' => notags(trim($_POST['newentry_url'])),
323                         'reason' => notags(trim($_POST['newentry_reason']))
324                 );
325                 Config::set('system', 'blocklist', $blocklist);
326                 info(t('Server added to blocklist.').EOL);
327         } else {
328                 // Edit the entries from blocklist
329                 $blocklist = array();
330                 foreach ($_POST['url'] as $id => $URL) {
331                         $URL = notags(trim($URL));
332                         $reason = notags(trim($_POST['reason'][$id]));
333                         if (!x($_POST['delete'][$id])) {
334                                 $blocklist[] = Array(
335                                         'URL' => $URL,
336                                         'reason' => $reason
337                                 );
338                         }
339                 }
340                 Config::set('system', 'blocklist', $blocklist);
341                 info(t('Site blocklist updated.').EOL);
342         }
343         goaway('admin/blocklist');
344         
345         return; // NOTREACHED
346 }
347
348 /**
349  * @brief Subpage with some stats about "the federation" network
350  *
351  * This function generates the "Federation Statistics" subpage for the admin
352  * panel. The page lists some numbers to the part of "The Federation" known to
353  * the node. This data includes the different connected networks (e.g.
354  * Diaspora, Hubzilla, GNU Social) and the used versions in the different
355  * networks.
356  *
357  * The returned string contains the HTML code of the subpage for display.
358  *
359  * @param App $a
360  * @return string
361  */
362 function admin_page_federation(App $a) {
363         // get counts on active friendica, diaspora, redmatrix, hubzilla, gnu
364         // social and statusnet nodes this node is knowing
365         //
366         // We are looking for the following platforms in the DB, "Red" should find
367         // all variants of that platform ID string as the q() function is stripping
368         // off one % two of them are needed in the query
369         // Add more platforms if you like, when one returns 0 known nodes it is not
370         // displayed on the stats page.
371         $platforms = array('Friendi%%a', 'Diaspora', '%%red%%', 'Hubzilla', 'BlaBlaNet', 'GNU Social', 'StatusNet', 'Mastodon');
372         $colors    = array('Friendi%%a' => '#ffc018',     // orange from the logo
373                            'Diaspora'  => '#a1a1a1',     // logo is black and white, makes a gray
374                            '%%red%%'   => '#c50001',     // fire red from the logo
375                            'Hubzilla'  => '#43488a',     // blue from the logo
376                            'BlaBlaNet' => '#3B5998',     // blue from the navbar at blablanet-dot-com
377                            'GNU Social'=> '#a22430',     // dark red from the logo
378                            'StatusNet' => '#789240',     // the green from the logo (red and blue have already others
379                            'Mastodon'  => '#1a9df9');    // blue from the Mastodon logo
380         $counts = array();
381         $total = 0;
382
383         foreach ($platforms as $p) {
384                 // get a total count for the platform, the name and version of the
385                 // highest version and the protocol tpe
386                 $c = qu('SELECT COUNT(*) AS `total`, ANY_VALUE(`platform`) AS `platform`,
387                                 ANY_VALUE(`network`) AS `network`, MAX(`version`) AS `version` FROM `gserver`
388                                 WHERE `platform` LIKE "%s" AND `last_contact` >= `last_failure`
389                                 ORDER BY `version` ASC;', $p);
390                 $total = $total + $c[0]['total'];
391
392                 // what versions for that platform do we know at all?
393                 // again only the active nodes
394                 $v = qu('SELECT COUNT(*) AS `total`, `version` FROM `gserver`
395                                 WHERE `last_contact` >= `last_failure` AND `platform` LIKE "%s"
396                                 GROUP BY `version`
397                                 ORDER BY `version`;', $p);
398
399                 //
400                 // clean up version numbers
401                 //
402                 // some platforms do not provide version information, add a unkown there
403                 // to the version string for the displayed list.
404                 foreach ($v as $key => $value) {
405                         if ($v[$key]['version'] == '') {
406                                 $v[$key] = array('total'=>$v[$key]['total'], 'version'=>t('unknown'));
407                         }
408                 }
409                 // in the DB the Diaspora versions have the format x.x.x.x-xx the last
410                 // part (-xx) should be removed to clean up the versions from the "head
411                 // commit" information and combined into a single entry for x.x.x.x
412                 if ($p == 'Diaspora') {
413                         $newV = array();
414                         $newVv = array();
415                         foreach ($v as $vv) {
416                                 $newVC = $vv['total'];
417                                 $newVV = $vv['version'];
418                                 $posDash = strpos($newVV, '-');
419                                 if ($posDash) {
420                                         $newVV = substr($newVV, 0, $posDash);
421                                 }
422                                 if (isset($newV[$newVV])) {
423                                         $newV[$newVV] += $newVC;
424                                 } else {
425                                         $newV[$newVV] = $newVC;
426                                 }
427                         }
428                         foreach ($newV as $key => $value) {
429                                 array_push($newVv, array('total'=>$value, 'version'=>$key));
430                         }
431                         $v = $newVv;
432                 }
433
434                 // early friendica versions have the format x.x.xxxx where xxxx is the
435                 // DB version stamp; those should be operated out and versions be
436                 // conbined
437                 if ($p == 'Friendi%%a') {
438                         $newV = array();
439                         $newVv = array();
440                         foreach ($v as $vv) {
441                                 $newVC = $vv['total'];
442                                 $newVV = $vv['version'];
443                                 $lastDot = strrpos($newVV,'.');
444                                 $len = strlen($newVV)-1;
445                                 if (($lastDot == $len-4) && (!strrpos($newVV,'-rc') == $len-3)) {
446                                         $newVV = substr($newVV, 0, $lastDot);
447                                 }
448                                 if (isset($newV[$newVV])) {
449                                         $newV[$newVV] += $newVC;
450                                 } else {
451                                         $newV[$newVV] = $newVC;
452                                 }
453                         }
454                         foreach ($newV as $key => $value) {
455                                 array_push($newVv, array('total'=>$value, 'version'=>$key));
456                         }
457                         $v = $newVv;
458                 }
459
460                 foreach ($v as $key => $vv)
461                         $v[$key]["version"] = trim(strip_tags($vv["version"]));
462
463                 // the 3rd array item is needed for the JavaScript graphs as JS does
464                 // not like some characters in the names of variables...
465                 $counts[$p]=array($c[0], $v, str_replace(array(' ','%'),'',$p), $colors[$p]);
466         }
467
468         // some helpful text
469         $intro = t('This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of.');
470         $hint = t('The <em>Auto Discovered Contact Directory</em> feature is not enabled, it will improve the data displayed here.');
471
472         // load the template, replace the macros and return the page content
473         $t = get_markup_template("admin_federation.tpl");
474         return replace_macros($t, array(
475                 '$title' => t('Administration'),
476                 '$page' => t('Federation Statistics'),
477                 '$intro' => $intro,
478                 '$hint' => $hint,
479                 '$autoactive' => get_config('system', 'poco_completion'),
480                 '$counts' => $counts,
481                 '$version' => FRIENDICA_VERSION,
482                 '$legendtext' => sprintf(t('Currently this node is aware of %d nodes from the following platforms:'), $total),
483                 '$baseurl' => App::get_baseurl(),
484         ));
485 }
486
487 /**
488  * @brief Admin Inspect Queue Page
489  *
490  * Generates a page for the admin to have a look into the current queue of
491  * postings that are not deliverabke. Shown are the name and url of the
492  * recipient, the delivery network and the dates when the posting was generated
493  * and the last time tried to deliver the posting.
494  *
495  * The returned string holds the content of the page.
496  *
497  * @param App $a
498  * @return string
499  */
500 function admin_page_queue(App $a) {
501         // get content from the queue table
502         $r = q("SELECT `c`.`name`, `c`.`nurl`, `q`.`id`, `q`.`network`, `q`.`created`, `q`.`last`
503                         FROM `queue` AS `q`, `contact` AS `c`
504                         WHERE `c`.`id` = `q`.`cid`
505                         ORDER BY `q`.`cid`, `q`.`created`;");
506
507         $t = get_markup_template("admin_queue.tpl");
508         return replace_macros($t, array(
509                 '$title' => t('Administration'),
510                 '$page' => t('Inspect Queue'),
511                 '$count' => sizeof($r),
512                 'id_header' => t('ID'),
513                 '$to_header' => t('Recipient Name'),
514                 '$url_header' => t('Recipient Profile'),
515                 '$network_header' => t('Network'),
516                 '$created_header' => t('Created'),
517                 '$last_header' => t('Last Tried'),
518                 '$info' => t('This page lists the content of the queue for outgoing postings. These are postings the initial delivery failed for. They will be resend later and eventually deleted if the delivery fails permanently.'),
519                 '$entries' => $r,
520         ));
521 }
522
523 /**
524  * @brief Admin Summary Page
525  *
526  * The summary page is the "start page" of the admin panel. It gives the admin
527  * a first overview of the open adminastrative tasks.
528  *
529  * The returned string contains the HTML content of the generated page.
530  *
531  * @param App $a
532  * @return string
533  */
534 function admin_page_summary(App $a) {
535         global $db;
536         // are there MyISAM tables in the DB? If so, trigger a warning message
537         $r = q("SELECT `engine` FROM `information_schema`.`tables` WHERE `engine` = 'myisam' AND `table_schema` = '%s' LIMIT 1",
538                 dbesc($db->database_name()));
539         $showwarning = false;
540         $warningtext = array();
541         if (dbm::is_result($r)) {
542                 $showwarning = true;
543                 $warningtext[] = sprintf(t('Your DB still runs with MyISAM tables. You should change the engine type to InnoDB. As Friendica will use InnoDB only features in the future, you should change this! See <a href="%s">here</a> for a guide that may be helpful converting the table engines. You may also use the command <tt>php include/dbstructure.php toinnodb</tt> of your Friendica installation for an automatic conversion.<br />'), 'https://dev.mysql.com/doc/refman/5.7/en/converting-tables-to-innodb.html');
544         }
545         // MySQL >= 5.7.4 doesn't support the IGNORE keyword in ALTER TABLE statements
546         if ((version_compare($db->server_info(), '5.7.4') >= 0) AND
547                 !(strpos($db->server_info(), 'MariaDB') !== false)) {
548                 $warningtext[] = t('You are using a MySQL version which does not support all features that Friendica uses. You should consider switching to MariaDB.');
549         }
550         $r = q("SELECT `page-flags`, COUNT(`uid`) AS `count` FROM `user` GROUP BY `page-flags`");
551         $accounts = array(
552                 array(t('Normal Account'), 0),
553                 array(t('Soapbox Account'), 0),
554                 array(t('Community/Celebrity Account'), 0),
555                 array(t('Automatic Friend Account'), 0),
556                 array(t('Blog Account'), 0),
557                 array(t('Private Forum'), 0)
558         );
559
560         $users=0;
561         foreach ($r as $u) {
562                 $accounts[$u['page-flags']][1] = $u['count'];
563                 $users+= $u['count'];
564         }
565
566         logger('accounts: '.print_r($accounts,true),LOGGER_DATA);
567
568         $r = qu("SELECT COUNT(`id`) AS `count` FROM `register`");
569         $pending = $r[0]['count'];
570
571         $r = qu("SELECT COUNT(*) AS `total` FROM `queue` WHERE 1");
572         $queue = (($r) ? $r[0]['total'] : 0);
573
574         $r = qu("SELECT COUNT(*) AS `total` FROM `workerqueue` WHERE 1");
575         $workerqueue = (($r) ? $r[0]['total'] : 0);
576
577         // We can do better, but this is a quick queue status
578
579         $queues = array('label' => t('Message queues'), 'queue' => $queue, 'workerq' => $workerqueue);
580
581
582         $t = get_markup_template("admin_summary.tpl");
583         return replace_macros($t, array(
584                 '$title' => t('Administration'),
585                 '$page' => t('Summary'),
586                 '$queues' => $queues,
587                 '$users' => array(t('Registered users'), $users),
588                 '$accounts' => $accounts,
589                 '$pending' => array(t('Pending registrations'), $pending),
590                 '$version' => array(t('Version'), FRIENDICA_VERSION),
591                 '$baseurl' => App::get_baseurl(),
592                 '$platform' => FRIENDICA_PLATFORM,
593                 '$codename' => FRIENDICA_CODENAME,
594                 '$build' =>  get_config('system','build'),
595                 '$plugins' => array(t('Active plugins'), $a->plugins),
596                 '$showwarning' => $showwarning,
597                 '$warningtext' => $warningtext
598         ));
599 }
600
601 /**
602  * @brief Process send data from Admin Site Page
603  *
604  * @param App $a
605  */
606 function admin_page_site_post(App $a) {
607         if (!x($_POST,"page_site")) {
608                 return;
609         }
610
611         check_form_security_token_redirectOnErr('/admin/site', 'admin_site');
612
613         // relocate
614         if (x($_POST,'relocate') && x($_POST,'relocate_url') && $_POST['relocate_url'] != "") {
615                 $new_url = $_POST['relocate_url'];
616                 $new_url = rtrim($new_url,"/");
617
618                 $parsed = @parse_url($new_url);
619                 if (!$parsed || (!x($parsed,'host') || !x($parsed,'scheme'))) {
620                         notice(t("Can not parse base url. Must have at least <scheme>://<domain>"));
621                         goaway('admin/site');
622                 }
623
624                 /* steps:
625                  * replace all "baseurl" to "new_url" in config, profile, term, items and contacts
626                  * send relocate for every local user
627                  * */
628
629                 $old_url = App::get_baseurl(true);
630
631                 // Generate host names for relocation the addresses in the format user@address.tld
632                 $new_host = str_replace("http://", "@", normalise_link($new_url));
633                 $old_host = str_replace("http://", "@", normalise_link($old_url));
634
635                 function update_table($table_name, $fields, $old_url, $new_url) {
636                         global $db, $a;
637
638                         $dbold = dbesc($old_url);
639                         $dbnew = dbesc($new_url);
640
641                         $upd = array();
642                         foreach ($fields as $f) {
643                                 $upd[] = "`$f` = REPLACE(`$f`, '$dbold', '$dbnew')";
644                         }
645
646                         $upds = implode(", ", $upd);
647
648
649
650                         $q = sprintf("UPDATE %s SET %s;", $table_name, $upds);
651                         $r = q($q);
652                         if (!$r) {
653                                 notice("Failed updating '$table_name': ".$db->error);
654                                 goaway('admin/site');
655                         }
656                 }
657
658                 // update tables
659                 // update profile links in the format "http://server.tld"
660                 update_table("profile", array('photo', 'thumb'), $old_url, $new_url);
661                 update_table("term", array('url'), $old_url, $new_url);
662                 update_table("contact", array('photo','thumb','micro','url','nurl','alias','request','notify','poll','confirm','poco', 'avatar'), $old_url, $new_url);
663                 update_table("gcontact", array('url','nurl','photo','server_url','notify','alias'), $old_url, $new_url);
664                 update_table("item", array('owner-link','owner-avatar','author-link','author-avatar','body','plink','tag'), $old_url, $new_url);
665
666                 // update profile addresses in the format "user@server.tld"
667                 update_table("contact", array('addr'), $old_host, $new_host);
668                 update_table("gcontact", array('connect','addr'), $old_host, $new_host);
669
670                 // update config
671                 $a->set_baseurl($new_url);
672                 set_config('system','url',$new_url);
673
674                 // send relocate
675                 $users = q("SELECT `uid` FROM `user` WHERE `account_removed` = 0 AND `account_expired` = 0");
676
677                 foreach ($users as $user) {
678                         proc_run(PRIORITY_HIGH, 'include/notifier.php', 'relocate', $user['uid']);
679                 }
680
681                 info("Relocation started. Could take a while to complete.");
682
683                 goaway('admin/site');
684         }
685         // end relocate
686
687         $sitename               =       ((x($_POST,'sitename'))                 ? notags(trim($_POST['sitename']))              : '');
688         $hostname               =       ((x($_POST,'hostname'))                 ? notags(trim($_POST['hostname']))              : '');
689         $sender_email           =       ((x($_POST,'sender_email'))             ? notags(trim($_POST['sender_email']))          : '');
690         $banner                 =       ((x($_POST,'banner'))                   ? trim($_POST['banner'])                        : false);
691         $shortcut_icon          =       ((x($_POST,'shortcut_icon'))            ? notags(trim($_POST['shortcut_icon']))         : '');
692         $touch_icon             =       ((x($_POST,'touch_icon'))               ? notags(trim($_POST['touch_icon']))            : '');
693         $info                   =       ((x($_POST,'info'))                     ? trim($_POST['info'])                          : false);
694         $language               =       ((x($_POST,'language'))                 ? notags(trim($_POST['language']))              : '');
695         $theme                  =       ((x($_POST,'theme'))                    ? notags(trim($_POST['theme']))                 : '');
696         $theme_mobile           =       ((x($_POST,'theme_mobile'))             ? notags(trim($_POST['theme_mobile']))          : '');
697         $maximagesize           =       ((x($_POST,'maximagesize'))             ? intval(trim($_POST['maximagesize']))          :  0);
698         $maximagelength         =       ((x($_POST,'maximagelength'))           ? intval(trim($_POST['maximagelength']))        :  MAX_IMAGE_LENGTH);
699         $jpegimagequality       =       ((x($_POST,'jpegimagequality'))         ? intval(trim($_POST['jpegimagequality']))      :  JPEG_QUALITY);
700
701
702         $register_policy        =       ((x($_POST,'register_policy'))          ? intval(trim($_POST['register_policy']))       :  0);
703         $daily_registrations    =       ((x($_POST,'max_daily_registrations'))  ? intval(trim($_POST['max_daily_registrations']))       :0);
704         $abandon_days           =       ((x($_POST,'abandon_days'))             ? intval(trim($_POST['abandon_days']))          :  0);
705
706         $register_text          =       ((x($_POST,'register_text'))            ? notags(trim($_POST['register_text']))         : '');
707
708         $allowed_sites          =       ((x($_POST,'allowed_sites'))            ? notags(trim($_POST['allowed_sites']))         : '');
709         $allowed_email          =       ((x($_POST,'allowed_email'))            ? notags(trim($_POST['allowed_email']))         : '');
710         $block_public           =       ((x($_POST,'block_public'))             ? True                                          : False);
711         $force_publish          =       ((x($_POST,'publish_all'))              ? True                                          : False);
712         $global_directory       =       ((x($_POST,'directory'))                ? notags(trim($_POST['directory']))             : '');
713         $thread_allow           =       ((x($_POST,'thread_allow'))             ? True                                          : False);
714         $newuser_private                =       ((x($_POST,'newuser_private'))          ? True                                  : False);
715         $enotify_no_content             =       ((x($_POST,'enotify_no_content'))       ? True                                  : False);
716         $private_addons                 =       ((x($_POST,'private_addons'))           ? True                                  : False);
717         $disable_embedded               =       ((x($_POST,'disable_embedded'))         ? True                                  : False);
718         $allow_users_remote_self        =       ((x($_POST,'allow_users_remote_self'))  ? True                                  : False);
719
720         $no_multi_reg           =       ((x($_POST,'no_multi_reg'))             ? True                                          : False);
721         $no_openid              =       !((x($_POST,'no_openid'))               ? True                                          : False);
722         $no_regfullname         =       !((x($_POST,'no_regfullname'))          ? True                                          : False);
723         $community_page_style   =       ((x($_POST,'community_page_style'))     ? intval(trim($_POST['community_page_style']))  : 0);
724         $max_author_posts_community_page        =       ((x($_POST,'max_author_posts_community_page'))  ? intval(trim($_POST['max_author_posts_community_page']))       : 0);
725
726         $verifyssl              =       ((x($_POST,'verifyssl'))                ? True                                          : False);
727         $proxyuser              =       ((x($_POST,'proxyuser'))                ? notags(trim($_POST['proxyuser']))             : '');
728         $proxy                  =       ((x($_POST,'proxy'))                    ? notags(trim($_POST['proxy']))                 : '');
729         $timeout                =       ((x($_POST,'timeout'))                  ? intval(trim($_POST['timeout']))               : 60);
730         $maxloadavg             =       ((x($_POST,'maxloadavg'))               ? intval(trim($_POST['maxloadavg']))            : 50);
731         $maxloadavg_frontend    =       ((x($_POST,'maxloadavg_frontend'))      ? intval(trim($_POST['maxloadavg_frontend']))   : 50);
732         $optimize_max_tablesize =       ((x($_POST,'optimize_max_tablesize'))   ? intval(trim($_POST['optimize_max_tablesize'])): 100);
733         $optimize_fragmentation =       ((x($_POST,'optimize_fragmentation'))   ? intval(trim($_POST['optimize_fragmentation'])): 30);
734         $poco_completion        =       ((x($_POST,'poco_completion'))          ? intval(trim($_POST['poco_completion']))       : false);
735         $poco_requery_days      =       ((x($_POST,'poco_requery_days'))        ? intval(trim($_POST['poco_requery_days']))     : 7);
736         $poco_discovery         =       ((x($_POST,'poco_discovery'))           ? intval(trim($_POST['poco_discovery']))        : 0);
737         $poco_discovery_since   =       ((x($_POST,'poco_discovery_since'))     ? intval(trim($_POST['poco_discovery_since']))  : 30);
738         $poco_local_search      =       ((x($_POST,'poco_local_search'))        ? intval(trim($_POST['poco_local_search']))     : false);
739         $nodeinfo               =       ((x($_POST,'nodeinfo'))                 ? intval(trim($_POST['nodeinfo']))              : false);
740         $dfrn_only              =       ((x($_POST,'dfrn_only'))                ? True                                          : False);
741         $ostatus_disabled       =       !((x($_POST,'ostatus_disabled'))        ? True                                          : False);
742         $ostatus_poll_interval  =       ((x($_POST,'ostatus_poll_interval'))    ? intval(trim($_POST['ostatus_poll_interval'])) :  0);
743         $ostatus_full_threads   =       ((x($_POST,'ostatus_full_threads'))     ? True                                          : False);
744         $diaspora_enabled       =       ((x($_POST,'diaspora_enabled'))         ? True                                          : False);
745         $ssl_policy             =       ((x($_POST,'ssl_policy'))               ? intval($_POST['ssl_policy'])                  : 0);
746         $force_ssl              =       ((x($_POST,'force_ssl'))                ? True                                          : False);
747         $hide_help              =       ((x($_POST,'hide_help'))                ? True                                          : False);
748         $suppress_tags          =       ((x($_POST,'suppress_tags'))            ? True                                          : False);
749         $itemcache              =       ((x($_POST,'itemcache'))                ? notags(trim($_POST['itemcache']))             : '');
750         $itemcache_duration     =       ((x($_POST,'itemcache_duration'))       ? intval($_POST['itemcache_duration'])          : 0);
751         $max_comments           =       ((x($_POST,'max_comments'))             ? intval($_POST['max_comments'])                : 0);
752         $temppath               =       ((x($_POST,'temppath'))                 ? notags(trim($_POST['temppath']))              : '');
753         $basepath               =       ((x($_POST,'basepath'))                 ? notags(trim($_POST['basepath']))              : '');
754         $singleuser             =       ((x($_POST,'singleuser'))               ? notags(trim($_POST['singleuser']))            : '');
755         $proxy_disabled         =       ((x($_POST,'proxy_disabled'))           ? True                                          : False);
756         $only_tag_search        =       ((x($_POST,'only_tag_search'))          ? True                                          : False);
757         $rino                   =       ((x($_POST,'rino'))                     ? intval($_POST['rino'])                        : 0);
758         $worker_queues          =       ((x($_POST,'worker_queues'))            ? intval($_POST['worker_queues'])               : 4);
759         $worker_dont_fork       =       ((x($_POST,'worker_dont_fork'))         ? True                                          : False);
760         $worker_fastlane        =       ((x($_POST,'worker_fastlane'))          ? True                                          : False);
761         $worker_frontend        =       ((x($_POST,'worker_frontend'))          ? True                                          : False);
762
763         // Has the directory url changed? If yes, then resubmit the existing profiles there
764         if ($global_directory != Config::get('system', 'directory') AND ($global_directory != '')) {
765                 Config::set('system', 'directory', $global_directory);
766                 proc_run(PRIORITY_LOW, 'include/directory.php');
767         }
768
769         if ($a->get_path() != "") {
770                 $diaspora_enabled = false;
771         }
772         if (!$thread_allow) {
773                 $ostatus_disabled = true;
774         }
775         if ($ssl_policy != intval(get_config('system','ssl_policy'))) {
776                 if ($ssl_policy == SSL_POLICY_FULL) {
777                         q("UPDATE `contact` SET
778                                 `url`     = REPLACE(`url`    , 'http:' , 'https:'),
779                                 `photo`   = REPLACE(`photo`  , 'http:' , 'https:'),
780                                 `thumb`   = REPLACE(`thumb`  , 'http:' , 'https:'),
781                                 `micro`   = REPLACE(`micro`  , 'http:' , 'https:'),
782                                 `request` = REPLACE(`request`, 'http:' , 'https:'),
783                                 `notify`  = REPLACE(`notify` , 'http:' , 'https:'),
784                                 `poll`    = REPLACE(`poll`   , 'http:' , 'https:'),
785                                 `confirm` = REPLACE(`confirm`, 'http:' , 'https:'),
786                                 `poco`    = REPLACE(`poco`   , 'http:' , 'https:')
787                                 WHERE `self` = 1"
788                         );
789                         q("UPDATE `profile` SET
790                                 `photo`   = REPLACE(`photo`  , 'http:' , 'https:'),
791                                 `thumb`   = REPLACE(`thumb`  , 'http:' , 'https:')
792                                 WHERE 1 "
793                         );
794                 } elseif ($ssl_policy == SSL_POLICY_SELFSIGN) {
795                         q("UPDATE `contact` SET
796                                 `url`     = REPLACE(`url`    , 'https:' , 'http:'),
797                                 `photo`   = REPLACE(`photo`  , 'https:' , 'http:'),
798                                 `thumb`   = REPLACE(`thumb`  , 'https:' , 'http:'),
799                                 `micro`   = REPLACE(`micro`  , 'https:' , 'http:'),
800                                 `request` = REPLACE(`request`, 'https:' , 'http:'),
801                                 `notify`  = REPLACE(`notify` , 'https:' , 'http:'),
802                                 `poll`    = REPLACE(`poll`   , 'https:' , 'http:'),
803                                 `confirm` = REPLACE(`confirm`, 'https:' , 'http:'),
804                                 `poco`    = REPLACE(`poco`   , 'https:' , 'http:')
805                                 WHERE `self` = 1"
806                         );
807                         q("UPDATE `profile` SET
808                                 `photo`   = REPLACE(`photo`  , 'https:' , 'http:'),
809                                 `thumb`   = REPLACE(`thumb`  , 'https:' , 'http:')
810                                 WHERE 1 "
811                         );
812                 }
813         }
814         set_config('system','ssl_policy',$ssl_policy);
815         set_config('system','maxloadavg',$maxloadavg);
816         set_config('system','maxloadavg_frontend',$maxloadavg_frontend);
817         set_config('system','optimize_max_tablesize',$optimize_max_tablesize);
818         set_config('system','optimize_fragmentation',$optimize_fragmentation);
819         set_config('system','poco_completion',$poco_completion);
820         set_config('system','poco_requery_days',$poco_requery_days);
821         set_config('system','poco_discovery',$poco_discovery);
822         set_config('system','poco_discovery_since',$poco_discovery_since);
823         set_config('system','poco_local_search',$poco_local_search);
824         set_config('system','nodeinfo',$nodeinfo);
825         set_config('config','sitename',$sitename);
826         set_config('config','hostname',$hostname);
827         set_config('config','sender_email', $sender_email);
828         set_config('system','suppress_tags',$suppress_tags);
829         set_config('system','shortcut_icon',$shortcut_icon);
830         set_config('system','touch_icon',$touch_icon);
831
832         if ($banner == "") {
833                 // don't know why, but del_config doesn't work...
834                 q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
835                         dbesc("system"),
836                         dbesc("banner")
837                 );
838         } else {
839                 set_config('system','banner', $banner);
840         }
841
842         if ($info == "") {
843                 del_config('config','info');
844         } else {
845                 set_config('config','info',$info);
846         }
847         set_config('system','language', $language);
848         set_config('system','theme', $theme);
849
850         if ($theme_mobile === '---') {
851                 del_config('system','mobile-theme');
852         } else {
853                 set_config('system','mobile-theme', $theme_mobile);
854                 }
855                 if ($singleuser === '---') {
856                         del_config('system','singleuser');
857                 } else {
858                         set_config('system','singleuser', $singleuser);
859                 }
860         set_config('system', 'maximagesize', $maximagesize);
861         set_config('system', 'max_image_length', $maximagelength);
862         set_config('system', 'jpeg_quality', $jpegimagequality);
863
864         set_config('config', 'register_policy', $register_policy);
865         set_config('system', 'max_daily_registrations', $daily_registrations);
866         set_config('system', 'account_abandon_days', $abandon_days);
867         set_config('config', 'register_text', $register_text);
868         set_config('system', 'allowed_sites', $allowed_sites);
869         set_config('system', 'allowed_email', $allowed_email);
870         set_config('system', 'block_public', $block_public);
871         set_config('system', 'publish_all', $force_publish);
872         set_config('system', 'thread_allow', $thread_allow);
873         set_config('system', 'newuser_private', $newuser_private);
874         set_config('system', 'enotify_no_content', $enotify_no_content);
875         set_config('system', 'disable_embedded', $disable_embedded);
876         set_config('system', 'allow_users_remote_self', $allow_users_remote_self);
877
878         set_config('system', 'block_extended_register', $no_multi_reg);
879         set_config('system', 'no_openid', $no_openid);
880         set_config('system', 'no_regfullname', $no_regfullname);
881         set_config('system', 'community_page_style', $community_page_style);
882         set_config('system', 'max_author_posts_community_page', $max_author_posts_community_page);
883         set_config('system', 'verifyssl', $verifyssl);
884         set_config('system', 'proxyuser', $proxyuser);
885         set_config('system', 'proxy', $proxy);
886         set_config('system', 'curl_timeout', $timeout);
887         set_config('system', 'dfrn_only', $dfrn_only);
888         set_config('system', 'ostatus_disabled', $ostatus_disabled);
889         set_config('system', 'ostatus_poll_interval', $ostatus_poll_interval);
890         set_config('system', 'ostatus_full_threads', $ostatus_full_threads);
891         set_config('system', 'diaspora_enabled', $diaspora_enabled);
892
893         set_config('config', 'private_addons', $private_addons);
894
895         set_config('system', 'force_ssl', $force_ssl);
896         set_config('system', 'hide_help', $hide_help);
897         set_config('system', 'itemcache', $itemcache);
898         set_config('system', 'itemcache_duration', $itemcache_duration);
899         set_config('system', 'max_comments', $max_comments);
900         set_config('system', 'temppath', $temppath);
901         set_config('system', 'basepath', $basepath);
902         set_config('system', 'proxy_disabled', $proxy_disabled);
903         set_config('system', 'only_tag_search', $only_tag_search);
904         set_config('system', 'worker_queues', $worker_queues);
905         set_config('system', 'worker_dont_fork', $worker_dont_fork);
906         set_config('system', 'worker_fastlane', $worker_fastlane);
907         set_config('system', 'frontend_worker', $worker_frontend);
908         set_config('system', 'rino_encrypt', $rino);
909
910         info(t('Site settings updated.').EOL);
911         goaway('admin/site');
912         return; // NOTREACHED
913
914 }
915
916 /**
917  * @brief Generate Admin Site subpage
918  *
919  * This function generates the main configuration page of the admin panel.
920  *
921  * @param  App $a
922  * @return string
923  */
924 function admin_page_site(App $a) {
925
926         /* Installed langs */
927         $lang_choices = get_available_languages();
928
929         if (strlen(get_config('system','directory_submit_url')) AND
930                 !strlen(get_config('system','directory'))) {
931                         set_config('system','directory', dirname(get_config('system','directory_submit_url')));
932                         del_config('system','directory_submit_url');
933         }
934
935         /* Installed themes */
936         $theme_choices = array();
937         $theme_choices_mobile = array();
938         $theme_choices_mobile["---"] = t("No special theme for mobile devices");
939         $files = glob('view/theme/*');
940         if ($files) {
941
942                 $allowed_theme_list = Config::get('system', 'allowed_themes');
943
944                 foreach ($files as $file) {
945                         if (intval(file_exists($file.'/unsupported')))
946                                 continue;
947
948                         $f = basename($file);
949
950                         // Only show allowed themes here
951                         if (($allowed_theme_list != '') AND !strstr($allowed_theme_list, $f)) {
952                                 continue;
953                         }
954
955                         $theme_name = ((file_exists($file.'/experimental')) ?  sprintf("%s - \x28Experimental\x29", $f) : $f);
956
957                         if (file_exists($file.'/mobile')) {
958                                 $theme_choices_mobile[$f] = $theme_name;
959                         } else {
960                                 $theme_choices[$f] = $theme_name;
961                         }
962                 }
963         }
964
965         /* Community page style */
966         $community_page_style_choices = array(
967                 CP_NO_COMMUNITY_PAGE => t("No community page"),
968                 CP_USERS_ON_SERVER => t("Public postings from users of this site"),
969                 CP_GLOBAL_COMMUNITY => t("Global community page")
970                 );
971
972         /* OStatus conversation poll choices */
973         $ostatus_poll_choices = array(
974                 "-2" => t("Never"),
975                 "-1" => t("At post arrival"),
976                 "0" => t("Frequently"),
977                 "60" => t("Hourly"),
978                 "720" => t("Twice daily"),
979                 "1440" => t("Daily")
980                 );
981
982         $poco_discovery_choices = array(
983                 "0" => t("Disabled"),
984                 "1" => t("Users"),
985                 "2" => t("Users, Global Contacts"),
986                 "3" => t("Users, Global Contacts/fallback"),
987                 );
988
989         $poco_discovery_since_choices = array(
990                 "30" => t("One month"),
991                 "91" => t("Three months"),
992                 "182" => t("Half a year"),
993                 "365" => t("One year"),
994                 );
995
996         /* get user names to make the install a personal install of X */
997         $user_names = array();
998         $user_names['---'] = t('Multi user instance');
999         $users = q("SELECT `username`, `nickname` FROM `user`");
1000         foreach ($users as $user) {
1001                 $user_names[$user['nickname']] = $user['username'];
1002         }
1003
1004         /* Banner */
1005         $banner = get_config('system','banner');
1006         if ($banner == false) {
1007                 $banner = '<a href="http://friendica.com"><img id="logo-img" src="images/friendica-32.png" alt="logo" /></a><span id="logo-text"><a href="http://friendica.com">Friendica</a></span>';
1008         }
1009         $banner = htmlspecialchars($banner);
1010         $info = get_config('config','info');
1011         $info = htmlspecialchars($info);
1012
1013         // Automatically create temporary paths
1014         get_temppath();
1015         get_itemcachepath();
1016
1017         //echo "<pre>"; var_dump($lang_choices); die("</pre>");
1018
1019         /* Register policy */
1020         $register_choices = array(
1021                 REGISTER_CLOSED => t("Closed"),
1022                 REGISTER_APPROVE => t("Requires approval"),
1023                 REGISTER_OPEN => t("Open")
1024         );
1025
1026         $ssl_choices = array(
1027                 SSL_POLICY_NONE => t("No SSL policy, links will track page SSL state"),
1028                 SSL_POLICY_FULL => t("Force all links to use SSL"),
1029                 SSL_POLICY_SELFSIGN => t("Self-signed certificate, use SSL for local links only (discouraged)")
1030         );
1031
1032         if ($a->config['hostname'] == "") {
1033                 $a->config['hostname'] = $a->get_hostname();
1034         }
1035         $diaspora_able = ($a->get_path() == "");
1036
1037         $optimize_max_tablesize = Config::get('system','optimize_max_tablesize', 100);
1038
1039         if ($optimize_max_tablesize < -1) {
1040                 $optimize_max_tablesize = -1;
1041         }
1042
1043         if ($optimize_max_tablesize == 0) {
1044                 $optimize_max_tablesize = 100;
1045         }
1046
1047         $t = get_markup_template("admin_site.tpl");
1048         return replace_macros($t, array(
1049                 '$title' => t('Administration'),
1050                 '$page' => t('Site'),
1051                 '$submit' => t('Save Settings'),
1052                 '$registration' => t('Registration'),
1053                 '$upload' => t('File upload'),
1054                 '$corporate' => t('Policies'),
1055                 '$advanced' => t('Advanced'),
1056                 '$portable_contacts' => t('Auto Discovered Contact Directory'),
1057                 '$performance' => t('Performance'),
1058                 '$worker_title' => t('Worker'),
1059                 '$relocate'=> t('Relocate - WARNING: advanced function. Could make this server unreachable.'),
1060                 '$baseurl' => App::get_baseurl(true),
1061                 // name, label, value, help string, extra data...
1062                 '$sitename'             => array('sitename', t("Site name"), $a->config['sitename'],''),
1063                 '$hostname'             => array('hostname', t("Host name"), $a->config['hostname'], ""),
1064                 '$sender_email'         => array('sender_email', t("Sender Email"), $a->config['sender_email'], t("The email address your server shall use to send notification emails from."), "", "", "email"),
1065                 '$banner'               => array('banner', t("Banner/Logo"), $banner, ""),
1066                 '$shortcut_icon'        => array('shortcut_icon', t("Shortcut icon"), get_config('system','shortcut_icon'),  t("Link to an icon that will be used for browsers.")),
1067                 '$touch_icon'           => array('touch_icon', t("Touch icon"), get_config('system','touch_icon'),  t("Link to an icon that will be used for tablets and mobiles.")),
1068                 '$info'                 => array('info', t('Additional Info'), $info, sprintf(t('For public servers: you can add additional information here that will be listed at %s/siteinfo.'), get_server())),
1069                 '$language'             => array('language', t("System language"), get_config('system','language'), "", $lang_choices),
1070                 '$theme'                => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices),
1071                 '$theme_mobile'         => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile-theme'), t("Theme for mobile devices"), $theme_choices_mobile),
1072                 '$ssl_policy'           => array('ssl_policy', t("SSL link policy"), (string) intval(get_config('system','ssl_policy')), t("Determines whether generated links should be forced to use SSL"), $ssl_choices),
1073                 '$force_ssl'            => array('force_ssl', t("Force SSL"), get_config('system','force_ssl'), t("Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops.")),
1074                 '$hide_help'            => array('hide_help', t("Hide help entry from navigation menu"), get_config('system','hide_help'), t("Hides the menu entry for the Help pages from the navigation menu. You can still access it calling /help directly.")),
1075                 '$singleuser'           => array('singleuser', t("Single user instance"), get_config('system','singleuser'), t("Make this instance multi-user or single-user for the named user"), $user_names),
1076                 '$maximagesize'         => array('maximagesize', t("Maximum image size"), get_config('system','maximagesize'), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")),
1077                 '$maximagelength'       => array('maximagelength', t("Maximum image length"), get_config('system','max_image_length'), t("Maximum length in pixels of the longest side of uploaded images. Default is -1, which means no limits.")),
1078                 '$jpegimagequality'     => array('jpegimagequality', t("JPEG image quality"), get_config('system','jpeg_quality'), t("Uploaded JPEGS will be saved at this quality setting [0-100]. Default is 100, which is full quality.")),
1079
1080                 '$register_policy'      => array('register_policy', t("Register policy"), $a->config['register_policy'], "", $register_choices),
1081                 '$daily_registrations'  => array('max_daily_registrations', t("Maximum Daily Registrations"), get_config('system', 'max_daily_registrations'), t("If registration is permitted above, this sets the maximum number of new user registrations to accept per day.  If register is set to closed, this setting has no effect.")),
1082                 '$register_text'        => array('register_text', t("Register text"), $a->config['register_text'], t("Will be displayed prominently on the registration page.")),
1083                 '$abandon_days'         => array('abandon_days', t('Accounts abandoned after x days'), get_config('system','account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')),
1084                 '$allowed_sites'        => array('allowed_sites', t("Allowed friend domains"), get_config('system','allowed_sites'), t("Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains")),
1085                 '$allowed_email'        => array('allowed_email', t("Allowed email domains"), get_config('system','allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")),
1086                 '$block_public'         => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently logged in.")),
1087                 '$force_publish'        => array('publish_all', t("Force publish"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")),
1088                 '$global_directory'     => array('directory', t("Global directory URL"), get_config('system','directory'), t("URL to the global directory. If this is not set, the global directory is completely unavailable to the application.")),
1089                 '$thread_allow'         => array('thread_allow', t("Allow threaded items"), get_config('system','thread_allow'), t("Allow infinite level threading for items on this site.")),
1090                 '$newuser_private'      => array('newuser_private', t("Private posts by default for new users"), get_config('system','newuser_private'), t("Set default post permissions for all new members to the default privacy group rather than public.")),
1091                 '$enotify_no_content'   => array('enotify_no_content', t("Don't include post content in email notifications"), get_config('system','enotify_no_content'), t("Don't include the content of a post/comment/private message/etc. in the email notifications that are sent out from this site, as a privacy measure.")),
1092                 '$private_addons'       => array('private_addons', t("Disallow public access to addons listed in the apps menu."), get_config('config','private_addons'), t("Checking this box will restrict addons listed in the apps menu to members only.")),
1093                 '$disable_embedded'     => array('disable_embedded', t("Don't embed private images in posts"), get_config('system','disable_embedded'), t("Don't replace locally-hosted private photos in posts with an embedded copy of the image. This means that contacts who receive posts containing private photos will have to authenticate and load each image, which may take a while.")),
1094                 '$allow_users_remote_self' => array('allow_users_remote_self', t('Allow Users to set remote_self'), get_config('system','allow_users_remote_self'), t('With checking this, every user is allowed to mark every contact as a remote_self in the repair contact dialog. Setting this flag on a contact causes mirroring every posting of that contact in the users stream.')),
1095                 '$no_multi_reg'         => array('no_multi_reg', t("Block multiple registrations"),  get_config('system','block_extended_register'), t("Disallow users to register additional accounts for use as pages.")),
1096                 '$no_openid'            => array('no_openid', t("OpenID support"), !get_config('system','no_openid'), t("OpenID support for registration and logins.")),
1097                 '$no_regfullname'       => array('no_regfullname', t("Fullname check"), !get_config('system','no_regfullname'), t("Force users to register with a space between firstname and lastname in Full name, as an antispam measure")),
1098                 '$community_page_style' => array('community_page_style', t("Community Page Style"), get_config('system','community_page_style'), t("Type of community page to show. 'Global community' shows every public posting from an open distributed network that arrived on this server."), $community_page_style_choices),
1099                 '$max_author_posts_community_page' => array('max_author_posts_community_page', t("Posts per user on community page"), get_config('system','max_author_posts_community_page'), t("The maximum number of posts per user on the community page. (Not valid for 'Global Community')")),
1100                 '$ostatus_disabled'     => array('ostatus_disabled', t("Enable OStatus support"), !get_config('system','ostatus_disabled'), t("Provide built-in OStatus \x28StatusNet, GNU Social etc.\x29 compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed.")),
1101                 '$ostatus_poll_interval' => array('ostatus_poll_interval', t("OStatus conversation completion interval"), (string) intval(get_config('system','ostatus_poll_interval')), t("How often shall the poller check for new entries in OStatus conversations? This can be a very ressource task."), $ostatus_poll_choices),
1102                 '$ostatus_full_threads' => array('ostatus_full_threads', t("Only import OStatus threads from our contacts"), get_config('system','ostatus_full_threads'), t("Normally we import every content from our OStatus contacts. With this option we only store threads that are started by a contact that is known on our system.")),
1103                 '$ostatus_not_able'     => t("OStatus support can only be enabled if threading is enabled."),
1104                 '$diaspora_able'        => $diaspora_able,
1105                 '$diaspora_not_able'    => t("Diaspora support can't be enabled because Friendica was installed into a sub directory."),
1106                 '$diaspora_enabled'     => array('diaspora_enabled', t("Enable Diaspora support"), get_config('system','diaspora_enabled'), t("Provide built-in Diaspora network compatibility.")),
1107                 '$dfrn_only'            => array('dfrn_only', t('Only allow Friendica contacts'), get_config('system','dfrn_only'), t("All contacts must use Friendica protocols. All other built-in communication protocols disabled.")),
1108                 '$verifyssl'            => array('verifyssl', t("Verify SSL"), get_config('system','verifyssl'), t("If you wish, you can turn on strict certificate checking. This will mean you cannot connect (at all) to self-signed SSL sites.")),
1109                 '$proxyuser'            => array('proxyuser', t("Proxy user"), get_config('system','proxyuser'), ""),
1110                 '$proxy'                => array('proxy', t("Proxy URL"), get_config('system','proxy'), ""),
1111                 '$timeout'              => array('timeout', t("Network timeout"), (x(get_config('system','curl_timeout'))?get_config('system','curl_timeout'):60), t("Value is in seconds. Set to 0 for unlimited (not recommended).")),
1112                 '$maxloadavg'           => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")),
1113                 '$maxloadavg_frontend'  => array('maxloadavg_frontend', t("Maximum Load Average (Frontend)"), ((intval(get_config('system','maxloadavg_frontend')) > 0)?get_config('system','maxloadavg_frontend'):50), t("Maximum system load before the frontend quits service - default 50.")),
1114                 '$optimize_max_tablesize'=> array('optimize_max_tablesize', t("Maximum table size for optimization"), $optimize_max_tablesize, t("Maximum table size (in MB) for the automatic optimization - default 100 MB. Enter -1 to disable it.")),
1115                 '$optimize_fragmentation'=> array('optimize_fragmentation', t("Minimum level of fragmentation"), ((intval(get_config('system','optimize_fragmentation')) > 0)?get_config('system','optimize_fragmentation'):30), t("Minimum fragmenation level to start the automatic optimization - default value is 30%.")),
1116
1117                 '$poco_completion'      => array('poco_completion', t("Periodical check of global contacts"), get_config('system','poco_completion'), t("If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers.")),
1118                 '$poco_requery_days'    => array('poco_requery_days', t("Days between requery"), get_config('system','poco_requery_days'), t("Number of days after which a server is requeried for his contacts.")),
1119                 '$poco_discovery'       => array('poco_discovery', t("Discover contacts from other servers"), (string) intval(get_config('system','poco_discovery')), t("Periodically query other servers for contacts. You can choose between 'users': the users on the remote system, 'Global Contacts': active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren't available. The fallback increases the server load, so the recommened setting is 'Users, Global Contacts'."), $poco_discovery_choices),
1120                 '$poco_discovery_since' => array('poco_discovery_since', t("Timeframe for fetching global contacts"), (string) intval(get_config('system','poco_discovery_since')), t("When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers."), $poco_discovery_since_choices),
1121                 '$poco_local_search'    => array('poco_local_search', t("Search the local directory"), get_config('system','poco_local_search'), t("Search the local directory instead of the global directory. When searching locally, every search will be executed on the global directory in the background. This improves the search results when the search is repeated.")),
1122
1123                 '$nodeinfo'             => array('nodeinfo', t("Publish server information"), get_config('system','nodeinfo'), t("If enabled, general server and usage data will be published. The data contains the name and version of the server, number of users with public profiles, number of posts and the activated protocols and connectors. See <a href='http://the-federation.info/'>the-federation.info</a> for details.")),
1124
1125                 '$suppress_tags'        => array('suppress_tags', t("Suppress Tags"), get_config('system','suppress_tags'), t("Suppress showing a list of hashtags at the end of the posting.")),
1126                 '$itemcache'            => array('itemcache', t("Path to item cache"), get_config('system','itemcache'), t("The item caches buffers generated bbcode and external images.")),
1127                 '$itemcache_duration'   => array('itemcache_duration', t("Cache duration in seconds"), get_config('system','itemcache_duration'), t("How long should the cache files be hold? Default value is 86400 seconds (One day). To disable the item cache, set the value to -1.")),
1128                 '$max_comments'         => array('max_comments', t("Maximum numbers of comments per post"), get_config('system','max_comments'), t("How much comments should be shown for each post? Default value is 100.")),
1129                 '$temppath'             => array('temppath', t("Temp path"), get_config('system','temppath'), t("If you have a restricted system where the webserver can't access the system temp path, enter another path here.")),
1130                 '$basepath'             => array('basepath', t("Base path to installation"), get_config('system','basepath'), t("If the system cannot detect the correct path to your installation, enter the correct path here. This setting should only be set if you are using a restricted system and symbolic links to your webroot.")),
1131                 '$proxy_disabled'       => array('proxy_disabled', t("Disable picture proxy"), get_config('system','proxy_disabled'), t("The picture proxy increases performance and privacy. It shouldn't be used on systems with very low bandwith.")),
1132                 '$only_tag_search'      => array('only_tag_search', t("Only search in tags"), get_config('system','only_tag_search'), t("On large systems the text search can slow down the system extremely.")),
1133
1134                 '$relocate_url'         => array('relocate_url', t("New base url"), App::get_baseurl(), t("Change base url for this server. Sends relocate message to all DFRN contacts of all users.")),
1135
1136                 '$rino'                 => array('rino', t("RINO Encryption"), intval(get_config('system','rino_encrypt')), t("Encryption layer between nodes."), array("Disabled", "RINO1 (deprecated)", "RINO2")),
1137
1138                 '$worker_queues'        => array('worker_queues', t("Maximum number of parallel workers"), get_config('system','worker_queues'), t("On shared hosters set this to 2. On larger systems, values of 10 are great. Default value is 4.")),
1139                 '$worker_dont_fork'     => array('worker_dont_fork', t("Don't use 'proc_open' with the worker"), get_config('system','worker_dont_fork'), t("Enable this if your system doesn't allow the use of 'proc_open'. This can happen on shared hosters. If this is enabled you should increase the frequency of poller calls in your crontab.")),
1140                 '$worker_fastlane'      => array('worker_fastlane', t("Enable fastlane"), get_config('system','worker_fastlane'), t("When enabed, the fastlane mechanism starts an additional worker if processes with higher priority are blocked by processes of lower priority.")),
1141                 '$worker_frontend'      => array('worker_frontend', t('Enable frontend worker'), get_config('system','frontend_worker'), t('When enabled the Worker process is triggered when backend access is performed (e.g. messages being delivered). On smaller sites you might want to call yourdomain.tld/worker on a regular basis via an external cron job. You should only enable this option if you cannot utilize cron/scheduled jobs on your server. The worker background process needs to be activated for this.')),
1142
1143                 '$form_security_token'  => get_form_security_token("admin_site")
1144
1145         ));
1146
1147 }
1148
1149 /**
1150  * @brief Generates admin panel subpage for DB syncronization
1151  *
1152  * This page checks if the database of friendica is in sync with the specs.
1153  * Should this not be the case, it attemps to sync the structure and notifies
1154  * the admin if the automatic process was failing.
1155  *
1156  * The returned string holds the HTML code of the page.
1157  *
1158  * @param App $a
1159  * @return string
1160  **/
1161 function admin_page_dbsync(App $a) {
1162
1163         $o = '';
1164
1165         if ($a->argc > 3 && intval($a->argv[3]) && $a->argv[2] === 'mark') {
1166                 set_config('database', 'update_'.intval($a->argv[3]), 'success');
1167                 $curr = get_config('system','build');
1168                 if (intval($curr) == intval($a->argv[3])) {
1169                         set_config('system','build',intval($curr) + 1);
1170                 }
1171                 info(t('Update has been marked successful').EOL);
1172                 goaway('admin/dbsync');
1173         }
1174
1175         if (($a->argc > 2) AND (intval($a->argv[2]) OR ($a->argv[2] === 'check'))) {
1176                 require_once("include/dbstructure.php");
1177                 $retval = update_structure(false, true);
1178                 if (!$retval) {
1179                         $o .= sprintf(t("Database structure update %s was successfully applied."), DB_UPDATE_VERSION)."<br />";
1180                         set_config('database', 'dbupdate_'.DB_UPDATE_VERSION, 'success');
1181                 } else {
1182                         $o .= sprintf(t("Executing of database structure update %s failed with error: %s"),
1183                                         DB_UPDATE_VERSION, $retval)."<br />";
1184                 }
1185                 if ($a->argv[2] === 'check') {
1186                         return $o;
1187                 }
1188         }
1189
1190         if ($a->argc > 2 && intval($a->argv[2])) {
1191                 require_once('update.php');
1192                 $func = 'update_'.intval($a->argv[2]);
1193                 if (function_exists($func)) {
1194                         $retval = $func();
1195                         if ($retval === UPDATE_FAILED) {
1196                                 $o .= sprintf(t("Executing %s failed with error: %s"), $func, $retval);
1197                         }
1198                         elseif ($retval === UPDATE_SUCCESS) {
1199                                 $o .= sprintf(t('Update %s was successfully applied.', $func));
1200                                 set_config('database',$func, 'success');
1201                         } else {
1202                                 $o .= sprintf(t('Update %s did not return a status. Unknown if it succeeded.'), $func);
1203                         }
1204                 } else {
1205                         $o .= sprintf(t('There was no additional update function %s that needed to be called.'), $func)."<br />";
1206                         set_config('database',$func, 'success');
1207                 }
1208                 return $o;
1209         }
1210
1211         $failed = array();
1212         $r = q("SELECT `k`, `v` FROM `config` WHERE `cat` = 'database' ");
1213         if (dbm::is_result($r)) {
1214                 foreach ($r as $rr) {
1215                         $upd = intval(substr($rr['k'],7));
1216                         if ($upd < 1139 || $rr['v'] === 'success') {
1217                                 continue;
1218                         }
1219                         $failed[] = $upd;
1220                 }
1221         }
1222         if (! count($failed)) {
1223                 $o = replace_macros(get_markup_template('structure_check.tpl'),array(
1224                         '$base'   => App::get_baseurl(true),
1225                         '$banner' => t('No failed updates.'),
1226                         '$check'  => t('Check database structure'),
1227                 ));
1228         } else {
1229                 $o = replace_macros(get_markup_template('failed_updates.tpl'),array(
1230                         '$base'   => App::get_baseurl(true),
1231                         '$banner' => t('Failed Updates'),
1232                         '$desc'   => t('This does not include updates prior to 1139, which did not return a status.'),
1233                         '$mark'   => t('Mark success (if update was manually applied)'),
1234                         '$apply'  => t('Attempt to execute this update step automatically'),
1235                         '$failed' => $failed
1236                 ));
1237         }
1238
1239         return $o;
1240
1241 }
1242
1243 /**
1244  * @brief Process data send by Users admin page
1245  *
1246  * @param App $a
1247  */
1248 function admin_page_users_post(App $a) {
1249         $pending     = (x($_POST, 'pending')           ? $_POST['pending']           : array());
1250         $users       = (x($_POST, 'user')              ? $_POST['user']               : array());
1251         $nu_name     = (x($_POST, 'new_user_name')     ? $_POST['new_user_name']     : '');
1252         $nu_nickname = (x($_POST, 'new_user_nickname') ? $_POST['new_user_nickname'] : '');
1253         $nu_email    = (x($_POST, 'new_user_email')    ? $_POST['new_user_email']    : '');
1254         $nu_language = get_config('system', 'language');
1255
1256         check_form_security_token_redirectOnErr('/admin/users', 'admin_users');
1257
1258         if (!($nu_name === "") && !($nu_email === "") && !($nu_nickname === "")) {
1259                 require_once('include/user.php');
1260
1261                 $result = create_user(array('username'=>$nu_name, 'email'=>$nu_email,
1262                         'nickname'=>$nu_nickname, 'verified'=>1, 'language'=>$nu_language));
1263                 if (! $result['success']) {
1264                         notice($result['message']);
1265                         return;
1266                 }
1267                 $nu = $result['user'];
1268                 $preamble = deindent(t('
1269                         Dear %1$s,
1270                                 the administrator of %2$s has set up an account for you.'));
1271                 $body = deindent(t('
1272                         The login details are as follows:
1273
1274                         Site Location:  %1$s
1275                         Login Name:             %2$s
1276                         Password:               %3$s
1277
1278                         You may change your password from your account "Settings" page after logging
1279                         in.
1280
1281                         Please take a few moments to review the other account settings on that page.
1282
1283                         You may also wish to add some basic information to your default profile
1284                         (on the "Profiles" page) so that other people can easily find you.
1285
1286                         We recommend setting your full name, adding a profile photo,
1287                         adding some profile "keywords" (very useful in making new friends) - and
1288                         perhaps what country you live in; if you do not wish to be more specific
1289                         than that.
1290
1291                         We fully respect your right to privacy, and none of these items are necessary.
1292                         If you are new and do not know anybody here, they may help
1293                         you to make some new and interesting friends.
1294
1295                         Thank you and welcome to %4$s.'));
1296
1297                 $preamble = sprintf($preamble, $nu['username'], $a->config['sitename']);
1298                 $body = sprintf($body, App::get_baseurl(), $nu['email'], $result['password'], $a->config['sitename']);
1299
1300                 notification(array(
1301                         'type' => "SYSTEM_EMAIL",
1302                         'to_email' => $nu['email'],
1303                         'subject'=> sprintf(t('Registration details for %s'), $a->config['sitename']),
1304                         'preamble'=> $preamble,
1305                         'body' => $body));
1306
1307         }
1308
1309         if (x($_POST,'page_users_block')) {
1310                 foreach ($users as $uid) {
1311                         q("UPDATE `user` SET `blocked` = 1-`blocked` WHERE `uid` = %s",
1312                                 intval($uid)
1313                         );
1314                 }
1315                 notice(sprintf(tt("%s user blocked/unblocked", "%s users blocked/unblocked", count($users)), count($users)));
1316         }
1317         if (x($_POST,'page_users_delete')) {
1318                 require_once("include/Contact.php");
1319                 foreach ($users as $uid) {
1320                         user_remove($uid);
1321                 }
1322                 notice(sprintf(tt("%s user deleted", "%s users deleted", count($users)), count($users)));
1323         }
1324
1325         if (x($_POST,'page_users_approve')) {
1326                 require_once("mod/regmod.php");
1327                 foreach ($pending as $hash) {
1328                         user_allow($hash);
1329                 }
1330         }
1331         if (x($_POST,'page_users_deny')) {
1332                 require_once("mod/regmod.php");
1333                 foreach ($pending as $hash) {
1334                         user_deny($hash);
1335                 }
1336         }
1337         goaway('admin/users');
1338         return; // NOTREACHED
1339 }
1340
1341 /**
1342  * @brief Admin panel subpage for User management
1343  *
1344  * This function generates the admin panel page for user management of the
1345  * node. It offers functionality to add/block/delete users and offers some
1346  * statistics about the userbase.
1347  *
1348  * The returned string holds the HTML code of the page.
1349  *
1350  * @param App $a
1351  * @return string
1352  */
1353 function admin_page_users(App $a) {
1354         if ($a->argc>2) {
1355                 $uid = $a->argv[3];
1356                 $user = q("SELECT `username`, `blocked` FROM `user` WHERE `uid` = %d", intval($uid));
1357                 if (count($user) == 0) {
1358                         notice('User not found'.EOL);
1359                         goaway('admin/users');
1360                         return ''; // NOTREACHED
1361                 }
1362                 switch($a->argv[2]) {
1363                         case "delete":
1364                                 check_form_security_token_redirectOnErr('/admin/users', 'admin_users', 't');
1365                                 // delete user
1366                                 require_once("include/Contact.php");
1367                                 user_remove($uid);
1368
1369                                 notice(sprintf(t("User '%s' deleted"), $user[0]['username']).EOL);
1370                                 break;
1371                         case "block":
1372                                 check_form_security_token_redirectOnErr('/admin/users', 'admin_users', 't');
1373                                 q("UPDATE `user` SET `blocked` = %d WHERE `uid` = %s",
1374                                         intval(1-$user[0]['blocked']),
1375                                         intval($uid)
1376                                 );
1377                                 notice(sprintf(($user[0]['blocked']?t("User '%s' unblocked"):t("User '%s' blocked")) , $user[0]['username']).EOL);
1378                                 break;
1379                 }
1380                 goaway('admin/users');
1381                 return ''; // NOTREACHED
1382
1383         }
1384
1385         /* get pending */
1386         $pending = q("SELECT `register`.*, `contact`.`name`, `user`.`email`
1387                                  FROM `register`
1388                                  LEFT JOIN `contact` ON `register`.`uid` = `contact`.`uid`
1389                                  LEFT JOIN `user` ON `register`.`uid` = `user`.`uid`;");
1390
1391
1392         /* get users */
1393         $total = qu("SELECT COUNT(*) AS `total` FROM `user` WHERE 1");
1394         if (count($total)) {
1395                 $a->set_pager_total($total[0]['total']);
1396                 $a->set_pager_itemspage(100);
1397         }
1398
1399         /* ordering */
1400         $valid_orders = array(
1401                 'contact.name',
1402                 'user.email',
1403                 'user.register_date',
1404                 'user.login_date',
1405                 'lastitem_date',
1406                 'user.page-flags'
1407         );
1408
1409         $order = "contact.name";
1410         $order_direction = "+";
1411         if (x($_GET,'o')) {
1412                 $new_order = $_GET['o'];
1413                 if ($new_order[0] === "-") {
1414                         $order_direction = "-";
1415                         $new_order = substr($new_order,1);
1416                 }
1417
1418                 if (in_array($new_order, $valid_orders)) {
1419                         $order = $new_order;
1420                 }
1421                 if (x($_GET,'d')) {
1422                         $new_direction = $_GET['d'];
1423                 }
1424         }
1425         $sql_order = "`".str_replace('.','`.`',$order)."`";
1426         $sql_order_direction = ($order_direction === "+")?"ASC":"DESC";
1427
1428         $users = qu("SELECT `user`.*, `contact`.`name`, `contact`.`url`, `contact`.`micro`, `user`.`account_expired`, `contact`.`last-item` AS `lastitem_date`
1429                                 FROM `user`
1430                                 INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`
1431                                 WHERE `user`.`verified`
1432                                 ORDER BY $sql_order $sql_order_direction LIMIT %d, %d",
1433                                 intval($a->pager['start']),
1434                                 intval($a->pager['itemspage'])
1435                                 );
1436
1437         //echo "<pre>$users"; killme();
1438
1439         $adminlist = explode(",", str_replace(" ", "", $a->config['admin_email']));
1440         $_setup_users = function ($e) use ($adminlist) {
1441                 $accounts = array(
1442                         t('Normal Account'),
1443                         t('Soapbox Account'),
1444                         t('Community/Celebrity Account'),
1445                                                 t('Automatic Friend Account')
1446                 );
1447                 $e['page-flags'] = $accounts[$e['page-flags']];
1448                 $e['register_date'] = relative_date($e['register_date']);
1449                 $e['login_date'] = relative_date($e['login_date']);
1450                 $e['lastitem_date'] = relative_date($e['lastitem_date']);
1451                 //$e['is_admin'] = ($e['email'] === $a->config['admin_email']);
1452                 $e['is_admin'] = in_array($e['email'], $adminlist);
1453                 $e['is_deletable'] = (intval($e['uid']) != local_user());
1454                 $e['deleted'] = ($e['account_removed']?relative_date($e['account_expires_on']):False);
1455                 return $e;
1456         };
1457         $users = array_map($_setup_users, $users);
1458
1459
1460         // Get rid of dashes in key names, Smarty3 can't handle them
1461         // and extracting deleted users
1462
1463         $tmp_users = array();
1464         $deleted = array();
1465
1466         while (count($users)) {
1467                 $new_user = array();
1468                 foreach (array_pop($users) as $k => $v) {
1469                         $k = str_replace('-','_',$k);
1470                         $new_user[$k] = $v;
1471                 }
1472                 if ($new_user['deleted']) {
1473                         array_push($deleted, $new_user);
1474                 } else {
1475                         array_push($tmp_users, $new_user);
1476                 }
1477         }
1478         //Reversing the two array, and moving $tmp_users to $users
1479         array_reverse($deleted);
1480         while (count($tmp_users)) {
1481                 array_push($users, array_pop($tmp_users));
1482         }
1483
1484         $th_users = array_map(null,
1485                 array(t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'),  t('Account')),
1486                 $valid_orders
1487         );
1488
1489         $t = get_markup_template("admin_users.tpl");
1490         $o = replace_macros($t, array(
1491                 // strings //
1492                 '$title' => t('Administration'),
1493                 '$page' => t('Users'),
1494                 '$submit' => t('Add User'),
1495                 '$select_all' => t('select all'),
1496                 '$h_pending' => t('User registrations waiting for confirm'),
1497                 '$h_deleted' => t('User waiting for permanent deletion'),
1498                 '$th_pending' => array(t('Request date'), t('Name'), t('Email')),
1499                 '$no_pending' =>  t('No registrations.'),
1500                 '$pendingnotetext' => t('Note from the user'),
1501                 '$approve' => t('Approve'),
1502                 '$deny' => t('Deny'),
1503                 '$delete' => t('Delete'),
1504                 '$block' => t('Block'),
1505                 '$unblock' => t('Unblock'),
1506                 '$siteadmin' => t('Site admin'),
1507                 '$accountexpired' => t('Account expired'),
1508
1509                 '$h_users' => t('Users'),
1510                 '$h_newuser' => t('New User'),
1511                 '$th_deleted' => array(t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Deleted since')),
1512                 '$th_users' => $th_users,
1513                 '$order_users' => $order,
1514                 '$order_direction_users' => $order_direction,
1515
1516                 '$confirm_delete_multi' => t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'),
1517                 '$confirm_delete' => t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'),
1518
1519                 '$form_security_token' => get_form_security_token("admin_users"),
1520
1521                 // values //
1522                 '$baseurl' => App::get_baseurl(true),
1523
1524                 '$pending' => $pending,
1525                 'deleted' => $deleted,
1526                 '$users' => $users,
1527                 '$newusername' => array('new_user_name', t("Name"), '', t("Name of the new user.")),
1528                 '$newusernickname' => array('new_user_nickname', t("Nickname"), '', t("Nickname of the new user.")),
1529                 '$newuseremail' => array('new_user_email', t("Email"), '', t("Email address of the new user."), '', '', 'email'),
1530         ));
1531         $o .= paginate($a);
1532         return $o;
1533 }
1534
1535
1536 /**
1537  * @brief Plugins admin page
1538  *
1539  * This function generates the admin panel page for managing plugins on the
1540  * friendica node. If a plugin name is given a single page showing the details
1541  * for this addon is generated. If no name is given, a list of available
1542  * plugins is shown.
1543  *
1544  * The template used for displaying the list of plugins and the details of the
1545  * plugin are the same as used for the templates.
1546  *
1547  * The returned string returned hulds the HTML code of the page.
1548  *
1549  * @param App $a
1550  * @return string
1551  */
1552 function admin_page_plugins(App $a) {
1553
1554         /*
1555          * Single plugin
1556          */
1557         if ($a->argc == 3) {
1558                 $plugin = $a->argv[2];
1559                 if (!is_file("addon/$plugin/$plugin.php")) {
1560                         notice(t("Item not found."));
1561                         return '';
1562                 }
1563
1564                 if (x($_GET,"a") && $_GET['a']=="t") {
1565                         check_form_security_token_redirectOnErr('/admin/plugins', 'admin_themes', 't');
1566
1567                         // Toggle plugin status
1568                         $idx = array_search($plugin, $a->plugins);
1569                         if ($idx !== false) {
1570                                 unset($a->plugins[$idx]);
1571                                 uninstall_plugin($plugin);
1572                                 info(sprintf(t("Plugin %s disabled."), $plugin));
1573                         } else {
1574                                 $a->plugins[] = $plugin;
1575                                 install_plugin($plugin);
1576                                 info(sprintf(t("Plugin %s enabled."), $plugin));
1577                         }
1578                         set_config("system","addon", implode(", ",$a->plugins));
1579                         goaway('admin/plugins');
1580                         return ''; // NOTREACHED
1581                 }
1582
1583                 // display plugin details
1584                 require_once('library/markdown.php');
1585
1586                 if (in_array($plugin, $a->plugins)) {
1587                         $status="on"; $action= t("Disable");
1588                 } else {
1589                         $status="off"; $action= t("Enable");
1590                 }
1591
1592                 $readme=Null;
1593                 if (is_file("addon/$plugin/README.md")) {
1594                         $readme = file_get_contents("addon/$plugin/README.md");
1595                         $readme = Markdown($readme);
1596                 } elseif (is_file("addon/$plugin/README")) {
1597                         $readme = "<pre>". file_get_contents("addon/$plugin/README") ."</pre>";
1598                 }
1599
1600                 $admin_form="";
1601                 if (is_array($a->plugins_admin) && in_array($plugin, $a->plugins_admin)) {
1602                         @require_once("addon/$plugin/$plugin.php");
1603                         $func = $plugin.'_plugin_admin';
1604                         $func($a, $admin_form);
1605                 }
1606
1607                 $t = get_markup_template("admin_plugins_details.tpl");
1608
1609                 return replace_macros($t, array(
1610                         '$title' => t('Administration'),
1611                         '$page' => t('Plugins'),
1612                         '$toggle' => t('Toggle'),
1613                         '$settings' => t('Settings'),
1614                         '$baseurl' => App::get_baseurl(true),
1615
1616                         '$plugin' => $plugin,
1617                         '$status' => $status,
1618                         '$action' => $action,
1619                         '$info' => get_plugin_info($plugin),
1620                         '$str_author' => t('Author: '),
1621                         '$str_maintainer' => t('Maintainer: '),
1622
1623                         '$admin_form' => $admin_form,
1624                         '$function' => 'plugins',
1625                         '$screenshot' => '',
1626                         '$readme' => $readme,
1627
1628                         '$form_security_token' => get_form_security_token("admin_themes"),
1629                 ));
1630         }
1631
1632
1633
1634         /*
1635          * List plugins
1636          */
1637
1638         if (x($_GET,"a") && $_GET['a']=="r") {
1639                 check_form_security_token_redirectOnErr(App::get_baseurl().'/admin/plugins', 'admin_themes', 't');
1640                 reload_plugins();
1641                 info("Plugins reloaded");
1642                 goaway(App::get_baseurl().'/admin/plugins');
1643         }
1644
1645         $plugins = array();
1646         $files = glob("addon/*/");
1647         if ($files) {
1648                 foreach ($files as $file) {
1649                         if (is_dir($file)) {
1650                                 list($tmp, $id)=array_map("trim", explode("/",$file));
1651                                 $info = get_plugin_info($id);
1652                                 $show_plugin = true;
1653
1654                                 // If the addon is unsupported, then only show it, when it is enabled
1655                                 if ((strtolower($info["status"]) == "unsupported") AND !in_array($id,  $a->plugins)) {
1656                                         $show_plugin = false;
1657                                 }
1658
1659                                 // Override the above szenario, when the admin really wants to see outdated stuff
1660                                 if (get_config("system", "show_unsupported_addons")) {
1661                                         $show_plugin = true;
1662                                 }
1663
1664                                 if ($show_plugin) {
1665                                         $plugins[] = array($id, (in_array($id,  $a->plugins)?"on":"off") , $info);
1666                                 }
1667                         }
1668                 }
1669         }
1670
1671         $t = get_markup_template("admin_plugins.tpl");
1672         return replace_macros($t, array(
1673                 '$title' => t('Administration'),
1674                 '$page' => t('Plugins'),
1675                 '$submit' => t('Save Settings'),
1676                 '$reload' => t('Reload active plugins'),
1677                 '$baseurl' => App::get_baseurl(true),
1678                 '$function' => 'plugins',
1679                 '$plugins' => $plugins,
1680                 '$pcount' => count($plugins),
1681                 '$noplugshint' => sprintf(t('There are currently no plugins available on your node. You can find the official plugin repository at %1$s and might find other interesting plugins in the open plugin registry at %2$s'), 'https://github.com/friendica/friendica-addons', 'http://addons.friendi.ca'),
1682                 '$form_security_token' => get_form_security_token("admin_themes"),
1683         ));
1684 }
1685
1686 /**
1687  * @param array $themes
1688  * @param string $th
1689  * @param int $result
1690  */
1691 function toggle_theme(&$themes,$th,&$result) {
1692         for($x = 0; $x < count($themes); $x ++) {
1693                 if ($themes[$x]['name'] === $th) {
1694                         if ($themes[$x]['allowed']) {
1695                                 $themes[$x]['allowed'] = 0;
1696                                 $result = 0;
1697                         }
1698                         else {
1699                                 $themes[$x]['allowed'] = 1;
1700                                 $result = 1;
1701                         }
1702                 }
1703         }
1704 }
1705
1706 /**
1707  * @param array $themes
1708  * @param string $th
1709  * @return int
1710  */
1711 function theme_status($themes,$th) {
1712         for($x = 0; $x < count($themes); $x ++) {
1713                 if ($themes[$x]['name'] === $th) {
1714                         if ($themes[$x]['allowed']) {
1715                                 return 1;
1716                         }
1717                         else {
1718                                 return 0;
1719                         }
1720                 }
1721         }
1722         return 0;
1723 }
1724
1725
1726 /**
1727  * @param array $themes
1728  * @return string
1729  */
1730 function rebuild_theme_table($themes) {
1731         $o = '';
1732         if (count($themes)) {
1733                 foreach ($themes as $th) {
1734                         if ($th['allowed']) {
1735                                 if (strlen($o)) {
1736                                         $o .= ',';
1737                                 }
1738                                 $o .= $th['name'];
1739                         }
1740                 }
1741         }
1742         return $o;
1743 }
1744
1745
1746 /**
1747  * @brief Themes admin page
1748  *
1749  * This function generates the admin panel page to control the themes available
1750  * on the friendica node. If the name of a theme is given as parameter a page
1751  * with the details for the theme is shown. Otherwise a list of available
1752  * themes is generated.
1753  *
1754  * The template used for displaying the list of themes and the details of the
1755  * themes are the same as used for the plugins.
1756  *
1757  * The returned string contains the HTML code of the admin panel page.
1758  *
1759  * @param App $a
1760  * @return string
1761  */
1762 function admin_page_themes(App $a) {
1763
1764         $allowed_themes_str = get_config('system','allowed_themes');
1765         $allowed_themes_raw = explode(',',$allowed_themes_str);
1766         $allowed_themes = array();
1767         if (count($allowed_themes_raw)) {
1768                 foreach ($allowed_themes_raw as $x) {
1769                         if (strlen(trim($x))) {
1770                                 $allowed_themes[] = trim($x);
1771                         }
1772                 }
1773         }
1774
1775         $themes = array();
1776         $files = glob('view/theme/*');
1777         if ($files) {
1778                 foreach ($files as $file) {
1779                         $f = basename($file);
1780
1781                         // Is there a style file?
1782                         $theme_files = glob('view/theme/'.$f.'/style.*');
1783
1784                         // If not then quit
1785                         if (count($theme_files) == 0) {
1786                                 continue;
1787                         }
1788
1789                         $is_experimental = intval(file_exists($file.'/experimental'));
1790                         $is_supported = 1-(intval(file_exists($file.'/unsupported')));
1791                         $is_allowed = intval(in_array($f,$allowed_themes));
1792
1793                         if ($is_allowed OR $is_supported OR get_config("system", "show_unsupported_themes")) {
1794                                 $themes[] = array('name' => $f, 'experimental' => $is_experimental, 'supported' => $is_supported, 'allowed' => $is_allowed);
1795                         }
1796                 }
1797         }
1798
1799         if (! count($themes)) {
1800                 notice(t('No themes found.'));
1801                 return '';
1802         }
1803
1804         /*
1805          * Single theme
1806          */
1807
1808         if ($a->argc == 3) {
1809                 $theme = $a->argv[2];
1810                 if (! is_dir("view/theme/$theme")) {
1811                         notice(t("Item not found."));
1812                         return '';
1813                 }
1814
1815                 if (x($_GET,"a") && $_GET['a']=="t") {
1816                         check_form_security_token_redirectOnErr('/admin/themes', 'admin_themes', 't');
1817
1818                         // Toggle theme status
1819
1820                         toggle_theme($themes,$theme,$result);
1821                         $s = rebuild_theme_table($themes);
1822                         if ($result) {
1823                                 install_theme($theme);
1824                                 info(sprintf('Theme %s enabled.',$theme));
1825                         } else {
1826                                 uninstall_theme($theme);
1827                                 info(sprintf('Theme %s disabled.',$theme));
1828                         }
1829
1830                         set_config('system','allowed_themes',$s);
1831                         goaway('admin/themes');
1832                         return ''; // NOTREACHED
1833                 }
1834
1835                 // display theme details
1836                 require_once('library/markdown.php');
1837
1838                 if (theme_status($themes,$theme)) {
1839                         $status="on"; $action= t("Disable");
1840                 } else {
1841                         $status="off"; $action= t("Enable");
1842                 }
1843
1844                 $readme = Null;
1845                 if (is_file("view/theme/$theme/README.md")) {
1846                         $readme = file_get_contents("view/theme/$theme/README.md");
1847                         $readme = Markdown($readme);
1848                 } elseif (is_file("view/theme/$theme/README")) {
1849                         $readme = "<pre>". file_get_contents("view/theme/$theme/README") ."</pre>";
1850                 }
1851
1852                 $admin_form = "";
1853                 if (is_file("view/theme/$theme/config.php")) {
1854                         function __get_theme_admin_form(App $a, $theme) {
1855                                 $orig_theme = $a->theme;
1856                                 $orig_page = $a->page;
1857                                 $orig_session_theme = $_SESSION['theme'];
1858                                 require_once("view/theme/$theme/theme.php");
1859                                 require_once("view/theme/$theme/config.php");
1860                                 $_SESSION['theme'] = $theme;
1861
1862
1863                                 $init = $theme."_init";
1864                                 if (function_exists($init)) {
1865                                         $init($a);
1866                                 }
1867                                 if (function_exists("theme_admin")) {
1868                                         $admin_form = theme_admin($a);
1869                                 }
1870
1871                                 $_SESSION['theme'] = $orig_session_theme;
1872                                 $a->theme = $orig_theme;
1873                                 $a->page = $orig_page;
1874                                 return $admin_form;
1875                         }
1876                         $admin_form = __get_theme_admin_form($a, $theme);
1877                 }
1878
1879                 $screenshot = array(get_theme_screenshot($theme), t('Screenshot'));
1880                 if (! stristr($screenshot[0],$theme)) {
1881                         $screenshot = null;
1882                 }
1883
1884                 $t = get_markup_template("admin_plugins_details.tpl");
1885                 return replace_macros($t, array(
1886                         '$title' => t('Administration'),
1887                         '$page' => t('Themes'),
1888                         '$toggle' => t('Toggle'),
1889                         '$settings' => t('Settings'),
1890                         '$baseurl' => App::get_baseurl(true),
1891                         '$plugin' => $theme,
1892                         '$status' => $status,
1893                         '$action' => $action,
1894                         '$info' => get_theme_info($theme),
1895                         '$function' => 'themes',
1896                         '$admin_form' => $admin_form,
1897                         '$str_author' => t('Author: '),
1898                         '$str_maintainer' => t('Maintainer: '),
1899                         '$screenshot' => $screenshot,
1900                         '$readme' => $readme,
1901
1902                         '$form_security_token' => get_form_security_token("admin_themes"),
1903                 ));
1904         }
1905
1906
1907         // reload active themes
1908         if (x($_GET,"a") && $_GET['a']=="r") {
1909                 check_form_security_token_redirectOnErr(App::get_baseurl().'/admin/themes', 'admin_themes', 't');
1910                 if ($themes) {
1911                         foreach ($themes as $th) {
1912                                 if ($th['allowed']) {
1913                                         uninstall_theme($th['name']);
1914                                         install_theme($th['name']);
1915                                 }
1916                         }
1917                 }
1918                 info("Themes reloaded");
1919                 goaway(App::get_baseurl().'/admin/themes');
1920         }
1921
1922         /*
1923          * List themes
1924          */
1925
1926         $xthemes = array();
1927         if ($themes) {
1928                 foreach ($themes as $th) {
1929                         $xthemes[] = array($th['name'],(($th['allowed']) ? "on" : "off"), get_theme_info($th['name']));
1930                 }
1931         }
1932
1933
1934         $t = get_markup_template("admin_plugins.tpl");
1935         return replace_macros($t, array(
1936                 '$title'               => t('Administration'),
1937                 '$page'                => t('Themes'),
1938                 '$submit'              => t('Save Settings'),
1939                 '$reload'              => t('Reload active themes'),
1940                 '$baseurl'             => App::get_baseurl(true),
1941                 '$function'            => 'themes',
1942                 '$plugins'             => $xthemes,
1943                 '$pcount'              => count($themes),
1944                 '$noplugshint'         => sprintf(t('No themes found on the system. They should be paced in %1$s'),'<code>/view/themes</code>'),
1945                 '$experimental'        => t('[Experimental]'),
1946                 '$unsupported'         => t('[Unsupported]'),
1947                 '$form_security_token' => get_form_security_token("admin_themes"),
1948         ));
1949 }
1950
1951
1952 /**
1953  * @brief Prosesses data send by Logs admin page
1954  *
1955  * @param App $a
1956  */
1957 function admin_page_logs_post(App $a) {
1958         if (x($_POST,"page_logs")) {
1959                 check_form_security_token_redirectOnErr('/admin/logs', 'admin_logs');
1960
1961                 $logfile   = ((x($_POST,'logfile'))   ? notags(trim($_POST['logfile']))  : '');
1962                 $debugging = ((x($_POST,'debugging')) ? true                             : false);
1963                 $loglevel  = ((x($_POST,'loglevel'))  ? intval(trim($_POST['loglevel'])) : 0);
1964
1965                 set_config('system','logfile', $logfile);
1966                 set_config('system','debugging',  $debugging);
1967                 set_config('system','loglevel', $loglevel);
1968         }
1969
1970         info(t("Log settings updated."));
1971         goaway('admin/logs');
1972         return; // NOTREACHED
1973 }
1974
1975 /**
1976  * @brief Generates admin panel subpage for configuration of the logs
1977  *
1978  * This function take the view/templates/admin_logs.tpl file and generates a
1979  * page where admin can configure the logging of friendica.
1980  *
1981  * Displaying the log is separated from the log config as the logfile can get
1982  * big depending on the settings and changing settings regarding the logs can
1983  * thus waste bandwidth.
1984  *
1985  * The string returned contains the content of the template file with replaced
1986  * macros.
1987  *
1988  * @param App $a
1989  * @return string
1990  */
1991 function admin_page_logs(App $a) {
1992
1993         $log_choices = array(
1994                 LOGGER_NORMAL   => 'Normal',
1995                 LOGGER_TRACE    => 'Trace',
1996                 LOGGER_DEBUG    => 'Debug',
1997                 LOGGER_DATA     => 'Data',
1998                 LOGGER_ALL      => 'All'
1999         );
2000
2001         if (ini_get('log_errors')) {
2002                 $phplogenabled = t('PHP log currently enabled.');
2003         } else {
2004                 $phplogenabled = t('PHP log currently disabled.');
2005         }
2006
2007         $t = get_markup_template("admin_logs.tpl");
2008
2009         return replace_macros($t, array(
2010                 '$title' => t('Administration'),
2011                 '$page' => t('Logs'),
2012                 '$submit' => t('Save Settings'),
2013                 '$clear' => t('Clear'),
2014                 '$baseurl' => App::get_baseurl(true),
2015                 '$logname' =>  get_config('system','logfile'),
2016
2017                 // name, label, value, help string, extra data...
2018                 '$debugging' => array('debugging', t("Enable Debugging"),get_config('system','debugging'), ""),
2019                 '$logfile' => array('logfile', t("Log file"), get_config('system','logfile'), t("Must be writable by web server. Relative to your Friendica top-level directory.")),
2020                 '$loglevel' => array('loglevel', t("Log level"), get_config('system','loglevel'), "", $log_choices),
2021
2022                 '$form_security_token' => get_form_security_token("admin_logs"),
2023                 '$phpheader' => t("PHP logging"),
2024                 '$phphint' => t("To enable logging of PHP errors and warnings you can add the following to the .htconfig.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."),
2025                 '$phplogcode' => "error_reporting(E_ERROR | E_WARNING | E_PARSE);\nini_set('error_log','php.out');\nini_set('log_errors','1');\nini_set('display_errors', '1');",
2026                 '$phplogenabled' => $phplogenabled,
2027         ));
2028 }
2029
2030 /**
2031  * @brief Generates admin panel subpage to view the Friendica log
2032  *
2033  * This function loads the template view/templates/admin_viewlogs.tpl to
2034  * display the systemlog content. The filename for the systemlog of friendica
2035  * is relative to the base directory and taken from the config entry 'logfile'
2036  * in the 'system' category.
2037  *
2038  * Displaying the log is separated from the log config as the logfile can get
2039  * big depending on the settings and changing settings regarding the logs can
2040  * thus waste bandwidth.
2041  *
2042  * The string returned contains the content of the template file with replaced
2043  * macros.
2044  *
2045  * @param App $a
2046  * @return string
2047  */
2048 function admin_page_viewlogs(App $a) {
2049         $t = get_markup_template("admin_viewlogs.tpl");
2050         $f = get_config('system','logfile');
2051         $data = '';
2052
2053         if (!file_exists($f)) {
2054                 $data = t("Error trying to open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f exist and is readable.");
2055         } else {
2056                 $fp = fopen($f, 'r');
2057                 if (!$fp) {
2058                         $data = t("Couldn't open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f is readable.");
2059                 } else {
2060                         $fstat = fstat($fp);
2061                         $size = $fstat['size'];
2062                         if ($size != 0) {
2063                                 if ($size > 5000000 || $size < 0) {
2064                                         $size = 5000000;
2065                                 }
2066                                 $seek = fseek($fp,0-$size,SEEK_END);
2067                                 if ($seek === 0) {
2068                                         $data = escape_tags(fread($fp,$size));
2069                                         while (! feof($fp)) {
2070                                                 $data .= escape_tags(fread($fp,4096));
2071                                         }
2072                                 }
2073                         }
2074                         fclose($fp);
2075                 }
2076         }
2077         return replace_macros($t, array(
2078                 '$title' => t('Administration'),
2079                 '$page' => t('View Logs'),
2080                 '$data' => $data,
2081                 '$logname' =>  get_config('system','logfile')
2082         ));
2083 }
2084
2085 /**
2086  * @brief Prosesses data send by the features admin page
2087  *
2088  * @param App $a
2089  */
2090 function admin_page_features_post(App $a) {
2091
2092         check_form_security_token_redirectOnErr('/admin/features', 'admin_manage_features');
2093
2094         logger('postvars: '.print_r($_POST,true),LOGGER_DATA);
2095
2096         $arr = array();
2097         $features = get_features(false);
2098
2099         foreach ($features as $fname => $fdata) {
2100                 foreach (array_slice($fdata, 1) as $f) {
2101                         $feature = $f[0];
2102                         $feature_state = 'feature_' . $feature;
2103                         $featurelock = 'featurelock_' . $feature;
2104
2105                         if (x($_POST, $feature_state)) {
2106                                 $val = intval($_POST[$feature_state]);
2107                         } else {
2108                                 $val = 0;
2109                         }
2110                         set_config('feature',$feature,$val);
2111
2112                         if (x($_POST, $featurelock)) {
2113                                 set_config('feature_lock', $feature, $val);
2114                         } else {
2115                                 del_config('feature_lock', $feature);
2116                         }
2117                 }
2118         }
2119
2120         goaway('admin/features');
2121         return; // NOTREACHED
2122 }
2123
2124 /**
2125  * @brief Subpage for global additional feature management
2126  *
2127  * This functin generates the subpage 'Manage Additional Features'
2128  * for the admin panel. At this page the admin can set preferences
2129  * for the user settings of the 'additional features'. If needed this
2130  * preferences can be locked through the admin.
2131  *
2132  * The returned string contains the HTML code of the subpage 'Manage
2133  * Additional Features'
2134  *
2135  * @param App $a
2136  * @return string
2137  */
2138 function admin_page_features(App $a) {
2139
2140         if ((argc() > 1) && (argv(1) === 'features')) {
2141                 $arr = array();
2142                 $features = get_features(false);
2143
2144                 foreach ($features as $fname => $fdata) {
2145                         $arr[$fname] = array();
2146                         $arr[$fname][0] = $fdata[0];
2147                         foreach (array_slice($fdata,1) as $f) {
2148
2149                                 $set = get_config('feature',$f[0]);
2150                                 if ($set === false) {
2151                                         $set = $f[3];
2152                                 }
2153                                 $arr[$fname][1][] = array(
2154                                         array('feature_' .$f[0],$f[1],$set,$f[2],array(t('Off'), t('On'))),
2155                                         array('featurelock_' .$f[0],sprintf(t('Lock feature %s'),$f[1]),(($f[4] !== false) ? "1" : ''),'',array(t('Off'), t('On')))
2156                                 );
2157                         }
2158                 }
2159
2160                 $tpl = get_markup_template("admin_settings_features.tpl");
2161                 $o .= replace_macros($tpl, array(
2162                         '$form_security_token' => get_form_security_token("admin_manage_features"),
2163                         '$title' => t('Manage Additional Features'),
2164                         '$features' => $arr,
2165                         '$submit' => t('Save Settings'),
2166                 ));
2167
2168                 return $o;
2169         }
2170 }