Merge pull request #2255 from tobiasd/federationstats

work on the admin panel
This commit is contained in:
Michael Vogel 2016-01-16 15:36:59 +01:00
commit 2077316daf
13 changed files with 413 additions and 490 deletions

View file

@ -1,261 +0,0 @@
<?php
/* update friendica */
define('APIBASE', 'http://github.com/api/v2/');
define('F9KREPO', 'friendica/friendica');
$up_totalfiles = 0;
$up_countfiles = 0;
$up_lastp = -1;
function checkUpdate(){
$r = fetch_url( APIBASE."json/repos/show/".F9KREPO."/tags" );
$tags = json_decode($r);
$tag = 0.0;
foreach ($tags->tags as $i=>$v){
$i = (float)$i;
if ($i>$tag) $tag=$i;
}
if ($tag==0.0) return false;
$f = fetch_url("https://raw.github.com/".F9KREPO."/".$tag."/boot.php","r");
preg_match("|'FRIENDICA_VERSION', *'([^']*)'|", $f, $m);
$version = $m[1];
$lv = explode(".", FRIENDICA_VERSION);
$rv = explode(".",$version);
foreach($lv as $i=>$v){
if ((int)$lv[$i] < (int)$rv[$i]) {
return array($tag, $version, "https://github.com/friendica/friendica/zipball/".$tag);
break;
}
}
return false;
}
function canWeWrite(){
$bd = dirname(dirname(__file__));
return is_writable( $bd."/boot.php" );
}
function out($txt){ echo "§".$txt."§"; ob_end_flush(); flush();}
function up_count($path){
$file_count = 0;
$dir_handle = opendir($path);
if (!$dir_handle) return -1;
while ($file = readdir($dir_handle)) {
if ($file == '.' || $file == '..') continue;
$file_count++;
if (is_dir($path . $file)){
$file_count += up_count($path . $file . DIRECTORY_SEPARATOR);
}
}
closedir($dir_handle);
return $file_count;
}
function up_unzip($file, $folder="/tmp"){
$folder.="/";
$zip = zip_open($file);
if ($zip) {
while ($zip_entry = zip_read($zip)) {
$zip_entry_name = zip_entry_name($zip_entry);
if (substr($zip_entry_name,strlen($zip_entry_name)-1,1)=="/"){
mkdir($folder.$zip_entry_name,0777, true);
} else {
$fp = fopen($folder.$zip_entry_name, "w");
if (zip_entry_open($zip, $zip_entry, "r")) {
$buf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
fwrite($fp,"$buf");
zip_entry_close($zip_entry);
fclose($fp);
}
}
}
zip_close($zip);
}
}
/**
* Walk recoursively in a folder and call a callback function on every
* dir entry.
* args:
* $dir string base dir to walk
* $callback function callback function
* $sort int 0: ascending, 1: descending
* $cb_argv any extra value passed to callback
*
* callback signature:
* function name($fn, $dir [, $argv])
* $fn string full dir entry name
* $dir string start dir path
* $argv any user value to callback
*
*/
function up_walktree($dir, $callback=Null, $sort=0, $cb_argv=Null , $startdir=Null){
if (is_null($callback)) return;
if (is_null($startdir)) $startdir = $dir;
$res = scandir($dir, $sort);
foreach($res as $i=>$v){
if ($v!="." && $v!=".."){
$fn = $dir."/".$v;
if ($sort==0) $callback($fn, $startdir, $cb_argv);
if (is_dir($fn)) up_walktree($fn, $callback, $sort, $cb_argv, $startdir);
if ($sort==1) $callback($fn, $startdir, $cb_argv);
}
}
}
function up_copy($fn, $dir){
global $up_countfiles, $up_totalfiles, $up_lastp;
$up_countfiles++; $prc=(int)(((float)$up_countfiles/(float)$up_totalfiles)*100);
if (strpos($fn, ".gitignore")>-1 || strpos($fn, ".htaccess")>-1) return;
$ddest = dirname(dirname(__file__));
$fd = str_replace($dir, $ddest, $fn);
if (is_dir($fn) && !is_dir($fd)) {
$re=mkdir($fd,0777,true);
}
if (!is_dir($fn)){
$re=copy($fn, $fd);
}
if ($re===false) {
out("ERROR. Abort.");
killme();
}
out("copy@Copy@$prc%");
}
function up_ftp($fn, $dir, $argv){
global $up_countfiles, $up_totalfiles, $up_lastp;
$up_countfiles++; $prc=(int)(((float)$up_countfiles/(float)$up_totalfiles)*100);
if (strpos($fn, ".gitignore")>-1 || strpos($fn, ".htaccess")>-1) return;
list($ddest, $conn_id) = $argv;
$l = strlen($ddest)-1;
if (substr($ddest,$l,1)=="/") $ddest = substr($ddest,0,$l);
$fd = str_replace($dir, $ddest, $fn);
if (is_dir($fn)){
if (ftp_nlist($conn_id, $fd)===false) {
$ret = ftp_mkdir($conn_id, $fd);
} else {
$ret=true;
}
} else {
$ret = ftp_put($conn_id, $fd, $fn, FTP_BINARY);
}
if (!$ret) {
out("ERROR. Abort.");
killme();
}
out("copy@Copy@$prc%");
}
function up_rm($fn, $dir){
if (is_dir($fn)){
rmdir($fn);
} else {
unlink($fn);
}
}
function up_dlfile($url, $file) {
$in = fopen ($url, "r");
$out = fopen ($file, "w");
$fs = filesize($url);
if (!$in || !$out) return false;
$s=0; $count=0;
while (!feof ($in)) {
$line = fgets ($in, 1024);
fwrite( $out, $line);
$count++; $s += strlen($line);
if ($count==50){
$count=0;
$sp=$s/1024.0; $ex="Kb";
if ($sp>1024) { $sp=$sp/1024; $ex="Mb"; }
if ($sp>1024) { $sp=$sp/1024; $ex="Gb"; }
$sp = ((int)($sp*100))/100;
out("dwl@Download@".$sp.$ex);
}
}
fclose($in);
return true;
}
function doUpdate($remotefile, $ftpdata=false){
global $up_totalfiles;
$localtmpfile = tempnam("/tmp", "fk");
out("dwl@Download@starting...");
$rt= up_dlfile($remotefile, $localtmpfile);
if ($rt==false || filesize($localtmpfile)==0){
out("dwl@Download@ERROR.");
unlink($localtmpfile);
return;
}
out("dwl@Download@Ok.");
out("unzip@Unzip@");
$tmpdirname = $localfile."ex";
mkdir($tmpdirname);
up_unzip($localtmpfile, $tmpdirname);
$basedir = glob($tmpdirname."/*"); $basedir=$basedir[0];
out ("unzip@Unzip@Ok.");
$up_totalfiles = up_count($basedir."/");
if (canWeWrite()){
out("copy@Copy@");
up_walktree($basedir, 'up_copy');
}
if ($ftpdata!==false && is_array($ftpdata) && $ftpdata['ftphost']!="" ){
out("ftpcon@Connect to FTP@");
$conn_id = ftp_connect($ftpdata['ftphost']);
$login_result = ftp_login($conn_id, $ftpdata['ftpuser'], $ftpdata['ftppwd']);
if ((!$conn_id) || (!$login_result)) {
out("ftpcon@Connect to FTP@FAILED");
up_clean($tmpdirname, $localtmpfile);
return;
} else {
out("ftpcon@Connect to FTP@Ok.");
}
out("copy@Copy@");
up_walktree($basedir, 'up_ftp', 0, array( $ftpdata['ftppath'], $conn_id));
ftp_close($conn_id);
}
up_clean($tmpdirname, $localtmpfile);
}
function up_clean($tmpdirname, $localtmpfile){
out("clean@Clean up@");
unlink($localtmpfile);
up_walktree($tmpdirname, 'up_rm', 1);
rmdir($tmpdirname);
out("clean@Clean up@Ok.");
}

