Feature to control the concurrent opening of calendar events.

If an entry which is opened by another user, gets saved by another user before the former commits its changes, the former user is notified at the
time saving the event and asked to reload the event and reedit. A link to reedit is provided.
Additionally there is a feature to inform a user that a given entry is opened by another user within a configurable timespan.
This feature was developed by Stefan Becker
This commit is contained in:
Klaus Leithoff 2008-01-15 08:21:25 +00:00
parent d8cb081a1d
commit 573f7f2470
10 changed files with 163 additions and 7 deletions

View File

@ -703,6 +703,11 @@ class bocalupdate extends bocal
// we convert here from user-time to timestamps in server-time! // we convert here from user-time to timestamps in server-time!
if (isset($event[$ts])) $event[$ts] = $event[$ts] ? $this->date2ts($event[$ts],true) : 0; if (isset($event[$ts])) $event[$ts] = $event[$ts] ? $this->date2ts($event[$ts],true) : 0;
} }
// Lock realized with a counter, that is checked and incremented as we save the entry
$check_etag = $event['etag'];
if (!$check_etag){
$check_etag=$check_etag+1;
}
// same with the recur exceptions // same with the recur exceptions
if (isset($event['recur_exception']) && is_array($event['recur_exception'])) if (isset($event['recur_exception']) && is_array($event['recur_exception']))
{ {
@ -719,7 +724,7 @@ class bocalupdate extends bocal
$event['alarm'][$id]['time'] = $this->date2ts($alarm['time'],true); $event['alarm'][$id]['time'] = $this->date2ts($alarm['time'],true);
} }
} }
if (($cal_id = $this->so->save($event,$set_recurrences)) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE) if (($cal_id = $this->so->save($event,$set_recurrences,NULL,$check_etag)) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE)
{ {
$save_event['id'] = $cal_id; $save_event['id'] = $cal_id;
$this->set_recurrences($save_event); $this->set_recurrences($save_event);
@ -1103,4 +1108,17 @@ class bocalupdate extends bocal
return $cat_list; return $cat_list;
} }
/**
* updates the edit user information for timelocking an event
*
* @param array &$event2update event-array, on return some values might be changed due to set defaults
* this is a wrapper for the socal function
* cal_edit_time is set to current timestamp
* @return the returnvalue of so->save_edit_user (0 (someone else modified the entry), true (saved) or false (could not save)))
*/
function update_edit_user(&$event2update)
{
$event2update['cal_edit_time']=$this->now_su;
return $this->so->save_edit_user($event2update);
}
} }

View File

