diff --git a/phpgwapi/inc/class.egw_tail.inc.php b/phpgwapi/inc/class.egw_tail.inc.php index 87c190acbd..dc6456ada6 100644 --- a/phpgwapi/inc/class.egw_tail.inc.php +++ b/phpgwapi/inc/class.egw_tail.inc.php @@ -50,6 +50,15 @@ class egw_tail */ protected $filename; + /** + * Methods allowed to call via menuaction + * + * @var array + */ + public $public_functions = array( + 'download' => true, + ); + /** * Constructor * @@ -63,7 +72,7 @@ class egw_tail { $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 int $start=0 last position in log-file + * @throws egw_exception_wrong_parameter */ 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 (!$start || $start < 0) + if (file_exists($filename)) { - $start = filesize($filename) - 4*self::MAX_CHUNK_SIZE; - if ($start < 0) $start = 0; + $size = filesize($filename); + 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->data(array( // send all responses as data + 'size' => $size, + 'writable' => $writable, 'next' => $start + $length, 'length' => $length, '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 * + * @param string $header=null default $this->filename * @param string $id='log' * @return string + * @throws egw_exception_wrong_parameter */ - public function show($id='log') + public function show($header=null, $id='log') { if (!isset($this->filename)) { throw new egw_exception_wrong_parameter("Must be instanciated with filename!"); } + if (is_null($header)) $header = $this->filename; + return ' -
';
+

'.htmlspecialchars($header).'

+
+ '.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, + )).' +
+
';
+	}
+
+	/**
+	 * 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
-if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__)
+// 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__)
 {
 	$GLOBALS['egw_info'] = array(
 		'flags' => array(
 			'currentapp' => 'admin',
+			'nonavbar' => true,
 		),
 	);
 	include_once '../../header.inc.php';
 
-	$error_log = new egw_tail($file='/opt/local/apache2/logs/error_log');
-	echo "

$file

\n"; + $error_log = new egw_tail('/opt/local/apache2/logs/error_log'); echo $error_log->show(); -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/phpgwapi/inc/class.groupdav_hooks.inc.php b/phpgwapi/inc/class.groupdav_hooks.inc.php index 5d129ce987..0d355cfaa6 100644 --- a/phpgwapi/inc/class.groupdav_hooks.inc.php +++ b/phpgwapi/inc/class.groupdav_hooks.inc.php @@ -16,6 +16,10 @@ */ class groupdav_hooks { + public $public_functions = array( + 'log' => true, + ); + /** * Show GroupDAV preferences link in preferences * @@ -69,23 +73,88 @@ class groupdav_hooks } } + $settings[] = array( + 'type' => 'section', + 'title' => 'Debuging', + ); $settings['debug_level'] = array( 'type' => 'select', 'label' => 'Debug level for Apache/PHP error-log', 'name' => 'debug_level', 'help' => 'Enables debug-messages to Apache/PHP error-log, allowing to diagnose problems on a per user basis.', 'values' => array( - '0' => 'Off', - 'r' => 'Requests and truncated responses', - 'f' => 'Requests and full responses to files directory', - '1' => 'Debug 1 - function calls', - '2' => 'Debug 2 - more info', - '3' => 'Debug 3 - complete $_SERVER array', + '0' => lang('Off'), + 'r' => lang('Requests and truncated responses'), + 'f' => lang('Requests and full responses to files directory'), ), 'xmlrpc' => true, 'admin' => false, '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; } + + /** + * 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); + } } \ No newline at end of file diff --git a/preferences/inc/class.uisettings.inc.php b/preferences/inc/class.uisettings.inc.php index 5a22c95ba3..7c14dde6d4 100755 --- a/preferences/inc/class.uisettings.inc.php +++ b/preferences/inc/class.uisettings.inc.php @@ -267,7 +267,8 @@ class uisettings $valarray['help'], $valarray['default'], $valarray['run_lang'], - $valarray['type'] == 'multiselect' + $valarray['type'] == 'multiselect', + $valarray['onchange'] ); break; case 'check': @@ -518,7 +519,7 @@ class uisettings $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(); 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)); - $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 {