From 084a5a5b9329dc2825d4e3ce3bd744dcad051dda Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 24 Oct 2012 16:59:00 +0000 Subject: [PATCH 01/14] Use new translation::detect_encoding() for encoding detection --- importexport/inc/class.importexport_import_ui.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importexport/inc/class.importexport_import_ui.inc.php b/importexport/inc/class.importexport_import_ui.inc.php index 39d15a2539..abbd19412a 100644 --- a/importexport/inc/class.importexport_import_ui.inc.php +++ b/importexport/inc/class.importexport_import_ui.inc.php @@ -62,7 +62,7 @@ // Check file encoding matches import $sample = file_get_contents($content['file']['tmp_name'],false, null, 0, 1024); $required = $options['charset'] == 'user' || !$options['charset'] ? $GLOBALS['egw_info']['user']['preferences']['common']['csv_charset'] : $options['charset']; - $encoding = mb_detect_encoding($sample,$required,true); + $encoding = translation::detect_encoding($sample); if($encoding && strtoupper($required) != strtoupper($encoding)) { $this->message = lang("Encoding mismatch. Expected %1 file, you uploaded %2.
\n", From 7a981eba15321276fdf3fa91dccf457b3a0d19d0 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 25 Oct 2012 12:51:28 +0000 Subject: [PATCH 02/14] using pseudo-sessionid for ownCloud remote.php url too, as we otherwise generate lots of new sessions --- phpgwapi/inc/class.egw_session.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpgwapi/inc/class.egw_session.inc.php b/phpgwapi/inc/class.egw_session.inc.php index 3918552849..f5ab6af9c8 100644 --- a/phpgwapi/inc/class.egw_session.inc.php +++ b/phpgwapi/inc/class.egw_session.inc.php @@ -783,7 +783,7 @@ class egw_session // for WebDAV and GroupDAV we use a pseudo sessionid created from md5(user:passwd) // --> allows this stateless protocolls which use basic auth to use sessions! if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) && - in_array(basename($_SERVER['SCRIPT_NAME']),array('webdav.php','groupdav.php'))) + in_array(basename($_SERVER['SCRIPT_NAME']),array('webdav.php','groupdav.php','remote.php'))) { // we generate a pseudo-sessionid from the basic auth credentials $sessionid = md5($_SERVER['PHP_AUTH_USER'].':'.$_SERVER['PHP_AUTH_PW'].':'.$_SERVER['HTTP_HOST'].':'. @@ -791,7 +791,7 @@ class egw_session } // same for digest auth elseif (isset($_SERVER['PHP_AUTH_DIGEST']) && - in_array(basename($_SERVER['SCRIPT_NAME']),array('webdav.php','groupdav.php'))) + in_array(basename($_SERVER['SCRIPT_NAME']),array('webdav.php','groupdav.php','remote.php'))) { // we generate a pseudo-sessionid from the digest username, realm and nounce // can't use full $_SERVER['PHP_AUTH_DIGEST'], as it changes (contains eg. the url) From 78fc93df600ee977c22063f8391efc9f5bd35399 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 25 Oct 2012 16:16:41 +0000 Subject: [PATCH 03/14] * Addressbook/eMail: fixed wrong characterset when merge printing into eMail --- etemplate/inc/class.bo_merge.inc.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/etemplate/inc/class.bo_merge.inc.php b/etemplate/inc/class.bo_merge.inc.php index 2b7e68fd4d..9f9ad25be8 100644 --- a/etemplate/inc/class.bo_merge.inc.php +++ b/etemplate/inc/class.bo_merge.inc.php @@ -585,9 +585,10 @@ abstract class bo_merge * @param string &$err error-message on error * @param string $mimetype mimetype of complete document, eg. text/*, application/vnd.oasis.opendocument.text, application/rtf * @param array $fix=null regular expression => replacement pairs eg. to fix garbled placeholders + * @param string $charset=null charset to override default set by mimetype or export charset * @return string|boolean merged document or false on error */ - public function &merge_string($content,$ids,&$err,$mimetype,array $fix=null) + public function &merge_string($content,$ids,&$err,$mimetype,array $fix=null,$charset=null) { if ($mimetype == 'application/xml' && preg_match('/'.preg_quote('').'/',substr($content,0,200),$matches)) @@ -716,7 +717,7 @@ abstract class bo_merge $content = str_replace($match[0],$repeats,$content); } } - $content = $this->replace($content,$replacements,$mimetype,$mso_application_progid); + $content = $this->replace($content,$replacements,$mimetype,$mso_application_progid,$charset); $content = $this->process_commands($content, $replacements); @@ -795,9 +796,10 @@ abstract class bo_merge * @param array $replacements name => replacement pairs * @param string $mimetype mimetype of content * @param string $mso_application_progid='' MS Office 2003: 'Excel.Sheet' or 'Word.Document' + * @param string $charset=null charset to override default set by mimetype or export charset * @return string */ - protected function replace($content,array $replacements,$mimetype,$mso_application_progid='') + protected function replace($content,array $replacements,$mimetype,$mso_application_progid='',$charset=null) { switch($mimetype) { @@ -807,13 +809,24 @@ abstract class bo_merge case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': case 'application/xml': case 'text/xml': - case 'text/html': $is_xml = true; $charset = 'utf-8'; // xml files --> always use utf-8 break; + case 'text/html': + $is_xml = true; + if (preg_match('/ use our export-charset, defined in addressbook prefs - $charset = $this->contacts->prefs['csv_charset']; + if (empty($charset)) $charset = $this->contacts->prefs['csv_charset']; break; } //error_log(__METHOD__."('$document', ... ,$mimetype) --> $charset (egw=".translation::charset().', export='.$this->contacts->prefs['csv_charset'].')'); @@ -1452,7 +1465,7 @@ abstract class bo_merge } common::egw_exit(); } - + /** * Download document merged with contact(s) * frontend for HTTP POST requests @@ -1468,7 +1481,7 @@ abstract class bo_merge if(empty($_POST['data_document_name'])) return false; if(empty($_POST['data_document_dir'])) return false; if(empty($_POST['data_checked'])) return false; - + return $this->download( $_POST['data_document_name'], explode(',',$_POST['data_checked']), From f080e1ff34c102085a867f7ccf575373bcd806cd Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Thu, 25 Oct 2012 17:11:52 +0000 Subject: [PATCH 04/14] Hide undelete context menu option when not viewing deleted items --- timesheet/inc/class.timesheet_ui.inc.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/timesheet/inc/class.timesheet_ui.inc.php b/timesheet/inc/class.timesheet_ui.inc.php index 3e1bd93aa9..520089111a 100644 --- a/timesheet/inc/class.timesheet_ui.inc.php +++ b/timesheet/inc/class.timesheet_ui.inc.php @@ -514,6 +514,10 @@ class timesheet_ui extends timesheet_bo } //echo "

show_sums=".print_r($this->show_sums,true)."

\n"; if (!$id_only) $GLOBALS['egw']->session->appsession('index',TIMESHEET_APP,$query_in); + + // Refresh actions (undelete needs this) + $query_in['actions'] = $this->get_actions($query_in); + $query = $query_in; // keep the original query if($this->ts_viewtype == 'short') $query_in['options-selectcols'] = array('ts_quantity'=>false,'ts_unitprice'=>false,'ts_total'=>false); @@ -873,7 +877,7 @@ class timesheet_ui extends timesheet_bo 'default_cols' => '!legacy_actions', // switch legacy actions column and row off by default ); } - $content['nm']['actions'] = $this->get_actions(); + $content['nm']['actions'] = $this->get_actions($content['nm']); if($_GET['search']) { @@ -916,7 +920,7 @@ class timesheet_ui extends timesheet_bo * * @return array see nextmatch_widget::egw_actions() */ - private function get_actions() + private function get_actions(Array $query) { $actions = array( 'open' => array( // does edit if allowed, otherwise view @@ -980,14 +984,18 @@ class timesheet_ui extends timesheet_bo 'group' => ++$group, 'disableClass' => 'rowNoDelete', ), - 'undelete' => array( + ); + if ($query['col_filter']['ts_status'] == self::DELETED_STATUS) + { + $actions['undelete'] = array( 'caption' => 'Un-Delete', 'confirm' => 'Recover this entry', 'confirm_multiple' => 'Recover these entries', + 'icon' => 'revert', 'group' => $group, 'disableClass' => 'rowNoUndelete', - ) - ); + ); + } // enable additonal edit check for following actions, if they are generally available foreach(array('cat','status') as $action) { From a81d7126aab73684e2ca5aaad54ac6c64dab59ce Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 26 Oct 2012 08:50:07 +0000 Subject: [PATCH 05/14] setting and checking required PHP version of 5.3 --- setup/inc/class.setup.inc.php | 2 +- setup/inc/functions.inc.php | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/setup/inc/class.setup.inc.php b/setup/inc/class.setup.inc.php index b0ba27a179..d110a04a48 100644 --- a/setup/inc/class.setup.inc.php +++ b/setup/inc/class.setup.inc.php @@ -67,7 +67,7 @@ class setup * * @var float */ - var $required_php_version = 5.2; + var $required_php_version = 5.3; /** * PHP Version recommended for eGroupware * diff --git a/setup/inc/functions.inc.php b/setup/inc/functions.inc.php index fb584a4499..3a73b55757 100644 --- a/setup/inc/functions.inc.php +++ b/setup/inc/functions.inc.php @@ -32,13 +32,6 @@ if(file_exists('../header.inc.php')) // for an old header we need to setup a reference for the domains if (!is_array($GLOBALS['egw_domain'])) $GLOBALS['egw_domain'] =& $GLOBALS['phpgw_domain']; -if (!function_exists('version_compare'))//version_compare() is only available in PHP4.1+ -{ - echo 'eGroupWare now requires PHP 4.1 or greater.
'; - echo 'Please contact your System Administrator'; - exit; -} - /* If we included the header.inc.php, but it is somehow broken, cover ourselves... */ if(!defined('EGW_SERVER_ROOT') && !defined('EGW_INCLUDE_ROOT')) { @@ -97,3 +90,11 @@ $GLOBALS['egw_info']['server']['app_images'] = 'templates/default/images'; CreateObject('setup.setup',True,True); // setup constuctor assigns itself to $GLOBALS['egw_setup'], doing it twice fails on some php4 $GLOBALS['phpgw_setup'] =& $GLOBALS['egw_setup']; + +if (!function_exists('version_compare') || version_compare(PHP_VERSION,$GLOBALS['egw_setup']->required_php_version,'<')) +{ + if (isset($_SERVER['HTTP_HOST'])) echo "
\n";
+	echo "EGroupware now requires PHP {$GLOBALS['egw_setup']->required_php_version} or greater.\nYour PHP version is: ".PHP_VERSION."\n";
+	echo 'Please contact your System Administrator.';
+	exit;
+}

