* CalDAV/CardDAV: continous display (like tail -f) of logs inside EGroupware

This commit is contained in:
Ralf Becker 2012-02-16 16:35:58 +00:00
parent 9d704a7c0d
commit 9dd3b99353
3 changed files with 209 additions and 25 deletions

View File

@ -50,6 +50,15 @@ class egw_tail
*/ */
protected $filename; protected $filename;
/**
* Methods allowed to call via menuaction
*
* @var array
*/
public $public_functions = array(
'download' => true,
);
/** /**
* Constructor * Constructor
* *
@ -63,7 +72,7 @@ class egw_tail
{ {
$this->filename = $filename; $this->filename = $filename;
if (!in_array($filename,$this->filenames)) $this->filenames[] = $filename; if (!$this->filenames || !in_array($filename,$this->filenames)) $this->filenames[] = $filename;
} }
} }
@ -72,6 +81,7 @@ class egw_tail
* *
* @param string $filename * @param string $filename
* @param int $start=0 last position in log-file * @param int $start=0 last position in log-file
* @throws egw_exception_wrong_parameter
*/ */
public function ajax_chunk($filename,$start=0) public function ajax_chunk($filename,$start=0)
{ {
@ -81,37 +91,87 @@ class egw_tail
} }
if ($filename[0] != '/') $filename = $GLOBALS['egw_info']['server']['files_dir'].'/'.$filename; if ($filename[0] != '/') $filename = $GLOBALS['egw_info']['server']['files_dir'].'/'.$filename;
if (!$start || $start < 0) if (file_exists($filename))
{ {
$start = filesize($filename) - 4*self::MAX_CHUNK_SIZE; $size = filesize($filename);
if ($start < 0) $start = 0; if (!$start || $start < 0 || $start > $size || $size-$start > 4*self::MAX_CHUNK_SIZE)
{
$start = $size - 4*self::MAX_CHUNK_SIZE;
if ($start < 0) $start = 0;
}
$size = egw_vfs::hsize($size);
$content = file_get_contents($filename, false, null, $start, self::MAX_CHUNK_SIZE);
$length = bytes($content);
$writable = is_writable($filename) || is_writable(dirname($filename));
}
else
{
$start = $length = 0;
$content = '';
$writable = $size = false;
} }
$content = file_get_contents($filename, false, null, $start, self::MAX_CHUNK_SIZE);
$length = bytes($content);
$response = egw_json_response::get(); $response = egw_json_response::get();
$response->data(array( // send all responses as data $response->data(array( // send all responses as data
'size' => $size,
'writable' => $writable,
'next' => $start + $length, 'next' => $start + $length,
'length' => $length, 'length' => $length,
'content' => $content, 'content' => $content,
)); ));
} }
/**
* Ajax callback to delete log-file
*
* @param string $filename
* @param boolean $truncate=false true: truncate file, false: delete file
* @throws egw_exception_wrong_parameter
*/
public function ajax_delete($filename,$truncate=false)
{
if (!in_array($filename,$this->filenames))
{
throw new egw_exception_wrong_parameter("Not allowed to view '$filename'!");
}
if ($filename[0] != '/') $filename = $GLOBALS['egw_info']['server']['files_dir'].'/'.$filename;
if ($truncate)
{
file_put_contents($filename, '');
}
else
{
unlink($filename);
}
}
/** /**
* Return html & javascript for logviewer * Return html & javascript for logviewer
* *
* @param string $header=null default $this->filename
* @param string $id='log' * @param string $id='log'
* @return string * @return string
* @throws egw_exception_wrong_parameter
*/ */
public function show($id='log') public function show($header=null, $id='log')
{ {
if (!isset($this->filename)) if (!isset($this->filename))
{ {
throw new egw_exception_wrong_parameter("Must be instanciated with filename!"); throw new egw_exception_wrong_parameter("Must be instanciated with filename!");
} }
if (is_null($header)) $header = $this->filename;
return ' return '
<script type="text/javascript"> <script type="text/javascript">
var '.$id.'_tail_start = 0; var '.$id.'_tail_start = 0;
function button_'.$id.'(button)
{
if (button.id != "clear_'.$id.'")
{
var ajax = new egw_json_request("home.egw_tail.ajax_delete",["'.$this->filename.'",button.id=="empty_'.$id.'"]);
ajax.sendRequest(true);
}
$j("#'.$id.'").text("");
}
function refresh_'.$id.'() function refresh_'.$id.'()
{ {
var ajax = new egw_json_request("home.egw_tail.ajax_chunk",["'.$this->filename.'",'.$id.'_tail_start]); var ajax = new egw_json_request("home.egw_tail.ajax_chunk",["'.$this->filename.'",'.$id.'_tail_start]);
@ -121,31 +181,84 @@ function refresh_'.$id.'()
var log = $j("#'.$id.'").append(_data.content.replace(/</g,"&lt;")); var log = $j("#'.$id.'").append(_data.content.replace(/</g,"&lt;"));
log.animate({ scrollTop: log.attr("scrollHeight") - log.height() + 20 }, 500); log.animate({ scrollTop: log.attr("scrollHeight") - log.height() + 20 }, 500);
} }
if (_data.size === false)
{
$j("#download_'.$id.'").hide();
}
else
{
$j("#download_'.$id.'").show().attr("title","'.lang('Size').': "+_data.size);
}
if (_data.writable === false)
{
$j("#delete_'.$id.'").hide();
$j("#empty_'.$id.'").hide();
}
else
{
$j("#delete_'.$id.'").show();
$j("#empty_'.$id.'").show();
}
window.setTimeout(refresh_'.$id.',_data.length?200:2000); window.setTimeout(refresh_'.$id.',_data.length?200:2000);
}); });
} }
function resize_'.$id.'()
{
$j("#'.$id.'").width(egw_getWindowInnerWidth()-20).height(egw_getWindowInnerHeight()-33);
}
$j(document).ready(function() $j(document).ready(function()
{ {
var log = $j("#'.$id.'"); resize_'.$id.'();
log.width(log.width());
refresh_'.$id.'(); refresh_'.$id.'();
}); });
$j(window).resize(resize_'.$id.');
</script> </script>
<pre class="tail" id="'.$id.'" style="width: 100%; border: 2px groove silver; height: 480px; overflow: auto;"></pre>'; <p style="float: left; margin: 5px"><b>'.htmlspecialchars($header).'</b></p>
<div style="float: right; margin: 2px; margin-right: 5px">
'.html::form(
html::input('clear_'.$id,lang('Clear window'),'button','id="clear_'.$id.'" onClick="button_'.$id.'(this)"')."\n".
html::input('delete_'.$id,lang('Delete file'),'button','id="delete_'.$id.'" onClick="button_'.$id.'(this)"')."\n".
html::input('empty_'.$id,lang('Empty file'),'button','id="empty_'.$id.'" onClick="button_'.$id.'(this)"')."\n".
html::input('download_'.$id,lang('Download'),'submit','id="download_'.$id.'"'),
'','/index.php',array(
'menuaction' => 'phpgwapi.egw_tail.download',
'filename' => $this->filename,
)).'
</div>
<pre class="tail" id="'.$id.'" style="clear: both; width: 99.5%; border: 2px groove silver; margin-bottom: 0; overflow: auto;"></pre>';
}
/**
* Download a file specified per GET parameter (must be in $this->filesnames!)
*
* @throws egw_exception_wrong_parameter
*/
public function download()
{
$filename = $_GET['filename'];
if (!in_array($filename,$this->filenames))
{
throw new egw_exception_wrong_parameter("Not allowed to download '$filename'!");
}
html::content_header(basename($filename),'text/plain');
if ($filename[0] != '/') $filename = $GLOBALS['egw_info']['server']['files_dir'].'/'.$filename;
while(ob_get_level()) ob_end_clean(); // stop all output buffering, to NOT run into memory_limit
readfile($filename);
common::egw_exit();
} }
} }
// some testcode, if this file is called via it's URL // some testcode, if this file is called via it's URL (you need to uncomment and adapt filename!)
if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__) /*if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__)
{ {
$GLOBALS['egw_info'] = array( $GLOBALS['egw_info'] = array(
'flags' => array( 'flags' => array(
'currentapp' => 'admin', 'currentapp' => 'admin',
'nonavbar' => true,
), ),
); );
include_once '../../header.inc.php'; include_once '../../header.inc.php';
$error_log = new egw_tail($file='/opt/local/apache2/logs/error_log'); $error_log = new egw_tail('/opt/local/apache2/logs/error_log');
echo "<h3>$file</h3>\n";
echo $error_log->show(); echo $error_log->show();
} }*/

