diff --git a/calendar/inc/class.calendar_import_csv.inc.php b/calendar/inc/class.calendar_import_csv.inc.php new file mode 100644 index 0000000000..f4c2c971e8 --- /dev/null +++ b/calendar/inc/class.calendar_import_csv.inc.php @@ -0,0 +1,346 @@ + conversion) + 'field_mapping', // array( $csv_col_num => adb_filed) + 'conditions', /* => array containing condition arrays: + 'type' => exists, // exists + 'string' => '#kundennummer', + 'true' => array( + 'action' => update, + 'last' => true, + ), + 'false' => array( + 'action' => insert, + 'last' => true, + ),*/ + + ); + + /** + * actions wich could be done to data entries + */ + protected static $actions = array( 'none', 'update', 'insert' ); + + /** + * conditions for actions + * + * @var array + */ + protected static $conditions = array(); + + /** + * @var definition + */ + private $definition; + + /** + * @var bo + */ + private $bo; + + /** + * For figuring out if an entry has changed + */ + protected $tracking; + + /** + * @var bool + */ + private $dry_run = false; + + /** + * @var bool is current user admin? + */ + private $is_admin = false; + + /** + * @var int + */ + private $user = null; + + /** + * List of import errors + */ + protected $errors = array(); + + /** + * List of actions, and how many times that action was taken + */ + protected $results = array(); + + /** + * imports entries according to given definition object. + * @param resource $_stream + * @param string $_charset + * @param definition $_definition + */ + public function import( $_stream, importexport_definition $_definition ) { + $import_csv = new importexport_import_csv( $_stream, array( + 'fieldsep' => $_definition->plugin_options['fieldsep'], + 'charset' => $_definition->plugin_options['charset'], + )); + + $this->definition = $_definition; + + // user, is admin ? + $this->is_admin = isset( $GLOBALS['egw_info']['user']['apps']['admin'] ) && $GLOBALS['egw_info']['user']['apps']['admin']; + $this->user = $GLOBALS['egw_info']['user']['account_id']; + + // dry run? + $this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] : false; + + // fetch the addressbook bo + $this->bo= new calendar_boupdate(); + + // Get the tracker for changes + $this->tracking = new calendar_tracking(); + + // set FieldMapping. + $import_csv->mapping = $_definition->plugin_options['field_mapping']; + + // set FieldConversion + $import_csv->conversion = $_definition->plugin_options['field_conversion']; + + //check if file has a header lines + if ( isset( $_definition->plugin_options['num_header_lines'] ) && $_definition->plugin_options['num_header_lines'] > 0) { + $import_csv->skip_records($_definition->plugin_options['num_header_lines']); + } elseif(isset($_definition->plugin_options['has_header_line']) && $_definition->plugin_options['has_header_line']) { + // First method is preferred + $import_csv->skip_records(1); + } + + // set eventOwner + $_definition->plugin_options['owner'] = isset( $_definition->plugin_options['owner'] ) ? + $_definition->plugin_options['owner'] : $this->user; + + // Start counting successes + $count = 0; + $this->results = array(); + + // Failures + $this->errors = array(); + + while ( $record = $import_csv->get_record() ) { + $success = false; + + // don't import empty records + if( count( array_unique( $record ) ) < 2 ) continue; + + // Set owner, unless it's supposed to come from CSV file + if($_definition->plugin_options['owner_from_csv']) { + if(!is_numeric($record['owner'])) { + $this->errors[$import_csv->get_current_position()] = lang( + 'Invalid owner ID: %1. Might be a bad field translation. Used %2 instead.', + $record['owner'], + $_definition->plugin_options['owner'] + ); + $record['owner'] = $_definition->plugin_options['owner']; + } + } else { + $record['owner'] = $_definition->plugin_options['owner']; + } + + if ( $_definition->plugin_options['conditions'] ) { + foreach ( $_definition->plugin_options['conditions'] as $condition ) { + switch ( $condition['type'] ) { + // exists + case 'exists' : + $records = $this->bo->search( + + ); + + if ( is_array( $records ) && count( array_keys( $records ) >= 1 ) ) { + // apply action to all records matching this exists condition + $action = $condition['true']; + foreach ( (array)$records as $record ) { + $record['id'] = $record['id']; + if ( $_definition->plugin_options['update_cats'] == 'add' ) { + if ( !is_array( $record['cat_id'] ) ) $record['cat_id'] = explode( ',', $record['cat_id'] ); + $record['cat_id'] = implode( ',', array_unique( array_merge( $record['cat_id'], $record['cat_id'] ) ) ); + } + $success = $this->action( $action['action'], $record, $import_csv->get_current_position() ); + } + } else { + $action = $condition['false']; + $success = ($this->action( $action['action'], $record, $import_csv->get_current_position() )); + } + break; + + // not supported action + default : + die('condition / action not supported!!!'); + break; + } + if ($action['last']) break; + } + } else { + // unconditional insert + $success = $this->action( 'insert', $record, $import_csv->get_current_position() ); + } + if($success) $count++; + } + return $count; + } + + /** + * perform the required action + * + * @param int $_action one of $this->actions + * @param array $_data record data for the action + * @return bool success or not + */ + private function action ( $_action, $_data, $record_num = 0 ) { + switch ($_action) { + case 'none' : + return true; + case 'update' : + // Only update if there are changes + $old = $this->bo->read($_data['id']); + + // Don't change a user account into a record + if(!$this->definition->plugin_options['change_owner']) { + // Don't change owner of an existing record + unset($_data['owner']); + } + + // Merge to deal with fields not in import record + $_data = array_merge($old, $_data); + $changed = $this->tracking->changed_fields($_data, $old); + if(count($changed) == 0) { + return true; + } + + // Fall through + case 'insert' : + if($_action == 'insert') { + // Backend doesn't like inserting with ID specified, can overwrite existing + unset($_data['id']); + } + // Make sure participants are set + if(!$_data['participants']) { + $user = $_data['owner'] ? $_data['owner'] : $this->user; + $_data['participants'] = array( + $user => 'U' + ); + } + if ( $this->dry_run ) { + //print_r($_data); + $this->results[$_action]++; + return true; + } else { + $result = $this->bo->save( $_data, $this->is_admin); + if(!$result) { + $this->errors[$record_num] = lang('Unable to save'); + } else { + $this->results[$_action]++; + } + return $result; + } + default: + throw new egw_exception('Unsupported action'); + + } + } + + /** + * returns translated name of plugin + * + * @return string name + */ + public static function get_name() { + return lang('CSV import'); + } + + /** + * returns translated (user) description of plugin + * + * @return string descriprion + */ + public static function get_description() { + return lang("Imports events into your Calendar from a CSV File. CSV means 'Comma Seperated Values'. However in the options Tab you can also choose other seperators."); + } + + /** + * retruns file suffix(s) plugin can handle (e.g. csv) + * + * @return string suffix (comma seperated) + */ + public static function get_filesuffix() { + return 'csv'; + } + + /** + * return etemplate components for options. + * @abstract We can't deal with etemplate objects here, as an uietemplate + * objects itself are scipt orientated and not "dialog objects" + * + * @return array ( + * name => string, + * content => array, + * sel_options => array, + * preserv => array, + * ) + */ + public function get_options_etpl() { + // lets do it! + } + + /** + * returns etemplate name for slectors of this plugin + * + * @return string etemplate name + */ + public function get_selectors_etpl() { + // lets do it! + } + + /** + * Returns errors that were encountered during importing + * Maximum of one error message per record, but you can append if you need to + * + * @return Array ( + * record_# => error message + * ) + */ + public function get_errors() { + return $this->errors; + } + + /** + * Returns a list of actions taken, and the number of records for that action. + * Actions are things like 'insert', 'update', 'delete', and may be different for each plugin. + * + * @return Array ( + * action => record count + * ) + */ + public function get_results() { + return $this->results; + } +} // end of iface_export_plugin +?> diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php index 38ec4eb68d..5e6841906b 100644 --- a/calendar/inc/class.calendar_so.inc.php +++ b/calendar/inc/class.calendar_so.inc.php @@ -912,7 +912,7 @@ ORDER BY cal_user_type, cal_usre_id // event (without uid), not strong enough uid $event['cal_uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$cal_id); $this->db->update($this->cal_table, array('cal_uid' => $event['cal_uid']), - array('cal_id' => $event['cal_id']),__LINE__,__FILE__,'calendar'); + array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar'); } if ($event['recur_type'] == MCAL_RECUR_NONE) diff --git a/calendar/inc/class.calendar_wizard_import_csv.inc.php b/calendar/inc/class.calendar_wizard_import_csv.inc.php new file mode 100644 index 0000000000..ca12c0aa07 --- /dev/null +++ b/calendar/inc/class.calendar_wizard_import_csv.inc.php @@ -0,0 +1,52 @@ +steps += array( + 'wizard_step50' => lang('Manage mapping'), + ); + // No conditions yet + unset($this->steps['wizard_step55']); + + // Field mapping + $tracking = new calendar_tracking(); + $this->mapping_fields = $tracking->field2label; + + // Actions + $this->actions = array( + 'none' => lang('none'), + 'update' => lang('update'), + 'insert' => lang('insert'), + ); + + // Conditions + $this->conditions = array( + ); + } + + function wizard_step50(&$content, &$sel_options, &$readonlys, &$preserv) + { + $result = parent::wizard_step50($content, $sel_options, $readonlys, $preserv); + $content['msg'] .= "\n*" ; + + return $result; + } +}