From 186bd29e0d36eb5f7ad3c1ac1ac2f1c8b216dd55 Mon Sep 17 00:00:00 2001
From: Ralf Becker 
Date: Fri, 26 Oct 2012 09:06:34 +0000
Subject: [PATCH 06/14] manually including traditional.css, as setup does not
 resolve commented @include in css files

---
 setup/templates/default/head.tpl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/setup/templates/default/head.tpl b/setup/templates/default/head.tpl
index 4d51659c68..d63eb7be51 100644
--- a/setup/templates/default/head.tpl
+++ b/setup/templates/default/head.tpl
@@ -6,12 +6,13 @@
 		
 		
 		
-		
+		
 		
 		
 		
 		
 		
+		
 		
 
 		

From 18fe5c1d4b7c3d388462be110f832b2d2ba4d763 Mon Sep 17 00:00:00 2001
From: Ralf Becker 
Date: Fri, 26 Oct 2012 09:24:39 +0000
Subject: [PATCH 07/14] fixed minify issues: installation in docroot is now
 handeled, as well as problems with jscalendar and email

---
 phpgwapi/inc/class.egw_framework.inc.php | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/phpgwapi/inc/class.egw_framework.inc.php b/phpgwapi/inc/class.egw_framework.inc.php
index ff9f8930fe..8da3ce33ab 100644
--- a/phpgwapi/inc/class.egw_framework.inc.php
+++ b/phpgwapi/inc/class.egw_framework.inc.php
@@ -756,10 +756,10 @@ abstract class egw_framework
 		}
 		if (!$debug_minify)
 		{
-			$css_file = $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/inc/min/?';
-			if ($base_path && $base_path != '/') $css_file .= 'b='.substr($base_path, 1).'&';
-			$css_file .= 'f='.$css_file . '&'.$max_modified;
-			$css_file = ''."\n";
+			$css = $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/inc/min/?';
+			if ($base_path && $base_path != '/') $css .= 'b='.substr($base_path, 1).'&';
+			$css .= 'f='.$css_file . '&'.$max_modified;
+			$css_file = ''."\n";
 		}
 		return array(
 			'app_css'   => $app_css,
@@ -1286,7 +1286,8 @@ abstract class egw_framework
 			if ($base_path[0] != '/') $base_path = parse_url($base_path, PHP_URL_PATH);
 			$files = '/phpgwapi/inc/min/?'.($base_path && $base_path != '/' ? 'b='.substr($base_path, 1).'&' : '').
 				'f='.$files . '&'.$max_modified;
-			$links .= '\n";
+			// need to include minified javascript before not minified stuff like jscalendar-setup, as it might depend on it
+			$links = '".$links;
 		}
 		return $links."\n";
 	}