View File

@ -16,6 +16,10 @@
*/ */
class groupdav_hooks class groupdav_hooks
{ {
public $public_functions = array(
'log' => true,
);
/** /**
* Show GroupDAV preferences link in preferences * Show GroupDAV preferences link in preferences
* *
@ -69,23 +73,88 @@ class groupdav_hooks
} }
} }
$settings[] = array(
'type' => 'section',
'title' => 'Debuging',
);
$settings['debug_level'] = array( $settings['debug_level'] = array(
'type' => 'select', 'type' => 'select',
'label' => 'Debug level for Apache/PHP error-log', 'label' => 'Debug level for Apache/PHP error-log',
'name' => 'debug_level', 'name' => 'debug_level',
'help' => 'Enables debug-messages to Apache/PHP error-log, allowing to diagnose problems on a per user basis.', 'help' => 'Enables debug-messages to Apache/PHP error-log, allowing to diagnose problems on a per user basis.',
'values' => array( 'values' => array(
'0' => 'Off', '0' => lang('Off'),
'r' => 'Requests and truncated responses', 'r' => lang('Requests and truncated responses'),
'f' => 'Requests and full responses to files directory', 'f' => lang('Requests and full responses to files directory'),
'1' => 'Debug 1 - function calls',
'2' => 'Debug 2 - more info',
'3' => 'Debug 3 - complete $_SERVER array',
), ),
'xmlrpc' => true, 'xmlrpc' => true,
'admin' => false, 'admin' => false,
'default' => '0', 'default' => '0',
); );
if ($GLOBALS['type'] === 'forced' || $GLOBALS['type'] === 'user' &&
$GLOBALS['egw_info']['user']['preferences']['groupdav']['debug-log'] !== 'never')
{
if ($GLOBALS['type'] === 'user')
{
$logs = array();
if (($files = scandir($log_dir=$GLOBALS['egw_info']['server']['files_dir'].'/groupdav')))
{
$account_lid_len = strlen($GLOBALS['egw_info']['user']['account_lid']);
foreach($files as $log)
{
if (substr($log,0,$account_lid_len+1) == $GLOBALS['egw_info']['user']['account_lid'].'-' &&
substr($log,-4) == '.log')
{
$logs['groupdav/'.$log] = egw_time::to(filemtime($log_dir.'/'.$log)).': '.
str_replace('!','/',substr($log,$account_lid_len+1,-4));
}
}
}
$link = egw::link('/index.php',array(
'menuaction' => 'groupdav.groupdav_hooks.log',
'filename' => '',
));
$onchange = "egw_openWindowCentered('$link'+encodeURIComponent(this.value), '_blank', 1000, 500); this.value=''";
}
else // allow to force users to NOT be able to delete their profiles
{
$logs = array('never' => lang('Never'));
}
$settings['show-log'] = array(
'type' => 'select',
'label' => 'Show log of following device',
'name' => 'show-log',
'help' => lang('You need to set above debug-level to "%1" to create/update a log.',
lang('Requests and full responses to files directory')),
'values' => $logs,
'xmlrpc' => True,
'admin' => False,
'onchange' => $onchange,
);
}
return $settings; return $settings;
} }
/**
* Open log window for log-file specified in GET parameter filename (relative to files_dir)
*
* $_GET['filename'] has to be in groupdav sub-dir of files_dir and start with account_lid of current user
*
* @throws egw_exception_wrong_parameter
*/
public function log()
{
$filename = $_GET['filename'];
if (!preg_match('|^groupdav/'.preg_quote($GLOBALS['egw_info']['user']['account_lid'],'|').'-[^/]+\.log$|',$filename))
{
throw new egw_exception_wrong_parameter("Access denied to file '$filename'!");
}
$GLOBALS['egw_info']['flags']['css'] = '
body { background-color: #e0e0e0; }
pre.tail { background-color: white; padding-left: 5px; margin-left: 5px; }
';
$header = str_replace('!','/',substr($filename,10+strlen($GLOBALS['egw_info']['user']['account_lid']),-4));
$tail = new egw_tail($filename);
$GLOBALS['egw']->framework->render($tail->show($header),false,false);
}
} }

View File

@ -267,7 +267,8 @@ class uisettings
$valarray['help'], $valarray['help'],
$valarray['default'], $valarray['default'],
$valarray['run_lang'], $valarray['run_lang'],
$valarray['type'] == 'multiselect' $valarray['type'] == 'multiselect',
$valarray['onchange']
); );
break; break;
case 'check': case 'check':
@ -518,7 +519,7 @@ class uisettings
$this->t->fp('rows','section_row',True); $this->t->fp('rows','section_row',True);
} }
function create_select_box($label,$name,$values,$help='',$default='',$run_lang=True,$multiple=false) function create_select_box($label,$name,$values,$help='',$default='',$run_lang=True,$multiple=false,$onchange=null)
{ {
$_appname = $this->check_app(); $_appname = $this->check_app();
if($this->is_forced_value($_appname,$name)) if($this->is_forced_value($_appname,$name))
@ -548,7 +549,8 @@ class uisettings
} }
if (is_array($extra)) $values = $extra + (is_array($values)?$values:array($values)); if (is_array($extra)) $values = $extra + (is_array($values)?$values:array($values));
$select = html::select($GLOBALS['type'].'['.$name.']',$default,$values,true); $select = html::select($GLOBALS['type'].'['.$name.']',$default,$values,true,
$onchange?'onchange="'.str_replace('"','\\"',htmlspecialchars($onchange)).'"':'');
} }
else else
{ {