diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php
index 72cd3c7a59..cb93e3a969 100644
--- a/calendar/inc/class.calendar_ical.inc.php
+++ b/calendar/inc/class.calendar_ical.inc.php
@@ -168,7 +168,13 @@ class calendar_ical extends calendar_boupdate
var $log = false;
var $logfile="/tmp/log-vcal";
-
+ /**
+ * Conflict callback
+ * If set, conflict checking will be enabled, and the event as well as
+ * conflicts are passed as parameters to this callback
+ */
+ var $conflict_callback = null;
+
/**
* Constructor
*
@@ -1386,7 +1392,7 @@ class calendar_ical extends calendar_boupdate
// to not loose him, as EGroupware knows events without owner/ORGANIZER as participant
if (isset($event_info['stored_event']['participants'][$event['owner']]) && !isset($event['participants'][$event['owner']]))
{
- $event['participant'][$event['owner']] = $event_info['stored_event']['participants'][$event['owner']];
+ $event['participants'][$event['owner']] = $event_info['stored_event']['participants'][$event['owner']];
}
}
else // common adjustments for new events
@@ -1765,6 +1771,41 @@ class calendar_ical extends calendar_boupdate
return $updated_id === 0 ? 0 : $return_id;
}
+ /**
+ * Override parent update function to handle conflict checking callback, if set
+ *
+ * @param array &$event event-array, on return some values might be changed due to set defaults
+ * @param boolean $ignore_conflicts =false just ignore conflicts or do a conflict check and return the conflicting events.
+ * Set to false if $this->conflict_callback is set
+ * @param boolean $touch_modified =true NOT USED ANYMORE (was only used in old csv-import), modified&modifier is always updated!
+ * @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
+ * @param boolean $skip_notification =false true: send NO notifications, default false = send them
+ * @return mixed on success: int $cal_id > 0, on error or conflicts false.
+ * Conflicts are passed to $this->conflict_callback
+ */
+ public function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false,$updateTS=true,&$messages=null, $skip_notification=false)
+ {
+ if($this->conflict_callback !== null)
+ {
+ // calendar_ical overrides search(), which breaks conflict checking
+ // so we make sure to use the original from parent
+ static $bo;
+ if(!$bo)
+ {
+ $bo = new calendar_boupdate();
+ }
+ $conflicts = $bo->conflicts($event);
+ if(is_array($conflicts) && count($conflicts) > 0)
+ {
+ call_user_func_array($this->conflict_callback, array(&$event, &$conflicts));
+ return false;
+ }
+ }
+ return parent::update($event, $ignore_conflicts, $touch_modified, $ignore_acl, $updateTS, $messages, $skip_notification);
+ }
+
/**
* Sync alarms of current user: add alarms added on client and remove the ones removed
*
diff --git a/calendar/inc/class.calendar_import_csv.inc.php b/calendar/inc/class.calendar_import_csv.inc.php
index 8dbed1d120..85063052ae 100644
--- a/calendar/inc/class.calendar_import_csv.inc.php
+++ b/calendar/inc/class.calendar_import_csv.inc.php
@@ -97,7 +97,7 @@ class calendar_import_csv extends importexport_basic_import_csv {
{
$record->owner = $options['owner'];
}
-
+
// Handle errors in length or start/end date
if($record->start > $record->end)
{
@@ -148,7 +148,8 @@ class calendar_import_csv extends importexport_basic_import_csv {
else
{
// Search app via link query
- $result = Link::query($resource['app'], $search, $options);
+ $link_options = array();
+ $result = Link::query($resource['app'], $search, $link_options);
if($result)
{
@@ -294,13 +295,39 @@ class calendar_import_csv extends importexport_basic_import_csv {
}
if ( $this->dry_run ) {
//print_r($_data);
+ // User is interested in conflict checks, do so for dry run
+ // Otherwise, conflicts are just ignored and imported anyway
+ if($this->definition->plugin_options['skip_conflicts'])
+ {
+ $conflicts = $this->bo->conflicts($_data);
+ if($conflicts)
+ {
+ $this->conflict_warning($record_num, $conflicts);
+ $this->results['skipped']++;
+ return false;
+ }
+ }
$this->results[$_action]++;
return true;
} else {
- $result = $this->bo->save( $_data, $this->is_admin);
- if(!$result) {
+ $messages = null;
+ $result = $this->bo->update( $_data,
+ !$this->definition->plugin_options['skip_conflicts'],
+ true, $this->is_admin, true, $messages,
+ $this->definition->plugin_options['no_notification']
+ );
+ if(!$result)
+ {
$this->errors[$record_num] = lang('Unable to save');
- } else {
+ }
+ else if (is_array($result))
+ {
+ $this->conflict_warning($record_num, $result);
+ $this->results['skipped']++;
+ return false;
+ }
+ else
+ {
$this->results[$_action]++;
// This does nothing (yet?) but update the identifier
$record->save($result);
@@ -312,6 +339,21 @@ class calendar_import_csv extends importexport_basic_import_csv {
}
}
+
+ /**
+ * Add a warning message about conflicting events
+ *
+ * @param int $record_num Current record index
+ * @param Array $conflicts List of found conflicting events
+ */
+ protected function conflict_warning($record_num, &$conflicts)
+ {
+ $this->warnings[$record_num] = lang('Conflicts') . ':';
+ foreach($conflicts as $conflict)
+ {
+ $this->warnings[$record_num] .= "
\n" . Api\DateTime::to($conflict['start']) . "\t" . $conflict['title'];
+ }
+ }
/**
* returns translated name of plugin
diff --git a/calendar/inc/class.calendar_import_ical.inc.php b/calendar/inc/class.calendar_import_ical.inc.php
index 1d78a1b16f..dc20624726 100644
--- a/calendar/inc/class.calendar_import_ical.inc.php
+++ b/calendar/inc/class.calendar_import_ical.inc.php
@@ -94,9 +94,9 @@ class calendar_import_ical implements importexport_iface_import_plugin {
protected $errors = array();
/**
- * List of actions, and how many times that action was taken
- */
- protected $results = array();
+ * List of actions, and how many times that action was taken
+ */
+ protected $results = array();
/**
* imports entries according to given definition object.
@@ -136,18 +136,44 @@ class calendar_import_ical implements importexport_iface_import_plugin {
{
$_definition->plugin_options['no_notification'] = true;
}
+ // User wants conflicting events to not be imported
+ if($_definition->plugin_options['skip_conflicts'])
+ {
+ $calendar_ical->conflict_callback = array($this, 'conflict_warning');
+ }
if (!$calendar_ical->importVCal($_stream, -1,null,false,0,'',null,null,null,$_definition->plugin_options['no_notification']))
{
$this->errors[] = lang('Error: importing the iCal');
}
else
{
- $this->results['imported'] = $calendar_ical->events_imported;
+ $this->results['imported'] += $calendar_ical->events_imported;
}
return $calendar_ical->events_imported;
}
+
+ /**
+ * Add a warning message about conflicting events
+ *
+ * @param int $record_num Current record index
+ * @param Array $conflicts List of found conflicting events
+ */
+ public function conflict_warning(&$event, &$conflicts)
+ {
+ $warning = EGroupware\Api\DateTime::to($event['start']) . ' ' . $event['title'] . ' ' . lang('Conflicts') . ':';
+ foreach($conflicts as $conflict)
+ {
+ $warning .= "
\n" . EGroupware\Api\DateTime::to($conflict['start']) . "\t" . $conflict['title'];
+ }
+ $this->warnings[] = $warning;
+
+ // iCal will always count as imported, even if it wasn't
+ $this->results['imported'] -= 1;
+
+ $this->results['skipped']++;
+ }
/**
* returns translated name of plugin
diff --git a/calendar/inc/class.calendar_wizard_import_csv.inc.php b/calendar/inc/class.calendar_wizard_import_csv.inc.php
index 18718d5983..ecac6c4f92 100644
--- a/calendar/inc/class.calendar_wizard_import_csv.inc.php
+++ b/calendar/inc/class.calendar_wizard_import_csv.inc.php
@@ -15,7 +15,7 @@ use EGroupware\Api;
class calendar_wizard_import_csv extends importexport_wizard_basic_import_csv
{
- /**
+ /**
* constructor
*/
function __construct()
@@ -26,6 +26,9 @@ class calendar_wizard_import_csv extends importexport_wizard_basic_import_csv
'wizard_step50' => lang('Manage mapping'),
);
+ // Override conditions template to add conflict option
+ $this->step_templates['wizard_step55'] = 'calendar.import.conditions';
+
// Field mapping
$tracking = new calendar_tracking();
$this->mapping_fields = array('id' => 'Calendar ID') + $tracking->field2label;
@@ -67,6 +70,11 @@ class calendar_wizard_import_csv extends importexport_wizard_basic_import_csv
$sel_options['string'] = array(
'id' => 'Calendar ID'
);
+
+ if(!$content['skip_conflicts'] && $content['plugin_options']['skip_conflicts'])
+ {
+ $content['skip_conflicts'] = $content['plugin_options']['skip_conflicts'];
+ }
return $result;
}
}
diff --git a/calendar/inc/class.calendar_wizard_import_ical.inc.php b/calendar/inc/class.calendar_wizard_import_ical.inc.php
new file mode 100644
index 0000000000..9d71d95838
--- /dev/null
+++ b/calendar/inc/class.calendar_wizard_import_ical.inc.php
@@ -0,0 +1,84 @@
+ 'calendar.import.conditions'
+ );
+ /**
+ * constructor
+ */
+ function __construct()
+ {
+ $this->steps = array(
+ 'wizard_step55' => lang('Edit conditions'),
+ );
+ }
+
+ function wizard_step50(&$content, &$sel_options, &$readonlys, &$preserv)
+ {
+ $result = parent::wizard_step50($content, $sel_options, $readonlys, $preserv);
+ $content['msg'] .= "\n*" ;
+
+ return $result;
+ }
+
+ // Conditions
+ function wizard_step55(&$content, &$sel_options, &$readonlys, &$preserv)
+ {
+ // return from step55
+ if ($content['step'] == 'wizard_step55')
+ {
+ switch (array_search('pressed', $content['button']))
+ {
+ case 'next':
+ return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],1);
+ case 'previous' :
+ return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],-1);
+ case 'finish':
+ return 'wizard_finish';
+ default :
+ return $this->wizard_step55($content,$sel_options,$readonlys,$preserv);
+ }
+ }
+ // init step30
+ else
+ {
+ $content['text'] = $this->steps['wizard_step55'];
+ $content['step'] = 'wizard_step55';
+ if(!$content['skip_conflicts'] && array_key_exists('skip_conflicts', $content['plugin_options']))
+ {
+ $content['skip_conflicts'] = $content['plugin_options']['skip_conflicts'];
+ }
+ $preserv = $content;
+ unset ($preserv['button']);
+
+ // No real conditions, but we share a template
+ $content['no_conditions'] = true;
+
+ return $this->step_templates[$content['step']];
+ }
+
+ return $result;
+ }
+}
diff --git a/calendar/templates/default/import.conditions.xet b/calendar/templates/default/import.conditions.xet
new file mode 100644
index 0000000000..e8b62b8496
--- /dev/null
+++ b/calendar/templates/default/import.conditions.xet
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+