From f7cc51ad18e19cca409f29427ca8463a314682a3 Mon Sep 17 00:00:00 2001
From: Ralf Becker 
Date: Fri, 26 Oct 2012 10:00:05 +0000
Subject: [PATCH 08/14] do NOT minify ckeditor, as it breaks it (it is already
 minifyed anyway)

---
 phpgwapi/inc/class.egw_framework.inc.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/phpgwapi/inc/class.egw_framework.inc.php b/phpgwapi/inc/class.egw_framework.inc.php
index 8da3ce33ab..6698451dc6 100644
--- a/phpgwapi/inc/class.egw_framework.inc.php
+++ b/phpgwapi/inc/class.egw_framework.inc.php
@@ -1270,7 +1270,7 @@ abstract class egw_framework
 			if (($mod = filemtime(EGW_SERVER_ROOT.$path)) > $max_modified) $max_modified = $mod;
 
 			// for now minify does NOT support query parameters, nor php files generating javascript
-			if ($debug_minify || $query || substr($path, -3) != '.js')
+			if ($debug_minify || $query || substr($path, -3) != '.js' || strpos($path,'ckeditor') !== false)
 			{
 				$path .= '?'. $mod.($query ? '&'.$query : '');
 				$links .= '\n";

From 3e72371fd7c360fec2904710819a2a590ce8dbb0 Mon Sep 17 00:00:00 2001
From: Christian Binder 
Date: Sat, 27 Oct 2012 11:38:47 +0000
Subject: [PATCH 09/14] * New feature: cross-application document merge in
 filemanager GUI

---
 filemanager/inc/class.filemanager_ui.inc.php | 91 +++++++++++++++++++-
 filemanager/templates/default/file.xet       |  8 +-
 2 files changed, 94 insertions(+), 5 deletions(-)

diff --git a/filemanager/inc/class.filemanager_ui.inc.php b/filemanager/inc/class.filemanager_ui.inc.php
index c1ff36b0e6..4a390fa717 100644
--- a/filemanager/inc/class.filemanager_ui.inc.php
+++ b/filemanager/inc/class.filemanager_ui.inc.php
@@ -34,6 +34,12 @@ class filemanager_ui
 		'filemanager_ui::listview' => 'Listview',
 	);
 	public static $views_init = false;