11
library/Chart.js-1.0.2/Chart.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,7 @@
Copyright (c) 2013-2015 Nick Downie
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,20 @@
# Chart.js
[![Build Status](https://travis-ci.org/nnnick/Chart.js.svg?branch=master)](https://travis-ci.org/nnnick/Chart.js) [![Code Climate](https://codeclimate.com/github/nnnick/Chart.js/badges/gpa.svg)](https://codeclimate.com/github/nnnick/Chart.js)
*Simple HTML5 Charts using the canvas element* [chartjs.org](http://www.chartjs.org)
## Documentation
You can find documentation at [chartjs.org/docs](http://www.chartjs.org/docs/). The markdown files that build the site are available under `/docs`. Please note - in some of the json examples of configuration you might notice some liquid tags - this is just for the generating the site html, please disregard.
## Bugs, issues and contributing
Before submitting an issue or a pull request to the project, please take a moment to look over the [contributing guidelines](https://github.com/nnnick/Chart.js/blob/master/CONTRIBUTING.md) first.
For support using Chart.js, please post questions with the [`chartjs` tag on Stack Overflow](http://stackoverflow.com/questions/tagged/chartjs).
## License
Chart.js is available under the [MIT license](http://opensource.org/licenses/MIT).

View file

@ -3,13 +3,21 @@
/** /**
* Friendica admin * Friendica admin
*/ */
require_once("include/remoteupdate.php");
require_once("include/enotify.php"); require_once("include/enotify.php");
require_once("include/text.php"); require_once("include/text.php");
/** /**
* @brief process send data from the admin panels subpages
*
* This function acts as relais for processing the data send from the subpages
* of the admin panel. Depending on the 1st parameter of the url (argv[1])
* specialized functions are called to process the data from the subpages.
*
* The function itself does not return anything, but the subsequencely function
* return the HTML for the pages of the admin panel.
*
* @param App $a * @param App $a
*
*/ */
function admin_post(&$a){ function admin_post(&$a){
@ -89,9 +97,6 @@ function admin_post(&$a){
case 'dbsync': case 'dbsync':
admin_page_dbsync_post($a); admin_page_dbsync_post($a);
break; break;
case 'update':
admin_page_remoteupdate_post($a);
break;
} }
} }
@ -100,6 +105,10 @@ function admin_post(&$a){
} }
/** /**
* @brief generates content of the admin panel pages
*
* This function generates the content for the admin panel.
*
* @param App $a * @param App $a
* @return string * @return string
*/ */
@ -121,22 +130,23 @@ function admin_content(&$a) {
/** /**
* Side bar links * Side bar links
*/ */
$aside_tools = Array();
// array( url, name, extra css classes ) // array( url, name, extra css classes )
$aside = Array( // not part of $aside to make the template more adjustable
$aside_sub = Array(
'site' => Array($a->get_baseurl(true)."/admin/site/", t("Site") , "site"), 'site' => Array($a->get_baseurl(true)."/admin/site/", t("Site") , "site"),
'users' => Array($a->get_baseurl(true)."/admin/users/", t("Users") , "users"), 'users' => Array($a->get_baseurl(true)."/admin/users/", t("Users") , "users"),
'plugins'=> Array($a->get_baseurl(true)."/admin/plugins/", t("Plugins") , "plugins"), 'plugins'=> Array($a->get_baseurl(true)."/admin/plugins/", t("Plugins") , "plugins"),
'themes' => Array($a->get_baseurl(true)."/admin/themes/", t("Themes") , "themes"), 'themes' => Array($a->get_baseurl(true)."/admin/themes/", t("Themes") , "themes"),
'dbsync' => Array($a->get_baseurl(true)."/admin/dbsync/", t('DB updates'), "dbsync"), 'dbsync' => Array($a->get_baseurl(true)."/admin/dbsync/", t('DB updates'), "dbsync"),
'queue' => Array($a->get_baseurl(true)."/admin/queue/", t('Inspect Queue'), "queue"), 'queue' => Array($a->get_baseurl(true)."/admin/queue/", t('Inspect Queue'), "queue"),
//'update' => Array($a->get_baseurl(true)."/admin/update/", t("Software Update") , "update") 'federation' => Array($a->get_baseurl(true)."/admin/federation/", t('Federation Statistics'), "federation"),
); );
/* get plugins admin page */ /* get plugins admin page */
$r = q("SELECT `name` FROM `addon` WHERE `plugin_admin`=1 ORDER BY `name`"); $r = q("SELECT `name` FROM `addon` WHERE `plugin_admin`=1 ORDER BY `name`");
$aside['plugins_admin']=Array(); $aside_tools['plugins_admin']=Array();
foreach ($r as $h){ foreach ($r as $h){
$plugin =$h['name']; $plugin =$h['name'];
$aside['plugins_admin'][] = Array($a->get_baseurl(true)."/admin/plugins/".$plugin, $plugin, "plugin"); $aside['plugins_admin'][] = Array($a->get_baseurl(true)."/admin/plugins/".$plugin, $plugin, "plugin");
@ -144,13 +154,15 @@ function admin_content(&$a) {
$a->plugins_admin[] = $plugin; $a->plugins_admin[] = $plugin;
} }
$aside['logs'] = Array($a->get_baseurl(true)."/admin/logs/", t("Logs"), "logs"); $aside_tools['logs'] = Array($a->get_baseurl(true)."/admin/logs/", t("Logs"), "logs");
$aside['diagnostics_probe'] = Array($a->get_baseurl(true).'/probe/', t('probe address'), 'probe'); $aside_tools['viewlogs'] = Array($a->get_baseurl(true)."/admin/viewlogs/", t("View Logs"), 'viewlogs');
$aside['diagnostics_webfinger'] = Array($a->get_baseurl(true).'/webfinger/', t('check webfinger'), 'webfinger'); $aside_tools['diagnostics_probe'] = Array($a->get_baseurl(true).'/probe/', t('probe address'), 'probe');
$aside_tools['diagnostics_webfinger'] = Array($a->get_baseurl(true).'/webfinger/', t('check webfinger'), 'webfinger');
$t = get_markup_template("admin_aside.tpl"); $t = get_markup_template("admin_aside.tpl");
$a->page['aside'] .= replace_macros( $t, array( $a->page['aside'] .= replace_macros( $t, array(
'$admin' => $aside, '$admin' => $aside_tools,
'$subpages' => $aside_sub,
'$admtxt' => t('Admin'), '$admtxt' => t('Admin'),
'$plugadmtxt' => t('Plugin Features'), '$plugadmtxt' => t('Plugin Features'),
'$logtxt' => t('Logs'), '$logtxt' => t('Logs'),
@ -183,15 +195,18 @@ function admin_content(&$a) {
case 'logs': case 'logs':
$o = admin_page_logs($a); $o = admin_page_logs($a);
break; break;
case 'viewlogs':
$o = admin_page_viewlogs($a);
break;
case 'dbsync': case 'dbsync':
$o = admin_page_dbsync($a); $o = admin_page_dbsync($a);
break; break;
case 'update':
$o = admin_page_remoteupdate($a);
break;
case 'queue': case 'queue':
$o = admin_page_queue($a); $o = admin_page_queue($a);
break; break;
case 'federation':
$o = admin_page_federation($a);
break;
default: default:
notice( t("Item not found.") ); notice( t("Item not found.") );
} }
@ -209,9 +224,127 @@ function admin_content(&$a) {
} }
/** /**
* Admin Inspect Queue Page * @brief subpage with some stats about "the federation" network
*
* This function generates the "Federation Statistics" subpage for the admin
* panel. The page lists some numbers to the part of "The Federation" known to
* the node. This data includes the different connected networks (e.g.
* Diaspora, Hubzilla, GNU Social) and the used versions in the different
* networks.
*
* The returned string contains the HTML code of the subpage for display.
*
* @param App $a * @param App $a
* return string * @return string
*/
function admin_page_federation(&$a) {
// get counts on active friendica, diaspora, redmatrix, hubzilla, gnu
// social and statusnet nodes this node is knowing
//
// We are looking for the following platforms in the DB, "Red" should find
// all variants of that platform ID string as the q() function is stripping
// off one % two of them are needed in the query
// Add more platforms if you like, when one returns 0 known nodes it is not
// displayed on the stats page.
$platforms = array('Friendica', 'Diaspora', '%%red%%', 'Hubzilla', 'GNU Social', 'StatusNet');
$counts = array();
foreach ($platforms as $p) {
// get a total count for the platform, the name and version of the
// highest version and the protocol tpe
$c = q('SELECT count(*) AS total, platform, network, version FROM gserver
WHERE platform LIKE "%s" AND last_contact > last_failure
ORDER BY version ASC;', $p);
// what versions for that platform do we know at all?
// again only the active nodes
$v = q('SELECT count(*) AS total, version FROM gserver
WHERE last_contact > last_failure AND platform LIKE "%s"
GROUP BY version
ORDER BY version;', $p);
//
// clean up version numbers
//
// in the DB the Diaspora versions have the format x.x.x.x-xx the last
// part (-xx) should be removed to clean up the versions from the "head
// commit" information and combined into a single entry for x.x.x.x
if ($p=='Diaspora') {
$newV = array();
$newVv = array();
foreach($v as $vv) {
$newVC = $vv['total'];
$newVV = $vv['version'];
$posDash = strpos($newVV, '-');
if ($posDash)
$newVV = substr($newVV, 0, $posDash);
if (isset($newV[$newVV]))
{
$newV[$newVV] += $newVC;
} else {
$newV[$newVV] = $newVC;
}
}
foreach ($newV as $key => $value) {
array_push($newVv, array('total'=>$value, 'version'=>$key));
}
$v = $newVv;
}
// early friendica versions have the format x.x.xxxx where xxxx is the
// DB version stamp; those should be operated out and versions be
// conbined
if ($p=='Friendica') {
$newV = array();
$newVv = array();
foreach ($v as $vv) {
$newVC = $vv['total'];
$newVV = $vv['version'];
$lastDot = strrpos($newVV,'.');
$len = strlen($newVV)-1;
if (($lastDot == $len-4) && (!strrpos($newVV,'-rc')==$len-3))
$newVV = substr($newVV, 0, $lastDot);
if (isset($newV[$newVV]))
{
$newV[$newVV] += $newVC;
} else {
$newV[$newVV] = $newVC;
}
}
foreach ($newV as $key => $value) {
array_push($newVv, array('total'=>$value, 'version'=>$key));
}
$v = $newVv;
}
// the 3rd array item is needed for the JavaScript graphs as JS does
// not like some characters in the names of variables...
$counts[$p]=array($c[0], $v, str_replace(array(' ','%'),'',$p));
}
// some helpful text
$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.');
$hint = t('The <em>Auto Discovered Contact Directory</em> feature is not enabled, it will improve the data displayed here.');
// load the template, replace the macros and return the page content
$t = get_markup_template("admin_federation.tpl");
return replace_macros($t, array(
'$title' => t('Administration'),
'$page' => t('Federation Statistics'),
'$intro' => $intro,
'$hint' => $hint,
'$autoactive' => get_config('system', 'poco_completion'),
'$counts' => $counts,
'$version' => FRIENDICA_VERSION,
'$legendtext' => t('Currently this node is aware of nodes from the following platforms:'),
'$baseurl' => $a->get_baseurl(),
));
}
/**
* @brief Admin Inspect Queue Page
*
* Generates a page for the admin to have a look into the current queue of
* postings that are not deliverabke. Shown are the name and url of the
* recipient, the delivery network and the dates when the posting was generated
* and the last time tried to deliver the posting.
*
* The returned string holds the content of the page.
*
* @param App $a
* @return string
*/ */
function admin_page_queue(&$a) { function admin_page_queue(&$a) {
// get content from the queue table // get content from the queue table
@ -233,7 +366,13 @@ function admin_page_queue(&$a) {
)); ));
} }
/** /**
* Admin Summary Page * @brief Admin Summary Page
*
* The summary page is the "start page" of the admin panel. It gives the admin
* a first overview of the open adminastrative tasks.
*
* The returned string contains the HTML content of the generated page.
*
* @param App $a * @param App $a
* @return string * @return string
*/ */
@ -286,7 +425,7 @@ function admin_page_summary(&$a) {
/** /**
* Admin Site Page * @brief process send data from Admin Site Page
* @param App $a * @param App $a
*/ */
function admin_page_site_post(&$a){ function admin_page_site_post(&$a){
@ -601,6 +740,10 @@ function admin_page_site_post(&$a){
} }
/** /**
* @brief generate Admin Site subpage
*
* This function generates the main configuration page of the admin panel.
*
* @param App $a * @param App $a
* @return string * @return string
*/ */
@ -811,7 +954,18 @@ function admin_page_site(&$a) {
} }
/**
* @brief generates admin panel subpage for DB syncronization
*
* This page checks if the database of friendica is in sync with the specs.
* Should this not be the case, it attemps to sync the structure and notifies
* the admin if the automatic process was failing.
*
* The returned string holds the HTML code of the page.
*
* @param App $a
* @return string
**/
function admin_page_dbsync(&$a) { function admin_page_dbsync(&$a) {
$o = ''; $o = '';
@ -891,8 +1045,7 @@ function admin_page_dbsync(&$a) {
} }
/** /**
* Users admin page * @brief process data send by Users admin page
*
* @param App $a * @param App $a
*/ */
function admin_page_users_post(&$a){ function admin_page_users_post(&$a){
@ -987,6 +1140,14 @@ function admin_page_users_post(&$a){
} }
/** /**
* @brief admin panel subpage for User management
*
* This function generates the admin panel page for user management of the
* node. It offers functionality to add/block/delete users and offers some
* statistics about the userbase.
*
* The returned string holds the HTML code of the page.
*
* @param App $a * @param App $a
* @return string * @return string
*/ */
@ -1147,7 +1308,17 @@ function admin_page_users(&$a){
/** /**
* Plugins admin page * @brief Plugins admin page
*
* This function generates the admin panel page for managing plugins on the
* friendica node. If a plugin name is given a single page showing the details
* for this addon is generated. If no name is given, a list of available
* plugins is shown.
*
* The template used for displaying the list of plugins and the details of the
* plugin are the same as used for the templates.
*
* The returned string returned hulds the HTML code of the page.
* *
* @param App $a * @param App $a
* @return string * @return string
@ -1276,6 +1447,8 @@ function admin_page_plugins(&$a){
'$baseurl' => $a->get_baseurl(true), '$baseurl' => $a->get_baseurl(true),
'$function' => 'plugins', '$function' => 'plugins',
'$plugins' => $plugins, '$plugins' => $plugins,
'$pcount' => count($plugins),
'$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'),
'$form_security_token' => get_form_security_token("admin_themes"), '$form_security_token' => get_form_security_token("admin_themes"),
)); ));
} }
@ -1340,7 +1513,17 @@ function rebuild_theme_table($themes) {
/** /**
* Themes admin page * @brief Themes admin page
*
* This function generates the admin panel page to control the themes available
* on the friendica node. If the name of a theme is given as parameter a page
* with the details for the theme is shown. Otherwise a list of available
* themes is generated.
*
* The template used for displaying the list of themes and the details of the
* themes are the same as used for the plugins.
*
* The returned string contains the HTML code of the admin panel page.
* *
* @param App $a * @param App $a
* @return string * @return string
@ -1452,6 +1635,7 @@ function admin_page_themes(&$a){
if(! stristr($screenshot[0],$theme)) if(! stristr($screenshot[0],$theme))
$screenshot = null; $screenshot = null;
$t = get_markup_template("admin_plugins_details.tpl"); $t = get_markup_template("admin_plugins_details.tpl");
return replace_macros($t, array( return replace_macros($t, array(
'$title' => t('Administration'), '$title' => t('Administration'),
@ -1459,7 +1643,6 @@ function admin_page_themes(&$a){
'$toggle' => t('Toggle'), '$toggle' => t('Toggle'),
'$settings' => t('Settings'), '$settings' => t('Settings'),
'$baseurl' => $a->get_baseurl(true), '$baseurl' => $a->get_baseurl(true),
'$plugin' => $theme, '$plugin' => $theme,
'$status' => $status, '$status' => $status,
'$action' => $action, '$action' => $action,
@ -1512,6 +1695,8 @@ function admin_page_themes(&$a){
'$baseurl' => $a->get_baseurl(true), '$baseurl' => $a->get_baseurl(true),
'$function' => 'themes', '$function' => 'themes',
'$plugins' => $xthemes, '$plugins' => $xthemes,
'$pcount' => count($themes),
'$noplugshint' => sprintf(t('No themes found on the system. They should be paced in %1$s'),'<code>/view/themes</code>'),
'$experimental' => t('[Experimental]'), '$experimental' => t('[Experimental]'),
'$unsupported' => t('[Unsupported]'), '$unsupported' => t('[Unsupported]'),
'$form_security_token' => get_form_security_token("admin_themes"), '$form_security_token' => get_form_security_token("admin_themes"),
@ -1520,8 +1705,7 @@ function admin_page_themes(&$a){
/** /**
* Logs admin page * @brief prosesses data send by Logs admin page
*
* @param App $a * @param App $a
*/ */
@ -1536,8 +1720,6 @@ function admin_page_logs_post(&$a) {
set_config('system','logfile', $logfile); set_config('system','logfile', $logfile);
set_config('system','debugging', $debugging); set_config('system','debugging', $debugging);
set_config('system','loglevel', $loglevel); set_config('system','loglevel', $loglevel);
} }
info( t("Log settings updated.") ); info( t("Log settings updated.") );
@ -1546,6 +1728,18 @@ function admin_page_logs_post(&$a) {
} }
/** /**
* @brief generates admin panel subpage for configuration of the logs
*
* This function take the view/templates/admin_logs.tpl file and generates a
* page where admin can configure the logging of friendica.
*
* Displaying the log is separated from the log config as the logfile can get
* big depending on the settings and changing settings regarding the logs can
* thus waste bandwidth.
*
* The string returned contains the content of the template file with replaced
* macros.
*
* @param App $a * @param App $a
* @return string * @return string
*/ */
@ -1561,13 +1755,51 @@ function admin_page_logs(&$a){
$t = get_markup_template("admin_logs.tpl"); $t = get_markup_template("admin_logs.tpl");
$f = get_config('system','logfile'); return replace_macros($t, array(
'$title' => t('Administration'),
'$page' => t('Logs'),
'$submit' => t('Save Settings'),
'$clear' => t('Clear'),
'$baseurl' => $a->get_baseurl(true),
'$logname' => get_config('system','logfile'),
// name, label, value, help string, extra data...
'$debugging' => array('debugging', t("Enable Debugging"),get_config('system','debugging'), ""),
'$logfile' => array('logfile', t("Log file"), get_config('system','logfile'), t("Must be writable by web server. Relative to your Friendica top-level directory.")),
'$loglevel' => array('loglevel', t("Log level"), get_config('system','loglevel'), "", $log_choices),
'$form_security_token' => get_form_security_token("admin_logs"),
'$phpheader' => t("PHP logging"),
'$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."),
'$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');",
));
}
/**
* @brief generates admin panel subpage to view the Friendica log
*
* This function loads the template view/templates/admin_viewlogs.tpl to
* display the systemlog content. The filename for the systemlog of friendica
* is relative to the base directory and taken from the config entry 'logfile'
* in the 'system' category.
*
* Displaying the log is separated from the log config as the logfile can get
* big depending on the settings and changing settings regarding the logs can
* thus waste bandwidth.
*
* The string returned contains the content of the template file with replaced
* macros.
*
* @param App $a
* @return string
*/
function admin_page_viewlogs(&$a){
$t = get_markup_template("admin_viewlogs.tpl");
$f = get_config('system','logfile');
$data = ''; $data = '';
if(!file_exists($f)) { if(!file_exists($f)) {
$data = t("Error trying to open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f exist and is $data = t("Error trying to open <strong>$f</strong> log file.\r\n<br/>Check to see if file $f exist and is readable.");
readable.");
} }
else { else {
$fp = fopen($f, 'r'); $fp = fopen($f, 'r');
@ -1591,80 +1823,10 @@ readable.");
fclose($fp); fclose($fp);
} }
} }
return replace_macros($t, array( return replace_macros($t, array(
'$title' => t('Administration'), '$title' => t('Administration'),
'$page' => t('Logs'), '$page' => t('View Logs'),
'$submit' => t('Save Settings'),
'$clear' => t('Clear'),
'$data' => $data, '$data' => $data,
'$baseurl' => $a->get_baseurl(true), '$logname' => get_config('system','logfile')
'$logname' => get_config('system','logfile'),
// name, label, value, help string, extra data...
'$debugging' => array('debugging', t("Enable Debugging"),get_config('system','debugging'), ""),
'$logfile' => array('logfile', t("Log file"), get_config('system','logfile'), t("Must be writable by web server. Relative to your Friendica top-level directory.")),
'$loglevel' => array('loglevel', t("Log level"), get_config('system','loglevel'), "", $log_choices),
'$form_security_token' => get_form_security_token("admin_logs"),
)); ));
} }
/**
* @param App $a
*/
function admin_page_remoteupdate_post(&$a) {
// this function should be called via ajax post
if(!is_site_admin()) {
return;
}
if (x($_POST,'remotefile') && $_POST['remotefile']!=""){
$remotefile = $_POST['remotefile'];
$ftpdata = (x($_POST['ftphost'])?$_POST:false);
doUpdate($remotefile, $ftpdata);
} else {
echo "No remote file to download. Abort!";
}
killme();
}
/**
* @param App $a
* @return string
*/
function admin_page_remoteupdate(&$a) {
if(!is_site_admin()) {
return login(false);
}
$canwrite = canWeWrite();
$canftp = function_exists('ftp_connect');
$needupdate = true;
$u = checkUpdate();
if (!is_array($u)){
$needupdate = false;
$u = array('','','');
}
$tpl = get_markup_template("admin_remoteupdate.tpl");
return replace_macros($tpl, array(
'$baseurl' => $a->get_baseurl(true),
'$submit' => t("Update now"),
'$close' => t("Close"),
'$localversion' => FRIENDICA_VERSION,
'$remoteversion' => $u[1],
'$needupdate' => $needupdate,
'$canwrite' => $canwrite,
'$canftp' => $canftp,
'$ftphost' => array('ftphost', t("FTP Host"), '',''),
'$ftppath' => array('ftppath', t("FTP Path"), '/',''),
'$ftpuser' => array('ftpuser', t("FTP User"), '',''),
'$ftppwd' => array('ftppwd', t("FTP Password"), '',''),
'$remotefile'=>array('remotefile','', $u['2'],''),
));
}

View file

@ -177,7 +177,7 @@
content: "Z"; content: "Z";
} }
.shashape.hash:before{ .shashape.hash:before{
content: "/"; content: "#";
} }
.shashape.tag:before{ .shashape.tag:before{
content: "="; content: "=";

View file

@ -276,9 +276,15 @@ a {
#poke-recip-label, #poke-action-label, #prvmail-message-label { #poke-recip-label, #poke-action-label, #prvmail-message-label {
margin: 10px 0 10px; margin: 10px 0 10px;
} }
.version-match {
font-weight: bold;
color: #00a700;
}
ul.federation-stats,
ul.credits { ul.credits {
list-style: none; list-style: none;
} }
ul.federation-stats li,
ul.credits li { ul.credits li {
float: left; float: left;
width: 240px; width: 240px;

View file

@ -10,14 +10,12 @@
}); });
}); });
</script> </script>
<h4><a href="{{$admurl}}">{{$admtxt}}</a></h4> <h4><a href="{{$admurl}}">{{$admtxt}}</a></h4>
<ul class='admin linklist'> <ul class='admin linklist'>
<li class='admin link button {{$admin.site.2}}'><a href='{{$admin.site.0}}'>{{$admin.site.1}}</a></li> {{foreach $subpages as $page}}
<li class='admin link button {{$admin.users.2}}'><a href='{{$admin.users.0}}'>{{$admin.users.1}}</a><span id='pending-update' title='{{$h_pending}}'></span></li> <li class='admin link button {{$page.2}}'><a href='{{$page.0}}'>{{$page.1}}</a></li>
<li class='admin link button {{$admin.plugins.2}}'><a href='{{$admin.plugins.0}}'>{{$admin.plugins.1}}</a></li> {{/foreach}}
<li class='admin link button {{$admin.themes.2}}'><a href='{{$admin.themes.0}}'>{{$admin.themes.1}}</a></li>
<li class='admin link button {{$admin.dbsync.2}}'><a href='{{$admin.dbsync.0}}'>{{$admin.dbsync.1}}</a></li>
<li class='admin link button {{$admin.queue.2}}'><a href='{{$admin.queue.0}}'>{{$admin.queue.1}}</a></li>
</ul> </ul>
{{if $admin.update}} {{if $admin.update}}
@ -39,6 +37,7 @@
<h4>{{$logtxt}}</h4> <h4>{{$logtxt}}</h4>
<ul class='admin linklist'> <ul class='admin linklist'>
<li class='admin link button {{$admin.logs.2}}'><a href='{{$admin.logs.0}}'>{{$admin.logs.1}}</a></li> <li class='admin link button {{$admin.logs.2}}'><a href='{{$admin.logs.0}}'>{{$admin.logs.1}}</a></li>
<li class='admin link button {{$admin.viewlogs.2}}'><a href='{{$admin.viewlogs.0}}'>{{$admin.viewlogs.1}}</a></li>
</ul> </ul>
<h4>{{$diagnosticstxt}}</h4> <h4>{{$diagnosticstxt}}</h4>

