]> git.mxchange.org Git - friendica.git/blob - mod/admin.php
035a937e0cafdccad10f1b1b1d9c89dff4ab1894
[friendica.git] / mod / admin.php
1 <?php
2
3  /**
4  * @file mod/admin.php
5  * 
6  * @brief Friendica admin
7  */
8
9
10 require_once("include/enotify.php");
11 require_once("include/text.php");
12
13 /**
14  * @brief Process send data from the admin panels subpages
15  *
16  * This function acts as relais for processing the data send from the subpages
17  * of the admin panel. Depending on the 1st parameter of the url (argv[1])
18  * specialized functions are called to process the data from the subpages.
19  *
20  * The function itself does not return anything, but the subsequencely function
21  * return the HTML for the pages of the admin panel.
22  *
23  * @param App $a
24  *
25  */
26 function admin_post(&$a){
27
28
29         if(!is_site_admin()) {
30                 return;
31         }
32
33         // do not allow a page manager to access the admin panel at all.
34
35         if(x($_SESSION,'submanage') && intval($_SESSION['submanage']))
36                 return;
37
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($a->get_baseurl(true) . '/admin/plugins/' . $a->argv[2] );
59                                 return; // NOTREACHED
60                                 break;
61                         case 'themes':
62                                 if($a->argc < 2) {
63                                         if(is_ajax()) return;
64                                         goaway($a->get_baseurl(true) . '/admin/' );
65                                         return;
66                                 }
67
68                                 $theme = $a->argv[2];
69                                 if(is_file("view/theme/$theme/config.php")){
70                                         function __call_theme_admin_post(&$a, $theme) {
71                                                 $orig_theme = $a->theme;
72                                                 $orig_page = $a->page;
73                                                 $orig_session_theme = $_SESSION['theme'];
74                                                 require_once("view/theme/$theme/theme.php");
75                                                 require_once("view/theme/$theme/config.php");
76                                                 $_SESSION['theme'] = $theme;
77
78
79                                                 $init = $theme."_init";
80                                                 if(function_exists($init)) $init($a);
81                                                 if(function_exists("theme_admin_post")) {
82                                                         $admin_form = theme_admin_post($a);
83                                                 }
84
85                                                 $_SESSION['theme'] = $orig_session_theme;
86                                                 $a->theme = $orig_theme;
87                                                 $a->page = $orig_page;
88                                                 return $admin_form;
89                                         }
90                                         __call_theme_admin_post($a, $theme);
91                                 }
92                                 info(t('Theme settings updated.'));
93                                 if(is_ajax()) return;
94
95                                 goaway($a->get_baseurl(true) . '/admin/themes/' . $theme );
96                                 return;
97                                 break;
98                         case 'features':
99                                 admin_page_features_post($a);
100                                 break;
101                         case 'logs':
102                                 admin_page_logs_post($a);
103                                 break;
104                         case 'dbsync':
105                                 admin_page_dbsync_post($a);
106                                 break;
107                 }
108         }
109
110         goaway($a->get_baseurl(true) . '/admin' );
111         return; // NOTREACHED
112 }
113
114 /**
115  * @brief Generates content of the admin panel pages
116  *
117  * This function generates the content for the admin panel.
118  *
119  * @param App $a
120  * @return string
121  */
122 function admin_content(&$a) {
123
124         if(!is_site_admin()) {
125                 return login(false);
126         }
127
128         if(x($_SESSION,'submanage') && intval($_SESSION['submanage']))
129                 return "";
130
131         // APC deactivated, since there are problems with PHP 5.5
132         //if (function_exists("apc_delete")) {
133         //      $toDelete = new APCIterator('user', APC_ITER_VALUE);
134         //      apc_delete($toDelete);
135         //}
136
137         // Header stuff
138         $a->page['htmlhead'] .= replace_macros(get_markup_template('admin_settings_head.tpl'), array());
139
140         /*
141          * Side bar links
142          */
143         $aside_tools = array();
144         // array( url, name, extra css classes )
145         // not part of $aside to make the template more adjustable
146         $aside_sub = array(
147                 'site'   =>     array($a->get_baseurl(true)."/admin/site/", t("Site") , "site"),
148                 'users'  =>     array($a->get_baseurl(true)."/admin/users/", t("Users") , "users"),
149                 'plugins'=>     array($a->get_baseurl(true)."/admin/plugins/", t("Plugins") , "plugins"),
150                 'themes' =>     array($a->get_baseurl(true)."/admin/themes/", t("Themes") , "themes"),
151                 'features' =>   array($a->get_baseurl(true)."/admin/features/", t("Additional features") , "features"),
152                 'dbsync' =>     array($a->get_baseurl(true)."/admin/dbsync/", t('DB updates'), "dbsync"),
153                 'queue'  =>     array($a->get_baseurl(true)."/admin/queue/", t('Inspect Queue'), "queue"),
154                 'federation' => array($a->get_baseurl(true)."/admin/federation/", t('Federation Statistics'), "federation"),
155         );
156
157         /* get plugins admin page */
158
159         $r = q("SELECT `name` FROM `addon` WHERE `plugin_admin`=1 ORDER BY `name`");
160         $aside_tools['plugins_admin']=array();
161         foreach ($r as $h){
162                 $plugin =$h['name'];
163                 $aside['plugins_admin'][] = array($a->get_baseurl(true)."/admin/plugins/".$plugin, $plugin, "plugin");
164                 // temp plugins with admin
165                 $a->plugins_admin[] = $plugin;
166         }
167
168         $aside_tools['logs'] = array($a->get_baseurl(true)."/admin/logs/", t("Logs"), "logs");
169         $aside_tools['viewlogs'] = array($a->get_baseurl(true)."/admin/viewlogs/", t("View Logs"), 'viewlogs');
170         $aside_tools['diagnostics_probe'] = array($a->get_baseurl(true).'/probe/', t('probe address'), 'probe');
171         $aside_tools['diagnostics_webfinger'] = array($a->get_baseurl(true).'/webfinger/', t('check webfinger'), 'webfinger');
172
173         $t = get_markup_template("admin_aside.tpl");
174         $a->page['aside'] .= replace_macros( $t, array(
175                 '$admin' => $aside_tools,
176                 '$subpages' => $aside_sub,
177                 '$admtxt' => t('Admin'),
178                 '$plugadmtxt' => t('Plugin Features'),
179                 '$logtxt' => t('Logs'),
180                 '$diagnosticstxt' => t('diagnostics'),
181                 '$h_pending' => t('User registrations waiting for confirmation'),
182                 '$admurl'=> $a->get_baseurl(true)."/admin/"
183         ));
184
185
186
187         /*
188          * Page content
189          */
190         $o = '';
191         // urls
192         if($a->argc > 1) {
193                 switch ($a->argv[1]){
194                         case 'site':
195                                 $o = admin_page_site($a);
196                                 break;
197                         case 'users':
198                                 $o = admin_page_users($a);
199                                 break;
200                         case 'plugins':
201                                 $o = admin_page_plugins($a);
202                                 break;
203                         case 'themes':
204                                 $o = admin_page_themes($a);
205                                 break;
206                         case 'features':
207                                 $o = admin_page_features($a);
208                                 break;
209                         case 'logs':
210                                 $o = admin_page_logs($a);
211                                 break;
212                         case 'viewlogs':
213                                 $o = admin_page_viewlogs($a);
214                                 break;
215                         case 'viewlogs':
216                                 $o = admin_page_viewlogs($a);
217                                 break;
218                         case 'dbsync':
219                                 $o = admin_page_dbsync($a);
220                                 break;
221                         case 'queue':
222                                 $o = admin_page_queue($a);
223                                 break;
224                         case 'federation':
225                                 $o = admin_page_federation($a);
226                                 break;
227                         case 'federation':
228                                 $o = admin_page_federation($a);
229                                 break;
230                         default:
231                                 notice( t("Item not found.") );
232                 }
233         } else {
234                 $o = admin_page_summary($a);
235         }
236
237         if(is_ajax()) {
238                 echo $o;
239                 killme();
240                 return '';
241         } else {
242                 return $o;
243         }
244 }
245
246 /**
247  * @brief Subpage with some stats about "the federation" network
248  *
249  * This function generates the "Federation Statistics" subpage for the admin
250  * panel. The page lists some numbers to the part of "The Federation" known to
251  * the node. This data includes the different connected networks (e.g.
252  * Diaspora, Hubzilla, GNU Social) and the used versions in the different
253  * networks.
254  *
255  * The returned string contains the HTML code of the subpage for display.
256  *
257  * @param App $a
258  * @return string
259  */
260 function admin_page_federation(&$a) {
261         // get counts on active friendica, diaspora, redmatrix, hubzilla, gnu
262         // social and statusnet nodes this node is knowing
263         //
264         // We are looking for the following platforms in the DB, "Red" should find
265         // all variants of that platform ID string as the q() function is stripping
266         // off one % two of them are needed in the query
267         // Add more platforms if you like, when one returns 0 known nodes it is not
268         // displayed on the stats page.
269         $platforms = array('Friendica', 'Diaspora', '%%red%%', 'Hubzilla', 'GNU Social', 'StatusNet');
270         $counts = array();
271
272         foreach ($platforms as $p) {
273                 // get a total count for the platform, the name and version of the
274                 // highest version and the protocol tpe
275                 $c = q('SELECT count(*) AS total, platform, network, version FROM gserver
276                         WHERE platform LIKE "%s" AND last_contact > last_failure
277                         ORDER BY version ASC;', $p);
278
279                 // what versions for that platform do we know at all?
280                 // again only the active nodes
281                 $v = q('SELECT count(*) AS total, version FROM gserver
282                         WHERE last_contact > last_failure AND platform LIKE "%s" 
283                         GROUP BY version
284                         ORDER BY version;', $p);
285
286                 //
287                 // clean up version numbers
288                 //
289                 // in the DB the Diaspora versions have the format x.x.x.x-xx the last
290                 // part (-xx) should be removed to clean up the versions from the "head
291                 // commit" information and combined into a single entry for x.x.x.x
292                 if($p=='Diaspora') {
293                         $newV = array();
294                         $newVv = array();
295                         foreach($v as $vv) {
296                                 $newVC = $vv['total'];
297                                 $newVV = $vv['version'];
298                                 $posDash = strpos($newVV, '-');
299                                 if($posDash) 
300                                         $newVV = substr($newVV, 0, $posDash);
301                                 if(isset($newV[$newVV]))
302                                         $newV[$newVV] += $newVC; 
303                                 else
304                                         $newV[$newVV] = $newVC; 
305                         }
306                         foreach ($newV as $key => $value) {
307                                 array_push($newVv, array('total'=>$value, 'version'=>$key));
308                         }
309                         $v = $newVv;
310                 }
311
312                 // early friendica versions have the format x.x.xxxx where xxxx is the
313                 // DB version stamp; those should be operated out and versions be
314                 // conbined
315                 if($p=='Friendica') {
316                         $newV = array();
317                         $newVv = array();
318                         foreach ($v as $vv) {
319                                 $newVC = $vv['total'];
320                                 $newVV = $vv['version'];
321                                 $lastDot = strrpos($newVV,'.');
322                                 $len = strlen($newVV)-1;
323                                 if(($lastDot == $len-4) && (!strrpos($newVV,'-rc')==$len-3))
324                                         $newVV = substr($newVV, 0, $lastDot);
325                                 if(isset($newV[$newVV]))
326                                         $newV[$newVV] += $newVC;
327                                 else
328                                         $newV[$newVV] = $newVC;
329                         }
330                         foreach ($newV as $key => $value) {
331                                 array_push($newVv, array('total'=>$value, 'version'=>$key));
332                         }
333                         $v = $newVv;
334                 }
335
336                 // the 3rd array item is needed for the JavaScript graphs as JS does
337                 // not like some characters in the names of variables...
338                 $counts[$p]=array($c[0], $v, str_replace(array(' ','%'),'',$p));
339         }
340
341         // some helpful text
342         $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.');
343         $hint = t('The <em>Auto Discovered Contact Directory</em> feature is not enabled, it will improve the data displayed here.');
344
345         // load the template, replace the macros and return the page content
346         $t = get_markup_template("admin_federation.tpl");
347         return replace_macros($t, array(
348                 '$title' => t('Administration'),
349                 '$page' => t('Federation Statistics'),
350                 '$intro' => $intro,
351                 '$hint' => $hint,
352                 '$autoactive' => get_config('system', 'poco_completion'),
353                 '$counts' => $counts,
354                 '$version' => FRIENDICA_VERSION,
355                 '$legendtext' => t('Currently this node is aware of nodes from the following platforms:'),
356                 '$baseurl' => $a->get_baseurl(),
357         ));
358 }
359
360 /**
361  * @brief Admin Inspect Queue Page
362  *
363  * Generates a page for the admin to have a look into the current queue of
364  * postings that are not deliverabke. Shown are the name and url of the
365  * recipient, the delivery network and the dates when the posting was generated
366  * and the last time tried to deliver the posting.
367  *
368  * The returned string holds the content of the page.
369  *
370  * @param App $a
371  * @return string
372  */
373 function admin_page_queue(&$a) {
374         // get content from the queue table
375         $r = q("SELECT c.name,c.nurl,q.id,q.network,q.created,q.last from queue as q, contact as c where c.id=q.cid order by q.cid, q.created;");
376
377         $t = get_markup_template("admin_queue.tpl");
378         return replace_macros($t, array(
379                 '$title' => t('Administration'),
380                 '$page' => t('Inspect Queue'),
381                 '$count' => sizeof($r),
382                 'id_header' => t('ID'),
383                 '$to_header' => t('Recipient Name'),
384                 '$url_header' => t('Recipient Profile'),
385                 '$network_header' => t('Network'),
386                 '$created_header' => t('Created'),
387                 '$last_header' => t('Last Tried'),
388                 '$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.'),
389                 '$entries' => $r,
390         ));
391 }
392
393 /**
394  * @brief Admin Summary Page
395  *
396  * The summary page is the "start page" of the admin panel. It gives the admin
397  * a first overview of the open adminastrative tasks.
398  *
399  * The returned string contains the HTML content of the generated page.
400  *
401  * @param App $a
402  * @return string
403  */
404 function admin_page_summary(&$a) {
405         $r = q("SELECT `page-flags`, COUNT(uid) as `count` FROM `user` GROUP BY `page-flags`");
406         $accounts = array(
407                 array( t('Normal Account'), 0),
408                 array( t('Soapbox Account'), 0),
409                 array( t('Community/Celebrity Account'), 0),
410                 array( t('Automatic Friend Account'), 0),
411                 array( t('Blog Account'), 0),
412                 array( t('Private Forum'), 0)
413         );
414
415         $users=0;
416         foreach ($r as $u){ $accounts[$u['page-flags']][1] = $u['count']; $users+= $u['count']; }
417
418         logger('accounts: ' . print_r($accounts,true),LOGGER_DATA);
419
420         $r = q("SELECT COUNT(id) as `count` FROM `register`");
421         $pending = $r[0]['count'];
422
423         $r = q("select count(*) as total from deliverq where 1");
424         $deliverq = (($r) ? $r[0]['total'] : 0);
425
426         $r = q("select count(*) as total from queue where 1");
427         $queue = (($r) ? $r[0]['total'] : 0);
428
429         // We can do better, but this is a quick queue status
430
431         $queues = array( 'label' => t('Message queues'), 'deliverq' => $deliverq, 'queue' => $queue );
432
433
434         $t = get_markup_template("admin_summary.tpl");
435         return replace_macros($t, array(
436                 '$title' => t('Administration'),
437                 '$page' => t('Summary'),
438                 '$queues' => $queues,
439                 '$users' => array( t('Registered users'), $users),
440                 '$accounts' => $accounts,
441                 '$pending' => array( t('Pending registrations'), $pending),
442                 '$version' => array( t('Version'), FRIENDICA_VERSION),
443                 '$baseurl' => $a->get_baseurl(),
444                 '$platform' => FRIENDICA_PLATFORM,
445                 '$codename' => FRIENDICA_CODENAME,
446                 '$build' =>  get_config('system','build'),
447                 '$plugins' => array( t('Active plugins'), $a->plugins )
448         ));
449 }
450
451 /**
452  * @brief Process send data from Admin Site Page
453  * 
454  * @param App $a
455  */
456 function admin_page_site_post(&$a) {
457         if(!x($_POST,"page_site")) {
458                 return;
459         }
460
461         check_form_security_token_redirectOnErr('/admin/site', 'admin_site');
462
463         // relocate
464         if(x($_POST,'relocate') && x($_POST,'relocate_url') && $_POST['relocate_url']!="") {
465                 $new_url = $_POST['relocate_url'];
466                 $new_url = rtrim($new_url,"/");
467
468                 $parsed = @parse_url($new_url);
469                 if(!$parsed || (!x($parsed,'host') || !x($parsed,'scheme'))) {
470                         notice(t("Can not parse base url. Must have at least <scheme>://<domain>"));
471                         goaway($a->get_baseurl(true) . '/admin/site' );
472                 }
473
474                 /* steps:
475                  * replace all "baseurl" to "new_url" in config, profile, term, items and contacts
476                  * send relocate for every local user
477                  * */
478
479                 $old_url = $a->get_baseurl(true);
480
481                 function update_table($table_name, $fields, $old_url, $new_url) {
482                         global $db, $a;
483
484                         $dbold = dbesc($old_url);
485                         $dbnew = dbesc($new_url);
486
487                         $upd = array();
488                         foreach ($fields as $f) {
489                                 $upd[] = "`$f` = REPLACE(`$f`, '$dbold', '$dbnew')";
490                         }
491
492                         $upds = implode(", ", $upd);
493
494
495
496                         $q = sprintf("UPDATE %s SET %s;", $table_name, $upds);
497                         $r = q($q);
498                         if(!$r) {
499                                 notice( "Failed updating '$table_name': " . $db->error );
500                                 goaway($a->get_baseurl(true) . '/admin/site' );
501                         }
502                 }
503
504                 // update tables
505                 update_table("profile", array('photo', 'thumb'), $old_url, $new_url);
506                 update_table("term", array('url'), $old_url, $new_url);
507                 update_table("contact", array('photo','thumb','micro','url','nurl','request','notify','poll','confirm','poco'), $old_url, $new_url);
508                 update_table("gcontact", array('photo','url','nurl','server_url'), $old_url, $new_url);
509                 update_table("item", array('owner-link','owner-avatar','author-name','author-link','author-avatar','body','plink','tag'), $old_url, $new_url);
510
511                 // update config
512                 $a->set_baseurl($new_url);
513                 set_config('system','url',$new_url);
514
515                 // send relocate
516                 $users = q("SELECT uid FROM user WHERE account_removed = 0 AND account_expired = 0");
517
518                 foreach ($users as $user) {
519                         proc_run('php', 'include/notifier.php', 'relocate', $user['uid']);
520                 }
521
522                 info("Relocation started. Could take a while to complete.");
523
524                 goaway($a->get_baseurl(true) . '/admin/site' );
525         }
526         // end relocate
527
528         $sitename               =       ((x($_POST,'sitename'))                 ? notags(trim($_POST['sitename']))              : '');
529         $hostname               =       ((x($_POST,'hostname'))                 ? notags(trim($_POST['hostname']))              : '');
530         $sender_email           =       ((x($_POST,'sender_email'))             ? notags(trim($_POST['sender_email']))          : '');
531         $banner                 =       ((x($_POST,'banner'))                   ? trim($_POST['banner'])                        : false);
532         $shortcut_icon          =       ((x($_POST,'shortcut_icon'))            ? notags(trim($_POST['shortcut_icon']))         : '');
533         $touch_icon             =       ((x($_POST,'touch_icon'))               ? notags(trim($_POST['touch_icon']))            : '');
534         $info                   =       ((x($_POST,'info'))                     ? trim($_POST['info'])                          : false);
535         $language               =       ((x($_POST,'language'))                 ? notags(trim($_POST['language']))              : '');
536         $theme                  =       ((x($_POST,'theme'))                    ? notags(trim($_POST['theme']))                 : '');
537         $theme_mobile           =       ((x($_POST,'theme_mobile'))             ? notags(trim($_POST['theme_mobile']))          : '');
538         $maximagesize           =       ((x($_POST,'maximagesize'))             ? intval(trim($_POST['maximagesize']))          :  0);
539         $maximagelength         =       ((x($_POST,'maximagelength'))           ? intval(trim($_POST['maximagelength']))        :  MAX_IMAGE_LENGTH);
540         $jpegimagequality       =       ((x($_POST,'jpegimagequality'))         ? intval(trim($_POST['jpegimagequality']))      :  JPEG_QUALITY);
541
542
543         $register_policy        =       ((x($_POST,'register_policy'))          ? intval(trim($_POST['register_policy']))       :  0);
544         $daily_registrations    =       ((x($_POST,'max_daily_registrations'))  ? intval(trim($_POST['max_daily_registrations']))       :0);
545         $abandon_days           =       ((x($_POST,'abandon_days'))             ? intval(trim($_POST['abandon_days']))          :  0);
546
547         $register_text          =       ((x($_POST,'register_text'))            ? notags(trim($_POST['register_text']))         : '');
548
549         $allowed_sites          =       ((x($_POST,'allowed_sites'))            ? notags(trim($_POST['allowed_sites']))         : '');
550         $allowed_email          =       ((x($_POST,'allowed_email'))            ? notags(trim($_POST['allowed_email']))         : '');
551         $block_public           =       ((x($_POST,'block_public'))             ? True                                          : False);
552         $force_publish          =       ((x($_POST,'publish_all'))              ? True                                          : False);
553         $global_directory       =       ((x($_POST,'directory'))                ? notags(trim($_POST['directory']))             : '');
554         $thread_allow           =       ((x($_POST,'thread_allow'))             ? True                                          : False);
555         $newuser_private                =       ((x($_POST,'newuser_private'))          ? True                                  : False);
556         $enotify_no_content             =       ((x($_POST,'enotify_no_content'))       ? True                                  : False);
557         $private_addons                 =       ((x($_POST,'private_addons'))           ? True                                  : False);
558         $disable_embedded               =       ((x($_POST,'disable_embedded'))         ? True                                  : False);
559         $allow_users_remote_self        =       ((x($_POST,'allow_users_remote_self'))  ? True                                  : False);
560
561         $no_multi_reg           =       ((x($_POST,'no_multi_reg'))             ? True                                          : False);
562         $no_openid              =       !((x($_POST,'no_openid'))               ? True                                          : False);
563         $no_regfullname         =       !((x($_POST,'no_regfullname'))          ? True                                          : False);
564         $no_utf                 =       !((x($_POST,'no_utf'))                  ? True                                          : False);
565         $community_page_style   =       ((x($_POST,'community_page_style'))     ? intval(trim($_POST['community_page_style']))  : 0);
566         $max_author_posts_community_page        =       ((x($_POST,'max_author_posts_community_page'))  ? intval(trim($_POST['max_author_posts_community_page']))       : 0);
567
568         $verifyssl              =       ((x($_POST,'verifyssl'))                ? True                                          : False);
569         $proxyuser              =       ((x($_POST,'proxyuser'))                ? notags(trim($_POST['proxyuser']))             : '');
570         $proxy                  =       ((x($_POST,'proxy'))                    ? notags(trim($_POST['proxy']))                 : '');
571         $timeout                =       ((x($_POST,'timeout'))                  ? intval(trim($_POST['timeout']))               : 60);
572         $delivery_interval      =       ((x($_POST,'delivery_interval'))        ? intval(trim($_POST['delivery_interval']))     : 0);
573         $poll_interval          =       ((x($_POST,'poll_interval'))            ? intval(trim($_POST['poll_interval']))         : 0);
574         $maxloadavg             =       ((x($_POST,'maxloadavg'))               ? intval(trim($_POST['maxloadavg']))            : 50);
575         $maxloadavg_frontend    =       ((x($_POST,'maxloadavg_frontend'))      ? intval(trim($_POST['maxloadavg_frontend']))   : 50);
576         $optimize_max_tablesize =       ((x($_POST,'optimize_max_tablesize'))   ? intval(trim($_POST['optimize_max_tablesize'])): 100);
577         $optimize_fragmentation =       ((x($_POST,'optimize_fragmentation'))   ? intval(trim($_POST['optimize_fragmentation'])): 30);
578         $poco_completion        =       ((x($_POST,'poco_completion'))          ? intval(trim($_POST['poco_completion']))       : false);
579         $poco_requery_days      =       ((x($_POST,'poco_requery_days'))        ? intval(trim($_POST['poco_requery_days']))     : 7);
580         $poco_discovery         =       ((x($_POST,'poco_discovery'))           ? intval(trim($_POST['poco_discovery']))        : 0);
581         $poco_discovery_since   =       ((x($_POST,'poco_discovery_since'))     ? intval(trim($_POST['poco_discovery_since']))  : 30);
582         $poco_local_search      =       ((x($_POST,'poco_local_search'))        ? intval(trim($_POST['poco_local_search']))     : false);
583         $nodeinfo               =       ((x($_POST,'nodeinfo'))                 ? intval(trim($_POST['nodeinfo']))              : false);
584         $dfrn_only              =       ((x($_POST,'dfrn_only'))                ? True                                          : False);
585         $ostatus_disabled       =       !((x($_POST,'ostatus_disabled'))        ? True                                          : False);
586         $ostatus_poll_interval  =       ((x($_POST,'ostatus_poll_interval'))    ? intval(trim($_POST['ostatus_poll_interval'])) :  0);
587         $diaspora_enabled       =       ((x($_POST,'diaspora_enabled'))         ? True                                          : False);
588         $ssl_policy             =       ((x($_POST,'ssl_policy'))               ? intval($_POST['ssl_policy'])                  : 0);
589         $force_ssl              =       ((x($_POST,'force_ssl'))                ? True                                          : False);
590         $old_share              =       ((x($_POST,'old_share'))                ? True                                          : False);
591         $hide_help              =       ((x($_POST,'hide_help'))                ? True                                          : False);
592         $suppress_language      =       ((x($_POST,'suppress_language'))        ? True                                          : False);
593         $suppress_tags          =       ((x($_POST,'suppress_tags'))            ? True                                          : False);
594         $use_fulltext_engine    =       ((x($_POST,'use_fulltext_engine'))      ? True                                          : False);
595         $itemcache              =       ((x($_POST,'itemcache'))                ? notags(trim($_POST['itemcache']))             : '');
596         $itemcache_duration     =       ((x($_POST,'itemcache_duration'))       ? intval($_POST['itemcache_duration'])          : 0);
597         $max_comments           =       ((x($_POST,'max_comments'))             ? intval($_POST['max_comments'])                : 0);
598         $lockpath               =       ((x($_POST,'lockpath'))                 ? notags(trim($_POST['lockpath']))              : '');
599         $temppath               =       ((x($_POST,'temppath'))                 ? notags(trim($_POST['temppath']))              : '');
600         $basepath               =       ((x($_POST,'basepath'))                 ? notags(trim($_POST['basepath']))              : '');
601         $singleuser             =       ((x($_POST,'singleuser'))               ? notags(trim($_POST['singleuser']))            : '');
602         $proxy_disabled         =       ((x($_POST,'proxy_disabled'))           ? True                                          : False);
603         $old_pager              =       ((x($_POST,'old_pager'))                ? True                                          : False);
604         $only_tag_search        =       ((x($_POST,'only_tag_search'))          ? True                                          : False);
605         $rino                   =       ((x($_POST,'rino'))                     ? intval($_POST['rino'])                        : 0);
606         $embedly                =       ((x($_POST,'embedly'))                  ? notags(trim($_POST['embedly']))               : '');
607
608         if($a->get_path() != "")
609                 $diaspora_enabled = false;
610
611         if(!$thread_allow)
612                 $ostatus_disabled = true;
613
614         if($ssl_policy != intval(get_config('system','ssl_policy'))) {
615                 if($ssl_policy == SSL_POLICY_FULL) {
616                         q("update `contact` set
617                                 `url`     = replace(`url`    , 'http:' , 'https:'),
618                                 `photo`   = replace(`photo`  , 'http:' , 'https:'),
619                                 `thumb`   = replace(`thumb`  , 'http:' , 'https:'),
620                                 `micro`   = replace(`micro`  , 'http:' , 'https:'),
621                                 `request` = replace(`request`, 'http:' , 'https:'),
622                                 `notify`  = replace(`notify` , 'http:' , 'https:'),
623                                 `poll`    = replace(`poll`   , 'http:' , 'https:'),
624                                 `confirm` = replace(`confirm`, 'http:' , 'https:'),
625                                 `poco`    = replace(`poco`   , 'http:' , 'https:')
626                                 where `self` = 1"
627                         );
628                         q("update `profile` set
629                                 `photo`   = replace(`photo`  , 'http:' , 'https:'),
630                                 `thumb`   = replace(`thumb`  , 'http:' , 'https:')
631                                 where 1 "
632                         );
633                 }
634                 elseif($ssl_policy == SSL_POLICY_SELFSIGN) {
635                         q("update `contact` set
636                                 `url`     = replace(`url`    , 'https:' , 'http:'),
637                                 `photo`   = replace(`photo`  , 'https:' , 'http:'),
638                                 `thumb`   = replace(`thumb`  , 'https:' , 'http:'),
639                                 `micro`   = replace(`micro`  , 'https:' , 'http:'),
640                                 `request` = replace(`request`, 'https:' , 'http:'),
641                                 `notify`  = replace(`notify` , 'https:' , 'http:'),
642                                 `poll`    = replace(`poll`   , 'https:' , 'http:'),
643                                 `confirm` = replace(`confirm`, 'https:' , 'http:'),
644                                 `poco`    = replace(`poco`   , 'https:' , 'http:')
645                                 where `self` = 1"
646                         );
647                         q("update `profile` set
648                                 `photo`   = replace(`photo`  , 'https:' , 'http:'),
649                                 `thumb`   = replace(`thumb`  , 'https:' , 'http:')
650                                 where 1 "
651                         );
652                 }
653         }
654         set_config('system','ssl_policy',$ssl_policy);
655         set_config('system','delivery_interval',$delivery_interval);
656         set_config('system','poll_interval',$poll_interval);
657         set_config('system','maxloadavg',$maxloadavg);
658         set_config('system','maxloadavg_frontend',$maxloadavg_frontend);
659         set_config('system','optimize_max_tablesize',$optimize_max_tablesize);
660         set_config('system','optimize_fragmentation',$optimize_fragmentation);
661         set_config('system','poco_completion',$poco_completion);
662         set_config('system','poco_requery_days',$poco_requery_days);
663         set_config('system','poco_discovery',$poco_discovery);
664         set_config('system','poco_discovery_since',$poco_discovery_since);
665         set_config('system','poco_local_search',$poco_local_search);
666         set_config('system','nodeinfo',$nodeinfo);
667         set_config('config','sitename',$sitename);
668         set_config('config','hostname',$hostname);
669         set_config('config','sender_email', $sender_email);
670         set_config('system','suppress_language',$suppress_language);
671         set_config('system','suppress_tags',$suppress_tags);
672         set_config('system','shortcut_icon',$shortcut_icon);
673         set_config('system','touch_icon',$touch_icon);
674
675         if($banner=="") {
676                 // don't know why, but del_config doesn't work...
677                 q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
678                         dbesc("system"),
679                         dbesc("banner")
680                 );
681         } else {
682                 set_config('system','banner', $banner);
683         }
684
685         if($info=="") {
686                 del_config('config','info');
687         } else {
688                 set_config('config','info',$info);
689         }
690         set_config('system','language', $language);
691         set_config('system','theme', $theme);
692
693         if( $theme_mobile === '---' ) {
694                 del_config('system','mobile-theme');
695         } else {
696                 set_config('system','mobile-theme', $theme_mobile);
697                 }
698                 if( $singleuser === '---' ) {
699                         del_config('system','singleuser');
700                 } else {
701                         set_config('system','singleuser', $singleuser);
702                 }
703         set_config('system','maximagesize', $maximagesize);
704         set_config('system','max_image_length', $maximagelength);
705         set_config('system','jpeg_quality', $jpegimagequality);
706
707         set_config('config','register_policy', $register_policy);
708         set_config('system','max_daily_registrations', $daily_registrations);
709         set_config('system','account_abandon_days', $abandon_days);
710         set_config('config','register_text', $register_text);
711         set_config('system','allowed_sites', $allowed_sites);
712         set_config('system','allowed_email', $allowed_email);
713         set_config('system','block_public', $block_public);
714         set_config('system','publish_all', $force_publish);
715         set_config('system','directory', $global_directory);
716         set_config('system','thread_allow', $thread_allow);
717         set_config('system','newuser_private', $newuser_private);
718         set_config('system','enotify_no_content', $enotify_no_content);
719         set_config('system','disable_embedded', $disable_embedded);
720         set_config('system','allow_users_remote_self', $allow_users_remote_self);
721
722         set_config('system','block_extended_register', $no_multi_reg);
723         set_config('system','no_openid', $no_openid);
724         set_config('system','no_regfullname', $no_regfullname);
725         set_config('system','community_page_style', $community_page_style);
726         set_config('system','max_author_posts_community_page', $max_author_posts_community_page);
727         set_config('system','no_utf', $no_utf);
728         set_config('system','verifyssl', $verifyssl);
729         set_config('system','proxyuser', $proxyuser);
730         set_config('system','proxy', $proxy);
731         set_config('system','curl_timeout', $timeout);
732         set_config('system','dfrn_only', $dfrn_only);
733         set_config('system','ostatus_disabled', $ostatus_disabled);
734         set_config('system','ostatus_poll_interval', $ostatus_poll_interval);
735         set_config('system','diaspora_enabled', $diaspora_enabled);
736
737         set_config('config','private_addons', $private_addons);
738
739         set_config('system','force_ssl', $force_ssl);
740         set_config('system','old_share', $old_share);
741         set_config('system','hide_help', $hide_help);
742         set_config('system','use_fulltext_engine', $use_fulltext_engine);
743         set_config('system','itemcache', $itemcache);
744         set_config('system','itemcache_duration', $itemcache_duration);
745         set_config('system','max_comments', $max_comments);
746         set_config('system','lockpath', $lockpath);
747         set_config('system','temppath', $temppath);
748         set_config('system','basepath', $basepath);
749         set_config('system','proxy_disabled', $proxy_disabled);
750         set_config('system','old_pager', $old_pager);
751         set_config('system','only_tag_search', $only_tag_search);
752
753
754         if($rino==2 and !function_exists('mcrypt_create_iv')) {
755                 notice(t("RINO2 needs mcrypt php extension to work."));
756         } else {
757                 set_config('system','rino_encrypt', $rino);
758         }
759
760         set_config('system','embedly', $embedly);
761
762
763         info( t('Site settings updated.') . EOL);
764         goaway($a->get_baseurl(true) . '/admin/site' );
765         return; // NOTREACHED
766
767 }
768
769 /**
770  * @brief Generate Admin Site subpage
771  *
772  * This function generates the main configuration page of the admin panel.
773  *
774  * @param  App $a
775  * @return string
776  */
777 function admin_page_site(&$a) {
778
779         /* Installed langs */
780         $lang_choices = get_avaiable_languages();
781
782         if(strlen(get_config('system','directory_submit_url')) AND
783                 !strlen(get_config('system','directory'))) {
784                         set_config('system','directory', dirname(get_config('system','directory_submit_url')));
785                         del_config('system','directory_submit_url');
786         }
787
788         /* Installed themes */
789         $theme_choices = array();
790         $theme_choices_mobile = array();
791         $theme_choices_mobile["---"] = t("No special theme for mobile devices");
792         $files = glob('view/theme/*');
793         if($files) {
794                 foreach($files as $file) {
795                         if(intval(file_exists($file . '/unsupported')))
796                                 continue;
797
798                         $f = basename($file);
799                         $theme_name = ((file_exists($file . '/experimental')) ?  sprintf("%s - \x28Experimental\x29", $f) : $f);
800                         if(file_exists($file . '/mobile')) {
801                                 $theme_choices_mobile[$f] = $theme_name;
802                         } else {
803                                 $theme_choices[$f] = $theme_name;
804                         }
805                 }
806         }
807
808         /* Community page style */
809         $community_page_style_choices = array(
810                 CP_NO_COMMUNITY_PAGE => t("No community page"),
811                 CP_USERS_ON_SERVER => t("Public postings from users of this site"),
812                 CP_GLOBAL_COMMUNITY => t("Global community page")
813                 );
814
815         /* OStatus conversation poll choices */
816         $ostatus_poll_choices = array(
817                 "-2" => t("Never"),
818                 "-1" => t("At post arrival"),
819                 "0" => t("Frequently"),
820                 "60" => t("Hourly"),
821                 "720" => t("Twice daily"),
822                 "1440" => t("Daily")
823                 );
824
825         $poco_discovery_choices = array(
826                 "0" => t("Disabled"),
827                 "1" => t("Users"),
828                 "2" => t("Users, Global Contacts"),
829                 "3" => t("Users, Global Contacts/fallback"),
830                 );
831
832         $poco_discovery_since_choices = array(
833                 "30" => t("One month"),
834                 "91" => t("Three months"),
835                 "182" => t("Half a year"),
836                 "365" => t("One year"),
837                 );
838
839         /* get user names to make the install a personal install of X */
840         $user_names = array();
841         $user_names['---'] = t('Multi user instance');
842         $users = q("SELECT username, nickname FROM `user`");
843         foreach ($users as $user) {
844                 $user_names[$user['nickname']] = $user['username'];
845         }
846
847         /* Banner */
848         $banner = get_config('system','banner');
849         if($banner == false)
850                 $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>';
851         $banner = htmlspecialchars($banner);
852         $info = get_config('config','info');
853         $info = htmlspecialchars($info);
854
855         // Automatically create temporary paths
856         get_temppath();
857         get_lockpath();
858         get_itemcachepath();
859
860         //echo "<pre>"; var_dump($lang_choices); die("</pre>");
861
862         /* Register policy */
863         $register_choices = array(
864                 REGISTER_CLOSED => t("Closed"),
865                 REGISTER_APPROVE => t("Requires approval"),
866                 REGISTER_OPEN => t("Open")
867         );
868
869         $ssl_choices = array(
870                 SSL_POLICY_NONE => t("No SSL policy, links will track page SSL state"),
871                 SSL_POLICY_FULL => t("Force all links to use SSL"),
872                 SSL_POLICY_SELFSIGN => t("Self-signed certificate, use SSL for local links only (discouraged)")
873         );
874
875         if($a->config['hostname'] == "")
876                 $a->config['hostname'] = $a->get_hostname();
877
878         $diaspora_able = ($a->get_path() == "");
879
880         $t = get_markup_template("admin_site.tpl");
881         return replace_macros($t, array(
882                 '$title' => t('Administration'),
883                 '$page' => t('Site'),
884                 '$submit' => t('Save Settings'),
885                 '$registration' => t('Registration'),
886                 '$upload' => t('File upload'),
887                 '$corporate' => t('Policies'),
888                 '$advanced' => t('Advanced'),
889                 '$portable_contacts' => t('Auto Discovered Contact Directory'),
890                 '$performance' => t('Performance'),
891                 '$relocate'=> t('Relocate - WARNING: advanced function. Could make this server unreachable.'),
892                 '$baseurl' => $a->get_baseurl(true),
893                 // name, label, value, help string, extra data...
894                 '$sitename'             => array('sitename', t("Site name"), $a->config['sitename'],''),
895                 '$hostname'             => array('hostname', t("Host name"), $a->config['hostname'], ""),
896                 '$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"),
897                 '$banner'               => array('banner', t("Banner/Logo"), $banner, ""),
898                 '$shortcut_icon'        => array('shortcut_icon', t("Shortcut icon"), get_config('system','shortcut_icon'),  t("Link to an icon that will be used for browsers.")),
899                 '$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.")),
900                 '$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())),
901                 '$language'             => array('language', t("System language"), get_config('system','language'), "", $lang_choices),
902                 '$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),
903                 '$theme_mobile'         => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile-theme'), t("Theme for mobile devices"), $theme_choices_mobile),
904                 '$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),
905                 '$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.")),
906                 '$old_share'            => array('old_share', t("Old style 'Share'"), get_config('system','old_share'), t("Deactivates the bbcode element 'share' for repeating items.")),
907                 '$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.")),
908                 '$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),
909                 '$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.")),
910                 '$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.")),
911                 '$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.")),
912
913                 '$register_policy'      => array('register_policy', t("Register policy"), $a->config['register_policy'], "", $register_choices),
914                 '$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.")),
915                 '$register_text'        => array('register_text', t("Register text"), $a->config['register_text'], t("Will be displayed prominently on the registration page.")),
916                 '$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.')),
917                 '$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")),
918                 '$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")),
919                 '$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.")),
920                 '$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.")),
921                 '$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.")),
922                 '$thread_allow'         => array('thread_allow', t("Allow threaded items"), get_config('system','thread_allow'), t("Allow infinite level threading for items on this site.")),
923                 '$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.")),
924                 '$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.")),
925                 '$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.")),
926                 '$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.")),
927                 '$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.')),
928                 '$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.")),
929                 '$no_openid'            => array('no_openid', t("OpenID support"), !get_config('system','no_openid'), t("OpenID support for registration and logins.")),
930                 '$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")),
931                 '$no_utf'               => array('no_utf', t("UTF-8 Regular expressions"), !get_config('system','no_utf'), t("Use PHP UTF8 regular expressions")),
932                 '$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),
933                 '$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')")),
934                 '$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.")),
935                 '$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),
936                 '$ostatus_not_able'     => t("OStatus support can only be enabled if threading is enabled."),
937                 '$diaspora_able'        => $diaspora_able,
938                 '$diaspora_not_able'    => t("Diaspora support can't be enabled because Friendica was installed into a sub directory."),
939                 '$diaspora_enabled'     => array('diaspora_enabled', t("Enable Diaspora support"), get_config('system','diaspora_enabled'), t("Provide built-in Diaspora network compatibility.")),
940                 '$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.")),
941                 '$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.")),
942                 '$proxyuser'            => array('proxyuser', t("Proxy user"), get_config('system','proxyuser'), ""),
943                 '$proxy'                => array('proxy', t("Proxy URL"), get_config('system','proxy'), ""),
944                 '$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).")),
945                 '$delivery_interval'    => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")),
946                 '$poll_interval'        => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")),
947                 '$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.")),
948                 '$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.")),
949                 '$optimize_max_tablesize'=> array('optimize_max_tablesize', t("Maximum table size for optimization"), ((intval(get_config('system','optimize_max_tablesize')) > 0)?get_config('system','optimize_max_tablesize'):100), t("Maximum table size (in MB) for the automatic optimization - default 100 MB. Enter -1 to disable it.")),
950                 '$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%.")),
951
952                 '$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.")),
953                 '$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.")),
954                 '$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),
955                 '$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),
956                 '$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.")),
957
958                 '$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.")),
959
960                 '$use_fulltext_engine'  => array('use_fulltext_engine', t("Use MySQL full text engine"), get_config('system','use_fulltext_engine'), t("Activates the full text engine. Speeds up search - but can only search for four and more characters.")),
961                 '$suppress_language'    => array('suppress_language', t("Suppress Language"), get_config('system','suppress_language'), t("Suppress language information in meta information about a posting.")),
962                 '$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.")),
963                 '$itemcache'            => array('itemcache', t("Path to item cache"), get_config('system','itemcache'), t("The item caches buffers generated bbcode and external images.")),
964                 '$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.")),
965                 '$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.")),
966                 '$lockpath'             => array('lockpath', t("Path for lock file"), get_config('system','lockpath'), t("The lock file is used to avoid multiple pollers at one time. Only define a folder here.")),
967                 '$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.")),
968                 '$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.")),
969                 '$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.")),
970                 '$old_pager'            => array('old_pager', t("Enable old style pager"), get_config('system','old_pager'), t("The old style pager has page numbers but slows down massively the page speed.")),
971                 '$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.")),
972
973                 '$relocate_url'         => array('relocate_url', t("New base url"), $a->get_baseurl(), t("Change base url for this server. Sends relocate message to all DFRN contacts of all users.")),
974
975                 '$rino'                 => array('rino', t("RINO Encryption"), intval(get_config('system','rino_encrypt')), t("Encryption layer between nodes."), array("Disabled", "RINO1 (deprecated)", "RINO2")),
976                 '$embedly'              => array('embedly', t("Embedly API key"), get_config('system','embedly'), t("<a href='http://embed.ly'>Embedly</a> is used to fetch additional data for web pages. This is an optional parameter.")),
977
978                 '$form_security_token'  => get_form_security_token("admin_site")
979
980         ));
981
982 }
983
984 /**
985  * @brief Generates admin panel subpage for DB syncronization
986  *
987  * This page checks if the database of friendica is in sync with the specs.
988  * Should this not be the case, it attemps to sync the structure and notifies
989  * the admin if the automatic process was failing.
990  *
991  * The returned string holds the HTML code of the page.
992  *
993  * @param App $a
994  * @return string
995  **/
996 function admin_page_dbsync(&$a) {
997
998         $o = '';
999
1000         if($a->argc > 3 && intval($a->argv[3]) && $a->argv[2] === 'mark') {
1001                 set_config('database', 'update_' . intval($a->argv[3]), 'success');
1002                 $curr = get_config('system','build');
1003                 if(intval($curr) == intval($a->argv[3]))
1004                         set_config('system','build',intval($curr) + 1);
1005                 info( t('Update has been marked successful') . EOL);
1006                 goaway($a->get_baseurl(true) . '/admin/dbsync');
1007         }
1008
1009         if(($a->argc > 2) AND (intval($a->argv[2]) OR ($a->argv[2] === 'check'))) {
1010                 require_once("include/dbstructure.php");
1011                 $retval = update_structure(false, true);
1012                 if(!$retval) {
1013                         $o .= sprintf(t("Database structure update %s was successfully applied."), DB_UPDATE_VERSION)."<br />";
1014                         set_config('database', 'dbupdate_'.DB_UPDATE_VERSION, 'success');
1015                 } else
1016                         $o .= sprintf(t("Executing of database structure update %s failed with error: %s"),
1017                                         DB_UPDATE_VERSION, $retval)."<br />";
1018                 if($a->argv[2] === 'check')
1019                         return $o;
1020         }
1021
1022         if($a->argc > 2 && intval($a->argv[2])) {
1023                 require_once('update.php');
1024                 $func = 'update_' . intval($a->argv[2]);
1025                 if(function_exists($func)) {
1026                         $retval = $func();
1027                         if($retval === UPDATE_FAILED) {
1028                                 $o .= sprintf(t("Executing %s failed with error: %s"), $func, $retval);
1029                         }
1030                         elseif($retval === UPDATE_SUCCESS) {
1031                                 $o .= sprintf(t('Update %s was successfully applied.', $func));
1032                                 set_config('database',$func, 'success');
1033                         }
1034                         else
1035                                 $o .= sprintf(t('Update %s did not return a status. Unknown if it succeeded.'), $func);
1036                 } else {
1037                         $o .= sprintf(t('There was no additional update function %s that needed to be called.'), $func)."<br />";
1038                         set_config('database',$func, 'success');
1039                 }
1040                 return $o;
1041         }
1042
1043         $failed = array();
1044         $r = q("select k, v from config where `cat` = 'database' ");
1045         if(count($r)) {
1046                 foreach($r as $rr) {
1047                         $upd = intval(substr($rr['k'],7));
1048                         if($upd < 1139 || $rr['v'] === 'success')
1049                                 continue;
1050                         $failed[] = $upd;
1051                 }
1052         }
1053         if(! count($failed)) {
1054                 $o = replace_macros(get_markup_template('structure_check.tpl'),array(
1055                         '$base' => $a->get_baseurl(true),
1056                         '$banner' => t('No failed updates.'),
1057                         '$check' => t('Check database structure'),
1058                 ));
1059         } else {
1060                 $o = replace_macros(get_markup_template('failed_updates.tpl'),array(
1061                         '$base' => $a->get_baseurl(true),
1062                         '$banner' => t('Failed Updates'),
1063                         '$desc' => t('This does not include updates prior to 1139, which did not return a status.'),
1064                         '$mark' => t('Mark success (if update was manually applied)'),
1065                         '$apply' => t('Attempt to execute this update step automatically'),
1066                         '$failed' => $failed
1067                 ));
1068         }
1069
1070         return $o;
1071
1072 }
1073
1074 /**
1075  * @brief Process data send by Users admin page
1076  * 
1077  * @param App $a
1078  */
1079 function admin_page_users_post(&$a){
1080         $pending        =       ( x($_POST, 'pending')                  ? $_POST['pending']             : array() );
1081         $users          =       ( x($_POST, 'user')                     ? $_POST['user']                : array() );
1082         $nu_name        =       ( x($_POST, 'new_user_name')            ? $_POST['new_user_name']       : '');
1083         $nu_nickname    =       ( x($_POST, 'new_user_nickname')        ? $_POST['new_user_nickname']   : '');
1084         $nu_email       =       ( x($_POST, 'new_user_email')           ? $_POST['new_user_email']      : '');
1085
1086         check_form_security_token_redirectOnErr('/admin/users', 'admin_users');
1087
1088         if(!($nu_name==="") && !($nu_email==="") && !($nu_nickname==="")) {
1089                 require_once('include/user.php');
1090
1091                 $result = create_user( array('username'=>$nu_name, 'email'=>$nu_email, 'nickname'=>$nu_nickname, 'verified'=>1)  );
1092                 if(! $result['success']) {
1093                         notice($result['message']);
1094                         return;
1095                 }
1096                 $nu = $result['user'];
1097                 $preamble = deindent(t('
1098                         Dear %1$s,
1099                                 the administrator of %2$s has set up an account for you.'));
1100                 $body = deindent(t('
1101                         The login details are as follows:
1102
1103                         Site Location:  %1$s
1104                         Login Name:             %2$s
1105                         Password:               %3$s
1106
1107                         You may change your password from your account "Settings" page after logging
1108                         in.
1109
1110                         Please take a few moments to review the other account settings on that page.
1111
1112                         You may also wish to add some basic information to your default profile
1113                         (on the "Profiles" page) so that other people can easily find you.
1114
1115                         We recommend setting your full name, adding a profile photo,
1116                         adding some profile "keywords" (very useful in making new friends) - and
1117                         perhaps what country you live in; if you do not wish to be more specific
1118                         than that.
1119
1120                         We fully respect your right to privacy, and none of these items are necessary.
1121                         If you are new and do not know anybody here, they may help
1122                         you to make some new and interesting friends.
1123
1124                         Thank you and welcome to %4$s.'));
1125
1126                 $preamble = sprintf($preamble, $nu['username'], $a->config['sitename']);
1127                 $body = sprintf($body, $a->get_baseurl(), $nu['email'], $result['password'], $a->config['sitename']);
1128
1129                 notification(array(
1130                         'type' => "SYSTEM_EMAIL",
1131                         'to_email' => $nu['email'],
1132                         'subject'=> sprintf( t('Registration details for %s'), $a->config['sitename']),
1133                         'preamble'=> $preamble,
1134                         'body' => $body));
1135
1136         }
1137
1138         if(x($_POST,'page_users_block')) {
1139                 foreach($users as $uid){
1140                         q("UPDATE `user` SET `blocked`=1-`blocked` WHERE `uid`=%s",
1141                                 intval( $uid )
1142                         );
1143                 }
1144                 notice( sprintf( tt("%s user blocked/unblocked", "%s users blocked/unblocked", count($users)), count($users)) );
1145         }
1146         if(x($_POST,'page_users_delete')) {
1147                 require_once("include/Contact.php");
1148                 foreach($users as $uid){
1149                         user_remove($uid);
1150                 }
1151                 notice( sprintf( tt("%s user deleted", "%s users deleted", count($users)), count($users)) );
1152         }
1153
1154         if(x($_POST,'page_users_approve')) {
1155                 require_once("mod/regmod.php");
1156                 foreach($pending as $hash){
1157                         user_allow($hash);
1158                 }
1159         }
1160         if(x($_POST,'page_users_deny')) {
1161                 require_once("mod/regmod.php");
1162                 foreach($pending as $hash){
1163                         user_deny($hash);
1164                 }
1165         }
1166         goaway($a->get_baseurl(true) . '/admin/users' );
1167         return; // NOTREACHED
1168 }
1169
1170 /**
1171  * @brief Admin panel subpage for User management
1172  *
1173  * This function generates the admin panel page for user management of the
1174  * node. It offers functionality to add/block/delete users and offers some
1175  * statistics about the userbase.
1176  *
1177  * The returned string holds the HTML code of the page.
1178  *
1179  * @param App $a
1180  * @return string
1181  */
1182 function admin_page_users(&$a){
1183         if($a->argc>2) {
1184                 $uid = $a->argv[3];
1185                 $user = q("SELECT username, blocked FROM `user` WHERE `uid`=%d", intval($uid));
1186                 if(count($user)==0) {
1187                         notice( 'User not found' . EOL);
1188                         goaway($a->get_baseurl(true) . '/admin/users' );
1189                         return ''; // NOTREACHED
1190                 }
1191                 switch($a->argv[2]){
1192                         case "delete":{
1193                                 check_form_security_token_redirectOnErr('/admin/users', 'admin_users', 't');
1194                                 // delete user
1195                                 require_once("include/Contact.php");
1196                                 user_remove($uid);
1197
1198                                 notice( sprintf(t("User '%s' deleted"), $user[0]['username']) . EOL);
1199                         }; break;
1200                         case "block":{
1201                                 check_form_security_token_redirectOnErr('/admin/users', 'admin_users', 't');
1202                                 q("UPDATE `user` SET `blocked`=%d WHERE `uid`=%s",
1203                                         intval( 1-$user[0]['blocked'] ),
1204                                         intval( $uid )
1205                                 );
1206                                 notice( sprintf( ($user[0]['blocked']?t("User '%s' unblocked"):t("User '%s' blocked")) , $user[0]['username']) . EOL);
1207                         }; break;
1208                 }
1209                 goaway($a->get_baseurl(true) . '/admin/users' );
1210                 return ''; // NOTREACHED
1211
1212         }
1213
1214         /* get pending */
1215         $pending = q("SELECT `register`.*, `contact`.`name`, `user`.`email`
1216                                  FROM `register`
1217                                  LEFT JOIN `contact` ON `register`.`uid` = `contact`.`uid`
1218                                  LEFT JOIN `user` ON `register`.`uid` = `user`.`uid`;");
1219
1220
1221         /* get users */
1222         $total = q("SELECT count(*) as total FROM `user` where 1");
1223         if(count($total)) {
1224                 $a->set_pager_total($total[0]['total']);
1225                 $a->set_pager_itemspage(100);
1226         }
1227
1228         $users = q("SELECT `user` . * , `contact`.`name` , `contact`.`url` , `contact`.`micro`, `lastitem`.`lastitem_date`, `user`.`account_expired`
1229                                 FROM
1230                                         (SELECT MAX(`item`.`changed`) as `lastitem_date`, `item`.`uid`
1231                                         FROM `item`
1232                                         WHERE `item`.`type` = 'wall'
1233                                         GROUP BY `item`.`uid`) AS `lastitem`
1234                                                  RIGHT OUTER JOIN `user` ON `user`.`uid` = `lastitem`.`uid`,
1235                                            `contact`
1236                                 WHERE
1237                                            `user`.`uid` = `contact`.`uid`
1238                                                 AND `user`.`verified` =1
1239                                         AND `contact`.`self` =1
1240                                 ORDER BY `contact`.`name` LIMIT %d, %d
1241                                 ",
1242                                 intval($a->pager['start']),
1243                                 intval($a->pager['itemspage'])
1244                                 );
1245
1246         $adminlist = explode(",", str_replace(" ", "", $a->config['admin_email']));
1247         $_setup_users = function ($e) use ($adminlist){
1248                 $accounts = array(
1249                         t('Normal Account'),
1250                         t('Soapbox Account'),
1251                         t('Community/Celebrity Account'),
1252                                                 t('Automatic Friend Account')
1253                 );
1254                 $e['page-flags'] = $accounts[$e['page-flags']];
1255                 $e['register_date'] = relative_date($e['register_date']);
1256                 $e['login_date'] = relative_date($e['login_date']);
1257                 $e['lastitem_date'] = relative_date($e['lastitem_date']);
1258                 //$e['is_admin'] = ($e['email'] === $a->config['admin_email']);
1259                 $e['is_admin'] = in_array($e['email'], $adminlist);
1260                 $e['is_deletable'] = (intval($e['uid']) != local_user());
1261                 $e['deleted'] = ($e['account_removed']?relative_date($e['account_expires_on']):False);
1262                 return $e;
1263         };
1264         $users = array_map($_setup_users, $users);
1265
1266
1267         // Get rid of dashes in key names, Smarty3 can't handle them
1268         // and extracting deleted users
1269
1270         $tmp_users = array();
1271         $deleted = array();
1272
1273         while(count($users)) {
1274                 $new_user = array();
1275                 foreach( array_pop($users) as $k => $v) {
1276                         $k = str_replace('-','_',$k);
1277                         $new_user[$k] = $v;
1278                 }
1279                 if($new_user['deleted']) {
1280                         array_push($deleted, $new_user);
1281                 }
1282                 else {
1283                         array_push($tmp_users, $new_user);
1284                 }
1285         }
1286         //Reversing the two array, and moving $tmp_users to $users
1287         array_reverse($deleted);
1288         while(count($tmp_users)) {
1289                 array_push($users, array_pop($tmp_users));
1290         }
1291
1292         $t = get_markup_template("admin_users.tpl");
1293         $o = replace_macros($t, array(
1294                 // strings //
1295                 '$title' => t('Administration'),
1296                 '$page' => t('Users'),
1297                 '$submit' => t('Add User'),
1298                 '$select_all' => t('select all'),
1299                 '$h_pending' => t('User registrations waiting for confirm'),
1300                 '$h_deleted' => t('User waiting for permanent deletion'),
1301                 '$th_pending' => array( t('Request date'), t('Name'), t('Email') ),
1302                 '$no_pending' =>  t('No registrations.'),
1303                 '$approve' => t('Approve'),
1304                 '$deny' => t('Deny'),
1305                 '$delete' => t('Delete'),
1306                 '$block' => t('Block'),
1307                 '$unblock' => t('Unblock'),
1308                 '$siteadmin' => t('Site admin'),
1309                 '$accountexpired' => t('Account expired'),
1310
1311                 '$h_users' => t('Users'),
1312                 '$h_newuser' => t('New User'),
1313                 '$th_deleted' => array( t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Deleted since') ),
1314                 '$th_users' => array( t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'),  t('Account') ),
1315
1316                 '$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?'),
1317                 '$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?'),
1318
1319                 '$form_security_token' => get_form_security_token("admin_users"),
1320
1321                 // values //
1322                 '$baseurl' => $a->get_baseurl(true),
1323
1324                 '$pending' => $pending,
1325                 'deleted' => $deleted,
1326                 '$users' => $users,
1327                 '$newusername' => array('new_user_name', t("Name"), '', t("Name of the new user.")),
1328                 '$newusernickname' => array('new_user_nickname', t("Nickname"), '', t("Nickname of the new user.")),
1329                 '$newuseremail' => array('new_user_email', t("Email"), '', t("Email address of the new user."), '', '', 'email'),
1330         ));
1331         $o .= paginate($a);
1332         return $o;
1333 }
1334
1335
1336 /**
1337  * @brief Plugins admin page
1338  *
1339  * This function generates the admin panel page for managing plugins on the
1340  * friendica node. If a plugin name is given a single page showing the details
1341  * for this addon is generated. If no name is given, a list of available
1342  * plugins is shown.
1343  *
1344  * The template used for displaying the list of plugins and the details of the
1345  * plugin are the same as used for the templates.
1346  *
1347  * The returned string returned hulds the HTML code of the page.
1348  *
1349  * @param App $a
1350  * @return string
1351  */
1352 function admin_page_plugins(&$a){
1353
1354         /*
1355          * Single plugin
1356          */
1357         if($a->argc == 3) {
1358                 $plugin = $a->argv[2];
1359                 if(!is_file("addon/$plugin/$plugin.php")) {
1360                         notice( t("Item not found.") );
1361                         return '';
1362                 }
1363
1364                 if(x($_GET,"a") && $_GET['a']=="t") {
1365                         check_form_security_token_redirectOnErr('/admin/plugins', 'admin_themes', 't');
1366
1367                         // Toggle plugin status
1368                         $idx = array_search($plugin, $a->plugins);
1369                         if($idx !== false) {
1370                                 unset($a->plugins[$idx]);
1371                                 uninstall_plugin($plugin);
1372                                 info( sprintf( t("Plugin %s disabled."), $plugin ) );
1373                         } else {
1374                                 $a->plugins[] = $plugin;
1375                                 install_plugin($plugin);
1376                                 info( sprintf( t("Plugin %s enabled."), $plugin ) );
1377                         }
1378                         set_config("system","addon", implode(", ",$a->plugins));
1379                         goaway($a->get_baseurl(true) . '/admin/plugins' );
1380                         return ''; // NOTREACHED
1381                 }
1382
1383                 // display plugin details
1384                 require_once('library/markdown.php');
1385
1386                 if(in_array($plugin, $a->plugins)) {
1387                         $status="on"; $action= t("Disable");
1388                 } else {
1389                         $status="off"; $action= t("Enable");
1390                 }
1391
1392                 $readme=Null;
1393                 if(is_file("addon/$plugin/README.md")) {
1394                         $readme = file_get_contents("addon/$plugin/README.md");
1395                         $readme = Markdown($readme);
1396                 } elseif(is_file("addon/$plugin/README")) {
1397                         $readme = "<pre>". file_get_contents("addon/$plugin/README") ."</pre>";
1398                 }
1399
1400                 $admin_form="";
1401                 if(is_array($a->plugins_admin) && in_array($plugin, $a->plugins_admin)) {
1402                         @require_once("addon/$plugin/$plugin.php");
1403                         $func = $plugin.'_plugin_admin';
1404                         $func($a, $admin_form);
1405                 }
1406
1407                 $t = get_markup_template("admin_plugins_details.tpl");
1408
1409                 return replace_macros($t, array(
1410                         '$title' => t('Administration'),
1411                         '$page' => t('Plugins'),
1412                         '$toggle' => t('Toggle'),
1413                         '$settings' => t('Settings'),
1414                         '$baseurl' => $a->get_baseurl(true),
1415
1416                         '$plugin' => $plugin,
1417                         '$status' => $status,
1418                         '$action' => $action,
1419                         '$info' => get_plugin_info($plugin),
1420                         '$str_author' => t('Author: '),
1421                         '$str_maintainer' => t('Maintainer: '),
1422
1423                         '$admin_form' => $admin_form,
1424                         '$function' => 'plugins',
1425                         '$screenshot' => '',
1426                         '$readme' => $readme,
1427
1428                         '$form_security_token' => get_form_security_token("admin_themes"),
1429                 ));
1430         }
1431
1432
1433
1434         /*
1435          * List plugins
1436          */
1437
1438         if(x($_GET,"a") && $_GET['a']=="r") {
1439                 check_form_security_token_redirectOnErr($a->get_baseurl().'/admin/plugins', 'admin_themes', 't');
1440                 reload_plugins();
1441                 info("Plugins reloaded");
1442                 goaway($a->get_baseurl().'/admin/plugins');
1443         }
1444
1445         $plugins = array();
1446         $files = glob("addon/*/");
1447         if($files) {
1448                 foreach($files as $file) {
1449                         if(is_dir($file)) {
1450                                 list($tmp, $id)=array_map("trim", explode("/",$file));
1451                                 $info = get_plugin_info($id);
1452                                 $show_plugin = true;
1453
1454                                 // If the addon is unsupported, then only show it, when it is enabled
1455                                 if((strtolower($info["status"]) == "unsupported") AND !in_array($id,  $a->plugins))
1456                                         $show_plugin = false;
1457
1458                                 // Override the above szenario, when the admin really wants to see outdated stuff
1459                                 if(get_config("system", "show_unsupported_addons"))
1460                                         $show_plugin = true;
1461
1462                                 if($show_plugin)
1463                                         $plugins[] = array($id, (in_array($id,  $a->plugins)?"on":"off") , $info);
1464                         }
1465                 }
1466         }
1467
1468         $t = get_markup_template("admin_plugins.tpl");
1469         return replace_macros($t, array(
1470                 '$title' => t('Administration'),
1471                 '$page' => t('Plugins'),
1472                 '$submit' => t('Save Settings'),
1473                 '$reload' => t('Reload active plugins'),
1474                 '$baseurl' => $a->get_baseurl(true),
1475                 '$function' => 'plugins',
1476                 '$plugins' => $plugins,
1477                 '$pcount' => count($plugins), 
1478                 '$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'),
1479                 '$form_security_token' => get_form_security_token("admin_themes"),
1480         ));
1481 }
1482
1483 /**
1484  * @param array $themes
1485  * @param string $th
1486  * @param int $result
1487  */
1488 function toggle_theme(&$themes,$th,&$result) {
1489         for($x = 0; $x < count($themes); $x ++) {
1490                 if($themes[$x]['name'] === $th) {
1491                         if($themes[$x]['allowed']) {
1492                                 $themes[$x]['allowed'] = 0;
1493                                 $result = 0;
1494                         }
1495                         else {
1496                                 $themes[$x]['allowed'] = 1;
1497                                 $result = 1;
1498                         }
1499                 }
1500         }
1501 }
1502
1503 /**
1504  * @param array $themes
1505  * @param string $th
1506  * @return int
1507  */
1508 function theme_status($themes,$th) {
1509         for($x = 0; $x < count($themes); $x ++) {
1510                 if($themes[$x]['name'] === $th) {
1511                         if($themes[$x]['allowed']) {
1512                                 return 1;
1513                         }
1514                         else {
1515                                 return 0;
1516                         }
1517                 }
1518         }
1519         return 0;
1520 }
1521
1522
1523 /**
1524  * @param array $themes
1525  * @return string
1526  */
1527 function rebuild_theme_table($themes) {
1528         $o = '';
1529         if(count($themes)) {
1530                 foreach($themes as $th) {
1531                         if($th['allowed']) {
1532                                 if(strlen($o))
1533                                         $o .= ',';
1534                                 $o .= $th['name'];
1535                         }
1536                 }
1537         }
1538         return $o;
1539 }
1540
1541
1542 /**
1543  * @brief Themes admin page
1544  *
1545  * This function generates the admin panel page to control the themes available
1546  * on the friendica node. If the name of a theme is given as parameter a page
1547  * with the details for the theme is shown. Otherwise a list of available
1548  * themes is generated.
1549  *
1550  * The template used for displaying the list of themes and the details of the
1551  * themes are the same as used for the plugins.
1552  *
1553  * The returned string contains the HTML code of the admin panel page.
1554  *
1555  * @param App $a
1556  * @return string
1557  */
1558 function admin_page_themes(&$a){
1559
1560         $allowed_themes_str = get_config('system','allowed_themes');
1561         $allowed_themes_raw = explode(',',$allowed_themes_str);
1562         $allowed_themes = array();
1563         if(count($allowed_themes_raw))
1564                 foreach($allowed_themes_raw as $x)
1565                         if(strlen(trim($x)))
1566                                 $allowed_themes[] = trim($x);
1567
1568         $themes = array();
1569         $files = glob('view/theme/*');
1570         if($files) {
1571                 foreach($files as $file) {
1572                         $f = basename($file);
1573                         $is_experimental = intval(file_exists($file . '/experimental'));
1574                         $is_supported = 1-(intval(file_exists($file . '/unsupported')));
1575                         $is_allowed = intval(in_array($f,$allowed_themes));
1576
1577                         if($is_allowed OR $is_supported OR get_config("system", "show_unsupported_themes"))
1578                                 $themes[] = array('name' => $f, 'experimental' => $is_experimental, 'supported' => $is_supported, 'allowed' => $is_allowed);
1579                 }
1580         }
1581
1582         if(! count($themes)) {
1583                 notice( t('No themes found.'));
1584                 return '';
1585         }
1586
1587         /*
1588          * Single theme
1589          */
1590
1591         if($a->argc == 3) {
1592                 $theme = $a->argv[2];
1593                 if(! is_dir("view/theme/$theme")) {
1594                         notice( t("Item not found.") );
1595                         return '';
1596                 }
1597
1598                 if(x($_GET,"a") && $_GET['a']=="t") {
1599                         check_form_security_token_redirectOnErr('/admin/themes', 'admin_themes', 't');
1600
1601                         // Toggle theme status
1602
1603                         toggle_theme($themes,$theme,$result);
1604                         $s = rebuild_theme_table($themes);
1605                         if($result) {
1606                                 install_theme($theme);
1607                                 info( sprintf('Theme %s enabled.',$theme));
1608                         }
1609                         else {
1610                                 uninstall_theme($theme);
1611                                 info( sprintf('Theme %s disabled.',$theme));
1612                         }
1613
1614                         set_config('system','allowed_themes',$s);
1615                         goaway($a->get_baseurl(true) . '/admin/themes' );
1616                         return ''; // NOTREACHED
1617                 }
1618
1619                 // display theme details
1620                 require_once('library/markdown.php');
1621
1622                 if(theme_status($themes,$theme)) {
1623                         $status="on"; $action= t("Disable");
1624                 } else {
1625                         $status="off"; $action= t("Enable");
1626                 }
1627
1628                 $readme=Null;
1629                 if(is_file("view/theme/$theme/README.md")) {
1630                         $readme = file_get_contents("view/theme/$theme/README.md");
1631                         $readme = Markdown($readme);
1632                 } elseif(is_file("view/theme/$theme/README")) {
1633                         $readme = "<pre>". file_get_contents("view/theme/$theme/README") ."</pre>";
1634                 }
1635
1636                 $admin_form="";
1637                 if(is_file("view/theme/$theme/config.php")) {
1638                         function __get_theme_admin_form(&$a, $theme) {
1639                                 $orig_theme = $a->theme;
1640                                 $orig_page = $a->page;
1641                                 $orig_session_theme = $_SESSION['theme'];
1642                                 require_once("view/theme/$theme/theme.php");
1643                                 require_once("view/theme/$theme/config.php");
1644                                 $_SESSION['theme'] = $theme;
1645
1646
1647                                 $init = $theme."_init";
1648                                 if(function_exists($init)) $init($a);
1649                                 if(function_exists("theme_admin")) {
1650                                         $admin_form = theme_admin($a);
1651                                 }
1652
1653                                 $_SESSION['theme'] = $orig_session_theme;
1654                                 $a->theme = $orig_theme;
1655                                 $a->page = $orig_page;
1656                                 return $admin_form;
1657                         }
1658                         $admin_form = __get_theme_admin_form($a, $theme);
1659                 }
1660
1661                 $screenshot = array( get_theme_screenshot($theme), t('Screenshot'));
1662                 if(! stristr($screenshot[0],$theme))
1663                         $screenshot = null;
1664
1665
1666                 $t = get_markup_template("admin_plugins_details.tpl");
1667                 return replace_macros($t, array(
1668                         '$title' => t('Administration'),
1669                         '$page' => t('Themes'),
1670                         '$toggle' => t('Toggle'),
1671                         '$settings' => t('Settings'),
1672                         '$baseurl' => $a->get_baseurl(true),
1673                         '$plugin' => $theme,
1674                         '$status' => $status,
1675                         '$action' => $action,
1676                         '$info' => get_theme_info($theme),
1677                         '$function' => 'themes',
1678                         '$admin_form' => $admin_form,
1679                         '$str_author' => t('Author: '),
1680                         '$str_maintainer' => t('Maintainer: '),
1681                         '$screenshot' => $screenshot,
1682                         '$readme' => $readme,
1683
1684                         '$form_security_token' => get_form_security_token("admin_themes"),
1685                 ));
1686         }
1687
1688
1689         // reload active themes
1690         if(x($_GET,"a") && $_GET['a']=="r") {
1691                 check_form_security_token_redirectOnErr($a->get_baseurl().'/admin/themes', 'admin_themes', 't');
1692                 if($themes) {
1693                         foreach($themes as $th) {
1694                                 if($th['allowed']) {
1695                                         uninstall_theme($th['name']);
1696                                         install_theme($th['name']);
1697                                 }
1698                         }
1699                 }
1700                 info("Themes reloaded");
1701                 goaway($a->get_baseurl().'/admin/themes');
1702         }
1703
1704         /*
1705          * List themes
1706          */
1707
1708         $xthemes = array();
1709         if($themes) {
1710                 foreach($themes as $th) {
1711                         $xthemes[] = array($th['name'],(($th['allowed']) ? "on" : "off"), get_theme_info($th['name']));
1712                 }
1713         }
1714
1715
1716         $t = get_markup_template("admin_plugins.tpl");
1717         return replace_macros($t, array(
1718                 '$title' => t('Administration'),
1719                 '$page' => t('Themes'),
1720                 '$submit' => t('Save Settings'),
1721                 '$reload' => t('Reload active themes'),
1722                 '$baseurl' => $a->get_baseurl(true),
1723                 '$function' => 'themes',
1724                 '$plugins' => $xthemes,
1725                 '$pcount' => count($themes),
1726                 '$noplugshint' => sprintf(t('No themes found on the system. They should be paced in %1$s'),'<code>/view/themes</code>'),
1727                 '$experimental' => t('[Experimental]'),
1728                 '$unsupported' => t('[Unsupported]'),
1729                 '$form_security_token' => get_form_security_token("admin_themes"),
1730         ));
1731 }
1732
1733
1734 /**
1735  * @brief Prosesses data send by Logs admin page
1736  * 
1737  * @param App $a
1738  */
1739 function admin_page_logs_post(&$a) {
1740         if(x($_POST,"page_logs")) {
1741                 check_form_security_token_redirectOnErr('/admin/logs', 'admin_logs');
1742
1743                 $logfile        =       ((x($_POST,'logfile'))          ? notags(trim($_POST['logfile']))       : '');
1744                 $debugging      =       ((x($_POST,'debugging'))        ? true                                  : false);
1745                 $loglevel       =       ((x($_POST,'loglevel'))         ? intval(trim($_POST['loglevel']))      : 0);
1746
1747                 set_config('system','logfile', $logfile);
1748                 set_config('system','debugging',  $debugging);
1749                 set_config('system','loglevel', $loglevel);
1750         }
1751
1752         info( t("Log settings updated.") );
1753         goaway($a->get_baseurl(true) . '/admin/logs' );
1754         return; // NOTREACHED
1755 }
1756
1757 /**
1758  * @brief Generates admin panel subpage for configuration of the logs
1759  *
1760  * This function take the view/templates/admin_logs.tpl file and generates a
1761  * page where admin can configure the logging of friendica.
1762  *
1763  * Displaying the log is separated from the log config as the logfile can get
1764  * big depending on the settings and changing settings regarding the logs can
1765  * thus waste bandwidth.
1766  *
1767  * The string returned contains the content of the template file with replaced
1768  * macros.
1769  *
1770  * @param App $a
1771  * @return string
1772  */
1773 function admin_page_logs(&$a){
1774
1775         $log_choices = array(
1776                 LOGGER_NORMAL => 'Normal',
1777                 LOGGER_TRACE => 'Trace',
1778                 LOGGER_DEBUG => 'Debug',
1779                 LOGGER_DATA => 'Data',
1780                 LOGGER_ALL => 'All'
1781         );
1782
1783         $t = get_markup_template("admin_logs.tpl");
1784
1785         return replace_macros($t, array(
1786                 '$title' => t('Administration'),
1787                 '$page' => t('Logs'),
1788                 '$submit' => t('Save Settings'),
1789                 '$clear' => t('Clear'),
1790                 '$baseurl' => $a->get_baseurl(true),
1791                 '$logname' =>  get_config('system','logfile'),
1792
1793                 // name, label, value, help string, extra data...
1794                 '$debugging' => array('debugging', t("Enable Debugging"),get_config('system','debugging'), ""),
1795                 '$logfile' => array('logfile', t("Log file"), get_config('system','logfile'), t("Must be writable by web server. Relative to your Friendica top-level directory.")),
1796                 '$loglevel' => array('loglevel', t("Log level"), get_config('system','loglevel'), "", $log_choices),
1797
1798                 '$form_security_token' => get_form_security_token("admin_logs"),
1799                 '$phpheader' => t("PHP logging"),
1800                 '$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."),
1801                 '$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');",
1802         ));
1803 }
1804
1805 /**
1806  * @brief Generates admin panel subpage to view the Friendica log
1807  *
1808  * This function loads the template view/templates/admin_viewlogs.tpl to
1809  * display the systemlog content. The filename for the systemlog of friendica
1810  * is relative to the base directory and taken from the config entry 'logfile'
1811  * in the 'system' category.
1812  *
1813  * Displaying the log is separated from the log config as the logfile can get
1814  * big depending on the settings and changing settings regarding the logs can
1815  * thus waste bandwidth.
1816  *
1817  * The string returned contains the content of the template file with replaced
1818  * macros.
1819  *
1820  * @param App $a
1821  * @return string
1822  */
1823 function admin_page_viewlogs(&$a){
1824         $t = get_markup_template("admin_viewlogs.tpl");
1825         $f = get_config('system','logfile');
1826         $data = '';
1827
1828         if(!file_exists($f)) {
1829                 $data = t("Error trying to open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f exist and is readable.");
1830         }
1831         else {
1832                 $fp = fopen($f, 'r');
1833                 if(!$fp) {
1834                         $data = t("Couldn't open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f is readable.");
1835                 }
1836                 else {
1837                         $fstat = fstat($fp);
1838                         $size = $fstat['size'];
1839                         if($size != 0) {
1840                                 if($size > 5000000 || $size < 0)
1841                                         $size = 5000000;
1842                                 $seek = fseek($fp,0-$size,SEEK_END);
1843                                 if($seek === 0) {
1844                                         $data = escape_tags(fread($fp,$size));
1845                                         while(! feof($fp))
1846                                                 $data .= escape_tags(fread($fp,4096));
1847                                 }
1848                         }
1849                         fclose($fp);
1850                 }
1851         }
1852         return replace_macros($t, array(
1853                 '$title' => t('Administration'),
1854                 '$page' => t('View Logs'),
1855                 '$data' => $data,
1856                 '$logname' =>  get_config('system','logfile')
1857         ));
1858 }
1859
1860 /**
1861  * @brief Prosesses data send by the features admin page
1862  * 
1863  * @param App $a
1864  */
1865 function admin_page_features_post(&$a) {
1866
1867         check_form_security_token_redirectOnErr('/admin/features', 'admin_manage_features');
1868
1869         logger('postvars: ' . print_r($_POST,true),LOGGER_DATA);
1870
1871         $arr = array();
1872         $features = get_features(false);
1873
1874         foreach($features as $fname => $fdata) {
1875                 foreach(array_slice($fdata,1) as $f) {
1876                         $feature = $f[0];
1877                         $feature_state = 'feature_' . $feature;
1878                         $featurelock = 'featurelock_' . $feature;
1879
1880                         if(x($_POST[$feature_state]))
1881                                 $val = intval($_POST['feature_' . $feature]);
1882                         else
1883                                 $val = 0;
1884                         set_config('feature',$feature,$val);
1885
1886                         if(x($_POST[$featurelock]))
1887                                 set_config('feature_lock',$feature,$val);
1888                         else
1889                                 del_config('feature_lock',$feature);
1890                 }
1891         }
1892
1893         goaway($a->get_baseurl(true) . '/admin/features' );
1894         return; // NOTREACHED
1895 }
1896
1897 /**
1898  * @brief Subpage for global additional feature management
1899  * 
1900  * This functin generates the subpage 'Manage Additional Features'
1901  * for the admin panel. At this page the admin can set preferences
1902  * for the user settings of the 'additional features'. If needed this 
1903  * preferences can be locked through the admin.
1904  * 
1905  * The returned string contains the HTML code of the subpage 'Manage
1906  * Additional Features'
1907  * 
1908  * @param App $a
1909  * @return string
1910  */
1911 function admin_page_features(&$a) {
1912         
1913         if((argc() > 1) && (argv(1) === 'features')) {
1914                 $arr = array();
1915                 $features = get_features(false);
1916
1917                 foreach($features as $fname => $fdata) {
1918                         $arr[$fname] = array();
1919                         $arr[$fname][0] = $fdata[0];
1920                         foreach(array_slice($fdata,1) as $f) {
1921
1922                                 $set = get_config('feature',$f[0]);
1923                                 if($set === false)
1924                                         $set = $f[3];
1925                                 $arr[$fname][1][] = array(
1926                                         array('feature_' .$f[0],$f[1],$set,$f[2],array(t('Off'),t('On'))),
1927                                         array('featurelock_' .$f[0],sprintf( t('Lock feature %s'),$f[1]),(($f[4] !== false) ? "1" : ''),'',array(t('Off'),t('On')))
1928                                 );
1929                         }
1930                 }
1931                 
1932                 $tpl = get_markup_template("admin_settings_features.tpl");
1933                 $o .= replace_macros($tpl, array(
1934                         '$form_security_token' => get_form_security_token("admin_manage_features"),
1935                         '$title' => t('Manage Additional Features'),
1936                         '$features' => $arr,
1937                         '$submit' => t('Save Settings'),
1938                 ));
1939
1940                 return $o;
1941         }
1942 }