+	
+	/**
+	 * vfs namespace for document merge properties
+	 *
+	 */
+	public static $merge_prop_namespace = '';
 
 	/**
 	 * Constructor
@@ -53,6 +59,7 @@ class filemanager_ui
 		}
 
 		self::init_views();
+		self::$merge_prop_namespace = egw_vfs::DEFAULT_PROP_NAMESPACE.$GLOBALS['egw_info']['flags']['currentapp'];
 	}
 
 	/**
@@ -169,6 +176,44 @@ class filemanager_ui
 		}
 		return $actions;
 	}
+	
+	/**
+	 * Get mergeapp property for given path
+	 *
+	 * @param string $path
+	 * @param string $scope='self' (default) or 'parents'
+	 *    $scope == 'self' query only the given path
+	 *    $scope == 'parents' query only path parents for property (first parent in hierarchy upwards wins)
+	 *
+	 * @return string merge application or NULL if no property found
+	 */
+	private static function get_mergeapp($path, $scope='self')
+	{
+		$app = null;
+		switch($scope)
+		{
+			case 'self':
+				$props = egw_vfs::propfind($path, self::$merge_prop_namespace);
+				$app = empty($props) ? null : $props[0]['val'];
+				break;
+			case 'parents':
+				// search for props in parent directories
+				$currentpath = $path;
+				while($dir = egw_vfs::dirname($currentpath))
+				{
+					$props = egw_vfs::propfind($dir, self::$merge_prop_namespace);
+					if(!empty($props))
+					{
+						// found prop in parent directory
+						return $app = $props[0]['val'];
+					}
+					$currentpath = $dir;
+				}
+				break;
+		}
+		
+		return $app;
+	}
 
 	/**
 	 * Main filemanager page
@@ -1060,6 +1105,25 @@ function force_download(_action, _senders)
 						{
 							$props[] = array('name' => $name, 'val' => $content[$name] ? $content[$name] : null);
 						}
+						elseif ($name == 'mergeapp')
+						{
+							$mergeprop = array(
+								array(
+									'ns'	=> self::$merge_prop_namespace,
+									'name'	=> 'mergeapp',
+									'val'	=> (!empty($content[$name]) ? $content[$name] : null),
+								),
+							);
+							if (egw_vfs::proppatch($path,$mergeprop))
+							{
+								$content['old'][$name] = $content[$name];
+								$msg .= lang('Setting for document merge saved.');
+							}
+							else
+							{
+								$msg .= lang('Saving setting for document merge failed!');
+							}
+						}
 						else
 						{
 							static $name2cmd = array('uid' => 'chown','gid' => 'chgrp','perms' => 'chmod');
@@ -1223,7 +1287,31 @@ function force_download(_action, _senders)
 				0 => lang('No access'),
 			);
 		}
-		$sel_options['mergeapp']= egw_link::app_list('merge');
+		
+		// mergeapp
+		$content['mergeapp'] = self::get_mergeapp($path, 'self');
+		$content['mergeapp_parent'] = self::get_mergeapp($path, 'parents');
+		if(!empty($content['mergeapp']))
+		{
+			$content['mergeapp_effective'] = $content['mergeapp'];
+		}
+		elseif(!empty($content['mergeapp_parent']))
+		{
+			$content['mergeapp_effective'] = $content['mergeapp_parent'];
+		}
+		else
+		{
+			$content['mergeapp_effective'] = null;
+		}
+		// mergeapp select options
+		$mergeapp_list = egw_link::app_list('merge');
+		$mergeapp_empty = !empty($content['mergeapp_parent'])
+			? $mergeapp_list[$content['mergeapp_parent']] . ' (parent setting)' : '';
+		$sel_options['mergeapp'] = array(''	=> $mergeapp_empty);
+		$sel_options['mergeapp'] = $sel_options['mergeapp'] + $mergeapp_list;
+		// mergeapp other gui options
+		$content['mergeapp_itempicker_disabled'] = $content['is_dir'] || empty($content['mergeapp_effective']);
+		
 		$preserve = $content;
 		if (!isset($preserve['old']))
 		{
@@ -1233,6 +1321,7 @@ function force_download(_action, _senders)
 				'uid'   => $content['uid'],
 				'gid'   => $content['gid'],
 				'comment' => (string)$content['comment'],
+				'mergeapp' => $content['mergeapp']
 			);
 			if ($cfs) foreach($cfs as $name => $data)
 			{
diff --git a/filemanager/templates/default/file.xet b/filemanager/templates/default/file.xet
index 9a24c589b0..1a829cccfc 100644
--- a/filemanager/templates/default/file.xet
+++ b/filemanager/templates/default/file.xet
@@ -209,9 +209,9 @@
 				
 			
 			
-				
+				
 					
-						
+						
 					
 				
 				
@@ -226,13 +226,13 @@
 								
 									
 									
-										
+										
 									
 								
 								
 									
 									
-										
+										
 									
 								
 							

From aca3b3b028f99ae1d2384a32849b17e06a3c1223 Mon Sep 17 00:00:00 2001
From: Ralf Becker 
Date: Mon, 29 Oct 2012 12:14:33 +0000
Subject: [PATCH 10/14] * CalDAV/CardDAV: fix not working logging of errors
 into traffic log

---
 phpgwapi/inc/class.groupdav.inc.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/phpgwapi/inc/class.groupdav.inc.php b/phpgwapi/inc/class.groupdav.inc.php
index d7227bbc14..17b574b052 100644
--- a/phpgwapi/inc/class.groupdav.inc.php
+++ b/phpgwapi/inc/class.groupdav.inc.php
@@ -1681,7 +1681,7 @@ class groupdav extends HTTP_WebDAV_Server
 	 */
 	public function log($str)
 	{
-		$to_log[] = $str;
+		$this->to_log[] = $str;
 
 		error_log($str);
 	}