View file

@ -0,0 +1,64 @@
<script src="{{$baseurl}}/library/Chart.js-1.0.2/Chart.min.js"></script>
<canvas id="FederationChart" style="width: 400px; height: 400px; float: right; margin: 20px;"></canvas>
<div id="adminpage">
<h1>{{$title}} - {{$page}}</h1>
<p>{{$intro}}</p>
{{if not $autoactive}}
<p class="error-message">{{$hint}}</p>
{{/if}}
<p>{{$legendtext}}
<ul>
{{foreach $counts as $c}}
{{if $c[0]['total'] > 0}}
<li>{{$c[0]['platform']}} ({{$c[0]['total']}})</li>
{{/if}}
{{/foreach}}
</ul>
</p>
</div>
<script>
var FedData = [
{{foreach $counts as $c}}
{ value: {{$c[0]['total']}}, label: "{{$c[0]['platform']}}", color: "#90EE90", highlight: "#EE90A1", },
{{/foreach}}
];
var ctx = document.getElementById("FederationChart").getContext("2d");
var myDoughnutChart = new Chart(ctx).Doughnut(FedData,
{
animateRotate : false,
});
document.getElementById('FederationLegend').innerHTML = myDoughnutChart.generateLegend();
</script>
<table style="width: 100%">
{{foreach $counts as $c}}
{{if $c[0]['total'] > 0}}
<tr>
<th>{{$c[0]['platform']}}</th>
<th><strong>{{$c[0]['total']}}</strong></td>
<td>{{$c[0]['network']}}</td>
</tr>
<tr>
<td colspan="3" style="border-bottom: 1px solid #000;">
<canvas id="{{$c[2]}}Chart" style="width: 240px; height: 240px; float: left;
margin: 20px;"></canvas>
<script>
var {{$c[2]}}data = [
{{foreach $c[1] as $v}}
{ value: {{$v['total']}}, label: '{{$v['version']}}', color: "#90EE90", highlight: "#EE90A1",},
{{/foreach}}
];
var ctx = document.getElementById("{{$c[2]}}Chart").getContext("2d");
var my{{$c[2]}}DoughnutChart = new Chart(ctx).Doughnut({{$c[2]}}data,
{animateRotate : false,});
</script>
<ul class="federation-stats">
{{foreach $c[1] as $v}}
<li>{{if ($c[0]['platform']==='Friendica' and $version===$v['version']) }}<span class="version-match">{{$v['version']}}</span>{{else}}{{$v['version']}}{{/if}} ({{$v['total']}})</li>
{{/foreach}}
</ul>
</td>
</tr>
{{/if}}
{{/foreach}}
</table>

