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 ***
This commit is contained in:
Ralf Becker 2009-12-03 18:24:19 +00:00
parent 5066c5c3b7
commit 44b15c1775
7 changed files with 317 additions and 13 deletions

View File

@ -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 "<p>".__METHOD__."($event[id]: $event[title], $right) = ".array2string($ret)."</p>\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 "<p>".__METHOD__."($cat_id) = ".array2string($cat_id ? self::$cat_rights_cache['L'.$cat_id] : self::$cat_rights_cache)."</p>\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 "<p>".__METHOD__."($cat_id,$user,$rights)</p>\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 "<p>".__METHOD__."($right,$cat_id) all=$all, own=$own returning ".array2string($cache[$cat_id][$right])."</p>\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)
*

View File

@ -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);

View File

@ -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!').'<br />'.
lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','<a href="'.
htmlspecialchars(egw::link('/index.php',array(
'menuaction' => 'calendar.calendar_uiforms.edit',
'cal_id' => $content['id'],
'referer' => $referer,
))).'">','</a>');
'cal_id' => $content['id'],
'referer' => $referer,
))).'">','</a>');
$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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="calendar.cat_acl" template="" lang="" group="0" version="1.7.001">
<groupbox>
<caption label="Category ACL"/>
<description value="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!"/>
</groupbox>
<grid id="rows">
<columns>
<column/>
<column/>
<column/>
</columns>
<rows>
<row class="th">
<description value="Category"/>
<description value="Restrict add category to"/>
<description value="Restrict set status to"/>
</row>
<row class="row" valign="top">
<menulist>
<menupopup type="select-cat" id="${row}[cat_id]" options="Select one"/>
</menulist>
<listbox type="select-account" rows="5" options="both" id="${row}[add]"/>
<listbox type="select-account" rows="5" options="both" id="${row}[status]"/>
</row>
</rows>
</grid>
<hbox>
<button label="Save" id="button[save]"/>
<button id="button[apply]" label="Apply"/>
<button id="button[cancel]" label="Cancel"/>
</hbox>
</template>
</overlay>