From c327cba15362deb43565805a57dbac42dd39db63 Mon Sep 17 00:00:00 2001
From: Ralf Becker 
Date: Mon, 29 Oct 2012 12:23:17 +0000
Subject: [PATCH 11/14] consistently send ETag by using and extending
 put_response_headers

---
 calendar/inc/class.calendar_groupdav.inc.php | 34 +++++++++++++-------
 phpgwapi/inc/class.groupdav.inc.php          |  1 +
 phpgwapi/inc/class.groupdav_handler.inc.php  | 10 +++---
 3 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php
index e70b8e8d00..85ee141df8 100644
--- a/calendar/inc/class.calendar_groupdav.inc.php
+++ b/calendar/inc/class.calendar_groupdav.inc.php
@@ -813,9 +813,8 @@ class calendar_groupdav extends groupdav_handler
 					{
 						$this->groupdav->log(__METHOD__."(,,$user) schedule-tag given, but NO changes for current user events=".array2string($events).', old-event='.array2string($oldEvent));
 					}
-					// we should not return an etag here, as we never store the PUT ical byte-by-byte
-					//header('ETag: "'.$etag.'"');
-					header('Schedule-Tag: "'.$schedule_tag.'"');
+					$this->put_response_headers($eventId, $options['path'], '204 No Content', self::$path_attr == 'caldav_name');
+
 					return '204 No Content';
 				}
 				if ($this->debug && !isset($events)) error_log(__METHOD__."(,,$user) only schedule-tag given for event without participants (only calendar owner) --> handle as regular PUT");
@@ -867,14 +866,6 @@ class calendar_groupdav extends groupdav_handler
 			}
 		}
 
-		if ($this->use_schedule_tag)
-		{
-			$etag = $this->get_etag($cal_id, $schedule_tag);
-			// we should not return an etag here, as we never store the PUT ical byte-by-byte
-			//header('ETag: "'.$etag.'"');
-			header('Schedule-Tag: "'.$schedule_tag.'"');
-		}
-
 		// send evtl. necessary respose headers: Location, etag, ...
 		$this->put_response_headers($cal_id, $options['path'], $retval, self::$path_attr == 'caldav_name');
 
@@ -1315,6 +1306,27 @@ class calendar_groupdav extends groupdav_handler
 		return $etag;
 	}
 