@ -491,8 +491,9 @@ ORDER BY cal_user_type, cal_usre_id
* @param boolean &$set_recurrences on return: true if the recurrences need to be written, false otherwise * @param boolean &$set_recurrences on return: true if the recurrences need to be written, false otherwise
* @param int $change_since=0 time from which on the repetitions should be changed, default 0=all * @param int $change_since=0 time from which on the repetitions should be changed, default 0=all
* @return boolean/int false on error, cal_id otherwise * @return boolean/int false on error, cal_id otherwise
* @return int $check_etag check etag from GUI, if there is any Change since the last save SB:Lock for etag
*/ */
function save($event,&$set_recurrences,$change_since=0) function save($event,&$set_recurrences,$change_since=0,$check_modified=0)
{ {
//echo "<p>socal::save(,$change_since) event="; _debug_array($event); //echo "<p>socal::save(,$change_since) event="; _debug_array($event);
@ -512,6 +513,30 @@ ORDER BY cal_user_type, cal_usre_id
} }
if (is_array($event['cal_category'])) $event['cal_category'] = implode(',',$event['cal_category']); if (is_array($event['cal_category'])) $event['cal_category'] = implode(',',$event['cal_category']);
// while saving handle the etag as condition for the update, to check if an entry was saved before this action occured
$check_etag = $event['cal_etag'];
if ($cal_id)
{
$event['cal_etag']=$check_etag+1;
$event['cal_edit_user']=NULL;
$event['cal_edit_time']=NULL;
$where = array('cal_id' => $cal_id);
if ($check_etag) $where['cal_etag'] = $check_etag;
if (!$this->db->update($this->cal_table,$event,$where,__LINE__,__FILE__))
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning false");
return false; // Error
}
//echo $this->db->affected_rows()."##";
if ($check_etag && $this->db->affected_rows() < 1)
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning 0 (nothing updated, eg. condition not met)");
return 0; // someone else updated the modtime or deleted the entry
}
}
if ($cal_id) if ($cal_id)
{ {
$this->db->update($this->cal_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__); $this->db->update($this->cal_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__);
@ -1114,4 +1139,42 @@ ORDER BY cal_user_type, cal_usre_id
$this->db->update($this->user_table,array('cal_user_id' => $new_user),array('cal_user_type' => 'u','cal_user_id' => $old_user),__LINE__,__FILE__); $this->db->update($this->user_table,array('cal_user_id' => $new_user),array('cal_user_type' => 'u','cal_user_id' => $old_user),__LINE__,__FILE__);
} }
} }
/**
* Save actually User, who is working on the Calenar Data if there is no user set or the timestamp is "expired"
*
* @param array $event
*
* @return (0 (someone else modified the entry), true (saved) or false (could not save)))
*/
function save_edit_user($event2update) {
$cal_id = (int) $event2update['id'];
//unset($event2update['id']);
if ($cal_id && $event2update['cal_edit_user'] && $event2update['cal_edit_time'])
{
$locktime = ($GLOBALS['egw_info']['server']['Lock_Time_Calender'] ? $GLOBALS['egw_info']['server']['Lock_Time_Calender'] : 1);
$where = array('cal_id' => $cal_id,'cal_edit_user is NULL or cal_edit_time<'.$event2update['cal_edit_time']-$locktime);
if (!$this->db->update($this->cal_table,$event2update,$where,__LINE__,__FILE__))
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning false");
return false; // Error
}
//echo $this->db->affected_rows()."##";
if ($this->db->affected_rows() < 1)
{
//error_log("### socal::write(".print_r($event,true).") where=".print_r($where,true)." returning 0 (nothing updated, eg. condition not met)");
return 0; // someone else updated the modtime or deleted the entry
}
else
{
return true;
}
}
}
} }

View File