View file

@ -12,8 +12,10 @@
</form> </form>
<h3>{{$logname}}</h3> <h2>{{$phpheader}}</h2>
<div style="width:100%; height:400px; overflow: auto; "><pre>{{$data}}</pre></div> <div>
<!-- <iframe src='{{$baseurl}}/{{$logname}}' style="width:100%; height:400px"></iframe> --> <p>{{$phphint}}</p>
<!-- <div class="submit"><input type="submit" name="page_logs_clear_log" value="{{$clear}}" /></div> --> <pre>{{$phplogcode}}</pre>
</div>
</div> </div>

View file

@ -1,6 +1,11 @@
<div id='adminpage'> <div id='adminpage'>
<h1>{{$title}} - {{$page}}</h1> <h1>{{$title}} - {{$page}}</h1>
{{if $pcount eq 0}}
<div class="error-message">
{{$noplugshint}}
</div>
{{else}}
<a class="btn" href="{{$baseurl}}/admin/{{$function}}?a=r&amp;t={{$form_security_token}}">{{$reload}}</a> <a class="btn" href="{{$baseurl}}/admin/{{$function}}?a=r&amp;t={{$form_security_token}}">{{$reload}}</a>
<ul id='pluginslist'> <ul id='pluginslist'>
{{foreach $plugins as $p}} {{foreach $plugins as $p}}
@ -13,4 +18,5 @@
</li> </li>
{{/foreach}} {{/foreach}}
</ul> </ul>
{{/if}}
</div> </div>