+	/**
+	 * Send response-headers for a PUT (or POST with add-member query parameter)
+	 *
+	 * Reimplemented to send
+	 *
+	 * @param int|array $entry id or array of new created entry
+	 * @param string $path
+	 * @param int|string $retval
+	 * @param boolean $path_attr_is_name=true true: path_attr is ca(l|rd)dav_name, false: id (GroupDAV needs Location header)
+	 */
+	function put_response_headers($entry, $path, $retval, $path_attr_is_name=true)
+	{
+		$etag = $this->get_etag($entry, $schedule_tag);
+
+		if ($this->use_schedule_tag)
+		{
+			header('Schedule-Tag: "'.$schedule_tag.'"');
+		}
+		parent::put_response_headers($entry, $path, $retval, $path_attr_is_name, $etag);
+	}
+
 	/**
 	 * Check if user has the neccessary rights on an event
 	 *
diff --git a/phpgwapi/inc/class.groupdav.inc.php b/phpgwapi/inc/class.groupdav.inc.php
index 17b574b052..739e1397d5 100644
--- a/phpgwapi/inc/class.groupdav.inc.php
+++ b/phpgwapi/inc/class.groupdav.inc.php
@@ -1003,6 +1003,7 @@ class groupdav extends HTTP_WebDAV_Server
 			'DAV:displayname'      => 'Displayname',
 			'DAV:getlastmodified'  => 'Last modified',
 			'DAV:getetag'          => 'ETag',
+			//'CalDAV:schedule-tag'  => 'Schedule-Tag',
 			'DAV:getcontenttype'   => 'Content type',
 			'DAV:resourcetype'     => 'Resource type',
 			//'http://calendarserver.org/ns/:created-by' => 'Created by',
diff --git a/phpgwapi/inc/class.groupdav_handler.inc.php b/phpgwapi/inc/class.groupdav_handler.inc.php
index 43fbe6e87b..2f1094a8ed 100644
--- a/phpgwapi/inc/class.groupdav_handler.inc.php
+++ b/phpgwapi/inc/class.groupdav_handler.inc.php
@@ -474,18 +474,20 @@ abstract class groupdav_handler
 	 * @param string $path
 	 * @param int|string $retval
 	 * @param boolean $path_attr_is_name=true true: path_attr is ca(l|rd)dav_name, false: id (GroupDAV needs Location header)
+	 * @param string $etag=null etag, to not calculate it again (if != null)
 	 */
