From 44b15c17757181c8b5a75c9fcc9f9d8b29dd7e96 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 3 Dec 2009 18:24:19 +0000 Subject: [PATCH] Category based ACL to limit adding of a certain category or changing the status of a participant (included moving the event), eg. to implement a restricted holiday calendar *** not yet fully tested *** --- calendar/inc/class.calendar_boupdate.inc.php | 190 ++++++++++++++++++- calendar/inc/class.calendar_hooks.inc.php | 1 + calendar/inc/class.calendar_uiforms.inc.php | 85 ++++++++- calendar/lang/egw_de.lang | 7 + calendar/lang/egw_en.lang | 7 + calendar/setup/etemplates.inc.php | 4 +- calendar/templates/default/cat_acl.xet | 36 ++++ 7 files changed, 317 insertions(+), 13 deletions(-) create mode 100644 calendar/templates/default/cat_acl.xet diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index d91a585f0f..4d0d60ed96 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -40,6 +40,15 @@ define('MSG_DISINVITE',7); class calendar_boupdate extends calendar_bo { + /** + * Category ACL allowing to add a given category + */ + const CAT_ACL_ADD = 512; + /** + * Category ACL allowing to change status of a participant + */ + const CAT_ACL_STATUS = 1024; + /** * name of method to debug or level of debug-messages: * False=Off as higher as more messages you get ;-) @@ -76,10 +85,11 @@ class calendar_boupdate extends calendar_bo * @param boolean $touch_modified=true touch modificatin time and set modifing user, default true=yes * @param boolean $ignore_acl=false should we ignore the acl * @param boolean $updateTS=true update the content history of the event + * @param array &$messages=null messages about because of missing ACL removed participants or categories * @return mixed on success: int $cal_id > 0, on error false or array with conflicting events (only if $check_conflicts) * Please note: the events are not garantied to be readable by the user (no read grant or private)! */ - function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false,$updateTS=true) + function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false,$updateTS=true,&$messages=null) { //error_log(__METHOD__."(".array2string($event).",$ignore_conflicts,$touch_modified,$ignore_acl)"); if ($this->debug > 1 || $this->debug == 'update') @@ -97,7 +107,7 @@ class calendar_boupdate extends calendar_bo return false; } - if (!$event['id']) // some defaults for new entries + if (($new_event = !$event['id'])) // some defaults for new entries { // if no owner given, set user to owner if (!$event['owner']) $event['owner'] = $this->user; @@ -108,8 +118,7 @@ class calendar_boupdate extends calendar_bo $status = calendar_so::combine_status($status, 1, 'CHAIR'); $event['participants'] = array($event['owner'] => $status); } - $new_event = true; - } else $new_event = false; + } // check if user has the permission to update / create the event if (!$ignore_acl && (!$new_event && !$this->check_perms(EGW_ACL_EDIT,$event['id']) || @@ -134,7 +143,55 @@ class calendar_boupdate extends calendar_bo // do we need to check, if user is allowed to invite the invited participants if ($this->require_acl_invite && ($removed = $this->remove_no_acl_invite($event,$old_event))) { - // todo: report removed participants back to user + // report removed participants back to user + foreach($removed as $key => $account_id) + { + $removed[$key] = $this->participant_name($account_id); + } + $messages[] = lang('%1 participants removed because of missing invite grants',count($removed)). + ': '.implode(', ',$removed); + } + // check category based ACL + if ($event['category']) + { + if (!is_array($event['category'])) $event['category'] = explode(',',$event['category']); + if ($old_event && $old_event['category'] && !is_array($old_event['category'])) + { + $old_event['category'] = explode(',',$old_event['category']); + } + foreach($event['category'] as $key => $cat_id) + { + // check if user is allowed to update event categories + if ((!$old_event || !isset($old_event['category'][$cat_id])) && !self::has_cat_right(self::CAT_ACL_ADD,$cat_id,$this->user)) + { + unset($event['category'][$key]); + // report removed category to user + $removed_cats[$cat_id] = $this->categories->id2name($cat_id); + } + // for new or moved events check status of participants, if no category status right --> set all status to 'U' = unknown + if (!$status_reset_to_unknown && !self::has_cat_right(self::CAT_ACL_STATUS,$cat_id,$this->user) && + (!$old_event || $old_event['start'] != $event['start'] || $old_event['end'] != $event['end'])) + { + foreach($event['participants'] as $uid => $status) + { + calendar_so::split_status($status,$q,$r); + if ($status != 'U') + { + $event['participants'][$uid] = calendar_so::combine_status('U',$q,$r); + // todo: report reset status to user + } + } + $status_reset_to_unknown = true; // once is enough + } + } + if ($removed_cats) + { + $messages[] = lang('Category %1 removed because of missing rights',implode(', ',$removed_cats)); + } + if ($status_reset_to_unknown) + { + $messages[] = lang('Status of participants set to unknown because of missing category rights'); + } } // check for conflicts only happens !$ignore_conflicts AND if start + end date are given if (!$ignore_conflicts && !$event['non_blocking'] && isset($event['start']) && isset($event['end'])) @@ -817,6 +874,10 @@ class calendar_boupdate extends calendar_bo return $this->check_perms(EGW_ACL_EDIT,0,$event['owner']); } + if (!$this->check_cat_acl(self::CAT_ACL_STATUS,$event)) + { + return false; + } if (!is_numeric($uid)) // this is eg. for resources (r123) { $resource = $this->resource_info($uid); @@ -827,6 +888,125 @@ class calendar_boupdate extends calendar_bo return $this->check_perms(EGW_ACL_EDIT,0,$uid); } + /** + * Check if current user has a certain right on the categories of an event + * + * Not having the given right for a single category, means not having it! + * + * @param int $right self::CAT_ACL_{ADD|STATUS} + * @param int|array $event + * @return boolean true if use has the right, false if not + */ + function check_cat_acl($right,$event) + { + if (!is_array($event)) $event = $this->read($event); + + $ret = true; + if ($event['category']) + { + foreach(is_array($event['category']) ? $event['category'] : explode(',',$event['category']) as $cat_id) + { + if (!self::has_cat_right($right,$cat_id,$this->user)) + { + $ret = false; + break; + } + } + } + //echo "

".__METHOD__."($event[id]: $event[title], $right) = ".array2string($ret)."

\n"; + return $ret; + } + + /** + * Array with $cat_id => $rights pairs for current user (no entry means, cat is not limited by ACL!) + * + * @var array + */ + private static $cat_rights_cache; + + /** + * Get rights for a given category id + * + * @param int $cat_id=null null to return array with all cats + * @return array with account_id => right pairs + */ + public static function get_cat_rights($cat_id=null) + { + if (!isset(self::$cat_rights_cache)) + { + self::$cat_rights_cache = egw_cache::getSession('calendar','cat_rights', + array($GLOBALS['egw']->acl,'get_location_grants'),array('L%','calendar')); + } + //echo "

".__METHOD__."($cat_id) = ".array2string($cat_id ? self::$cat_rights_cache['L'.$cat_id] : self::$cat_rights_cache)."

\n"; + return $cat_id ? self::$cat_rights_cache['L'.$cat_id] : self::$cat_rights_cache; + } + + /** + * Set rights for a given single category and user + * + * @param int $cat_id + * @param int $user + * @param int $rights self::CAT_ACL_{ADD|STATUS} or'ed together + */ + public static function set_cat_rights($cat_id,$user,$rights) + { + //echo "

".__METHOD__."($cat_id,$user,$rights)

\n"; + if (!isset(self::$cat_rights_cache)) self::get_cat_rights($cat_id); + + if ((int)$rights != (int)self::$cat_rights_cache['L'.$cat_id][$user]) + { + if ($rights) + { + self::$cat_rights_cache['L'.$cat_id][$user] = $rights; + $GLOBALS['egw']->acl->add_repository('calendar','L'.$cat_id,$user,$rights); + } + else + { + unset(self::$cat_rights_cache['L'.$cat_id][$user]); + if (!self::$cat_rights_cache['L'.$cat_id]) unset(self::$cat_rights_cache['L'.$cat_id]); + $GLOBALS['egw']->acl->delete_repository('calendar','L'.$cat_id,$user); + } + egw_cache::setSession('calendar','cat_rights',self::$cat_rights_cache); + } + } + + /** + * Check if current user has a given right on a category (if it's restricted!) + * + * @param int $cat_id + * @return boolean + */ + public static function has_cat_right($right,$cat_id,$user) + { + static $cache; + + if (!isset($cache[$cat_id][$right])) + { + $all = $own = 0; + $cat_rights = self::get_cat_rights($cat_id); + if (!is_null($cat_rights)) + { + static $memberships; + if (is_null($memberships)) + { + $memberships = $GLOBALS['egw']->accounts->memberships($user,true); + $memberships[] = $user; + } + foreach($cat_rights as $uid => $value) + { + $all |= $value; + if (in_array($uid,$memberships)) $own |= $value; + } + } + foreach(array(self::CAT_ACL_ADD,self::CAT_ACL_STATUS) as $mask) + { + $cache[$cat_id][$mask] = !($all & $mask) || ($own & $mask); + } + } + //echo "

".__METHOD__."($right,$cat_id) all=$all, own=$own returning ".array2string($cache[$cat_id][$right])."

\n"; + return $cache[$cat_id][$right]; + } + /** * set the status of one participant for a given recurrence or for all recurrences since now (includes recur_date=0) * diff --git a/calendar/inc/class.calendar_hooks.inc.php b/calendar/inc/class.calendar_hooks.inc.php index 9d5b32cf7c..cfec0967d6 100644 --- a/calendar/inc/class.calendar_hooks.inc.php +++ b/calendar/inc/class.calendar_hooks.inc.php @@ -115,6 +115,7 @@ class calendar_hooks 'Custom fields' => egw::link('/index.php','menuaction=admin.customfields.edit&appname=calendar'), 'Calendar Holiday Management' => egw::link('/index.php','menuaction=calendar.uiholiday.admin'), 'Global Categories' => egw::link('/index.php','menuaction=admin.uicategories.index&appname=calendar'), + 'Category ACL' => egw::link('/index.php','menuaction=calendar.calendar_uiforms.cat_acl'), 'Update timezones' => egw::link('/index.php','menuaction=calendar.calendar_timezones.update'), ); display_section('calendar','calendar',$file); diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 7138a1150a..8ac4f32a3a 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -29,6 +29,7 @@ class calendar_uiforms extends calendar_ui 'edit' => true, 'export' => true, 'import' => true, + 'cat_acl' => true, ); /** @@ -487,7 +488,7 @@ class calendar_uiforms extends calendar_ui $event['reference'] = $event['id']; $event['recurrence'] = $content['edit_single']; unset($event['id']); - $conflicts = $this->bo->update($event,$ignore_conflicts); + $conflicts = $this->bo->update($event,$ignore_conflicts,true,false,true,$messages); if (!is_array($conflicts) && $conflicts) { // now we need to add the original start as recur-execption to the series @@ -508,7 +509,7 @@ class calendar_uiforms extends calendar_ui } else // we edited a non-reccuring event or the whole series { - $conflicts = $this->bo->update($event,$ignore_conflicts); + $conflicts = $this->bo->update($event,$ignore_conflicts,true,false,true,$messages); unset($event['ignore']); } if (is_array($conflicts)) @@ -516,20 +517,25 @@ class calendar_uiforms extends calendar_ui $event['button_was'] = $button; // remember for ignore return $this->conflicts($event,$conflicts,$preserv); } - elseif ($conflicts === 0) + // check if there are messages from update, eg. removed participants or categories because of missing rights + if ($messages) + { + $msg .= ($msg ? ', ' : '').implode(', ',$messages); + } + if ($conflicts === 0) { $msg .= ($msg ? ', ' : '') .lang('Error: the entry has been updated since you opened it for editing!').'
'. lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','',''); + 'cal_id' => $content['id'], + 'referer' => $referer, + ))).'">',''); $noerror=false; } elseif ($conflicts > 0) { - $msg .= ($msg ? ', ' : '') . lang('Event saved'); + $msg = lang('Event saved').($msg ? ', '.$msg : ''); // writing links for new entry, existing ones are handled by the widget itself if (!$content['id'] && is_array($content['link_to']['to_id'])) @@ -1632,4 +1638,69 @@ class calendar_uiforms extends calendar_ui $etpl->exec('calendar.calendar_uiforms.import',$content); } + + /** + * Edit category ACL (admin only) + * + * @param array $content=null + */ + function cat_acl(array $content=null) + { + if (!$GLOBALS['egw_info']['user']['apps']['admin']) + { + throw new egw_exception_no_permission_admin(); + } + if ($content) + { + list($button) = each($content['button']); + unset($content['button']); + if ($button != 'cancel') // store changed acl + { + foreach($content['rows'] as $data) + { + if (!($cat_id = $data['cat_id'])) continue; + foreach(array_merge((array)$data['add'],(array)$data['status'],array_keys((array)$data['old'])) as $account_id) + { + $rights = 0; + if (in_array($account_id,(array)$data['add'])) $rights |= calendar_boupdate::CAT_ACL_ADD; + if (in_array($account_id,(array)$data['status'])) $rights |= calendar_boupdate::CAT_ACL_STATUS; + if ($account_id) $this->bo->set_cat_rights($cat_id,$account_id,$rights); + } + } + } + if ($button != 'apply') // end dialog + { + egw::redirect_link('/index.php',array('menuaction' => $this->view_menuaction)); + } + } + $content['rows'] = $preserv['rows'] = array(); + $n = 1; + foreach($this->bo->get_cat_rights() as $Lcat_id => $data) + { + $cat_id = (int)substr($Lcat_id,1); + $row = array( + 'cat_id' => $cat_id, + 'add' => array(), + 'status' => array(), + ); + foreach($data as $account_id => $rights) + { + if ($rights & calendar_boupdate::CAT_ACL_ADD) $row['add'][] = $account_id; + if ($rights & calendar_boupdate::CAT_ACL_STATUS) $row['status'][] = $account_id; + } + $content['rows'][$n] = $row; + $preserv['rows'][$n] = array( + 'cat_id' => $cat_id, + 'old' => $data, + ); + $readonlys[$n.'[cat_id]'] = true; + ++$n; + } + // add empty row for new entries + $content['rows'][] = array('cat_id' => ''); + + $GLOBALS['egw_info']['flags']['app_header'] = lang('Calendar').' - '.lang('Category ACL'); + $tmp = new etemplate('calendar.cat_acl'); + $tmp->exec('calendar.calendar_uiforms.cat_acl',$content,null,$readonlys,$preserv); + } } diff --git a/calendar/lang/egw_de.lang b/calendar/lang/egw_de.lang index 5f693e89dd..db6b6b5a47 100644 --- a/calendar/lang/egw_de.lang +++ b/calendar/lang/egw_de.lang @@ -1,4 +1,5 @@ %1 %2 in %3 calendar de %1 %2 im %3 +%1 participants removed because of missing invite grants calendar de %1 Teilnehmer entfernt wegen fehlender Einladungsrechte %1 records imported calendar de %1 Datensätze importiert %1 records read (not yet imported, you may go back and uncheck test import) calendar de %1 Datensätze gelesen (noch nicht importiert, sie können zurück gehen und Test Import ausschalten) %1 weeks calendar de %1 Wochen @@ -50,6 +51,9 @@ calendar-fieldname calendar de Kalender Feldname can't add alarms in the past !!! calendar de Kann keine Alarme in der Vergangenheit setzen !!! can't aquire lock! calendar de Kann Termin nicht sperren! canceled calendar de Abgesagt +category %1 removed because of missing rights calendar de Kategorie %1 entfertn wegen fehlender Rechte +category acl calendar de Kategorie Rechte +category acl only restrict adding a category to an event or changing status of a participant. it does not change the visibility of an event! calendar de Kategorie Reche beschrenken nur das Hinzufügen einer Kategorie zu einem Termin oder das Ändern des Status eines Teilnehmers. Sie ändern NICHT die Sichtbarkeit eines Termins! chair calendar de Vorsitz charset of file calendar de Zeichensatz der Datei check all calendar de Alle auswählen @@ -279,6 +283,8 @@ reset calendar de Zurücksetzen resources calendar de Ressourcen resources except conflicting ones calendar de Ressourcen ausgenommen bereits gebuchte resources with conflict detection calendar de Ressourcen mit bereits gebuchten +restrict add category to calendar de Beschränke hinzufügen der Kategorie auf +restrict set status to calendar de Beschränke Statusänderungen auf role calendar de Rolle rule calendar de Regel sat calendar de Sa @@ -324,6 +330,7 @@ startrecord calendar de Startdatensatz status changed calendar de Status geändert status for all future scheduled days changed calendar de Status alle zukünftig geplanten Termine geändert status for this particular day changed calendar de Status für diesen Tage geändert +status of participants set to unknown because of missing category rights calendar de Status der Teilnehmer auf unbekannt gesetzt wegen fehlender Kategorie Rechte submit to repository calendar de Übertragen zu EGroupware.org sun calendar de So tentative calendar de Vorläufige Zusage diff --git a/calendar/lang/egw_en.lang b/calendar/lang/egw_en.lang index ac0ea51385..ca0a2e75f3 100644 --- a/calendar/lang/egw_en.lang +++ b/calendar/lang/egw_en.lang @@ -1,4 +1,5 @@ %1 %2 in %3 calendar en %1 %2 in %3 +%1 participants removed because of missing invite grants calendar en %1 participants removed because of missing invite grants %1 records imported calendar en %1 records imported %1 records read (not yet imported, you may go back and uncheck test import) calendar en %1 records read (not yet imported, you may go back and uncheck Test Import) %1 weeks calendar en %1 weeks @@ -50,6 +51,9 @@ calendar-fieldname calendar en Calendar-Fieldname can't add alarms in the past !!! calendar en Can't add alarms in the past !!! can't aquire lock! calendar en Can't aquire lock! canceled calendar en Canceled +category %1 removed because of missing rights calendar en Category %1 removed because of missing rights +category acl calendar en Category ACL +category acl only restrict adding a category to an event or changing status of a participant. it does not change the visibility of an event! calendar en Category ACL only restrict adding a category to an event or changing status of a participant. It does NOT change the visibility of an event! chair calendar en Chair charset of file calendar en Charset of file check all calendar en Check all @@ -279,6 +283,8 @@ reset calendar en Reset resources calendar en Resources resources except conflicting ones calendar en Resources except conflicting ones resources with conflict detection calendar en Resources with conflict detection +restrict add category to calendar en Restrict add category to +restrict set status to calendar en Restrict set status to role calendar en Role rule calendar en Rule sat calendar en Sat @@ -324,6 +330,7 @@ startrecord calendar en Start record status changed calendar en Status changed status for all future scheduled days changed calendar en Status for all future scheduled days changed status for this particular day changed calendar en Status for this particular day changed +status of participants set to unknown because of missing category rights calendar en Status of participants set to unknown because of missing category rights submit to repository calendar en Submit to Repository sun calendar en Sun tentative calendar en Tentative diff --git a/calendar/setup/etemplates.inc.php b/calendar/setup/etemplates.inc.php index 012be5b935..5d15b30283 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() 2009-11-25 21:13 + * generated by soetemplate::dump4setup() 2009-12-03 17:35 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package calendar @@ -12,6 +12,8 @@ $templ_version=1; +$templ_data[] = array('name' => 'calendar.cat_acl','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:3:{i:0;a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:12:"Category ACL";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:140:"Category ACL only restrict adding a category to an event or changing status of a participant. It does NOT change the visibility of an event!";}}i:1;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:2:{s:2:"c1";s:2:"th";s:2:"c2";s:7:"row,top";}i:1;a:3:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"Category";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:24:"Restrict add category to";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:5:"label";s:22:"Restrict set status to";}}i:2;a:3:{s:1:"A";a:3:{s:4:"type";s:10:"select-cat";s:4:"name";s:14:"${row}[cat_id]";s:4:"size";s:10:"Select one";}s:1:"B";a:3:{s:4:"type";s:14:"select-account";s:4:"size";s:6:"5,both";s:4:"name";s:11:"${row}[add]";}s:1:"C";a:3:{s:4:"type";s:14:"select-account";s:4:"size";s:6:"5,both";s:4:"name";s:14:"${row}[status]";}}}s:4:"rows";i:2;s:4:"cols";i:3;s:4:"name";s:4:"rows";s:7:"options";a:0:{}}i:2;a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:3:{s:4:"type";s:6:"button";s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";}i:2;a:3:{s:4:"type";s:6:"button";s:4:"name";s:13:"button[apply]";s:5:"label";s:5:"Apply";}i:3;a:3:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"Cancel";}}}','size' => '','style' => '','modified' => '1259850038',); + $templ_data[] = array('name' => 'calendar.conflicts','template' => '','lang' => '','group' => '0','version' => '1.0.1.001','data' => 'a:3:{i:0;a:3:{s:4:"type";s:5:"label";s:5:"label";s:20:" Scheduling conflict";s:4:"span";s:9:",size120b";}i:1;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:1:{s:2:"c1";s:4:",top";}i:1;a:4:{s:1:"A";a:4:{s:4:"type";s:5:"image";s:4:"name";s:34:"conflicts[$row][icon_participants]";s:5:"label";s:38:"@conflicts[$row][tooltip_participants]";s:7:"no_lang";s:1:"1";}s:1:"B";a:4:{s:4:"type";s:5:"image";s:4:"name";s:27:"conflicts[$row][icon_recur]";s:5:"label";s:28:"@conflicts[$row][text_recur]";s:7:"no_lang";s:1:"1";}s:1:"C";a:3:{s:4:"type";s:5:"label";s:4:"name";s:21:"conflicts[$row][time]";s:7:"no_lang";s:1:"1";}s:1:"D";a:5:{s:4:"type";s:4:"vbox";s:4:"size";s:6:"2,,0,0";i:1;a:4:{s:4:"type";s:5:"label";s:4:"name";s:22:"conflicts[$row][title]";s:7:"no_lang";s:1:"1";s:4:"size";s:1:"b";}i:2;a:3:{s:4:"type";s:5:"label";s:4:"name";s:41:"conflicts[$row][conflicting_participants]";s:7:"no_lang";s:1:"1";}s:4:"help";s:23:"conflict[$row][tooltip]";}}}s:4:"rows";i:1;s:4:"cols";i:4;}i:2;a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:15:"Ignore conflict";s:4:"name";s:14:"button[ignore]";s:4:"help";s:37:"Saves the event ignoring the conflict";}i:2;a:4:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[reedit]";s:5:"label";s:13:"Re-Edit event";s:4:"help";s:30:"Allows to edit the event again";}i:3;a:4:{s:4:"type";s:6:"button";s:5:"label";s:15:"Freetime search";s:4:"name";s:16:"button[freetime]";s:4:"help";s:88:"Find free timeslots where the selected participants are availible for the given timespan";}}}','size' => '','style' => '','modified' => '1119080124',); $templ_data[] = array('name' => 'calendar.edit','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:6:{s:2:"h1";s:6:",!@msg";s:2:"c2";s:2:"th";s:1:"A";s:3:"100";s:2:"h4";s:8:",!@owner";s:1:"B";s:3:"300";s:2:"h2";s:2:"28";}i:1;a:4:{s:1:"A";a:5:{s:4:"type";s:4:"html";s:4:"span";s:13:"all,redItalic";s:4:"name";s:3:"msg";s:7:"no_lang";s:1:"1";s:5:"align";s:6:"center";}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:2;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Title";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"size";s:6:"80,255";s:4:"name";s:5:"title";s:4:"span";s:3:"all";s:6:"needed";s:1:"1";}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:5:{s:4:"type";s:3:"tab";s:4:"span";s:3:"all";s:5:"label";s:63:"General|Description|Participants|Recurrence|Custom|Links|Alarms";s:4:"name";s:63:"general|description|participants|recurrence|custom|links|alarms";s:4:"help";s:158:"Location, Start- and Endtimes, ...|Full description|Participants, Resources, ...|Repeating Event Information|Custom fields|Links, Attachments|Alarm management";}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:4;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Owner";}s:1:"B";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:5:"owner";s:8:"readonly";s:1:"1";}s:1:"C";a:7:{s:4:"type";s:9:"date-time";s:5:"label";s:7:"Updated";s:4:"name";s:8:"modified";s:8:"readonly";s:1:"1";s:7:"no_lang";s:1:"1";s:5:"align";s:5:"right";s:4:"span";s:8:",noBreak";}s:1:"D";a:6:{s:4:"type";s:14:"select-account";s:5:"label";s:2:"by";s:4:"name";s:8:"modifier";s:8:"readonly";s:1:"1";s:5:"align";s:5:"right";s:4:"span";s:8:",noBreak";}}i:5;a:4:{s:1:"A";a:7:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";s:4:"span";s:1:"3";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";s:4:"help";s:22:"saves the changes made";}i:2;a:4:{s:4:"type";s:6:"button";s:4:"name";s:13:"button[apply]";s:5:"label";s:5:"Apply";s:4:"help";s:17:"apply the changes";}i:3;a:5:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"Cancel";s:4:"help";s:16:"Close the window";s:7:"onclick";s:15:"window.close();";}i:4;a:5:{s:4:"type";s:6:"select";s:4:"name";s:6:"action";s:4:"help";s:39:"Execute a further action for this entry";s:4:"size";s:10:"Actions...";s:8:"onchange";s:34:"this.form.submit(); this.value=\'\';";}}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:6:{s:4:"type";s:6:"button";s:5:"label";s:6:"Delete";s:4:"name";s:14:"button[delete]";s:4:"help";s:17:"Delete this event";s:7:"onclick";s:36:"return confirm(\'Delete this event\');";s:5:"align";s:5:"right";}}}s:4:"rows";i:5;s:4:"cols";i:4;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.end_hide { display: block; white-space: nowrap; margin-left: 10px; } diff --git a/calendar/templates/default/cat_acl.xet b/calendar/templates/default/cat_acl.xet new file mode 100644 index 0000000000..3a4a6e095a --- /dev/null +++ b/calendar/templates/default/cat_acl.xet @@ -0,0 +1,36 @@ + + + + + \ No newline at end of file