View file

@ -1,99 +0,0 @@
<script src="js/jquery.htmlstream.js"></script>
<script>
/* ajax updater */
function updateEnd(data){
//$("#updatepopup .panel_text").html(data);
$("#remoteupdate_form").find("input").removeAttr('disabled');
$(".panel_action_close").fadeIn()
}
function updateOn(data){
var patt=/§([^§]*)§/g;
var matches = data.match(patt);
$(matches).each(function(id,data){
data = data.replace(/§/g,"");
d = data.split("@");
console.log(d);
elm = $("#updatepopup .panel_text #"+d[0]);
html = "<div id='"+d[0]+"' class='progress'>"+d[1]+"<span>"+d[2]+"</span></div>";
if (elm.length==0){
$("#updatepopup .panel_text").append(html);
} else {
$(elm).replaceWith(html);
}
});
}
$(function(){
$("#remoteupdate_form").submit(function(){
var data={};
$(this).find("input").each(function(i, e){
name = $(e).attr('name');
value = $(e).val();
e.disabled = true;
data[name]=value;
});
$("#updatepopup .panel_text").html("");
$("#updatepopup").show();
$("#updatepopup .panel").hide().slideDown(500);
$(".panel_action_close").hide().click(function(){
$("#updatepopup .panel").slideUp(500, function(){
$("#updatepopup").hide();
});
});
$.post(
$(this).attr('action'),
data,
updateEnd,
'text',
updateOn
);
return false;
})
});
</script>
<div id="updatepopup" class="popup">
<div class="background"></div>
<div class="panel">
<div class="panel_in">
<h1>Friendica Update</h1>
<div class="panel_text"></div>
<div class="panel_actions">
<input type="button" value="{{$close|escape:'html'}}" class="panel_action_close">
</div>
</div>
</div>
</div>
<div id="adminpage">
<dl> <dt>Your version:</dt><dd>{{$localversion}}</dd> </dl>
{{if $needupdate}}
<dl> <dt>New version:</dt><dd>{{$remoteversion}}</dd> </dl>
<form id="remoteupdate_form" method="POST" action="{{$baseurl}}/admin/update">
<input type="hidden" name="{{$remotefile.0}}" value="{{$remotefile.2|escape:'html'}}">
{{if $canwrite}}
<div class="submit"><input type="submit" name="remoteupdate" value="{{$submit|escape:'html'}}" /></div>
{{else}}
<h3>Your friendica installation is not writable by web server.</h3>
{{if $canftp}}
<p>You can try to update via FTP</p>
{{include file="field_input.tpl" field=$ftphost}}
{{include file="field_input.tpl" field=$ftppath}}
{{include file="field_input.tpl" field=$ftpuser}}
{{include file="field_password.tpl" field=$ftppwd}}
<div class="submit"><input type="submit" name="remoteupdate" value="{{$submit|escape:'html'}}" /></div>
{{/if}}
{{/if}}
</form>
{{else}}
<h4>No updates</h4>
{{/if}}
</div>

View file

@ -0,0 +1,6 @@
<div id='adminpage'>
<h1>{{$title}} - {{$page}}</h1>
<h3>{{$logname}}</h3>
<div style="width:100%; height:400px; overflow: auto; "><pre>{{$data}}</pre></div>
</div>