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