From 1ddd40f94b7687a86f3e4ba554690b102eb15ce6 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Mon, 15 Nov 2010 20:14:50 +0000 Subject: [PATCH] Add importexport support --- .../inc/class.resources_egw_record.inc.php | 145 ++++++++ .../inc/class.resources_export_csv.inc.php | 110 +++++++ .../inc/class.resources_import_csv.inc.php | 310 ++++++++++++++++++ .../class.resources_wizard_export_csv.inc.php | 73 +++++ .../class.resources_wizard_import_csv.inc.php | 62 ++++ resources/inc/class.ui_resources.inc.php | 1 + resources/setup/etemplates.inc.php | 4 +- 7 files changed, 704 insertions(+), 1 deletion(-) create mode 100644 resources/inc/class.resources_egw_record.inc.php create mode 100644 resources/inc/class.resources_export_csv.inc.php create mode 100644 resources/inc/class.resources_import_csv.inc.php create mode 100644 resources/inc/class.resources_wizard_export_csv.inc.php create mode 100644 resources/inc/class.resources_wizard_import_csv.inc.php diff --git a/resources/inc/class.resources_egw_record.inc.php b/resources/inc/class.resources_egw_record.inc.php new file mode 100644 index 0000000000..2c4916c39f --- /dev/null +++ b/resources/inc/class.resources_egw_record.inc.php @@ -0,0 +1,145 @@ +identifier = $_identifier; + if($this->identifier) { + $this->record = ExecMethod('resources.bo_resources.read', $this->identifier); + } + } + + /** + * magic method to set attributes of record + * + * @param string $_attribute_name + */ + public function __get($_attribute_name) { + return $this->record[$_attribute_name]; + } + + /** + * magig method to set attributes of record + * + * @param string $_attribute_name + * @param data $data + */ + public function __set($_attribute_name, $data) { + $this->record[$_attribute_name] = $data; + } + + /** + * converts this object to array. + * @abstract We need such a function cause PHP5 + * dosn't allow objects do define it's own casts :-( + * once PHP can deal with object casts we will change to them! + * + * @return array complete record as associative array + */ + public function get_record_array() { + return $this->record; + } + + /** + * gets title of record + * + *@return string tiltle + */ + public function get_title() { + if (empty($this->record)) { + $this->get_record(); + } + return $this->record['name']; + } + + /** + * sets complete record from associative array + * + * @todo add some checks + * @return void + */ + public function set_record(array $_record){ + $this->record = $_record; + } + + /** + * gets identifier of this record + * + * @return string identifier of current record + */ + public function get_identifier() { + return $this->identifier; + } + + /** + * saves record into backend + * + * @return string identifier + */ + public function save ( $_dst_identifier ) { + + } + + /** + * copys current record to record identified by $_dst_identifier + * + * @param string $_dst_identifier + * @return string dst_identifier + */ + public function copy ( $_dst_identifier ) { + + } + + /** + * moves current record to record identified by $_dst_identifier + * $this will become moved record + * + * @param string $_dst_identifier + * @return string dst_identifier + */ + public function move ( $_dst_identifier ) { + + } + + /** + * delets current record from backend + * + */ + public function delete () { + + } + + /** + * destructor + * + */ + public function __destruct() { + } + +} // end of egw_addressbook_record +?> diff --git a/resources/inc/class.resources_export_csv.inc.php b/resources/inc/class.resources_export_csv.inc.php new file mode 100644 index 0000000000..e3296cc944 --- /dev/null +++ b/resources/inc/class.resources_export_csv.inc.php @@ -0,0 +1,110 @@ +plugin_options; + + $bo = CreateObject('resources.bo_resources'); + $selection = array(); + if ($options['selection'] == 'selected') { + // ui selection with checkbox 'selected' + $query = egw_cache::getSession('resources', 'get_rows'); + $query['num_rows'] = -1; // all + unset($query['store_state']); + $bo->get_rows($query,$selection,$readonlys); + } + elseif ( $options['selection'] == 'all' ) { + $query = array( + 'num_rows' => -1, + ); // all + $bo->get_rows($query,$selection,$readonlys); + } else { + $selection = explode(',',$options['selection']); + } + + $export_object = new importexport_export_csv($_stream, (array)$options); + $export_object->set_mapping($options['mapping']); + + $types = importexport_export_csv::$types; + $types['select-bool'] = array('bookable'); + + foreach ($selection as $record) { + if(!is_array($record) || !$record['res_id']) continue; + + $resource = new resources_egw_record(); + $resource->set_record($record); + importexport_export_csv::convert($resource, $types, 'resources'); + $export_object->export_record($resource); + unset($resource); + } + } + + /** + * returns translated name of plugin + * + * @return string name + */ + public static function get_name() { + return lang('Resources CSV export'); + } + + /** + * returns translated (user) description of plugin + * + * @return string descriprion + */ + public static function get_description() { + return lang("Exports a list of resources to a CSV File."); + } + + /** + * retruns file suffix for exported file + * + * @return string suffix + */ + public static function get_filesuffix() { + return 'csv'; + } + + public static function get_mimetype() { + return 'text/csv'; + } + + /** + * return html for options. + * this way the plugin has all opportunities for options tab + * + */ + public function get_options_etpl() { + } + + /** + * returns selectors information + * + */ + public function get_selectors_etpl() { + return array( + 'name' => 'resources.export_csv_selectors' + ); + } +} diff --git a/resources/inc/class.resources_import_csv.inc.php b/resources/inc/class.resources_import_csv.inc.php new file mode 100644 index 0000000000..984b41af4d --- /dev/null +++ b/resources/inc/class.resources_import_csv.inc.php @@ -0,0 +1,310 @@ + 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', 'delete', ); + + /** + * conditions for actions + * + * @var array + */ + protected static $conditions = array( 'exists' ); + + /** + * @var definition + */ + private $definition; + + /** + * @var bo + */ + private $bo; + + /** + * @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 resource bo + $this->bo = CreateObject('resources.bo_resources'); + + // 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); + } + + // 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; + + if(!is_numeric($record['cat_id']) && strpos($record['cat_id'], ',') === False) { + $this->errors[$import_csv->get_current_position()] = lang('Bad category ID: %1. Try cat(|[]) in definition.', $record['cat_id']); + continue; + } + if ( $_definition->plugin_options['conditions'] ) { + foreach ( $_definition->plugin_options['conditions'] as $condition ) { + switch ( $condition['type'] ) { + // exists + case 'exists' : + $results = $this->bo->so->search( + array( $condition['string'] => $record[$condition['string']]), + False + ); + + if ( is_array( $results ) && count( array_keys( $results ) >= 1 ) ) { + // apply action to all contacts matching this exists condition + $action = $condition['true']; + foreach ( (array)$results as $resource ) { + $record['res_id'] = $resource['res_id']; + if ( $_definition->plugin_options['update_cats'] == 'add' ) { + if ( !is_array( $resource['cat_id'] ) ) $resource['cat_id'] = explode( ',', $resource['cat_id'] ); + if ( !is_array( $record['cat_id'] ) ) $record['cat_id'] = explode( ',', $record['cat_id'] ); + $record['cat_id'] = implode( ',', array_unique( array_merge( $record['cat_id'], $resource['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 contact 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['res_id']); + + // Merge to deal with fields not in import record + $_data = array_merge($old, $_data); + + // Fall through + case 'insert' : + if($_action == 'insert') { + // Backend doesn't like inserting with ID specified, it can overwrite + unset($_data['res_id']); + } + if ( $this->dry_run ) { + //print_r($_data); + $this->results[$_action]++; + return true; + } else { + $result = $this->bo->save( $_data ); + if($result) { + $this->errors[$record_num] = $result; + return false; + } else { + $this->results[$_action]++; + return true; + } + } + default: + throw new egw_exception('Unsupported action'); + + } + } + + /** + * returns translated name of plugin + * + * @return string name + */ + public static function get_name() { + return lang('Resources CSV import'); + } + + /** + * returns translated (user) description of plugin + * + * @return string descriprion + */ + public static function get_description() { + return lang("Imports a list of resources from a CSV file."); + } + + /** + * 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/resources/inc/class.resources_wizard_export_csv.inc.php b/resources/inc/class.resources_wizard_export_csv.inc.php new file mode 100644 index 0000000000..33dc0c95f1 --- /dev/null +++ b/resources/inc/class.resources_wizard_export_csv.inc.php @@ -0,0 +1,73 @@ +export_fields = array( + 'res_id' => lang('ID'), + 'name' => lang('name'), + 'short_description' => lang('short description'), + 'cat_id' => lang('Category'), + 'quantity' => lang('Quantity'), + 'useable' => lang('Useable'), + 'location' => lang('Location'), + 'bookable' => lang('Bookable'), + 'buyable' => lang('Buyable'), + 'prize' => lang('Prize'), + 'long_description' => lang('Long description'), + 'inventory_number' => lang('inventory number'), + ); + + // Custom fields + $custom = config::get_customfields('resources', true); + foreach($custom as $name => $data) { + $this->export_fields['#'.$name] = $data['label']; + } + } + + public function wizard_step50(&$content, &$sel_options, &$readonlys, &$preserv) { + if($this->debug || true) error_log(get_class($this) . '::wizard_step50->$content '.print_r($content,true)); + // return + if ($content['step'] == 'wizard_step50') + { + 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_step50($content,$sel_options,$readonlys,$preserv); + } + } + // init step + else + { + $content['step'] = 'wizard_step50'; + $content['msg'] = $this->steps[$content['step']]; + $preserv = $content; + unset ($preserv['button']); + $fields = array('pm_used_time', 'pm_planned_time', 'pm_replanned_time'); + $sel_options = array_fill_keys($fields, array('h' => lang('hours'), 'd' => lang('days'))); + foreach($fields as $field) { + $content[$field] = $content[$field] ? $content[$field] : $content['plugin_options'][$field]; + } + } + return $this->step_templates[$content['step']]; + } +} diff --git a/resources/inc/class.resources_wizard_import_csv.inc.php b/resources/inc/class.resources_wizard_import_csv.inc.php new file mode 100644 index 0000000000..01a2f3d33a --- /dev/null +++ b/resources/inc/class.resources_wizard_import_csv.inc.php @@ -0,0 +1,62 @@ +steps += array( + 'wizard_step50' => lang('Manage mapping'), + ); + + // Field mapping + $export = new resources_wizard_export_csv(); + $this->mapping_fields = array( + 'res_id' => lang('ID'), + 'name' => lang('name'), + 'short_description' => lang('short description'), + 'cat_id' => lang('Category'), + 'quantity' => lang('Quantity'), + 'useable' => lang('Useable'), + 'location' => lang('Location'), + 'bookable' => lang('Bookable'), + 'buyable' => lang('Buyable'), + 'prize' => lang('Prize'), + 'long_description' => lang('Long description'), + 'inventory_number' => lang('inventory number'), + ); + + $custom = config::get_customfields('resources', true); + foreach($custom as $name => $data) { + $this->mapping_fields['#'.$name] = $data['label']; + } + + // Actions + $this->actions = array( + 'none' => lang('none'), + 'update' => lang('update'), + 'insert' => lang('insert'), + 'delete' => lang('delete'), + ); + + // Conditions + $this->conditions = array( + 'exists' => lang('exists'), + ); + } +} diff --git a/resources/inc/class.ui_resources.inc.php b/resources/inc/class.ui_resources.inc.php index 79ca26f321..de73c25ab7 100755 --- a/resources/inc/class.ui_resources.inc.php +++ b/resources/inc/class.ui_resources.inc.php @@ -111,6 +111,7 @@ class ui_resources $content['nm']['bottom_too'] = true; $content['nm']['order'] = 'name'; $content['nm']['sort'] = 'ASC'; + $content['nm']['store_state'] = 'get_rows'; $nm_session_data = $GLOBALS['egw']->session->appsession('session_data','resources_index_nm'); if($nm_session_data) diff --git a/resources/setup/etemplates.inc.php b/resources/setup/etemplates.inc.php index aecfc14da8..f17fee52dd 100644 --- a/resources/setup/etemplates.inc.php +++ b/resources/setup/etemplates.inc.php @@ -2,7 +2,7 @@ /** * eGroupWare - eTemplates for Application resources * http://www.egroupware.org - * generated by soetemplate::dump4setup() 2009-11-28 16:46 + * generated by soetemplate::dump4setup() 2010-11-15 13:13 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package resources @@ -38,6 +38,8 @@ $templ_data[] = array('name' => 'resources.edit_tabs.page','template' => '','lan $templ_data[] = array('name' => 'resources.edit_tabs.pictures','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:2:{s:2:"c1";s:4:",top";s:2:"h2";s:4:"100%";}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:2:{s:4:"type";s:5:"image";s:4:"name";s:16:"resource_picture";}i:2;a:1:{s:4:"type";s:5:"label";}}i:2;a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";i:1;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:0:{}i:1;a:3:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:26:"Use general resources icon";s:5:"align";s:5:"right";}s:1:"B";a:3:{s:4:"type";s:5:"radio";s:4:"size";s:7:"gen_src";s:4:"name";s:11:"picture_src";}s:1:"C";a:3:{s:4:"type";s:6:"select";s:7:"no_lang";s:1:"1";s:4:"name";s:12:"gen_src_list";}}i:2;a:3:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:23:"Use the category\'s icon";s:5:"align";s:5:"right";}s:1:"B";a:3:{s:4:"type";s:5:"radio";s:4:"size";s:7:"cat_src";s:4:"name";s:11:"picture_src";}s:1:"C";a:1:{s:4:"type";s:5:"label";}}i:3;a:3:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:15:"Use own picture";s:5:"align";s:5:"right";}s:1:"B";a:3:{s:4:"type";s:5:"radio";s:4:"size";s:7:"own_src";s:4:"name";s:11:"picture_src";}s:1:"C";a:2:{s:4:"type";s:4:"file";s:4:"name";s:8:"own_file";}}}s:4:"rows";i:3;s:4:"cols";i:3;}s:5:"label";s:14:"picture source";}}}i:2;a:1:{s:1:"A";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:2;s:4:"cols";i:1;s:4:"size";s:7:"700,380";s:7:"options";a:2:{i:0;s:3:"700";i:1;s:3:"380";}}}','size' => '700,380','style' => '','modified' => '1131048665',); +$templ_data[] = array('name' => 'resources.export_csv_selectors','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:5:"radio";s:5:"label";s:7:"Use all";s:4:"size";s:3:"all";s:4:"name";s:9:"selection";}}i:2;a:1:{s:1:"A";a:4:{s:4:"type";s:5:"radio";s:5:"label";s:18:"Use search results";s:4:"name";s:9:"selection";s:4:"size";s:8:"selected";}}}s:4:"rows";i:2;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1289840391',); + $templ_data[] = array('name' => 'resources.resource_select','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:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:9:"nextmatch";s:4:"size";s:29:"resources.resource_select.row";s:4:"name";s:2:"nm";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1254212129',); $templ_data[] = array('name' => 'resources.resource_select.header','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"image";s:4:"name";s:6:"navbar";s:5:"label";s:16:"Select resources";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:4:"name";s:4:"$msg";s:7:"no_lang";s:1:"1";}}}s:4:"rows";i:1;s:4:"cols";i:2;}}','size' => '','style' => '','modified' => '1118502303',);