@ -53,6 +53,13 @@ class uiforms extends uical
*/ */
var $tabs = 'general|description|participants|recurrence|custom|links|alarms'; var $tabs = 'general|description|participants|recurrence|custom|links|alarms';
/**
* default timelock for entries, that are opened by another user
*
* @var time in secomdas
*/
var $locktime_default=1;
/** /**
* Constructor * Constructor
*/ */
@ -315,6 +322,7 @@ class uiforms extends uical
'no_popup' => $content['no_popup'], 'no_popup' => $content['no_popup'],
$this->tabs => $content[$this->tabs], $this->tabs => $content[$this->tabs],
); );
$noerror=true;
switch((string)$button) switch((string)$button)
{ {
case 'exception': // create an exception in a recuring event case 'exception': // create an exception in a recuring event
@ -407,7 +415,19 @@ class uiforms extends uical
$event['button_was'] = $button; // remember for ignore $event['button_was'] = $button; // remember for ignore
return $this->conflicts($event,$conflicts,$preserv); return $this->conflicts($event,$conflicts,$preserv);
} }
elseif($conflicts) // true and non-array means Ok ;-) elseif ($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($GLOBALS['egw']->link('/index.php',array(
'menuaction' => 'calendar.uiforms.edit',
'cal_id' => $content['id'],
'referer' => $referer,
))).'">','</a>');
$noerror=false;
}
elseif ($conflicts>0)
{ {
$msg .= ($msg ? ', ' : '') . lang('Event saved'); $msg .= ($msg ? ', ' : '') . lang('Event saved');
@ -502,7 +522,7 @@ class uiforms extends uical
} }
break; break;
} }
if (in_array($button,array('cancel','save','delete'))) if (in_array($button,array('cancel','save','delete')) && $noerror)
{ {
if ($content['no_popup']) if ($content['no_popup'])
{ {
@ -848,6 +868,25 @@ class uiforms extends uical
$content['cancel_needs_refresh'] = (bool)$_GET['cancel_needs_refresh']; $content['cancel_needs_refresh'] = (bool)$_GET['cancel_needs_refresh'];
// time locking for entries
$locktime = $GLOBALS['egw_info']['server']['Lock_Time_Calender'];
if ($locktime == '') {
//default Lock Time
$locktime=$GLOBALS['egw_info']['server']['Lock_Time_Calender']=$this->locktime_default;
}
if (($this->bo->now_su>($event['edit_time']+$locktime)) || ($event['edit_time']==''))
{
//echo "write Lock!!->DB";
$event2update['id']=$event['id'];
$event2update['cal_edit_user']=$this->user;
$event2update['cal_edit_time']=''; // this is set in bo->update_edit_user
$this->bo->update_edit_user($event2update);
}
else
{
if ($event['edit_user'] && $event['edit_user']!=$this->user) $content['msg'].=" ".lang('This entry is opened by user: ').$GLOBALS['egw']->accounts->id2name($event['edit_user']);
}
// non_interactive==true from $_GET calls immediate save action without displaying the edit form // non_interactive==true from $_GET calls immediate save action without displaying the edit form
if(isset($_GET['non_interactive']) && (bool)$_GET['non_interactive'] === true) if(isset($_GET['non_interactive']) && (bool)$_GET['non_interactive'] === true)
{ {

View File

@ -249,6 +249,7 @@ select who should get the alarm calendar de Auswählen wer den Alarm erhalten so
selected range calendar de Ausgewählter Zeitraum selected range calendar de Ausgewählter Zeitraum
set a year only for one-time / non-regular holidays. calendar de Nur für einmalige/unregelmäßige Feiertage das Jahr angeben. set a year only for one-time / non-regular holidays. calendar de Nur für einmalige/unregelmäßige Feiertage das Jahr angeben.
set new events to private calendar de Neue Termine als private Termine eintragen set new events to private calendar de Neue Termine als private Termine eintragen
setting lock time calender calendar de Zeitintervall für Datensatzlock (default 1 sec.)
should invitations you rejected still be shown in your calendar ?<br>you can only accept them later (eg. when your scheduling conflict is removed), if they are still shown in your calendar! calendar de Sollen Einladungen welche von Ihnen abgelehnt wurden in Ihrem Kalender angezeigt werden?<br> Sie können diese Einladungen dann später akzeptieren (z. B. wenn Sie eine Terminkolission gelöst haben), wenn Sie in Ihrem Kalender noch vorhanden sind! should invitations you rejected still be shown in your calendar ?<br>you can only accept them later (eg. when your scheduling conflict is removed), if they are still shown in your calendar! calendar de Sollen Einladungen welche von Ihnen abgelehnt wurden in Ihrem Kalender angezeigt werden?<br> Sie können diese Einladungen dann später akzeptieren (z. B. wenn Sie eine Terminkolission gelöst haben), wenn Sie in Ihrem Kalender noch vorhanden sind!
should new events created as private by default ? calendar de Sollen neue Termine generell als Privat angelegt werden? 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 %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.
@ -275,6 +276,7 @@ test import (show importable records <u>only</u> in browser) calendar de Test Im
this day is shown as first day in the week or month view. calendar de Dieser Tag wird als erster in der Wochen- oder Monatsansicht angezeigt this day is shown as first day in the week or month view. calendar de Dieser Tag wird als erster in der Wochen- oder Monatsansicht angezeigt
this defines the end of your dayview. events after this time, are shown below the dayview. calendar de Diese Zeit definiert das Ende des Arbeitstags in der Tagesansicht. Alle späteren Einträge werden darunter dargestellt. this defines the end of your dayview. events after this time, are shown below the dayview. calendar de Diese Zeit definiert das Ende des Arbeitstags in der Tagesansicht. Alle späteren Einträge werden darunter dargestellt.
this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar de Diese Zeit definiert den Anfang des Arbeitstags in der Tagesansicht. Alle früheren Einträge werden darüber dargestellt. this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar de Diese Zeit definiert den Anfang des Arbeitstags in der Tagesansicht. Alle früheren Einträge werden darüber dargestellt.
this entry is opened by user: calendar de Dieser Eintrag ist von einem anderen Benutzer innerhalb des konfigurierten Zeitintervalls geöffnet worden:
this group that is preselected when you enter the planner. you can change it in the planner anytime you want. calendar de Diese Gruppe wird als vorauswahl ausgewählt wenn Sie den Planer öffnen. Sie können die Gruppe jederzeit wechseln wenn Sie möchten. this group that is preselected when you enter the planner. you can change it in the planner anytime you want. calendar de Diese Gruppe wird als vorauswahl ausgewählt wenn Sie den Planer öffnen. Sie können die Gruppe jederzeit wechseln wenn Sie möchten.
this message is sent for canceled or deleted events. calendar de Diese Benachrichtigung wird für stornierte oder gelöschte Termine versandt. this message is sent for canceled or deleted events. calendar de Diese Benachrichtigung wird für stornierte oder gelöschte Termine versandt.
this message is sent for modified or moved events. calendar de Diese Benachrichtigung wird für geänderte Termine versandt. this message is sent for modified or moved events. calendar de Diese Benachrichtigung wird für geänderte Termine versandt.

View File

@ -249,6 +249,7 @@ select who should get the alarm calendar en Select who should get the alarm
selected range calendar en Selected range selected range calendar en Selected range
set a year only for one-time / non-regular holidays. calendar en Set a Year only for one-time / non-regular holidays. set a year only for one-time / non-regular holidays. calendar en Set a Year only for one-time / non-regular holidays.
set new events to private calendar en Set new events to private set new events to private calendar en Set new events to private
setting lock time calender admin en Setting Datalock Time for Calendar (default 1 sec.)
should invitations you rejected still be shown in your calendar ?<br>you can only accept them later (eg. when your scheduling conflict is removed), if they are still shown in your calendar! calendar en Should invitations you rejected still be shown in your calendar ?<br>You can only accept them later (eg. when your scheduling conflict is removed), if they are still shown in your calendar! should invitations you rejected still be shown in your calendar ?<br>you can only accept them later (eg. when your scheduling conflict is removed), if they are still shown in your calendar! calendar en Should invitations you rejected still be shown in your calendar ?<br>You can only accept them later (eg. when your scheduling conflict is removed), if they are still shown in your calendar!
should new events created as private by default ? calendar en Should new events created as private by default ? 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 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. 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 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.
@ -275,6 +276,7 @@ test import (show importable records <u>only</u> in browser) calendar en Test Im
this day is shown as first day in the week or month view. calendar en This day is shown as first day in the week or month view. this day is shown as first day in the week or month view. calendar en This day is shown as first day in the week or month view.
this defines the end of your dayview. events after this time, are shown below the dayview. calendar en This defines the end of your dayview. Events after this time, are shown below the dayview. this defines the end of your dayview. events after this time, are shown below the dayview. calendar en This defines the end of your dayview. Events after this time, are shown below the dayview.
this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar en This defines the start of your dayview. Events before this time, are shown above the dayview.<br>This time is also used as a default starttime for new events. this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar en This defines the start of your dayview. Events before this time, are shown above the dayview.<br>This time is also used as a default starttime for new events.
this entry is opened by user: calendar en This entry was opened within the configured time interval by user:
this group that is preselected when you enter the planner. you can change it in the planner anytime you want. calendar en This group that is preselected when you enter the planner. You can change it in the planner anytime you want. this group that is preselected when you enter the planner. you can change it in the planner anytime you want. calendar en This group that is preselected when you enter the planner. You can change it in the planner anytime you want.
this message is sent for canceled or deleted events. calendar en This message is sent for canceled or deleted events. this message is sent for canceled or deleted events. calendar en This message is sent for canceled or deleted events.
this message is sent for modified or moved events. calendar en This message is sent for modified or moved events. this message is sent for modified or moved events. calendar en This message is sent for modified or moved events.

View File

@ -2,7 +2,7 @@
/** /**
* eGroupWare - eTemplates for Application calendar * eGroupWare - eTemplates for Application calendar
* http://www.egroupware.org * http://www.egroupware.org
* generated by soetemplate::dump4setup() 2007-07-11 17:29 * generated by soetemplate::dump4setup() 2008-01-14 11:10
* *
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package calendar * @package calendar
@ -28,6 +28,8 @@ $templ_data[] = array('name' => 'calendar.edit','template' => '','lang' => '','g
$templ_data[] = array('name' => 'calendar.edit','template' => '','lang' => '','group' => '0','version' => '1.3.004','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:3:{s:1:"A";a:5:{s:4:"type";s:5:"label";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";}}i:2;a:3:{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";}}i:3;a:3:{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";}}i:4;a:3:{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:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";s:5:"align";s:5:"right";i:1;a:5:{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";}i:2;a:4:{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";}}}i:5;a:3:{s:1:"A";a:7:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";s:4:"span";s:1:"2";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: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:3;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.end_hide { visibility: visible; white-space: nowrap; margin-left: 10px; }','modified' => '1181140110',); $templ_data[] = array('name' => 'calendar.edit','template' => '','lang' => '','group' => '0','version' => '1.3.004','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:3:{s:1:"A";a:5:{s:4:"type";s:5:"label";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";}}i:2;a:3:{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";}}i:3;a:3:{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";}}i:4;a:3:{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:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";s:5:"align";s:5:"right";i:1;a:5:{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";}i:2;a:4:{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";}}}i:5;a:3:{s:1:"A";a:7:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";s:4:"span";s:1:"2";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: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:3;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.end_hide { visibility: visible; white-space: nowrap; margin-left: 10px; }','modified' => '1181140110',);
$templ_data[] = array('name' => 'calendar.edit','template' => '','lang' => '','group' => '0','version' => '1.3.005','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:3:{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";}}i:2;a:3:{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";}}i:3;a:3:{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";}}i:4;a:3:{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:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";s:5:"align";s:5:"right";i:1;a:5:{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";}i:2;a:4:{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";}}}i:5;a:3:{s:1:"A";a:7:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";s:4:"span";s:1:"2";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: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:3;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.end_hide { visibility: visible; white-space: nowrap; margin-left: 10px; }','modified' => '1199959741',);
$templ_data[] = array('name' => 'calendar.edit.alarms','template' => '','lang' => '','group' => '0','version' => '1.0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:5:{s:2:"c1";s:3:"row";s:1:"A";s:2:"95";s:2:"h1";s:16:"20,@no_add_alarm";s:2:"c2";s:4:",top";s:2:"h2";s:8:",!@alarm";}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"before the event";}s:1:"B";a:10:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"8";i:1;a:4:{s:4:"type";s:13:"select-number";s:4:"size";s:4:",0,7";s:4:"name";s:15:"new_alarm[days]";s:4:"help";s:4:"days";}i:2;a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,new_alarm[days]";s:5:"label";s:4:"days";}i:3;a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:16:"new_alarm[hours]";s:4:"size";s:5:",0,23";s:4:"help";s:5:"hours";}i:4;a:3:{s:4:"type";s:5:"label";s:4:"size";s:19:",,,new_alarm[hours]";s:5:"label";s:5:"hours";}i:5;a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:15:"new_alarm[mins]";s:4:"size";s:7:",0,55,5";s:4:"help";s:7:"Minutes";}i:6;a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,new_alarm[mins]";s:5:"label";s:7:"Minutes";}i:7;a:5:{s:4:"type";s:6:"select";s:4:"name";s:16:"new_alarm[owner]";s:7:"no_lang";s:1:"1";s:5:"label";s:3:"for";s:4:"help";s:31:"Select who should get the alarm";}i:8;a:3:{s:4:"type";s:6:"button";s:4:"name";s:17:"button[add_alarm]";s:5:"label";s:9:"Add alarm";}}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Alarms";}s:1:"B";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:3:"row";}i:1;a:5:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"Time";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"before the event";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"All participants";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Owner";}s:1:"E";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Action";}}i:2;a:5:{s:1:"A";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:12:"${row}[time]";s:8:"readonly";s:1:"1";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:4:"name";s:14:"${row}[offset]";s:7:"no_lang";s:1:"1";}s:1:"C";a:4:{s:4:"type";s:8:"checkbox";s:5:"align";s:6:"center";s:4:"name";s:11:"${row}[all]";s:8:"readonly";s:1:"1";}s:1:"D";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:13:"${row}[owner]";s:8:"readonly";s:1:"1";}s:1:"E";a:7:{s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:5:"align";s:6:"center";s:4:"name";s:27:"delete_alarm[$row_cont[id]]";s:4:"help";s:17:"Delete this alarm";s:7:"onclick";s:36:"return confirm(\'Delete this alarm\');";}}}s:4:"rows";i:2;s:4:"cols";i:5;s:4:"name";s:5:"alarm";s:7:"options";a:0:{}}}}s:4:"rows";i:2;s:4:"cols";i:2;s:4:"size";s:17:"100%,200,,,,,auto";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' => '1118780740',); $templ_data[] = array('name' => 'calendar.edit.alarms','template' => '','lang' => '','group' => '0','version' => '1.0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:5:{s:2:"c1";s:3:"row";s:1:"A";s:2:"95";s:2:"h1";s:16:"20,@no_add_alarm";s:2:"c2";s:4:",top";s:2:"h2";s:8:",!@alarm";}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"before the event";}s:1:"B";a:10:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"8";i:1;a:4:{s:4:"type";s:13:"select-number";s:4:"size";s:4:",0,7";s:4:"name";s:15:"new_alarm[days]";s:4:"help";s:4:"days";}i:2;a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,new_alarm[days]";s:5:"label";s:4:"days";}i:3;a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:16:"new_alarm[hours]";s:4:"size";s:5:",0,23";s:4:"help";s:5:"hours";}i:4;a:3:{s:4:"type";s:5:"label";s:4:"size";s:19:",,,new_alarm[hours]";s:5:"label";s:5:"hours";}i:5;a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:15:"new_alarm[mins]";s:4:"size";s:7:",0,55,5";s:4:"help";s:7:"Minutes";}i:6;a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,new_alarm[mins]";s:5:"label";s:7:"Minutes";}i:7;a:5:{s:4:"type";s:6:"select";s:4:"name";s:16:"new_alarm[owner]";s:7:"no_lang";s:1:"1";s:5:"label";s:3:"for";s:4:"help";s:31:"Select who should get the alarm";}i:8;a:3:{s:4:"type";s:6:"button";s:4:"name";s:17:"button[add_alarm]";s:5:"label";s:9:"Add alarm";}}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Alarms";}s:1:"B";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:3:"row";}i:1;a:5:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"Time";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"before the event";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"All participants";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Owner";}s:1:"E";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Action";}}i:2;a:5:{s:1:"A";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:12:"${row}[time]";s:8:"readonly";s:1:"1";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:4:"name";s:14:"${row}[offset]";s:7:"no_lang";s:1:"1";}s:1:"C";a:4:{s:4:"type";s:8:"checkbox";s:5:"align";s:6:"center";s:4:"name";s:11:"${row}[all]";s:8:"readonly";s:1:"1";}s:1:"D";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:13:"${row}[owner]";s:8:"readonly";s:1:"1";}s:1:"E";a:7:{s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:5:"align";s:6:"center";s:4:"name";s:27:"delete_alarm[$row_cont[id]]";s:4:"help";s:17:"Delete this alarm";s:7:"onclick";s:36:"return confirm(\'Delete this alarm\');";}}}s:4:"rows";i:2;s:4:"cols";i:5;s:4:"name";s:5:"alarm";s:7:"options";a:0:{}}}}s:4:"rows";i:2;s:4:"cols";i:2;s:4:"size";s:17:"100%,200,,,,,auto";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' => '1118780740',);
$templ_data[] = array('name' => 'calendar.edit.custom','template' => '','lang' => '','group' => '0','version' => '1.0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:1:{s:2:"c1";s:4:",top";}i:1;a:1:{s:1:"A";a:1:{s:4:"type";s:12:"customfields";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:17:"100%,200,,,,,auto";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' => '1118737582',); $templ_data[] = array('name' => 'calendar.edit.custom','template' => '','lang' => '','group' => '0','version' => '1.0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:1:{s:2:"c1";s:4:",top";}i:1;a:1:{s:1:"A";a:1:{s:4:"type";s:12:"customfields";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:17:"100%,200,,,,,auto";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' => '1118737582',);

View File

@ -12,7 +12,7 @@
/* $Id$ */ /* $Id$ */
$setup_info['calendar']['name'] = 'calendar'; $setup_info['calendar']['name'] = 'calendar';
$setup_info['calendar']['version'] = '1.4'; $setup_info['calendar']['version'] = '1.5';
$setup_info['calendar']['app_order'] = 3; $setup_info['calendar']['app_order'] = 3;
$setup_info['calendar']['enable'] = 1; $setup_info['calendar']['enable'] = 1;

View File

@ -27,7 +27,10 @@
'cal_reference' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'), 'cal_reference' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'cal_modifier' => array('type' => 'int','precision' => '4'), 'cal_modifier' => array('type' => 'int','precision' => '4'),
'cal_non_blocking' => array('type' => 'int','precision' => '2','default' => '0'), 'cal_non_blocking' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_special' => array('type' => 'int','precision' => '2','default' => '0') 'cal_special' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_etag' => array('type' => 'int','precision' => '4'),
'cal_edit_user' => array('type' => 'int','precision' => '4'),
'cal_edit_time' => array('type' => 'int','precision' => '8')
), ),
'pk' => array('cal_id'), 'pk' => array('cal_id'),
'fk' => array(), 'fk' => array(),

View File

@ -1526,4 +1526,23 @@
{ {
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.4'; return $GLOBALS['setup_info']['calendar']['currentver'] = '1.4';
} }
$test[] = '1.4';
function calendar_upgrade1_4()
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','cal_etag',array(
'type' => 'int',
'precision' => '4'
));
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','cal_edit_user',array(
'type' => 'int',
'precision' => '4'
));
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','cal_edit_time',array(
'type' => 'int',
'precision' => '8'
));
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.5';
}
?> ?>

View File

@ -32,6 +32,14 @@
</select> </select>
</td> </td>
</tr> </tr>
<!--- lock setting -->
<tr bgcolor="{row_off}">
<td>{lang_setting_lock_time_calender}:</td>
<td><input name="newsettings[Lock_Time_Calender]" value="{value_Lock_Time_Calender}" size="40"></td>
</td>
</tr>
<!-- END body --> <!-- END body -->
<!-- BEGIN footer --> <!-- BEGIN footer -->