-	function put_response_headers($entry, $path, $retval, $path_attr_is_name=true)
+	function put_response_headers($entry, $path, $retval, $path_attr_is_name=true, $etag=null)
 	{
 		// we should not return an etag here, as EGroupware never stores ical/vcard byte-by-byte
 		// as SOGO Connector requires ETag header to recognice as successful PUT, we are sending them again for it
 		// --> as all clients dislike not getting an ETag for a PUT, we sending it again even not storing byte-by-byte
 		//if (get_class($this) == 'addressbook_groupdav' && in_array(self::get_agent(),array('thunderbird','lightning')))
 		{
-			header('ETag: "'.$this->get_etag($entry).'"');
+			if (is_null($etag)) $etag = $this->get_etag($entry);
+			header('ETag: "'.$etag.'"');
 		}
-		// send Location header only if we dont use caldav_name as path-attribute or
-		if ($retval !== true && (!$path_attr_is_name ||
+		// send Location header only on success AND if we dont use caldav_name as path-attribute or
+		if (!(is_bool($retval) ? $retval : $retval[0] === '2') && (!$path_attr_is_name ||
 			// POST with add-member query parameter
 			$_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['add-member'])))
 		{

From 9c73af1acaa30977e5af9a66f1715db32c67d6d0 Mon Sep 17 00:00:00 2001
From: Klaus Leithoff 
Date: Mon, 29 Oct 2012 15:49:13 +0000
Subject: [PATCH 12/14] * eMail: allow table as inline element while balancing
 tags (new internal config option); try to correct common errors/problems in
 html-code which caused display/layout problems

---
 phpgwapi/inc/class.egw_htmLawed.inc.php | 1 +
 phpgwapi/inc/htmLawed/htmLawed.php      | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/phpgwapi/inc/class.egw_htmLawed.inc.php b/phpgwapi/inc/class.egw_htmLawed.inc.php
index d1c6c7eca2..f584edaf39 100644
--- a/phpgwapi/inc/class.egw_htmLawed.inc.php
+++ b/phpgwapi/inc/class.egw_htmLawed.inc.php
@@ -76,6 +76,7 @@ class egw_htmLawed
 			'unique_ids'=>array('2', '1', 'unique id values', '0', '8', 'my_', 'prefix'),
 			'valid_xhtml'=>array('2', 'nil', 'auto-set various parameters for most valid XHTML', 'nil'),
 			'xml:lang'=>array('3', 'nil', 'auto-add xml:lang attribute', '0'),
+			'allow_for_inline' => array('table'),//block elements allowed for nesting when only inline is allowed; Example span does not allow block elements as table; table is the only element tested so far
 		);
 		*/
 
diff --git a/phpgwapi/inc/htmLawed/htmLawed.php b/phpgwapi/inc/htmLawed/htmLawed.php
index c7be528964..b6a3c74451 100644
--- a/phpgwapi/inc/htmLawed/htmLawed.php
+++ b/phpgwapi/inc/htmLawed/htmLawed.php
@@ -80,6 +80,9 @@ $C['style_pass'] = empty($C['style_pass']) ? 0 : 1;
 $C['tidy'] = empty($C['tidy']) ? 0 : $C['tidy'];
 $C['unique_ids'] = isset($C['unique_ids']) ? $C['unique_ids'] : 1;
 $C['xml:lang'] = isset($C['xml:lang']) ? $C['xml:lang'] : 0;
+// own config options
+// block elements allowed for nesting when only inline is allowed; Example span does not allow block elements as table; table is the only element tested so far
+$C['allow_for_inline'] = isset($C['allow_for_inline'])?$C['allow_for_inline']:0;
 
 if(isset($GLOBALS['C'])){$reC = $GLOBALS['C'];}
 $GLOBALS['C'] = $C;
@@ -157,6 +160,7 @@ $cT = array('colgroup'=>1, 'dd'=>1, 'dt'=>1, 'li'=>1, 'option'=>1, 'p'=>1, 'td'=
 // block/inline type; ins & del both type; #pcdata: text
 $eB = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'del'=>1, 'dir'=>1, 'dl'=>1, 'div'=>1, 'fieldset'=>1, 'form'=>1, 'ins'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'isindex'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'p'=>1, 'pre'=>1, 'table'=>1, 'ul'=>1);
 $eI = array('#pcdata'=>1, 'a'=>1, 'abbr'=>1, 'acronym'=>1, 'applet'=>1, 'b'=>1, 'bdo'=>1, 'big'=>1, 'br'=>1, 'button'=>1, 'cite'=>1, 'code'=>1, 'del'=>1, 'dfn'=>1, 'em'=>1, 'embed'=>1, 'font'=>1, 'i'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'ins'=>1, 'kbd'=>1, 'label'=>1, 'map'=>1, 'object'=>1, 'q'=>1, 'ruby'=>1, 's'=>1, 'samp'=>1, 'select'=>1, 'script'=>1, 'small'=>1, 'span'=>1, 'strike'=>1, 'strong'=>1, 'sub'=>1, 'sup'=>1, 'textarea'=>1, 'tt'=>1, 'u'=>1, 'var'=>1);
+if($GLOBALS['C']['allow_for_inline'] && is_array($GLOBALS['C']['allow_for_inline'])) foreach($GLOBALS['C']['allow_for_inline'] as $khai => $vhai) {$eI[$vhai]=1;}//allow table as if it was an inline element as  some Text ...
more text
is quite common $eN = array('a'=>1, 'big'=>1, 'button'=>1, 'fieldset'=>1, 'font'=>1, 'form'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'label'=>1, 'object'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'small'=>1, 'sub'=>1, 'sup'=>1, 'textarea'=>1); // Exclude from specific ele; $cN values $eO = array('area'=>1, 'caption'=>1, 'col'=>1, 'colgroup'=>1, 'dd'=>1, 'dt'=>1, 'legend'=>1, 'li'=>1, 'optgroup'=>1, 'option'=>1, 'param'=>1, 'rb'=>1, 'rbc'=>1, 'rp'=>1, 'rt'=>1, 'rtc'=>1, 'script'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'thead'=>1, 'th'=>1, 'tr'=>1); // Missing in $eB & $eI $eF = $eB + $eI; From a2744f82f076e4000536772483a63393a1465f70 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 29 Oct 2012 17:09:12 +0000 Subject: [PATCH 13/14] Align series popup with selected row when in list view --- calendar/inc/class.calendar_uiviews.inc.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/calendar/inc/class.calendar_uiviews.inc.php b/calendar/inc/class.calendar_uiviews.inc.php index edca6f2bbe..572250b740 100644 --- a/calendar/inc/class.calendar_uiviews.inc.php +++ b/calendar/inc/class.calendar_uiviews.inc.php @@ -1040,6 +1040,7 @@ function edit_series(event,id,date) calendar_edit_date = date; var popup = jQuery("#edit_series").show(); + var row = null; if(event) { @@ -1049,6 +1050,13 @@ function edit_series(event,id,date) top: event.pageY, left: (event.pageX + popup.width() > $j(window).width() ? $j(window).width() - popup.width() : event.pageX) }); + } else if (row = jQuery("#"+id+"\\\:"+date)) { + // Open at row + popup.css({ + position: "absolute", + top: row.position().top + row.height() -popup.height()/2, + left: $j(window).width()/2-popup.width()/2 + }); } else { // Open popup in the middle popup.css({ From 9f4118e947ab35c43228efc538d0ee42829b492f Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 29 Oct 2012 18:29:40 +0000 Subject: [PATCH 14/14] Use addressbook vCard charset preference for exporting vcards using Import/Export --- ...ss.addressbook_wizard_export_vcard.inc.php | 80 +++++++++++++++++++ .../class.importexport_definitions_ui.inc.php | 6 +- .../inc/class.importexport_export_ui.inc.php | 12 ++- 3 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 addressbook/inc/class.addressbook_wizard_export_vcard.inc.php diff --git a/addressbook/inc/class.addressbook_wizard_export_vcard.inc.php b/addressbook/inc/class.addressbook_wizard_export_vcard.inc.php new file mode 100644 index 0000000000..646ea00697 --- /dev/null +++ b/addressbook/inc/class.addressbook_wizard_export_vcard.inc.php @@ -0,0 +1,80 @@ +steps = array( + 'wizard_step40' => '' + ); + $this->step_templates = array( + 'wizard_step40' => 'addressbook.importexport_wizard_vcard_charset' + ); + + } + + /** + * choose charset + * + * @param array $content + * @param array $sel_options + * @param array $readonlys + * @param array $preserv + * @return string template name + */ + function wizard_step40(&$content, &$sel_options, &$readonlys, &$preserv) + { + if($this->debug) error_log(get_class($this) . '::wizard_step40->$content '.print_r($content,true)); + // return from step40 + if ($content['step'] == 'wizard_step40') { + switch (array_search('pressed', $content['button'])) + { + case 'next': + return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],1); + case 'previous' : + return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],-1); + case 'finish': + return 'wizard_finish'; + default : + return $this->wizard_step40($content,$sel_options,$readonlys,$preserv); + } + } + // init step40 + else + { + $content['msg'] = $this->steps['wizard_step40']; + $content['step'] = 'wizard_step40'; + if(!$content['charset'] && $content['plugin_options']['charset']) { + $content['charset'] = $content['plugin_options']['charset'] ? $content['plugin_options']['charset'] : 'user'; + } + $sel_options['charset'] = $GLOBALS['egw']->translation->get_installed_charsets()+ + array( + 'user' => lang('User preference'), + ); + $preserv = $content; + + // Add in extra allowed charsets + $config = config::read('importexport'); + $extra_charsets = array_intersect(explode(',',$config['import_charsets']), mb_list_encodings()); + if($extra_charsets) + { + $sel_options['charset'] += array(lang('Extra encodings') => array_combine($extra_charsets,$extra_charsets)); + } + unset ($preserv['button']); + return $this->step_templates[$content['step']]; + } + } + +} diff --git a/importexport/inc/class.importexport_definitions_ui.inc.php b/importexport/inc/class.importexport_definitions_ui.inc.php index a9c4b7bd6a..326a403d91 100644 --- a/importexport/inc/class.importexport_definitions_ui.inc.php +++ b/importexport/inc/class.importexport_definitions_ui.inc.php @@ -544,7 +544,9 @@ class importexport_definitions_ui $next_step = $this->plugin->$content['step']($content); } else + { $next_step = $this->$content['step']($content); + } } else { die('Cannot find next step'); } @@ -730,9 +732,9 @@ class importexport_definitions_ui { case 'next': // There's no real reason the plugin has to come from any of these, as long as it has a $steps variable - if($this->plugin instanceof importexport_iface_import_plugin || $this->plugin instanceof importexport_wizard_basic_import_csv) { + if($this->plugin instanceof importexport_iface_import_plugin || $this->plugin instanceof importexport_wizard_basic_import_csv || strpos(get_class($this->plugin), 'import') !== false) { $content['type'] = 'import'; - } elseif($this->plugin instanceof importexport_iface_export_plugin || $this->plugin instanceof importexport_wizard_basic_export_csv) { + } elseif($this->plugin instanceof importexport_iface_export_plugin || $this->plugin instanceof importexport_wizard_basic_export_csv || strpos(get_class($this->plugin),'export') !== false) { $content['type'] = 'export'; } else { throw new egw_exception('Invalid plugin'); diff --git a/importexport/inc/class.importexport_export_ui.inc.php b/importexport/inc/class.importexport_export_ui.inc.php index afc6bc40a1..927eb1c723 100644 --- a/importexport/inc/class.importexport_export_ui.inc.php +++ b/importexport/inc/class.importexport_export_ui.inc.php @@ -275,7 +275,17 @@ class importexport_export_ui { if (! $charset = $definition->plugin_options['charset']) { $charset = $GLOBALS['egw']->translation->charset(); } - if($charset == 'user') $charset = $GLOBALS['egw_info']['user']['preferences']['common']['csv_charset']; + if($charset == 'user') + { + switch($definition->plugin) + { + case 'addressbook_export_vcard': + $charset = $GLOBALS['egw_info']['user']['preferences']['addressbook']['vcard_charset']; + break; + default: + $charset = $GLOBALS['egw_info']['user']['preferences']['common']['csv_charset']; + } + } $plugin_object = new $definition->plugin; $result = $plugin_object->export( $file, $definition );