From 435e282efa4a1298551d092c2ce1a33a714ae64c Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 25 Jun 2010 13:51:22 +0000 Subject: [PATCH 01/91] propset svn:keywords Id --- admin/inc/class.admin_applications.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/inc/class.admin_applications.inc.php b/admin/inc/class.admin_applications.inc.php index 04cada28b0..907e50fa48 100644 --- a/admin/inc/class.admin_applications.inc.php +++ b/admin/inc/class.admin_applications.inc.php @@ -6,7 +6,7 @@ * @author Christian Füller * @package admin * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @version $Id: class.admin_register_hooks.inc.php 29103 2010-05-25 15:30:25Z füller $ + * @version $Id$ */ class admin_applications From 4e42eb30f0bdf478df137e0ee45f1c9272af5003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Fri, 25 Jun 2010 17:08:09 +0000 Subject: [PATCH 02/91] Enable Funambol client 8.5 again; various SyncML optimizations --- phpgwapi/inc/horde/Horde/RPC/syncml.php | 3 +- phpgwapi/inc/horde/Horde/SyncML.php | 50 ++++++- phpgwapi/inc/horde/Horde/SyncML/Command.php | 1 + .../inc/horde/Horde/SyncML/Command/Alert.php | 41 ------ .../inc/horde/Horde/SyncML/Command/Get.php | 59 ++++++-- .../horde/Horde/SyncML/Command/Replace.php | 2 +- .../inc/horde/Horde/SyncML/Command/Sync.php | 3 +- phpgwapi/inc/horde/Horde/SyncML/State.php | 128 ++++++++++-------- phpgwapi/inc/horde/Horde/SyncML/Sync.php | 22 ++- .../inc/horde/Horde/SyncML/Sync/SlowSync.php | 2 + phpgwapi/inc/horde/config/registry.php | 10 ++ 11 files changed, 206 insertions(+), 115 deletions(-) diff --git a/phpgwapi/inc/horde/Horde/RPC/syncml.php b/phpgwapi/inc/horde/Horde/RPC/syncml.php index 93350b03da..2920523bdc 100644 --- a/phpgwapi/inc/horde/Horde/RPC/syncml.php +++ b/phpgwapi/inc/horde/Horde/RPC/syncml.php @@ -92,7 +92,8 @@ class Horde_RPC_syncml extends Horde_RPC { $this->_output = new XML_WBXML_ContentHandler(); $this->_parse($request); - $response = $this->_output->getOutput(); + $response = '_charset . '"?>'; + $response .= $this->_output->getOutput(); /* Very useful for debugging. */ if (!empty($this->_debugDir) && is_dir($this->_debugDir)) { diff --git a/phpgwapi/inc/horde/Horde/SyncML.php b/phpgwapi/inc/horde/Horde/SyncML.php index fbabda2eec..6eed33fafc 100644 --- a/phpgwapi/inc/horde/Horde/SyncML.php +++ b/phpgwapi/inc/horde/Horde/SyncML.php @@ -569,7 +569,8 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler { } $state->setAlert222Received(false); } - + + if ($state->needDeviceInfo()) $this->outputGetRequest(); // send the sync reply // we do still have some data to send OR @@ -700,5 +701,52 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler { $this->_currentCommand->characters($str); } } + + function outputGetRequest() + { + $attrs = array(); + $state =& $_SESSION['SyncML.state']; + + $uri = $state->getURI(); + $uriMeta = $state->getURIMeta(); + + Horde::logMessage('SyncML: PreferedContentTypeClient missing, sending ', + __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $this->_output->startElement($uri, 'Get', $attrs); + + $this->_output->startElement($uri, 'CmdID', $attrs); + $this->_output->characters($this->_currentCmdID); + $this->_currentCmdID++; + $this->_output->endElement($uri, 'CmdID'); + + $this->_output->startElement($uri, 'Meta', $attrs); + $this->_output->startElement($uriMeta, 'Type', $attrs); + if (is_a($this->_output, 'XML_WBXML_Encoder')) { + $this->_output->characters('application/vnd.syncml-devinf+wbxml'); + } else { + $this->_output->characters('application/vnd.syncml-devinf+xml'); + } + $this->_output->endElement($uriMeta, 'Type'); + $this->_output->endElement($uri, 'Meta'); + + $this->_output->startElement($uri, 'Item', $attrs); + $this->_output->startElement($uri, 'Target', $attrs); + $this->_output->startElement($uri, 'LocURI', $attrs); + if ($state->getVersion() == 2) { + $this->_output->characters('./devinf12'); + } elseif ($state->getVersion() == 1) { + $this->_output->characters('./devinf11'); + } else { + $this->_output->characters('./devinf10'); + } + $this->_output->endElement($uri, 'LocURI'); + $this->_output->endElement($uri, 'Target'); + $this->_output->endElement($uri, 'Item'); + + $this->_output->endElement($uri, 'Get'); + + $state->deviceInfoRequested(); + } } diff --git a/phpgwapi/inc/horde/Horde/SyncML/Command.php b/phpgwapi/inc/horde/Horde/SyncML/Command.php index 505a32e12c..123d0e0fd3 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Command.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Command.php @@ -153,6 +153,7 @@ class Horde_SyncML_Command { } if (class_exists($class)) { $cmd = new $class($params); + if (empty($cmd->_cmdName)) $cmd->_cmdName = $command; } else { $msg = 'SyncML: Class definition of ' . $class . ' not found.'; Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_ERR); diff --git a/phpgwapi/inc/horde/Horde/SyncML/Command/Alert.php b/phpgwapi/inc/horde/Horde/SyncML/Command/Alert.php index 5785b00e0d..1327a66add 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Command/Alert.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Command/Alert.php @@ -401,47 +401,6 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command { $currentCmdID++; - if ($state->_devinfoRequested == false && - $this->_sourceLocURI != null && - is_a($state->getPreferedContentTypeClient($this->_sourceLocURI), 'PEAR_Error')) { - - Horde::logMessage("SyncML: PreferedContentTypeClient missing, sending ", __FILE__, __LINE__, PEAR_LOG_DEBUG); - - $output->startElement($state->getURI(), 'Get', $attrs); - - $output->startElement($state->getURI(), 'CmdID', $attrs); - $output->characters($currentCmdID); - $currentCmdID++; - $output->endElement($state->getURI(), 'CmdID'); - - $output->startElement($state->getURI(), 'Meta', $attrs); - $output->startElement($state->getURIMeta(), 'Type', $attrs); - if (is_a($output, 'XML_WBXML_Encoder')) { - $output->characters('application/vnd.syncml-devinf+wbxml'); - } else { - $output->characters('application/vnd.syncml-devinf+xml'); - } - $output->endElement($state->getURIMeta(), 'Type'); - $output->endElement($state->getURI(), 'Meta'); - - $output->startElement($state->getURI(), 'Item', $attrs); - $output->startElement($state->getURI(), 'Target', $attrs); - $output->startElement($state->getURI(), 'LocURI', $attrs); - if ($state->getVersion() == 2) { - $output->characters('./devinf12'); - } elseif ($state->getVersion() == 1) { - $output->characters('./devinf11'); - } else { - $output->characters('./devinf10'); - } - $output->endElement($state->getURI(), 'LocURI'); - $output->endElement($state->getURI(), 'Target'); - $output->endElement($state->getURI(), 'Item'); - - $output->endElement($state->getURI(), 'Get'); - - $state->_devinfoRequested = true; - } return $currentCmdID; } diff --git a/phpgwapi/inc/horde/Horde/SyncML/Command/Get.php b/phpgwapi/inc/horde/Horde/SyncML/Command/Get.php index 2ffa10133b..49de9cdcdf 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Command/Get.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Command/Get.php @@ -20,6 +20,13 @@ include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command/Results.php'; class Horde_SyncML_Command_Get extends Horde_SyncML_Command { + + /** + * Name of the command. + * + * @var string + */ + var $_cmdName = 'Get'; function output($currentCmdID, &$output) { @@ -33,7 +40,7 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command { $ref = './devinf10'; } - $status = new Horde_SyncML_Command_Status((($state->isAuthorized()) ? RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS), 'Get'); + $status = new Horde_SyncML_Command_Status((($state->isAuthorized()) ? RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS), $this->_cmdName); $status->setCmdRef($this->_cmdID); $status->setTargetRef($ref); $currentCmdID = $status->output($currentCmdID, $output); @@ -80,16 +87,31 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command { $output->startElement($state->getURIDevInf() , 'DevInf', $attrs); $output->startElement($state->getURIDevInf() , 'VerDTD', $attrs); if ($state->getVersion() == 2) { - $output->characters('1.2'); + $output->characters('1.2'); } elseif($state->getVersion() == 1) { - $output->characters('1.1'); + $output->characters('1.1'); } else { - $output->characters('1.0'); + $output->characters('1.0'); } $output->endElement($state->getURIDevInf() , 'VerDTD', $attrs); $output->startElement($state->getURIDevInf() , 'Man', $attrs); $output->characters('www.egroupware.org'); $output->endElement($state->getURIDevInf() , 'Man', $attrs); + $output->startElement($state->getURIDevInf() , 'Mod', $attrs); + $output->characters('DS Server'); + $output->endElement($state->getURIDevInf() , 'Mod', $attrs); + $output->startElement($state->getURIDevInf() , 'OEM', $attrs); + $output->characters('-'); + $output->endElement($state->getURIDevInf() , 'OEM', $attrs); + $output->startElement($state->getURIDevInf() , 'FwV', $attrs); + $output->characters('-'); + $output->endElement($state->getURIDevInf() , 'FwV', $attrs); + $output->startElement($state->getURIDevInf() , 'SwV', $attrs); + $output->characters('1.7.x'); + $output->endElement($state->getURIDevInf() , 'SwV', $attrs); + $output->startElement($state->getURIDevInf() , 'HwV', $attrs); + $output->characters('-'); + $output->endElement($state->getURIDevInf() , 'HwV', $attrs); $output->startElement($state->getURIDevInf() , 'DevID', $attrs); $output->characters($_SERVER['HTTP_HOST']); $output->endElement($state->getURIDevInf() , 'DevID', $attrs); @@ -102,28 +124,36 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command { $output->endElement($state->getURIDevInf() , 'SupportNumberOfChanges', $attrs); $output->startElement($state->getURIDevInf() , 'SupportLargeObjs', $attrs); $output->endElement($state->getURIDevInf() , 'SupportLargeObjs', $attrs); - $this->_writeDataStore('./notes', 'text/x-vnote', '1.1', $output, + $this->_writeDataStore('notes', 'text/x-vnote', '1.1', $output, array('text/plain' => '1.0')); - $this->_writeDataStore('./contacts', 'text/vcard', '3.0', $output, + $this->_writeDataStore('contacts', 'text/vcard', '3.0', $output, array('text/x-vcard' => '2.1')); - $this->_writeDataStore('./card', 'text/vcard', '3.0', $output, + $this->_writeDataStore('card', 'text/vcard', '3.0', $output, array('text/x-vcard' => '2.1')); - $this->_writeDataStore('./tasks', 'text/calendar', '2.0', $output, + $this->_writeDataStore('tasks', 'text/calendar', '2.0', $output, array('text/x-vcalendar' => '1.0')); - $this->_writeDataStore('./jobs', 'text/calendar', '2.0', $output, + $this->_writeDataStore('jobs', 'text/calendar', '2.0', $output, array('text/x-vcalendar' => '1.0')); - $this->_writeDataStore('./calendar', 'text/calendar', '2.0', $output, + $this->_writeDataStore('calendar', 'text/calendar', '2.0', $output, array('text/x-vcalendar' => '1.0')); - $this->_writeDataStore('./events', 'text/calendar', '2.0', $output, + $this->_writeDataStore('events', 'text/calendar', '2.0', $output, array('text/x-vcalendar' => '1.0')); - $this->_writeDataStore('./caltasks', 'text/calendar', '2.0', $output, + $this->_writeDataStore('caltasks', 'text/calendar', '2.0', $output, array('text/x-vcalendar' => '1.0')); + // Funambol special Datastore + $this->_writeDataStore('configuration', 'text/plain', '1.0', $output); $output->endElement($state->getURIDevInf() , 'DevInf', $attrs); $output->endElement($state->getURI(), 'Data'); $output->endElement($state->getURI(), 'Item'); $output->endElement($state->getURI(), 'Results'); - + /* + $output->startElement($state->getURIDevInf() , 'Ext', $attrs); + $output->startElement($state->getURIDevInf() , 'XNam', $attrs); + $output->characters('X-funambol-smartslow'); + $output->endElement($state->getURIDevInf() , 'XNam', $attrs); + $output->endElement($state->getURIDevInf() , 'Ext', $attrs); + */ $currentCmdID++; } @@ -151,6 +181,9 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command { $output->startElement($state->getURIDevInf() , 'SourceRef', $attrs); $output->characters($sourceref); $output->endElement($state->getURIDevInf() , 'SourceRef', $attrs); + $output->startElement($state->getURIDevInf() , 'DisplayName', $attrs); + $output->characters($sourceref); + $output->endElement($state->getURIDevInf() , 'DisplayName', $attrs); $output->startElement($state->getURIDevInf() , 'MaxGUIDSize', $attrs); $output->characters(255); $output->endElement($state->getURIDevInf() , 'MaxGUIDSize', $attrs); diff --git a/phpgwapi/inc/horde/Horde/SyncML/Command/Replace.php b/phpgwapi/inc/horde/Horde/SyncML/Command/Replace.php index 010083bbf2..19f0751c01 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Command/Replace.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Command/Replace.php @@ -15,7 +15,7 @@ */ include_once 'Horde/SyncML/Command.php'; -class Horde_SyncML_Command_Final extends Horde_SyncML_Command { +class Horde_SyncML_Command_Replace extends Horde_SyncML_Command { /** * Name of the command. diff --git a/phpgwapi/inc/horde/Horde/SyncML/Command/Sync.php b/phpgwapi/inc/horde/Horde/SyncML/Command/Sync.php index 9e4704df22..d65944a47b 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Command/Sync.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Command/Sync.php @@ -88,8 +88,7 @@ class Horde_SyncML_Command_Sync extends Horde_SyncML_Command { $currentCmdID = $status->output($currentCmdID, $output); - if ($this->_targetURI != "configuration" && // Fix Funambol issue - ($sync = &$state->getSync($this->_targetURI))) { + if (($sync = &$state->getSync($this->_targetURI))) { $currentCmdID = $sync->startSync($currentCmdID, $output); foreach ($this->_syncElements as $element) { diff --git a/phpgwapi/inc/horde/Horde/SyncML/State.php b/phpgwapi/inc/horde/Horde/SyncML/State.php index a088667917..3635cab7cb 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/State.php +++ b/phpgwapi/inc/horde/Horde/SyncML/State.php @@ -69,7 +69,7 @@ define('RESPONSE_NO_CONTENT', 204); define('RESPONSE_RESET_CONTENT', 205); define('RESPONSE_PARTIAL_CONTENT', 206); define('RESPONSE_CONFLICT_RESOLVED_WITH_MERGE', 207); -define('RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINNING', 208); +define('RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINS', 208); define('RESPONSE_CONFILCT_RESOLVED_WITH_DUPLICATE', 209); define('RESPONSE_DELETE_WITHOUT_ARCHIVE', 210); define('RESPONSE_ITEM_NO_DELETED', 211); @@ -80,60 +80,60 @@ define('RESPONSE_NO_EXECUTED', 215); define('RESPONSE_ATOMIC_ROLL_BACK_OK', 216); define('RESPONSE_MULTIPLE_CHOICES', 300); -// Need to change names. -// define('RESPONSE_MULTIPLE_CHOICES', 301); -// define('RESPONSE_MULTIPLE_CHOICES', 302); -// define('RESPONSE_MULTIPLE_CHOICES', 303); -// define('RESPONSE_MULTIPLE_CHOICES', 304); +define('RESPONSE_MOVED_PERMANENTLY', 301); +define('RESPONSE_FOUND', 302); +define('RESPONSE_SEE_OTHER', 303); +define('RESPONSE_NOT_MODIFIED', 304); define('RESPONSE_USE_PROXY', 305); define('RESPONSE_BAD_REQUEST', 400); define('RESPONSE_INVALID_CREDENTIALS', 401); -// Need to change names. -// define('RESPONSE_INVALID_CREDENTIALS', 402); -// define('RESPONSE_INVALID_CREDENTIALS', 403); +define('RESPONSE_PAYMENT_NEEDED', 402); +define('RESPONSE_FORBIDDEN', 403); define('RESPONSE_NOT_FOUND', 404); -// Need to change names. -// define('RESPONSE_INVALID_CREDENTIALS', 405); -// define('RESPONSE_INVALID_CREDENTIALS', 406); +define('RESPONSE_COMMAND_DENIED', 405); +define('RESPONSE_FEATURE_NOT_SUPPORTED', 406); define('RESPONSE_MISSING_CREDENTIALS', 407); -// define('RESPONSE_INVALID_CREDENTIALS', 408); -// define('RESPONSE_INVALID_CREDENTIALS', 409); -// define('RESPONSE_INVALID_CREDENTIALS', 410); -// define('RESPONSE_INVALID_CREDENTIALS', 411); -// define('RESPONSE_INVALID_CREDENTIALS', 412); -// define('RESPONSE_INVALID_CREDENTIALS', 413); -// define('RESPONSE_INVALID_CREDENTIALS', 414); -// define('RESPONSE_INVALID_CREDENTIALS', 415); +define('RESPONSE_REQUEST_TIMEOUT', 408); +define('RESPONSE_CONFLICT_DETECTED', 409); +define('RESPONSE_ITEM_GONE', 410); +define('RESPONSE_SIZE_REQUIRED', 411); +define('RESPONSE_INCOMPLETE_COMMAND', 412); +define('RESPONSE_ENTITY_TO_LARGE', 413); +define('RESPONSE_URI_TOO_LONG', 414); +define('RESPONSE_UNSUPPORTED_MEDIA_TYPE', 415); define('RESPONSE_REQUEST_SIZE_TOO_BIG', 416); -// Need to change names. -// define('RESPONSE_INVALID_CREDENTIALS', 417); -// define('RESPONSE_INVALID_CREDENTIALS', 418); -// define('RESPONSE_INVALID_CREDENTIALS', 419); -// define('RESPONSE_INVALID_CREDENTIALS', 420); -// define('RESPONSE_INVALID_CREDENTIALS', 421); -// define('RESPONSE_INVALID_CREDENTIALS', 422); -// define('RESPONSE_INVALID_CREDENTIALS', 423); +define('RESPONSE_RETRY_LATER', 417); +define('RESPONSE_ALREADY_EXISITS', 418); +define('RESPONSE_CONFLICT_RESOLVED_WITH_SERVER_WINS', 419); +define('RESPONSE_DEVICE_FULL', 420); +define('RESPONSE_UNKNOWN_SEARCH_GRAMMAR', 421); +define('RESPONSE_BAD_CGI', 422); +define('RESPONSE_SOFT_DELETE_CONFICT', 423); define('RESPONSE_SIZE_MISMATCH', 424); +define('RESPONSE_PERMISSION_DENIED', 425); +define('RESPONSE_PARTIAL_ITEM_NOT_ACCEPTED', 426); +define('RESPONSE_ITEM_NOT_EMPTY', 427); +define('RESPONSE_MOVE_FAILED', 428); define('RESPONSE_COMMAND_FAILED', 500); -// Need to change names. -// define('RESPONSE_COMMAND_FAILED', 501); -// define('RESPONSE_COMMAND_FAILED', 502); -// define('RESPONSE_COMMAND_FAILED', 503); -// define('RESPONSE_COMMAND_FAILED', 504); -// define('RESPONSE_COMMAND_FAILED', 505); -// define('RESPONSE_COMMAND_FAILED', 506); -// define('RESPONSE_COMMAND_FAILED', 507); +define('RESPONSE_COMMAND_NOT_IMPLEMENTED', 501); +define('RESPONSE_BAD_GATEWAY', 502); +define('RESPONSE_SERVICE_UNAVAILABLE', 503); +define('RESPONSE_GATEWAY_TIMEOUT', 504); +define('RESPONSE_DTD_VERSION_NOT_SUPPORTED', 505); +define('RESPONSE_PROCESSING_ERROR', 506); +define('RESPONSE_ATOMIC_FAILED', 507); define('RESPONSE_REFRESH_REQUIRED', 508); -// define('RESPONSE_COMMAND_FAILED', 509); -// define('RESPONSE_COMMAND_FAILED', 510); -// define('RESPONSE_COMMAND_FAILED', 511); -// define('RESPONSE_COMMAND_FAILED', 512); -// define('RESPONSE_COMMAND_FAILED', 513); -// define('RESPONSE_COMMAND_FAILED', 514); -// define('RESPONSE_COMMAND_FAILED', 515); +// define('RESPONSE_FUTURE_USE', 509); +define('RESPONSE_DATASTORE_FAILURE', 510); +define('RESPONSE_SERVER_FAILURE', 511); +define('RESPONSE_SYNCHRONIZATION_FAILED', 512); +define('RESPONSE_PROTOCOL_VERSION_NOT_SUPPORTED', 513); +define('RESPONSE_OPERATION_CANCELLED', 514); +define('RESPONSE_COMMAND_FAILED', 515); define('RESPONSE_ATOMIC_ROLL_BACK_FAILED', 516); +define('RESPONSE_ATOMIC_RESPONSE_TOO_LARGE', 517); define('NAME_SPACE_URI_SYNCML_1_0', 'syncml:syncml1.0'); define('NAME_SPACE_URI_SYNCML_1_1', 'syncml:syncml1.1'); @@ -629,6 +629,29 @@ class Horde_SyncML_State { return $targets; } + + function needDeviceInfo() + { + if ($this->_devinfoRequested || count($this->_syncs) < 1) return false; + + $sendGetRequest = false; + + foreach($this->_syncs as $sync) + { + if (($source = $sync->getSourceLocURI()) && + is_a($this->getPreferedContentTypeClient($source), 'PEAR_Error')) { + $sendGetRequest = true; + break; + } + } + + return $sendGetRequest; + } + + function deviceInfoRequested() + { + $this->_devinfoRequested = true; + } function getURI() { @@ -912,53 +935,45 @@ class Horde_SyncML_State { case 'contacts': case 'card': return 'contacts'; - break; case 'notes': return 'notes'; - break; case 'tasks': case 'jobs': return 'tasks'; - break; case 'events': case 'calendar': return 'calendar'; - break; case 'caltasks': return 'caltasks'; - break; # funambol related types - case 'sifcalendar': case 'scal': return 'sifcalendar'; - break; case 'sifcontacts': case 'scard': return 'sifcontacts'; - break; case 'siftasks': case 'stask': return 'siftasks'; - break; case 'sifnotes': case 'snote': return 'sifnotes'; - break; + + case 'configuration': + return 'configuration'; default: Horde::logMessage("SyncML: unknown hordeType for type=$type ($_type)", __FILE__, __LINE__, PEAR_LOG_INFO); - return $_type; - break; } + return $_type; } /** @@ -993,6 +1008,9 @@ class Horde_SyncML_State { 'mayFragment' => 1, 'Properties' => $cprops, ); + + if ($_targetLocURI == 'configuration') $clientPrefs['ContentFormat'] = 'b64'; + #Horde::logMessage('SyncML: sourceLocURI ' . $_sourceLocURI . " clientPrefs:\n" # . print_r($clientPrefs, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); return $this->adjustContentType($clientPrefs, $_targetLocURI); diff --git a/phpgwapi/inc/horde/Horde/SyncML/Sync.php b/phpgwapi/inc/horde/Horde/SyncML/Sync.php index b826742bef..1fff5eaa90 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Sync.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Sync.php @@ -114,6 +114,18 @@ class Horde_SyncML_Sync { function setSourceLocURI($sourceURI) { $this->_sourceLocURI = $sourceURI; } + + /** + * Get sourceURI. + * + * @return string $sourceURI or false on error. + */ + function getSourceLocURI() { + if (empty($this->_sourceLocURI)) { + return false; + } + return $this->_sourceLocURI; + } /** * Setter for property targetURI. @@ -308,6 +320,10 @@ class Horde_SyncML_Sync { $replace = true; $ok = false; $merge = false; + if ($hordeType == 'configuration') + { + $command->setStatus(RESPONSE_ALREADY_EXISITS); + } if ($guid) { Horde::logMessage('SyncML: locuri '. $locURI . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_DEBUG); @@ -317,12 +333,13 @@ class Horde_SyncML_Sync { switch ($sync_conflicts) { case CONFLICT_CLIENT_WINNING: - $command->setStatus(RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINNING); + $command->setStatus(RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINS); break; case CONFLICT_SERVER_WINNING: Horde::logMessage('SyncML: REJECT client change for locuri ' . $locURI . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING); + $command->setStatus(RESPONSE_CONFLICT_RESOLVED_WITH_SERVER_WINS); $ok = true; $replace = false; $state->log('Client-AddReplaceIgnored'); @@ -331,6 +348,7 @@ class Horde_SyncML_Sync { Horde::logMessage('SyncML: Merge server and client data for locuri ' . $locURI . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING); + $command->setStatus(RESPONSE_CONFLICT_RESOLVED_WITH_MERGE); $merge = true; break; case CONFLICT_RESOLVED_WITH_DUPLICATE: @@ -340,6 +358,7 @@ class Horde_SyncML_Sync { Horde::logMessage('SyncML: Server RO! REJECT client change for locuri ' . $locURI . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING); + $command->setStatus(RESPONSE_PERMISSION_DENIED); $ok = true; $replace = false; $ts = $state->getSyncTSforAction($guid, 'modify'); @@ -350,6 +369,7 @@ class Horde_SyncML_Sync { Horde::logMessage('SyncML: Server RO! UNDO client change for locuri ' . $locURI . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING); + $command->setStatus(RESPONSE_PERMISSION_DENIED); $state->pushChangedItem($type, $guid); $ok = true; $replace = false; diff --git a/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php b/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php index 143aae2639..4f7ecb0749 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php @@ -244,6 +244,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync { . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING); if ($sync_conflicts != CONFLICT_RESOLVED_WITH_DUPLICATE) { $state->log("Client-AddReplaceIgnored"); + $command->setStatus(RESPONSE_CONFILCT_RESOLVED_WITH_DUPLICATE); continue; } } else { @@ -252,6 +253,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync { __FILE__, __LINE__, PEAR_LOG_DEBUG); $state->setUID($type, $locURI, $guid); $state->log("Client-Map"); + $command->setStatus(RESPONSE_ALREADY_EXISITS); continue; } } diff --git a/phpgwapi/inc/horde/config/registry.php b/phpgwapi/inc/horde/config/registry.php index d6f93773f3..fba8f4b48e 100644 --- a/phpgwapi/inc/horde/config/registry.php +++ b/phpgwapi/inc/horde/config/registry.php @@ -151,3 +151,13 @@ $this->applications['egwcaltaskssync'] = array( 'menu_parent' => 'organizing' ); +$this->applications['egwconfigurationsync'] = array( + 'fileroot' => EGW_SERVER_ROOT.'/syncml/configuration', + 'webroot' => $this->applications['horde']['webroot'] . '/mnemo', + 'icon' => $this->applications['horde']['webroot'] . '/mnemo/graphics/mnemo.gif', + 'name' => _("Funambol Configurations"), + 'status' => 'active', + 'provides' => array('configuration'), + 'menu_parent' => 'organizing' +); + From 99cb0d791a92173909f660cfb060b2774a6db075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sat, 26 Jun 2010 11:26:46 +0000 Subject: [PATCH 03/91] Fix SyncML SourceURI issue --- phpgwapi/inc/horde/Horde/SyncML/Command/Put.php | 2 +- phpgwapi/inc/horde/Horde/SyncML/State.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/phpgwapi/inc/horde/Horde/SyncML/Command/Put.php b/phpgwapi/inc/horde/Horde/SyncML/Command/Put.php index c99a7b0c16..a1bcc14251 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Command/Put.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Command/Put.php @@ -133,7 +133,7 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command { break; case 'SourceRef': - $this->_sourceReference = trim($this->_chars); + $this->_sourceReference = strtolower(trim($this->_chars)); break; case 'Tx-Pref': diff --git a/phpgwapi/inc/horde/Horde/SyncML/State.php b/phpgwapi/inc/horde/Horde/SyncML/State.php index 3635cab7cb..eca8b74a78 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/State.php +++ b/phpgwapi/inc/horde/Horde/SyncML/State.php @@ -985,6 +985,7 @@ class Horde_SyncML_State { function getPreferedContentTypeClient($_sourceLocURI, $_targetLocURI = null) { $deviceInfo = $this->getClientDeviceInfo(); + $_sourceLocURI = strtolower($_sourceLocURI); if(isset($deviceInfo['dataStore'][$_sourceLocURI]['maxGUIDSize']['contentType'])) { $this->_maxGUIDSize = $deviceInfo['dataStore'][$this->_sourceURI]['maxGUIDSize']['contentType']; From c5ae8dd8e257f6cef59a77ec0c717d3832d8a382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sat, 26 Jun 2010 11:35:11 +0000 Subject: [PATCH 04/91] Support Free/Busy information with credentials as part of the URL --- addressbook/inc/class.addressbook_bo.inc.php | 12 +++-- calendar/freebusy.php | 48 ++++++++++++++++---- calendar/inc/class.calendar_bo.inc.php | 19 ++++++-- calendar/inc/class.calendar_hooks.inc.php | 16 +++++-- calendar/lang/egw_de.lang | 6 +-- calendar/lang/egw_en.lang | 7 ++- groupdav.php | 2 + 7 files changed, 83 insertions(+), 27 deletions(-) diff --git a/addressbook/inc/class.addressbook_bo.inc.php b/addressbook/inc/class.addressbook_bo.inc.php index f924b0313c..81f7e40ef2 100755 --- a/addressbook/inc/class.addressbook_bo.inc.php +++ b/addressbook/inc/class.addressbook_bo.inc.php @@ -607,6 +607,8 @@ class addressbook_bo extends addressbook_so */ function db2data($data, $date_format='ts') { + static $fb_url = false; + // convert timestamps from server-time in the db to user-time foreach ($this->timestamps as $name) { @@ -620,10 +622,12 @@ class addressbook_bo extends addressbook_so // set freebusy_uri for accounts if (!$data['freebusy_uri'] && !$data['owner'] && $data['account_id'] && !is_object($GLOBALS['egw_setup'])) { - static $fb_url; - if (!$fb_url && @is_dir(EGW_SERVER_ROOT.'/calendar/inc')) $fb_url = calendar_bo::freebusy_url(''); - if ($fb_url) $data['freebusy_uri'] = $fb_url.urlencode( - isset($data['account_lid']) ? $data['account_lid'] : $GLOBALS['egw']->accounts->id2name($data['account_id'])); + if ($fb_url || @is_dir(EGW_SERVER_ROOT.'/calendar/inc')) + { + $fb_url = true; + $user = isset($data['account_lid']) ? $data['account_lid'] : $GLOBALS['egw']->accounts->id2name($data['account_id']); + $data['freebusy_uri'] = calendar_bo::freebusy_url($user); + } } return $data; } diff --git a/calendar/freebusy.php b/calendar/freebusy.php index eb7c124b05..21d68b98d0 100644 --- a/calendar/freebusy.php +++ b/calendar/freebusy.php @@ -43,6 +43,7 @@ if (strpos($_SERVER['QUERY_STRING'],'=3D') !== false && substr($_GET['user'],0,2 { $_GET['user'] = substr($_GET['user'],2); if (isset($_GET['password'])) $_GET['password'] = substr($_GET['password'],2); + if (isset($_GET['cred'])) $_GET['cred'] = substr($_GET['cred'],2); } if (!is_numeric($user = $_GET['user'])) { @@ -58,15 +59,46 @@ if ($user === false || !($username = $GLOBALS['egw']->accounts->id2name($user))) } if (!$loged_in) { - $GLOBALS['egw']->preferences->account_id = $user; - $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); - $GLOBALS['egw_info']['user']['account_id'] = $user; - $GLOBALS['egw_info']['user']['account_lid'] = $username; - - $cal_prefs = &$GLOBALS['egw_info']['user']['preferences']['calendar']; - if (!$cal_prefs['freebusy'] || !empty($cal_prefs['freebusy_pw']) && $cal_prefs['freebusy_pw'] != $_GET['password']) + if (empty($_GET['cred'])) { - fail_exit(lang("freebusy: Unknow user '%1', wrong password or not availible to not loged in users !!!",$_GET['user'])); + $GLOBALS['egw_info']['user']['account_id'] = $user; + $GLOBALS['egw_info']['user']['account_lid'] = $username; + $GLOBALS['egw']->preferences->account_id = $user; + $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); + $cal_prefs = &$GLOBALS['egw_info']['user']['preferences']['calendar']; + $loged_in = !empty($cal_prefs['freebusy']) && + (empty($cal_prefs['freebusy_pw']) || $cal_prefs['freebusy_pw'] == $_GET['password']); + } + else + { + $credentials = base64_decode($_GET['cred']); + list($authuser, $password) = explode(':', $credentials, 2); + if (strpos($authuser, '@') === false) + { + $domain = $GLOBALS['egw_info']['server']['default_domain']; + $authuser .= '@' . $domain; + } + else + { + list(, $domain) = explode('@',$authuser, 2); + } + if (array_key_exists($domain, $GLOBALS['egw_domain'])) + { + $_POST['login'] = $authname; + $_REQUEST['domain'] = $domain; + $GLOBALS['egw_info']['server']['default_domain'] = $domain; + $GLOBALS['egw_info']['user']['domain'] = $domain; + $GLOBALS['egw_info']['flags']['currentapp'] = 'login'; + $GLOBALS['egw_info']['flags']['noapi'] = false; + require_once(EGW_API_INC . '/functions.inc.php'); + $loged_in = $GLOBALS['egw']->session->create($authuser, $password, 'text'); + session_unset(); + session_destroy(); + } + } + if (!$loged_in) + { + fail_exit(lang("freebusy: Unknow user '%1', or not available for unauthenticated users!", $_GET['user'])); } } if ($_GET['debug']) diff --git a/calendar/inc/class.calendar_bo.inc.php b/calendar/inc/class.calendar_bo.inc.php index 177a1b520a..87352ec3d7 100644 --- a/calendar/inc/class.calendar_bo.inc.php +++ b/calendar/inc/class.calendar_bo.inc.php @@ -1772,14 +1772,25 @@ class calendar_bo * @param int|string $user account_id or account_lid * @param string $pw=null password */ - static function freebusy_url($user,$pw=null) + static function freebusy_url($user='',$pw=null) { if (is_numeric($user)) $user = $GLOBALS['egw']->accounts->id2name($user); - + + $credentials = ''; + + if ($pw) + { + $credentials = '&password='.urlencode($pw); + } + elseif ($GLOBALS['egw_info']['user']['preferences']['calendar']['freebusy'] == 2) + { + $credentials = $GLOBALS['egw_info']['user']['account_lid'] + . ':' . $GLOBALS['egw_info']['user']['passwd']; + $credentials = '&cred=' . base64_encode($credentials); + } return (!$GLOBALS['egw_info']['server']['webserver_url'] || $GLOBALS['egw_info']['server']['webserver_url'][0] == '/' ? ($_SERVER['HTTPS'] ? 'https://' : 'http://').$_SERVER['HTTP_HOST'] : ''). - $GLOBALS['egw_info']['server']['webserver_url'].'/calendar/freebusy.php?user='.urlencode($user). - ($pw ? '&password='.urlencode($pw) : ''); + $GLOBALS['egw_info']['server']['webserver_url'].'/calendar/freebusy.php/?user='.urlencode($user).$credentials; } /** diff --git a/calendar/inc/class.calendar_hooks.inc.php b/calendar/inc/class.calendar_hooks.inc.php index 2515ea5fd7..6c7c446bba 100644 --- a/calendar/inc/class.calendar_hooks.inc.php +++ b/calendar/inc/class.calendar_hooks.inc.php @@ -240,6 +240,11 @@ class calendar_hooks 'all' => lang('Always'), 'startday' => lang('If start day differs'), ); + $freebusy_values = array( + 0 => lang('No'), + 1 => lang('Yes'), + 2 => lang('With credentials included'), + ); if (!$hook_data['setup']) // does not work at setup time { $options = array('0' => lang('none')); @@ -248,7 +253,9 @@ class calendar_hooks $options[$group['account_id']] = common::grab_owner_name($group['account_id']); } $freebusy_url = calendar_bo::freebusy_url($GLOBALS['egw_info']['user']['account_lid'],$GLOBALS['egw_info']['user']['preferences']['calendar']['freebusy_pw']); - $freebusy_help = lang('Should not loged in persons be able to see your freebusy information? You can set an extra password, different from your normal password, to protect this informations. The freebusy information is in iCal format and only include the times when you are busy. It does not include the event-name, description or locations. The URL to your freebusy information is %1.',''.$freebusy_url.''); + $freebusy_url = ''.$freebusy_url.''; + $freebusy_help = lang('Should not loged in persons be able to see your freebusy information? You can set an extra password, different from your normal password, to protect this informations. The freebusy information is in iCal format and only include the times when you are busy. It does not include the event-name, description or locations. The URL to your freebusy information is'); + $freebusy_help .= ' ' . $freebusy_url; // Timezone for file exports $export_tzs = array('0' => 'Use Event TZ'); @@ -536,15 +543,16 @@ class calendar_hooks 'admin' => False, ), 'freebusy' => array( - 'type' => 'check', + 'type' => 'select', 'label' => 'Make freebusy information available to not loged in persons?', 'name' => 'freebusy', 'help' => $freebusy_help, + 'values' => $freebusy_values, 'run_lang' => false, 'subst_help' => False, 'xmlrpc' => True, 'admin' => False, - 'forced' => false, + 'forced' => 0, ), 'freebusy_pw' => array( 'type' => 'input', @@ -553,7 +561,7 @@ class calendar_hooks 'help' => 'If you dont set a password here, the information is available to everyone, who knows the URL!!!', 'xmlrpc' => True, 'admin' => False, - 'forced' => 'no' + 'forced' => '' ) ); } diff --git a/calendar/lang/egw_de.lang b/calendar/lang/egw_de.lang index 99c21b2f0c..d63c9f51a2 100644 --- a/calendar/lang/egw_de.lang +++ b/calendar/lang/egw_de.lang @@ -180,7 +180,7 @@ forward one month calendar de einen Monat weiter forward one year calendar de ein Jahr weiter four days view calendar de Vier-Tagesansicht freebusy common de frei / belegt -freebusy: unknow user '%1', wrong password or not availible to not loged in users !!! calendar de Frei / Belegt: Unbekannter Benutzername '%1', falsches Passwort oder nicht verfügbar für nicht angemeldete Benutzer !!! +freebusy: unknow user '%1', or not available for unauthenticated users! calendar de Free/Busy: Unbekannter Benutzer '%1', oder Daten ohne Authentisierung nicht verfügbar! freetime search calendar de Terminsuche fri calendar de Fr full description calendar de vollständige Beschreibung @@ -355,8 +355,7 @@ set new events to private calendar de Neue Termine als private Termine eintragen setting lock time calender admin de Zeitintervall für Datensatzlock (default 1 sec.) shall the date parameter be accepted (e.g. from calendar module)? calendar de Soll der Parameter Datum akzeptiert werden (z.B. vom Kalender Modul)? should new events created as private by default ? calendar de Sollen neue Termine generell als Privat angelegt werden? -should not loged in persons be able to see your freebusy information? you can set an extra password, different from your normal password, to protect this informations. the freebusy information is in ical format and only include the times when you are busy. it does not include the event-name, description or locations. the url to your freebusy information is %1. calendar de Sollen nicht angemeldete Personen Ihre Belegtzeiten einsehen können? Sie können ein Passwort setzen um diese Informationen zu schützen. Das Passwort sollte sich von Ihrem normalen Passwort unterscheiden. Die Belegtzeiten sind im iCal Format und enthalten ausschließlich die Zeiten, an denen Sie nicht verfügbar sind. Sie enthalten NICHT den Namen, die Beschreibung oder den Ort des Termins. Die Adresse (URL) Ihrer Belegtzeiten ist %1. -should not loged in persons be able to see your freebusy information? you can set an extra password, different from your normal password, to protect this informations. the freebusy information is in ical format and only include the times when you are busy. it does not include the event-name, description or locations. the url to your freebusy information is http://213.183.76.121/egroupware/calendar/freebusy.php?user=mkk. calendar de Soll Ihre Verfügbarkeit auch ohne Anmeldung sichtbar sein? Sie können auch ein separates Passwort zum Schutz dieser Information vergeben. Ihre Verfügbarkeit wird im iCal-Format veröffnetlicht und enthält nur die Zeiten, zu denen sie bereits gebucht sind. Titel, Beschreibung und Ort sind so nicht einsehbar. +should not loged in persons be able to see your freebusy information? you can set an extra password, different from your normal password, to protect this informations. the freebusy information is in ical format and only include the times when you are busy. it does not include the event-name, description or locations. the url to your freebusy information is calendar de Sollen nicht angemeldete Personen Ihre Belegtzeiten einsehen können? Sie können ein Passwort setzen um diese Informationen zu schützen. Das Passwort sollte sich von Ihrem normalen Passwort unterscheiden. Die Belegtzeiten sind im iCal Format und enthaltena usschließlich die Zeiten, an denen Sie nicht verfügbar sind. Sie enthalten NICHT den Namen, die Beschreibung oder den Ort des Termins. Die Adresse (URL) Ihrer Belegtzeiten ist should the grid be shown in the calendar calendar de Soll das Gitternetz im Kalender angezeigt werden? should the number of weeks be shown on top of the calendar calendar de Soll die Wochenanzahl im Kalander oben abgezeigt werden? should the number of weeks be shown on top of the calendar (only if offset = 0) calendar de Soll die Kalenderwoche im oberen Teil des Kalenders angezeigt werden (Nur wenn Abstabd=0) @@ -466,6 +465,7 @@ which events do you want to see when you enter the calendar. calendar de Welche which of calendar view do you want to see, when you start calendar ? calendar de Welche der möglichen Ansichten des Kalenders möchten Sie als Standard sehen, wenn der Kalender geöffnet wird? whole day calendar de ganztägig whole query calendar de Ganze Abfrage +with credentials included calendar de Mit Zugangsdaten im URL wk calendar de KW work day ends on calendar de Arbeitstag endet um work day starts on calendar de Arbeitstag beginnt um diff --git a/calendar/lang/egw_en.lang b/calendar/lang/egw_en.lang index 53a1d0fc93..1143dd80d1 100644 --- a/calendar/lang/egw_en.lang +++ b/calendar/lang/egw_en.lang @@ -180,7 +180,7 @@ forward one month calendar en forward one month forward one year calendar en forward one year four days view calendar en Four days view freebusy common en Free/Busy -freebusy: unknow user '%1', wrong password or not availible to not loged in users !!! calendar en Free/Busy: Unknown user '%1', wrong password or not available to not logged in users !!! +freebusy: unknow user '%1', or not available for unauthenticated users! calendar en freebusy: Unknow user '%1', or not available for unauthenticated users! freetime search calendar en Freetime Search fri calendar en Fri full description calendar en Full description @@ -355,9 +355,7 @@ set new events to private calendar en Set new events to private setting lock time calender admin en Setting Data lock Time for Calendar (default 1 sec.) shall the date parameter be accepted (e.g. from calendar module)? calendar en Shall the date parameter be accepted (e.g. from calendar module)? should new events created as private by default ? calendar en Should new events created as private by default ? -should not loged in persons be able to see your freebusy information? you can set an extra password, different from your normal password, to protect this informations. the freebusy information is in ical format and only include the times when you are busy. it does not include the event-name, description or locations. the url to your freebusy information is %1. calendar en Should not logged in persons be able to see your Free/Busy information? You can set an extra password, different from your normal password, to protect this information. The Free/Busy information is in iCal format and only includes the times when you are busy. It does not include the event-name, description or locations. The URL to your Free/Busy information is %1. -should not loged in persons be able to see your freebusy information? you can set an extra password, different from your normal password, to protect this informations. the freebusy information is in ical format and only include the times when you are busy. it does not include the event-name, description or locations. the url to your freebusy information is http://213.183.76.121/egroupware/calendar/freebusy.php?user=mkk. calendar en Should not loged in persons be able to see your freebusy information? You can set an extra password, different from your normal password, to protect this informations. The freebusy information is in iCal format and only include the times when you are busy. It does not include the event-name, description or locations. The URL to your freebusy information is http://213.183.76.121/egroupware/calendar/freebusy.php?user=mkk. -should not loged in persons be able to see your freebusy information? you can set an extra password, different from your normal password, to protect this informations. the freebusy information is in ical format and only include the times when you are busy. it does not include the event-name, description or locations. the url to your freebusy information is https://www.serveahead.de/trunk/calendar/freebusy.php?user=admin. calendar en Should not loged in persons be able to see your freebusy information? You can set an extra password, different from your normal password, to protect this informations. The freebusy information is in iCal format and only include the times when you are busy. It does not include the event-name, description or locations. The URL to your freebusy information is https://www.serveahead.de/trunk/calendar/freebusy.php?user=admin. +should not loged in persons be able to see your freebusy information? you can set an extra password, different from your normal password, to protect this informations. the freebusy information is in ical format and only include the times when you are busy. it does not include the event-name, description or locations. the url to your freebusy information is calendar en Should not loged in persons be able to see your freebusy information? You can set an extra password, different from your normal password, to protect this informations. The freebusy information is in iCal format and only include the times when you are busy. It does not include the event-name, description or locations. The URL to your freebusy information is should the grid be shown in the calendar calendar en Should the grid be shown in the calendar should the number of weeks be shown on top of the calendar calendar en Should the number of weeks be shown on top of the calendar should the number of weeks be shown on top of the calendar (only if offset = 0) calendar en Should the number of weeks be shown on top of the calendar (only if offset = 0) @@ -467,6 +465,7 @@ which events do you want to see when you enter the calendar. calendar en Which e which of calendar view do you want to see, when you start calendar ? calendar en Which of calendar views do you want to see, when you start calendar ? whole day calendar en Whole day whole query calendar en whole query +with credentials included calendar en With credentials included wk calendar en Wk work day ends on calendar en Work day ends on work day starts on calendar en Work day starts on diff --git a/groupdav.php b/groupdav.php index bfe90c7362..e6b1daa890 100644 --- a/groupdav.php +++ b/groupdav.php @@ -28,6 +28,8 @@ $GLOBALS['egw_info'] = array( require_once('phpgwapi/inc/class.egw_digest_auth.inc.php'); include(dirname(__FILE__).'/header.inc.php'); +$GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); + $headertime = microtime(true); $groupdav = new groupdav(); From c6b0f825d92411129be55906f508dfc47f515de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sat, 26 Jun 2010 15:58:33 +0000 Subject: [PATCH 05/91] Fix SyncML replace and recur_enddate issue --- calendar/inc/class.calendar_ical.inc.php | 149 ++++++----------------- calendar/inc/class.calendar_so.inc.php | 28 +++-- 2 files changed, 52 insertions(+), 125 deletions(-) diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index f41389e841..3bd314fa25 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -615,6 +615,8 @@ class calendar_ical extends calendar_boupdate $rrule = $rriter->generate_rrule($version); if ($event['recur_enddate']) { + $length = $event['end'] - $event['start']; + $rrule['UNTIL']->modify($length . ' second'); if (!$tzid || $version != '1.0') { if (!isset(self::$tz_cache['UTC'])) @@ -1052,6 +1054,7 @@ class calendar_ical extends calendar_boupdate function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0, $principalURL='', $user=null, $charset=null) { $this->events_imported = 0; + $replace = $delete_exceptions= false; if (!is_array($this->supportedFields)) $this->setSupportedFields(); @@ -1065,6 +1068,7 @@ class calendar_ical extends calendar_boupdate { if (count($events) == 1) { + $replace = $recur_date == 0; $events[0]['id'] = $cal_id; if (!is_null($etag)) $events[0]['etag'] = (int) $etag; if ($recur_date) $events[0]['recurrence'] = $recur_date; @@ -1120,43 +1124,42 @@ class calendar_ical extends calendar_boupdate . array2string($event)."\n",3,$this->logfile); } - /* - if ($event['recur_type'] != MCAL_RECUR_NONE) + $updated_id = false; + + if ($replace) { - // Adjust the event start -- no exceptions before and at the start - $length = $event['end'] - $event['start']; - $rriter = calendar_rrule::event2rrule($event, false); - $rriter->rewind(); - if (!$rriter->valid()) continue; // completely disolved into exceptions - - $newstart = egw_time::to($rriter->current, 'server'); - if ($newstart != $event['start']) + $event_info['type'] = $event['recur_type'] == MCAL_RECUR_NONE ? + 'SINGLE' : 'SERIES-MASTER'; + $event_info['acl_edit'] = $this->check_perms(EGW_ACL_EDIT, $cal_id); + if (($event_info['stored_event'] = $this->read($cal_id, 0, false, 'server')) && + $event_info['stored_event']['recur_type'] != MCAL_RECUR_NONE && + ($event_info['stored_event']['recur_type'] != $event['recur_type'] + || $event_info['stored_event']['recur_interval'] != $event['recur_interval'] + || $event_info['stored_event']['recur_data'] != $event['recur_data'] + || $event_info['stored_event']['start'] != $event['start'])) { - // leading exceptions skiped - $event['start'] = $newstart; - $event['end'] = $newstart + $length; - } - - $exceptions = $event['recur_exception']; - foreach($exceptions as $key => $day) - { - // remove leading exceptions - if ($day <= $event['start']) + // handle the old exceptions + $recur_exceptions = $this->so->get_related($event_info['stored_event']['uid']); + foreach ($recur_exceptions as $id) { - if ($this->log) + if ($delete_exceptions) { - error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. - '(): event SERIES-MASTER skip leading exception ' . - $day . "\n",3,$this->logfile); + $this->delete($id); + } + else + { + if (!($exception = $this->read($id))) continue; + $exception['uid'] = common::generate_uid('calendar', $id); + $exception['reference'] = $exception['recurrence'] = 0; + $this->update($exception, true); } - unset($exceptions[$key]); } } - $event['recur_exception'] = $exceptions; } - */ - $updated_id = false; - $event_info = $this->get_event_info($event); + else + { + $event_info = $this->get_event_info($event); + } // common adjustments for existing events if (is_array($event_info['stored_event'])) @@ -1471,55 +1474,7 @@ class calendar_ical extends calendar_boupdate if (is_array($days)) { $recur_exceptions = array(); - /* - if (!isset($days[$event_info['stored_event']['start']]) && - $event_info['stored_event']['start'] < $event['start']) - { - // We started with a pseudo exception and moved the - // event start therefore to the future; let's try to go back - $exceptions = $this->so->get_recurrence_exceptions($event_info['stored_event'], $this->tzid, 0, 0, 'rrule'); - if ($this->log) - { - error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."(START ADJUSTMENT):\n" . - array2string($exceptions)."\n",3,$this->logfile); - } - $startdate = $event_info['stored_event']['start']; - $length = $event['end'] - $event['start']; - $rriter = calendar_rrule::event2rrule($event_info['stored_event'], false); - $rriter->rewind(); - do - { - // start is a pseudo excpetion for sure - $rriter->next_no_exception(); - $day = $this->date2ts($rriter->current()); - if ($this->log) - { - error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. - '(): event SERIES-MASTER try leading pseudo exception ' . - $day . "\n",3,$this->logfile); - } - if ($day >= $event['start']) break; - if (!isset($exceptions[$day])) - { - // all leading occurrences have to be exceptions; - // if not -> no restore - if ($this->log) - { - error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. - '(): event SERIES-MASTER pseudo exception series broken at ' . - $rriter->current() . "!\n",3,$this->logfile); - } - $startdate = $event['start']; - $recur_exceptions = array(); - break; - } - $recur_exceptions[] = $day; - } while ($rriter->valid()); - - $event['start'] = $startdate; - $event['end'] = $startdate + $length; - } */ - + foreach ($event['recur_exception'] as $recur_exception) { if (isset($days[$recur_exception])) @@ -1592,43 +1547,7 @@ class calendar_ical extends calendar_boupdate array_unique(array_merge($event_info['master_event']['recur_exception'], array($event['recurrence']))); } - /* - // Adjust the event start -- must not be an exception - $length = $event_info['master_event']['end'] - $event_info['master_event']['start']; - $rriter = calendar_rrule::event2rrule($event_info['master_event'], false); - $rriter->rewind(); - if ($rriter->valid()) - { - $newstart = egw_time::to($rriter->current, 'server'); - foreach($event_info['master_event']['recur_exception'] as $key => $day) - { - // remove leading exceptions - if ($day < $newstart) - { - if (($foundEvents = $this->find_event( - array('uid' => $event_info['master_event']['uid'], - 'recurrence' => $day), 'exact')) && - ($eventId = array_shift($foundEvents)) && - ($exception = read($eventId, 0, 'server'))) - { - // Unlink this exception - unset($exception['uid']); - $this->update($exception, true); - } - if ($event['recurrence'] == $day) - { - // Unlink this exception - unset($event['uid']); - } - unset($event_info['master_event']['recur_exception'][$key]); - } - } - } - if ($event_info['master_event']['start'] < $newstart) - { - $event_info['master_event']['start'] = $newstart; - $event_info['master_event']['end'] = $newstart + $length; - }*/ + $event['reference'] = $event_info['master_event']['id']; $event['category'] = $event_info['master_event']['category']; $event['owner'] = $event_info['master_event']['owner']; diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php index 9bc7bce4ba..5b0fe85e32 100644 --- a/calendar/inc/class.calendar_so.inc.php +++ b/calendar/inc/class.calendar_so.inc.php @@ -914,8 +914,23 @@ ORDER BY cal_user_type, cal_usre_id $this->db->update($this->cal_table, array('cal_uid' => $event['cal_uid']), array('cal_id' => $event['cal_id']),__LINE__,__FILE__,'calendar'); } - // write information about recuring event, if recur_type is present in the array - if ($event['recur_type'] != MCAL_RECUR_NONE) + + if ($event['recur_type'] == MCAL_RECUR_NONE) + { + $this->db->delete($this->dates_table,array( + 'cal_id' => $cal_id), + __LINE__,__FILE__,'calendar'); + + // delete all user-records, with recur-date != 0 + $this->db->delete($this->user_table,array( + 'cal_id' => $cal_id, 'cal_recur_date != 0'), + __LINE__,__FILE__,'calendar'); + + $this->db->delete($this->repeats_table,array( + 'cal_id' => $cal_id), + __LINE__,__FILE__,'calendar'); + } + else // write information about recuring event, if recur_type is present in the array { // fetch information about the currently saved (old) event $old_min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn(); @@ -1024,14 +1039,7 @@ ORDER BY cal_user_type, cal_usre_id // write the repeats table $event['recur_exception'] = empty($event['recur_exception']) ? null : implode(',',$event['recur_exception']); unset($event[0]); // unset the 'etag=etag+1', as it's not in the repeats table - if($event['recur_type'] != MCAL_RECUR_NONE) - { - $this->db->insert($this->repeats_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar'); - } - else - { - $this->db->delete($this->repeats_table,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar'); - } + $this->db->insert($this->repeats_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar'); } // update start- and endtime if present in the event-array, evtl. we need to move all recurrences if (isset($event['cal_start']) && isset($event['cal_end'])) From bce68a9e821d86eae1d6d2f05045537f072beab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sun, 27 Jun 2010 22:16:22 +0000 Subject: [PATCH 06/91] Fix CalDAV group attendee issue; various GroupDAV improvements --- .../inc/class.addressbook_groupdav.inc.php | 4 +- calendar/inc/class.calendar_groupdav.inc.php | 12 ++- calendar/inc/class.calendar_ical.inc.php | 78 ++++++++----------- phpgwapi/inc/class.groupdav.inc.php | 34 +++++--- .../inc/class.groupdav_principals.inc.php | 29 +++++-- 5 files changed, 90 insertions(+), 67 deletions(-) diff --git a/addressbook/inc/class.addressbook_groupdav.inc.php b/addressbook/inc/class.addressbook_groupdav.inc.php index 97c14c6798..70ae27d8b8 100644 --- a/addressbook/inc/class.addressbook_groupdav.inc.php +++ b/addressbook/inc/class.addressbook_groupdav.inc.php @@ -391,9 +391,9 @@ class addressbook_groupdav extends groupdav_handler // should we hide the accounts addressbook if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null; - $result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','','','',$filter); + $result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','',false,'AND',false,$filter); - $ctag = 'EGw-'.$result[0]['modified'].'-wGE'; + $ctag = 'EGw-'.$result[0]['contact_modified'].'-wGE'; return $ctag; } diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index 73c0e95616..18dfcc61d4 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -591,6 +591,8 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f */ function post(&$options,$id,$user=null) { + $status = $this->put($options,$id,$user); + // error_log("CalDAV POST: $status" . print_r($options, true)); return true; } @@ -869,8 +871,12 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/'))); */ // email of the current user, see caldav-sheduling draft - $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( - HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))); + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']), + HTTP_WebDAV_Server::mkprop('href',$base_uri.'/principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/'), + HTTP_WebDAV_Server::mkprop('href','urn:uuid:'.$GLOBALS['egw_info']['user']['account_lid']))); + //$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + // HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))); // supported components, currently only VEVENT $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array( HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VCALENDAR')), @@ -886,6 +892,8 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data', array('content-type' => 'text/calendar', 'version'=> '2.0')), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data', array('content-type' => 'text/x-calendar', 'version'=> '1.0')))); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::ICAL,'calendar-color','#0040A0FF'); // TODO: make it configurable + //$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'publish-url',array( + // HTTP_WebDAV_Server::mkprop('href',$base_uri.'/calendar/'))); //$props = self::current_user_privilege_set($props); return $props; diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 3bd314fa25..4f5fbad689 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -433,27 +433,6 @@ class calendar_ical extends calendar_boupdate sort($exceptions); } $event['recur_exception'] = $exceptions; - /* - // Adjust the event start -- must not be an exception - $length = $event['end'] - $event['start']; - $rriter = calendar_rrule::event2rrule($event, false, $tzid); - $rriter->rewind(); - if ($rriter->valid()) - { - $event['start'] = egw_time::to($rriter->current, 'server'); - $event['end'] = $event['start'] + $length; - foreach($exceptions as $key => $day) - { - // remove leading exceptions - if ($day <= $event['start']) unset($exceptions[$key]); - } - $event['recur_exception'] = $exceptions; - } - else - { - // the series dissolved completely into exceptions - continue; - }*/ } foreach ($egwSupportedFields as $icalFieldName => $egwFieldName) @@ -496,15 +475,15 @@ class calendar_ical extends calendar_boupdate { $participantURL = empty($info['email']) ? '' : 'MAILTO:' . $info['email']; } + // RSVP={TRUE|FALSE} // resonse expected, not set in eGW => status=U + $rsvp = $status == 'U' ? 'TRUE' : 'FALSE'; if ($role == 'CHAIR') { $organizerURL = $participantURL; + $rsvp = ''; $organizerCN = $participantCN; $organizerUID = ($info['type'] != 'e' ? $uid : ''); } - $attributes['ATTENDEE'][] = $participantURL; - // RSVP={TRUE|FALSE} // resonse expected, not set in eGW => status=U - $rsvp = $status == 'U' ? 'TRUE' : 'FALSE'; // PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm. $status = $this->status_egw2ical[$status]; // CUTYPE={INDIVIDUAL|GROUP|RESOURCE|ROOM|UNKNOWN} @@ -512,6 +491,11 @@ class calendar_ical extends calendar_boupdate { case 'g': $cutype = 'GROUP'; + if ($this->productManufacturer == 'groupdav') + { + $participantURL = 'invalid:nomail'; + $cutype = 'INDIVIDUAL'; + } break; case 'r': $cutype = 'RESOURCE'; @@ -526,14 +510,17 @@ class calendar_ical extends calendar_boupdate break; }; // ROLE={CHAIR|REQ-PARTICIPANT|OPT-PARTICIPANT|NON-PARTICIPANT|X-*} - $parameters['ATTENDEE'][] = array( - 'CN' => $participantCN, - 'ROLE' => $role, - 'PARTSTAT' => $status, - 'CUTYPE' => $cutype, - 'RSVP' => $rsvp, - )+($info['type'] != 'e' ? array('X-EGROUPWARE-UID' => $uid) : array())+ - ($quantity > 1 ? array('X-EGROUPWARE-QUANTITY' => $quantity) : array()); + $options = array(); + if (!empty($participantCN)) $options['CN'] = $participantCN; + if (!empty($role)) $options['ROLE'] = $role; + if (!empty($status)) $options['PARTSTAT'] = $status; + if (!empty($cutype)) $options['CUTYPE'] = $cutype; + if (!empty($rsvp)) $options['RSVP'] = $rsvp; + if (!empty($info['email'])) $options['EMAIL'] = $info['email']; + if ($info['type'] != 'e') $options['X-EGROUPWARE-UID'] = $uid; + if ($quantity > 1) $options['X-EGROUPWARE-QUANTITY'] = $quantity; + $attributes['ATTENDEE'][] = $participantURL; + $parameters['ATTENDEE'][] = $options; } break; @@ -547,37 +534,38 @@ class calendar_ical extends calendar_boupdate { $organizerCN = '"' . trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname') . ' ' . $GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname')) . '"'; - $organizerURL = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email'); + $organizerEMail = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email'); if ($version == '1.0') { $organizerURL = trim($organizerCN . (empty($organizerURL) ? '' : ' <' . $organizerURL .'>')); } else { - $organizerURL = empty($organizerURL) ? '' : 'MAILTO:' . $organizerURL; + $organizerURL = empty($organizerEMail) ? '' : 'MAILTO:' . $organizerEMail; } $organizerUID = $event['owner']; if (!isset($event['participants'][$event['owner']])) { - $attributes['ATTENDEE'][] = $organizerURL; - $parameters['ATTENDEE'][] = array( - 'CN' => $organizerCN, + $options = array( 'ROLE' => 'CHAIR', 'PARTSTAT' => 'DELEGATED', 'CUTYPE' => 'INDIVIDUAL', - 'RSVP' => 'FALSE', - 'X-EGROUPWARE-UID' => $event['owner'], - ); + //'RSVP' => 'FALSE', + ); + if (!empty($organizerCN)) $options['CN'] = $organizerCN; + if (!empty($organizerEMail)) $options['EMAIL'] = $organizerEMail; + if (!empty($event['owner'])) $options['X-EGROUPWARE-UID'] = $event['owner']; + $attributes['ATTENDEE'][] = $organizerURL; + $parameters['ATTENDEE'][] = $options; } } - if ($this->productManufacturer != 'groupdav' || - !$this->check_perms(EGW_ACL_EDIT,$event)) - { + if ($this->productManufacturer != 'groupdav' || !$this->check_perms(EGW_ACL_EDIT,$event)) + { $attributes['ORGANIZER'] = $organizerURL; $parameters['ORGANIZER']['CN'] = $organizerCN; if (!empty($organizerUID)) { - $parameters['ORGANIZER']['X-EGROUPWARE-UID'] = $organizerUID; + $parameters['ORGANIZER']['X-EGROUPWARE-UID'] = $organizerUID; } } break; @@ -615,7 +603,7 @@ class calendar_ical extends calendar_boupdate $rrule = $rriter->generate_rrule($version); if ($event['recur_enddate']) { - $length = $event['end'] - $event['start']; + $length = ($event['end'] - $event['start']) / 2; $rrule['UNTIL']->modify($length . ' second'); if (!$tzid || $version != '1.0') { diff --git a/phpgwapi/inc/class.groupdav.inc.php b/phpgwapi/inc/class.groupdav.inc.php index 773bd96bf5..988fdc2649 100644 --- a/phpgwapi/inc/class.groupdav.inc.php +++ b/phpgwapi/inc/class.groupdav.inc.php @@ -273,12 +273,16 @@ class groupdav extends HTTP_WebDAV_Server self::mkprop('href',$this->base_uri.$user_prefix))), self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))), self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( - self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))), - self::mkprop('principal-collection-set',array( - self::mkprop('href',$this->base_uri.'/principals/users/'), - self::mkprop('href',$this->base_uri.'/principals/groups/'))), + self::mkprop('href','MAILTO:'.$account['account_email']), + self::mkprop('href',$this->base_uri.'/principals/users/'.$account['account_lid'].'/'), + self::mkprop('href','urn:uuid:'.$account['account_lid']))), + self::mkprop(groupdav::CALENDARSERVER,'email-address-set',array( + self::mkprop(groupdav::CALENDARSERVER,'email-address',$GLOBALS['egw_info']['user']['email']))), //self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), - //self::mkprop('principal-collection-set',array(self::mkprop('href',$this->base_uri.'/principals/'))), + self::mkprop('principal-collection-set',array(self::mkprop('href',$this->base_uri.'/principals/'))), + // OUTBOX URLs of the current user + self::mkprop(groupdav::CALDAV,'schedule-outbox-URL',array( + self::mkprop(groupdav::DAV,'href',$this->base_uri.'/calendar/'))), ); //$props = self::current_user_privilege_set($props); $files['files'][] = array( @@ -296,11 +300,11 @@ class groupdav extends HTTP_WebDAV_Server self::mkprop('displayname',lang('Accounts')), self::mkprop('resourcetype',array(self::mkprop('principals',''))), self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))), - self::mkprop(groupdav::CALDAV,'calendar-home-set',array( - self::mkprop('href',$this->base_uri.$user_prefix))), - self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( - self::mkprop('href',$this->base_uri.$user_prefix))), - self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), + //self::mkprop(groupdav::CALDAV,'calendar-home-set',array( + // self::mkprop('href',$this->base_uri.$user_prefix))), + //self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( + // self::mkprop('href',$this->base_uri.$user_prefix))), + //self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), ), ); } @@ -382,9 +386,15 @@ class groupdav extends HTTP_WebDAV_Server self::mkprop('alternate-URI-set',array( self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))), self::mkprop('principal-collection-set',array( - self::mkprop('href',$this->base_uri.'/principals/users/'), - self::mkprop('href',$this->base_uri.'/principals/groups/'), + self::mkprop('href',$this->base_uri.'/principals/'), )), + self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), + self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']), + self::mkprop('href',$this->base_uri.'/principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/'), + self::mkprop('href','urn:uuid:'.$GLOBALS['egw_info']['user']['account_lid']))), + self::mkprop(groupdav::CALENDARSERVER,'email-address-set',array( + self::mkprop(groupdav::CALENDARSERVER,'email-address',$GLOBALS['egw_info']['user']['email']))), ); switch ($app) diff --git a/phpgwapi/inc/class.groupdav_principals.inc.php b/phpgwapi/inc/class.groupdav_principals.inc.php index 1ae8150950..731e57fc99 100644 --- a/phpgwapi/inc/class.groupdav_principals.inc.php +++ b/phpgwapi/inc/class.groupdav_principals.inc.php @@ -84,6 +84,7 @@ class groupdav_principals extends groupdav_handler { $displayname = $this->translation->convert($account['account_fullname'], $this->translation->charset(),'utf-8'); + $props = array( HTTP_WebDAV_Server::mkprop('displayname',$displayname), HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)), @@ -91,9 +92,12 @@ class groupdav_principals extends groupdav_handler HTTP_WebDAV_Server::mkprop('principal', ''))), HTTP_WebDAV_Server::mkprop('alternate-URI-set',''), HTTP_WebDAV_Server::mkprop('principal-URL',$this->base_uri.'/principals/'.$account['account_lid']), - HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.$account['account_lid'].'/'), - HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), + HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), ); + foreach($this->accounts->memberships($account['account_id']) as $gid => $group) { $props[] = HTTP_WebDAV_Server::mkprop('group-membership',$this->base_uri.'/groups/'.$group); @@ -146,8 +150,11 @@ class groupdav_principals extends groupdav_handler { case '': $files[] = $this->add_account($account); - $files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-read'); - $files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-write'); + if ($options['depth']) + { + $files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-read'); + $files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-write'); + } break; case 'calendar-proxy-read': case 'calendar-proxy-write': @@ -250,10 +257,20 @@ class groupdav_principals extends groupdav_handler HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array( HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( - HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']))), + HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']), + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/users/'.$account['account_lid'].'/'), + HTTP_WebDAV_Server::mkprop('href','urn:uuid:'.$account['account_lid']))), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-outbox-URL',array( + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$this->base_uri.'/calendar/'))), + HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'email-address-set',array( + HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'email-address',$account['account_email']))), HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), HTTP_WebDAV_Server::mkprop('group-member-ship', $memberships), + HTTP_WebDAV_Server::mkprop('supported-report-set',array( + HTTP_WebDAV_Server::mkprop('supported-report',array( + HTTP_WebDAV_Server::mkprop('report', + HTTP_WebDAV_Server::mkprop('acl-principal-prop-set')))))), ); if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/users/".$account['account_lid'].', props='.array2string($props)); return array( @@ -288,7 +305,7 @@ class groupdav_principals extends groupdav_handler HTTP_WebDAV_Server::mkprop('principal', ''))), HTTP_WebDAV_Server::mkprop('alternate-URI-set',''), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array( - HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))), + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), HTTP_WebDAV_Server::mkprop('group-member-set', $members), From 2bfebbdb7fb1d495047c0a89421451d9daf32c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Mon, 28 Jun 2010 05:08:22 +0000 Subject: [PATCH 07/91] Improve group invitation handling --- calendar/inc/class.calendar_ical.inc.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 4f5fbad689..154b22ec52 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -496,6 +496,22 @@ class calendar_ical extends calendar_boupdate $participantURL = 'invalid:nomail'; $cutype = 'INDIVIDUAL'; } + $members = $GLOBALS['egw']->accounts->members($uid, true); + if (!isset($event['participants'][$this->user]) && in_array($this->user, $members)) + { + $user = $this->resource_info($this->user); + $attributes['ATTENDEE'][] = 'MAILTO:' . $user['email']; + $parameters['ATTENDEE'][] = array( + 'CN' => $user['name'], + 'ROLE' => 'REQ-PARTICIPANT', + 'PARTSTAT' => 'NEEDS-ACTION', + 'CUTYPE' => 'INDIVIDUAL', + 'RSVP' => 'TRUE', + 'X-EGROUPWARE-UID' => $this->user, + 'EMAIL' => $user['email'], + ); + $event['participants'][$this->user] = true; + } break; case 'r': $cutype = 'RESOURCE'; From aa6b4adcc139fa51fbe14b8af2f7efbace75e147 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 28 Jun 2010 10:52:37 +0000 Subject: [PATCH 08/91] return default instead of referer containing "cd=yes", which causes a load of the framework inside the framework --- phpgwapi/inc/class.common.inc.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phpgwapi/inc/class.common.inc.php b/phpgwapi/inc/class.common.inc.php index 011cb54563..3cbda42ac5 100644 --- a/phpgwapi/inc/class.common.inc.php +++ b/phpgwapi/inc/class.common.inc.php @@ -1556,6 +1556,7 @@ class common * @param string $default='' default to use if referer is not set by webserver or not determinable * @param string $referer='' referer string to use, default ('') use $_SERVER['HTTP_REFERER'] * @return string + * @todo get "real" referer for jDots template */ static function get_referer($default='',$referer='') { @@ -1573,7 +1574,7 @@ class common } $referer = str_replace('/etemplate/process_exec.php','/index.php',$referer); - if (empty($referer)) $referer = $default; + if (empty($referer) || strpos($referer,'cd=yes') !== false) $referer = $default; return $referer; } From cb3590ebab80d2bd597b3c0db4d362625268c097 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 28 Jun 2010 11:15:25 +0000 Subject: [PATCH 09/91] Do not send notification when a deleted event is purged --- calendar/inc/class.calendar_boupdate.inc.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index ac14c13e52..a6525236b1 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -1210,7 +1210,12 @@ class calendar_boupdate extends calendar_bo { return false; } - $this->send_update(MSG_DELETED,$event['participants'],$event); + + // Don't send notification if the event has already been deleted + if(!$event['deleted']) + { + $this->send_update(MSG_DELETED,$event['participants'],$event); + } if (!$recur_date || $event['recur_type'] == MCAL_RECUR_NONE) { From 3a4bd1e45aee3a240d17659b397480c49c34c91b Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 28 Jun 2010 11:38:28 +0000 Subject: [PATCH 10/91] Allow admins to restore deleted events --- calendar/inc/class.calendar_uiforms.inc.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 4d85e2c4ce..9429140615 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -1445,6 +1445,15 @@ function replace_eTemplate_onsubmit() $etpl->set_cell_attribute('button[cancel]','onclick',''); } + // Allow admins to restore deleted events + $config = config::read('phpgwapi'); + if($config['calendar_delete_history'] && $event['deleted'] && $GLOBALS['egw_info']['user']['apps']['admin']) + { + $content['deleted'] = $preserv['deleted'] = false; + $etpl->set_cell_attribute('button[save]', 'label', 'recover'); + $etpl->set_cell_attribute('button[apply]', 'disabled', true); + } + // Setup history tab $this->setup_history($content, $sel_options); From 5b9a4cb7e93bc235dbec723f132f1d451d804149 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 28 Jun 2010 11:38:43 +0000 Subject: [PATCH 11/91] Allow admins to restore deleted events - translation --- calendar/lang/egw_en.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/calendar/lang/egw_en.lang b/calendar/lang/egw_en.lang index 1143dd80d1..a353043eea 100644 --- a/calendar/lang/egw_en.lang +++ b/calendar/lang/egw_en.lang @@ -313,6 +313,7 @@ re-edit event calendar en Re-Edit event receive email updates calendar en Receive email updates receive notifications about events you created/modified/deleted calendar en Receive notifications about events you created/modified/deleted receive summary of appointments calendar en Receive summary of appointments +recover calendar en Recover recurrence calendar en Recurrence recurring event calendar en Recurring event rejected calendar en Rejected From ff7601188fbbbaeccd6d1349b78dfca5d50c3c32 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 28 Jun 2010 12:36:10 +0000 Subject: [PATCH 12/91] Fix category search when category ID is sent as an array in (addressbook advanced search) --- addressbook/inc/class.addressbook_sql.inc.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addressbook/inc/class.addressbook_sql.inc.php b/addressbook/inc/class.addressbook_sql.inc.php index 65cab23046..8b5aad9e3f 100644 --- a/addressbook/inc/class.addressbook_sql.inc.php +++ b/addressbook/inc/class.addressbook_sql.inc.php @@ -246,6 +246,12 @@ class addressbook_sql extends so_sql_cf $owner = isset($filter['owner']) ? $filter['owner'] : (isset($criteria['owner']) ? $criteria['owner'] : null); + // fix cat_id criteria to search in comma-separated multiple cats and return subcats + if (($cats = $criteria['cat_id'])) + { + $criteria += $this->_cat_search($criteria['cat_id'],$not); + unset($criteria['cat_id']); + } // fix cat_id filter to search in comma-separated multiple cats and return subcats if (($cats = $filter['cat_id'])) { From 30ce9afd8bd33b80d5ce0f63ea3baaded056d10d Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 28 Jun 2010 12:51:52 +0000 Subject: [PATCH 13/91] Set the timesheet title to the first linked entry --- timesheet/inc/class.timesheet_ui.inc.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/timesheet/inc/class.timesheet_ui.inc.php b/timesheet/inc/class.timesheet_ui.inc.php index 5430aa0c20..3cc0de2713 100644 --- a/timesheet/inc/class.timesheet_ui.inc.php +++ b/timesheet/inc/class.timesheet_ui.inc.php @@ -316,7 +316,10 @@ class timesheet_ui extends timesheet_bo unset($content['end_time']); break; default: - $preserv['ts_title_blur'] = egw_link::title($link_app,$link_id); + if(!$preserv['ts_title_blur']) + { + $preserv['ts_title_blur'] = egw_link::title($link_app,$link_id); + } break; } egw_link::link(TIMESHEET_APP,$content['link_to']['to_id'],$link_app,$link_id); From f096249cabe7b380e88bef82d7bd65dac348d980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20St=C3=B6ckel?= Date: Mon, 28 Jun 2010 13:35:10 +0000 Subject: [PATCH 14/91] Fixed bug with simple timesheet view mode and the unused quantity field --- timesheet/inc/class.timesheet_ui.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timesheet/inc/class.timesheet_ui.inc.php b/timesheet/inc/class.timesheet_ui.inc.php index 3cc0de2713..b42d88a15b 100644 --- a/timesheet/inc/class.timesheet_ui.inc.php +++ b/timesheet/inc/class.timesheet_ui.inc.php @@ -158,7 +158,7 @@ class timesheet_ui extends timesheet_bo case 'save': case 'save_new': case 'apply': - if (!$this->data['ts_quantity'] && $this->data['ts_duration']) // set the quantity (in h) from the duration (in min) + if ((!$this->data['ts_quantity'] || $this->ts_viewtype == 'short') && $this->data['ts_duration']) // set the quantity (in h) from the duration (in min) { $this->data['ts_quantity'] = $this->data['ts_duration'] / 60.0; } From f616c628b53812eeae351d3eed6caebffa3369fa Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 28 Jun 2010 13:46:39 +0000 Subject: [PATCH 15/91] fixed not working display and deleting of recurence exceptions (name of grid in template got somehow lost) --- calendar/setup/etemplates.inc.php | 4 ++-- calendar/templates/default/edit.xet | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/calendar/setup/etemplates.inc.php b/calendar/setup/etemplates.inc.php index 55318b2fdd..6b8daae838 100644 --- a/calendar/setup/etemplates.inc.php +++ b/calendar/setup/etemplates.inc.php @@ -2,7 +2,7 @@ /** * eGroupWare - eTemplates for Application calendar * http://www.egroupware.org - * generated by soetemplate::dump4setup() 2010-06-09 19:36 + * generated by soetemplate::dump4setup() 2010-06-28 15:43 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package calendar @@ -39,7 +39,7 @@ $templ_data[] = array('name' => 'calendar.edit.participants','template' => '','l $templ_data[] = array('name' => 'calendar.edit.print','template' => '','lang' => '','group' => '0','version' => '1.6.001','data' => 'a:1:{i:0;a:7:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:6:{s:1:"A";s:2:"95";s:2:"c3";s:4:",top";s:2:"c1";s:3:"row";s:2:"c2";s:2:"th";s:2:"h1";s:8:",@no_add";s:1:"D";s:24:",@hide_status_recurrence";}i:1;a:6:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:3:"New";}s:1:"B";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:6:"2,,0,0";i:1;a:4:{s:4:"type";s:14:"select-account";s:4:"size";s:27:"User or group,calendar+,,10";s:4:"name";s:7:"account";s:4:"help";s:13:"User or group";}i:2;a:3:{s:4:"type";s:10:"link-entry";s:4:"name";s:8:"resource";s:4:"size";s:14:"@cal_resources";}}s:1:"C";a:3:{s:4:"type";s:3:"int";s:4:"size";s:4:"1,,3";s:4:"name";s:8:"quantity";}s:1:"D";a:1:{s:4:"type";s:5:"label";}s:1:"E";a:3:{s:4:"type";s:6:"button";s:5:"label";s:3:"Add";s:4:"name";s:3:"add";}s:1:"F";a:1:{s:4:"type";s:5:"label";}}i:2;a:6:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"Type";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:12:"Participants";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"Quantity";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:5:"label";s:10:"All future";}s:1:"E";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Status";}s:1:"F";a:2:{s:4:"type";s:5:"label";s:5:"label";s:7:"Actions";}}i:3;a:6:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:11:"${row}[app]";}s:1:"B";a:6:{s:4:"type";s:5:"label";s:4:"data";a:2:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:6:"${row}";s:8:"readonly";s:1:"1";}s:1:"B";a:5:{s:4:"type";s:6:"select";s:4:"name";s:26:"accounts_status[$row_cont]";s:8:"onchange";s:1:"1";s:4:"help";s:30:"Accept or reject an invitation";s:7:"no_lang";s:1:"1";}}}s:4:"rows";i:1;s:4:"cols";i:2;s:4:"name";s:13:"${row}[title]";s:7:"no_lang";s:1:"1";}s:1:"C";a:3:{s:4:"type";s:3:"int";s:4:"name";s:16:"${row}[quantity]";s:4:"size";s:4:"1,,3";}s:1:"D";a:3:{s:4:"type";s:8:"checkbox";s:4:"name";s:25:"${row}[status_recurrence]";s:5:"align";s:6:"center";}s:1:"E";a:4:{s:4:"type";s:6:"select";s:4:"name";s:14:"${row}[status]";s:7:"no_lang";s:1:"1";s:8:"onchange";i:1;}s:1:"F";a:9:{s:4:"type";s:6:"button";s:4:"data";a:2:{i:0;a:1:{s:2:"h1";s:19:",!@resources_status";}i:1;a:2:{s:1:"A";a:4:{s:4:"type";s:16:"resources_select";s:4:"name";s:6:"${row}";s:8:"readonly";s:1:"1";s:7:"no_lang";s:1:"1";}s:1:"B";a:5:{s:4:"type";s:6:"select";s:4:"name";s:27:"resources_status[$row_cont]";s:8:"onchange";s:1:"1";s:4:"help";s:30:"Accept or reject an invitation";s:7:"no_lang";s:1:"1";}}}s:4:"rows";i:1;s:4:"cols";i:2;s:4:"name";s:22:"delete[$row_cont[uid]]";s:5:"align";s:6:"center";s:5:"label";s:6:"Delete";s:8:"onchange";i:1;s:4:"size";s:6:"delete";}}}s:4:"rows";i:3;s:4:"cols";i:6;s:4:"size";s:17:"100%,200,,,,,auto";s:4:"name";s:12:"participants";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"200";i:6;s:4:"auto";}}}','size' => '100%,200,,,,,auto','style' => '','modified' => '1229280346',); -$templ_data[] = array('name' => 'calendar.edit.recurrence','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:11:{s:1:"A";s:2:"95";s:1:"D";s:3:"50%";s:2:"c1";s:3:"row";s:2:"h1";s:12:",!@reference";s:2:"c2";s:2:"th";s:2:"h2";s:2:"12";s:2:"c3";s:3:"row";s:2:"h3";s:2:"12";s:2:"c4";s:3:"row";s:2:"h4";s:2:"12";s:2:"c5";s:7:"row,top";}i:1;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:9:"Exception";}s:1:"B";a:4:{s:8:"readonly";s:4:"true";s:4:"name";s:10:"recurrence";s:4:"type";s:9:"date-time";s:4:"span";s:5:",gray";}s:1:"C";a:3:{s:4:"name";s:9:"reference";s:4:"type";s:5:"label";s:4:"span";s:5:",gray";}s:1:"D";a:3:{s:4:"name";s:3:"uid";s:4:"type";s:5:"label";s:4:"span";s:5:",gray";}}i:2;a:4:{s:1:"A";a:3:{s:4:"span";s:8:"all,gray";s:4:"type";s:5:"label";s:5:"label";s:27:"Repeating Event Information";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:3;a:4:{s:1:"A";a:3:{s:4:"size";s:13:",,,recur_type";s:4:"type";s:5:"label";s:5:"label";s:11:"Repeat type";}s:1:"B";a:3:{s:8:"onchange";s:34:"check_recur_type(\'recur_type\', 2);";s:4:"name";s:10:"recur_type";s:4:"type";s:6:"select";}s:1:"C";a:3:{s:4:"size";s:17:",,,recur_interval";s:4:"type";s:5:"label";s:5:"label";s:8:"Interval";}s:1:"D";a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:14:"recur_interval";s:4:"size";s:9:"None,2,31";s:4:"help";s:53:"repeating interval, eg. 2 to repeat every second week";}}i:4;a:4:{s:1:"A";a:3:{s:4:"size";s:16:",,,recur_enddate";s:4:"type";s:5:"label";s:5:"label";s:8:"End date";}s:1:"B";a:3:{s:4:"name";s:13:"recur_enddate";s:4:"type";s:4:"date";s:4:"help";s:57:"repeat the event until which date (empty means unlimited)";}s:1:"C";a:3:{s:4:"size";s:7:",,,tzid";s:4:"type";s:5:"label";s:5:"label";s:8:"Timezone";}s:1:"D";a:3:{s:4:"type";s:15:"select-timezone";s:4:"name";s:4:"tzid";s:4:"help";s:49:"Timezone in which recurrences have identical time";}}i:5;a:4:{s:1:"A";a:3:{s:4:"size";s:13:",,,recur_data";s:4:"type";s:5:"label";s:5:"label";s:11:"Repeat days";}s:1:"B";a:4:{s:4:"type";s:10:"select-dow";s:4:"name";s:10:"recur_data";s:4:"size";s:3:"6,1";s:4:"help";s:44:"Days of the week for a weekly repeated event";}s:1:"C";a:4:{s:4:"size";s:6:"2,,0,0";s:4:"type";s:4:"vbox";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:10:"Exceptions";}i:2;a:5:{s:5:"label";s:16:"@exception_label";s:7:"no_lang";s:1:"1";s:4:"name";s:17:"button[exception]";s:4:"type";s:6:"button";s:4:"help";s:38:"Create an exception for the given date";}}s:1:"D";a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:3:{s:8:"readonly";s:4:"true";s:4:"name";s:4:"$row";s:4:"type";s:9:"date-time";}s:1:"B";a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:40:"return confirm(\'Delete this exception\');";s:4:"name";s:27:"delete_exception[$row_cont]";s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:4:"help";s:21:"Delete this exception";}}}s:4:"cols";i:2;s:4:"rows";i:1;}}}s:4:"cols";i:4;s:4:"rows";i:5;s:4:"size";s:8:"100%,210";}}','size' => '100%,210','style' => '','modified' => '1274345126',); +$templ_data[] = array('name' => 'calendar.edit.recurrence','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:11:{s:1:"A";s:2:"95";s:1:"D";s:3:"50%";s:2:"c1";s:3:"row";s:2:"h1";s:12:",!@reference";s:2:"c2";s:2:"th";s:2:"h2";s:2:"12";s:2:"c3";s:3:"row";s:2:"h3";s:2:"12";s:2:"c4";s:3:"row";s:2:"h4";s:2:"12";s:2:"c5";s:7:"row,top";}i:1;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:9:"Exception";}s:1:"B";a:4:{s:8:"readonly";s:4:"true";s:4:"name";s:10:"recurrence";s:4:"type";s:9:"date-time";s:4:"span";s:5:",gray";}s:1:"C";a:3:{s:4:"name";s:9:"reference";s:4:"type";s:5:"label";s:4:"span";s:5:",gray";}s:1:"D";a:3:{s:4:"name";s:3:"uid";s:4:"type";s:5:"label";s:4:"span";s:5:",gray";}}i:2;a:4:{s:1:"A";a:3:{s:4:"span";s:8:"all,gray";s:4:"type";s:5:"label";s:5:"label";s:27:"Repeating Event Information";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:3;a:4:{s:1:"A";a:3:{s:4:"size";s:13:",,,recur_type";s:4:"type";s:5:"label";s:5:"label";s:11:"Repeat type";}s:1:"B";a:3:{s:8:"onchange";s:34:"check_recur_type(\'recur_type\', 2);";s:4:"name";s:10:"recur_type";s:4:"type";s:6:"select";}s:1:"C";a:3:{s:4:"size";s:17:",,,recur_interval";s:4:"type";s:5:"label";s:5:"label";s:8:"Interval";}s:1:"D";a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:14:"recur_interval";s:4:"size";s:9:"None,2,31";s:4:"help";s:53:"repeating interval, eg. 2 to repeat every second week";}}i:4;a:4:{s:1:"A";a:3:{s:4:"size";s:16:",,,recur_enddate";s:4:"type";s:5:"label";s:5:"label";s:8:"End date";}s:1:"B";a:3:{s:4:"name";s:13:"recur_enddate";s:4:"type";s:4:"date";s:4:"help";s:57:"repeat the event until which date (empty means unlimited)";}s:1:"C";a:3:{s:4:"size";s:7:",,,tzid";s:4:"type";s:5:"label";s:5:"label";s:8:"Timezone";}s:1:"D";a:3:{s:4:"type";s:15:"select-timezone";s:4:"name";s:4:"tzid";s:4:"help";s:49:"Timezone in which recurrences have identical time";}}i:5;a:4:{s:1:"A";a:3:{s:4:"size";s:13:",,,recur_data";s:4:"type";s:5:"label";s:5:"label";s:11:"Repeat days";}s:1:"B";a:4:{s:4:"type";s:10:"select-dow";s:4:"name";s:10:"recur_data";s:4:"size";s:3:"6,1";s:4:"help";s:44:"Days of the week for a weekly repeated event";}s:1:"C";a:4:{s:4:"size";s:6:"2,,0,0";s:4:"type";s:4:"vbox";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:10:"Exceptions";}i:2;a:5:{s:5:"label";s:16:"@exception_label";s:7:"no_lang";s:1:"1";s:4:"name";s:17:"button[exception]";s:4:"type";s:6:"button";s:4:"help";s:38:"Create an exception for the given date";}}s:1:"D";a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:3:{s:8:"readonly";s:4:"true";s:4:"name";s:4:"$row";s:4:"type";s:9:"date-time";}s:1:"B";a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:40:"return confirm(\'Delete this exception\');";s:4:"name";s:27:"delete_exception[$row_cont]";s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:4:"help";s:21:"Delete this exception";}}}s:4:"cols";i:2;s:4:"rows";i:1;s:4:"name";s:15:"recur_exception";s:7:"options";a:0:{}}}}s:4:"cols";i:4;s:4:"rows";i:5;s:4:"size";s:8:"100%,210";}}','size' => '100%,210','style' => '','modified' => '1274345126',); $templ_data[] = array('name' => 'calendar.edit_series','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:8:{s:4:"type";s:4:"vbox";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:1:"2";i:1;a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"1";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:30:"This event is part of a series";}s:4:"name";s:13:"dialog-header";s:4:"span";s:13:",promptheader";}i:2;a:6:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:67:"Do you want to edit this event as an exception or the whole series?";}s:4:"span";s:7:",prompt";s:4:"name";s:14:"dialog-content";i:2;a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:3:{s:4:"type";s:10:"buttononly";s:5:"label";s:14:"Edit exception";s:7:"onclick";s:17:"open_edit(false);";}i:2;a:3:{s:4:"type";s:10:"buttononly";s:5:"label";s:11:"Edit series";s:7:"onclick";s:16:"open_edit(true);";}i:3;a:3:{s:4:"type";s:10:"buttononly";s:5:"label";s:6:"Cancel";s:7:"onclick";s:62:"document.getElementById(\'edit_series\').style.display = \'none\';";}s:5:"align";s:6:"center";}}s:4:"name";s:11:"edit_series";}}','size' => '','style' => '','modified' => '1272449924',); diff --git a/calendar/templates/default/edit.xet b/calendar/templates/default/edit.xet index 3e87636db3..aacc908900 100644 --- a/calendar/templates/default/edit.xet +++ b/calendar/templates/default/edit.xet @@ -165,7 +165,7 @@