Issues (4868)

inc/class.addressbook_export_contacts_csv.inc.php (10 issues)

1
<?php
2
/**
3
 * EGroupware - addressbook
4
 *
5
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
6
 * @package addressbook
7
 * @subpackage importexport
8
 * @link http://www.egroupware.org
9
 * @author Cornelius Weiss <[email protected]>
10
 * @copyright Cornelius Weiss <[email protected]>
11
 * @version $Id$
12
 */
13
14
use EGroupware\Api;
15
use EGroupware\Api\Acl;
16
17
/**
18
 * export plugin of addressbook
19
 */
20
class addressbook_export_contacts_csv implements importexport_iface_export_plugin
21
{
22
	/**
23
	 * Constants used for exploding categories & multi-selectboxes into seperate fields
24
	 */
25
	const NO_EXPLODE = False;
26
	const MAIN_CATS = 'main_cats';	// Only the top-level categories get their own field
27
	const EACH_CAT = 'each_cat';	// Every category gets its own field
28
	const EXPLODE = 'explode';	// For [custom] multi-selects, each option gets its own field
29
30
	public function __construct()
31
	{
32
		$this->ui= new addressbook_ui();
0 ignored issues
show
Bug Best Practice introduced by
The property ui does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
33
		$this->get_selects();
34
	}
35
36
	/**
37
	 * Exports records as defined in $_definition
38
	 *
39
	 * @param egw_record $_definition
40
	 */
41
	public function export( $_stream, importexport_definition $_definition) {
42
43
		$options = $_definition->plugin_options;
44
		$this->export_object = $export_object = new importexport_export_csv($_stream, (array)$options);
0 ignored issues
show
Bug Best Practice introduced by
The property export_object does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
45
46
		$selection = array();
47
48
		// Addressbook defines its own export imits
49
		$limit_exception = Api\Storage\Merge::is_export_limit_excepted();
50
		$export_limit = Api\Storage\Merge::getExportLimit($app='addressbook');
51
		if (!$limit_exception) $export_object->export_limit = $export_limit; // we may not need that after all
52
		if($export_limit == 'no' && !$limit_exception) {
53
			return;
54
		}
55
56
		// Need to switch the app to get the same results
57
		$old_app = $GLOBALS['egw_info']['flags']['currentapp'];
58
		$GLOBALS['egw_info']['flags']['currentapp'] = 'addressbook';
59
60
		if ($options['selection'] == 'search') {
61
			// uicontacts selection with checkbox 'use_all'
62
			$query = Api\Cache::getSession('addressbook', 'index');
63
			$query['num_rows'] = -1;	// all
64
			$query['csv_export'] = true;	// so get_rows method _can_ produce different content or not store state in the session
65
			$query['order'] = 'contact_id';
66
			if(!array_key_exists('filter',$query)) $query['filter'] = $GLOBALS['egw_info']['user']['account_id'];
67
			$readonlys = null;
68
			$this->ui->get_rows($query,$selection,$readonlys, true);	// only return the ids
69
		}
70
		elseif ( $options['selection'] == 'all' ) {
71
			if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts'] === '1') {
72
				$col_filter['account_id'] = null;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$col_filter was never initialized. Although not strictly required by PHP, it is generally a good practice to add $col_filter = array(); before regardless.
Loading history...
73
			}
74
			$selection = ExecMethod2('addressbook.addressbook_bo.search', array(), true, '', '','',false,'AND',false,$col_filter);
75
			//$uicontacts->get_rows($query,$selection,$readonlys,true);
76
		}
77
		elseif ($options['selection'] == 'filter')
78
		{
79
			$filter = $_definition->filter;
0 ignored issues
show
Bug Best Practice introduced by
The property filter does not exist on importexport_definition. Since you implemented __get, consider adding a @property annotation.
Loading history...
80
			$query = array();
81
82
			// Handle ranges
83
			foreach($filter as $field => $value)
84
			{
85
				if($field == 'cat_id')
86
				{
87
					$query['col_filter'][$field] = implode(',',$value);
88
					continue;
89
				}
90
91
				// Birthdays in addressbook are formatted Y-m-d
92
				if($field == 'bday')
93
				{
94
					if($value['from'])
95
					{
96
						$query['col_filter'][] = "contact_bday >= " . $GLOBALS['egw']->db->quote(date('Y-m-d', (int)$value['from']));
97
					}
98
					if($value['to'])
99
					{
100
						$query['col_filter'][] = "contact_bday <= " . $GLOBALS['egw']->db->quote(date('Y-m-d', (int)$value['to']));
101
					}
102
					continue;
103
				}
104
				// Custom fields & listed exceptions are not filtered with contact_ prefix
105
				if(strpos($field, '#') !== 0 && !in_array($field, array('tid','owner')))
106
				{
107
					$field = 'contact_'.$field;
108
				}
109
				$query['col_filter'][$field] = $value;
110
				if(!is_array($value) || (!$value['from'] && !$value['to'])) continue;
111
112
				// Ranges are inclusive, so should be provided that way (from 2 to 10 includes 2 and 10)
113
				if($value['from']) $query['col_filter'][] = "$field >= " . (int)$value['from'];
114
				if($value['to']) $query['col_filter'][] = "$field <= " . (int)$value['to'];
115
				unset($query['col_filter'][$field]);
116
			}
117
			$selection = ExecMethod2('addressbook.addressbook_bo.search', array(), true, '', '','',false,'AND',false,$query['col_filter']);
118
		}
119
		else
120
		{
121
			$selection = explode(',',$options['selection']);
122
		}
123
		if(!is_array($selection))
124
		{
125
			$selection = array();
126
		}
127
		$GLOBALS['egw_info']['flags']['currentapp'] = $old_app;
128
129
		if(Api\Storage\Merge::hasExportLimit($export_limit) && !$limit_exception) {
130
			$selection = array_slice($selection, 0, $export_limit);
131
		}
132
133
		if($options['explode_multiselects']) {
134
			$customfields = Api\Storage\Customfields::get('addressbook');
135
			$additional_fields = array();
136
			$cat_obj = new Api\Categories('', 'addressbook');
137
			foreach($options['explode_multiselects'] as $field => $explode) {
138
				switch($explode['explode']) {
139
					case self::MAIN_CATS:
140
						$cats = $cat_obj->return_array('mains', 0, false,'','ASC','',true);
141
						foreach($cats as $settings) {
142
							$additional_fields[$field][$settings['id']] = array(
143
								'count' => 0,
144
								'label' => $settings['name'],
145
								'subs' => array(),
146
							);
147
							$subs = $cat_obj->return_sorted_array(0, False, '', 'ASC', 'cat_name', True, $settings['id']);
148
							foreach($subs as $sub) {
149
								$name = $sub['name'];
150
								$path = $sub;
151
								while($path['parent'] != $settings['id']) {
152
									$path = $cat_obj->read($path['parent']);
153
									$name = $path['name'] . '/' . $name;
154
								}
155
								$additional_fields[$field][$settings['id']]['subs'][$sub['id']] = $name;
156
							}
157
						}
158
						break;
159
					case self::EACH_CAT:
160
						$cats = $cat_obj->return_array('all', 0, false,'','ASC','',true);
161
						foreach($cats as $settings) {
162
							$name = $settings['name'];
163
							$path = $settings;
164
							while($path['level'] != 0) {
165
								$path = $cat_obj->read($path['parent']);
166
								$name = $path['name'] . '/' . $name;
167
							}
168
							$additional_fields[$field][$settings['id']] = array(
169
								'count' => 0,
170
								'label' => $name
171
							);
172
						}
173
						break;
174
					case self::EXPLODE:
175
						// Only works for custom fields
176
						$index = substr($field, 1);
177
						foreach($customfields[$index]['values'] as $key => $value) {
178
							$additional_fields[$field][$key] = array(
179
								'count' => 0,
180
								'label' => $customfields[$index]['label'] . ': ' . $value,
181
							);
182
						}
183
						break;
184
				}
185
			}
186
187
			// Check records to see if additional fields are actually used
188
			foreach ($selection as $_contact) {
189
				if(is_array($_contact) && array_key_exists('photo', $_contact)) {
190
					unset($_contact['photo']);
191
				}
192
				if(is_array($_contact) && count($_contact) == 1 && $_contact['id']) {
193
					$_contact = $_contact['id'];
194
				}
195
				if(is_array($_contact) && $_contact['id']) {
196
					$contact = new addressbook_egw_record();
197
					$contact->set_record($_contact);
198
				} else {
199
					$contact = new addressbook_egw_record($_contact);
200
				}
201
				foreach($additional_fields as $field => &$values) {
202
					if(!$contact->$field) continue;
203
					foreach($values as $value => &$settings) {
204
						if(!is_array($contact->$field)) {
205
							$contact->$field = explode(',', $contact->$field);
206
						}
207
						if(is_array($contact->$field) && in_array($value, $contact->$field)) {
208
							$settings['count']++;
209
						} elseif($contact->$field == $value) {
210
							$settings['count']++;
211
						} elseif($options['explode_multiselects'][$field]['explode'] == self::MAIN_CATS && array_intersect($contact->$field, array_keys($settings['subs']))) {
212
							$settings['count']++;
213
						}
214
					}
215
				}
216
			}
217
218
			unset($field);
219
			unset($value);
220
			unset($settings);
221
222
			// Add additional columns
223
			foreach($additional_fields as $field => $additional_values) {
224
				// Remove original
225
				unset($options['mapping'][$field]);
226
				// Add exploded
227
				$field_count = 0;
228
				foreach($additional_values as $value => $settings) {
229
					if($settings['count'] > 0) {
230
						$field_count += $settings['count'];
231
						$options['mapping'][$field.'-'.$value] = $settings['label'];
232
					}
233
				}
234
				if($field_count > 0) {
235
					// Set some options for converting
236
					$options['explode_multiselects'][$field]['values'] = $additional_values;
237
				} else {
238
					// Don't need this anymore
239
					unset($options['explode_multiselects'][$field]);
240
				}
241
			}
242
		}
243
244
		$export_object->set_mapping($options['mapping']);
245
246
		// Add in last/next event, if needed
247
		if($options['mapping']['last_date'] || $options['mapping']['next_date'])
248
		{
249
			$contact_ids = array();
250
			foreach($selection as $_contact)
251
			{
252
				if(is_array($_contact) && $_contact['id'])
253
				{
254
					$contact_ids[] = $_contact['account_id'] ? $_contact['account_id'] : 'c'.$_contact['id'];
255
				}
256
				else
257
				{
258
					$contact_ids[] = 'c'.$contact;
259
				}
260
			}
261
			$events = $this->ui->read_calendar($contact_ids, false);
262
		}
263
264
		// $options['selection'] is array of identifiers as this plugin doesn't
265
		// support other selectors atm.
266
		foreach ($selection as $_contact) {
267
			if(is_array($_contact) && array_key_exists('photo', $_contact)) {
268
				unset($_contact['photo']);
269
			}
270
			if(is_array($_contact) && count($_contact) == 1 && $_contact['id']) {
271
				$_contact = $_contact['id'];
272
			}
273
			if(is_array($_contact) && $_contact['id']) {
274
				$contact = new addressbook_egw_record();
275
				$contact->set_record($_contact);
276
			} else {
277
				$contact = new addressbook_egw_record($_contact);
278
			}
279
280
			if($events && $events[$contact->id])
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on addressbook_egw_record. Since you implemented __get, consider adding a @property annotation.
Loading history...
281
			{
282
				// NB: last_date and next_date are used instead of last_event & next_event
283
				// to avoid automatic conversion - we want to export link title, not date-time
284
				$contact->last_date = $events[$contact->id]['last_link']['title'];
0 ignored issues
show
Bug Best Practice introduced by
The property last_date does not exist on addressbook_egw_record. Since you implemented __set, consider adding a @property annotation.
Loading history...
285
				$contact->next_date = $events[$contact->id]['next_link']['title'];
0 ignored issues
show
Bug Best Practice introduced by
The property next_date does not exist on addressbook_egw_record. Since you implemented __set, consider adding a @property annotation.
Loading history...
286
			}
287
			// Some conversion
288
			$this->convert($contact, $options);
289
			if($options['convert']) {
290
				importexport_export_csv::convert($contact, addressbook_egw_record::$types, 'addressbook',$this->selects);
291
			} else {
292
				// Implode arrays, so they don't say 'Array'
293
				foreach($contact->get_record_array() as $key => $value) {
294
					if(is_array($value)) $contact->$key = implode(',', $value);
295
				}
296
			}
297
298
			$export_object->export_record($contact);
299
			unset($contact);
300
		}
301
		return $export_object;
302
	}
303
304
	/**
305
	 * returns translated name of plugin
306
	 *
307
	 * @return string name
308
	 */
309
	public static function get_name() {
310
		return lang('Addressbook CSV export');
311
	}
312
313
	/**
314
	 * returns translated (user) description of plugin
315
	 *
316
	 * @return string descriprion
317
	 */
318
	public static function get_description() {
319
		return lang("Exports contacts from your Addressbook into a CSV File.");
320
	}
321
322
	/**
323
	 * retruns file suffix for exported file
324
	 *
325
	 * @return string suffix
326
	 */
327
	public static function get_filesuffix() {
328
		return 'csv';
329
	}
330
331
	public static function get_mimetype() {
332
		return 'text/csv';
333
	}
334
335
	/**
336
	 * Suggest a file name for the downloaded file
337
	 * No suffix
338
	 */
339
	public function get_filename()
340
	{
341
		if(is_object($this->export_object) && $this->export_object->get_num_of_records() == 1)
342
		{
343
			return $this->export_object->record->get_title();
344
		}
345
		return false;
346
	}
347
348
	/**
349
	 * return html for options.
350
	 * this way the plugin has all opertunities for options tab
351
	 *
352
	 * @param $definition Specific definition
353
	 *
354
	 * @return array (
355
	 * 		name 		=> string,
356
	 * 		content		=> array,
357
	 * 		sel_options	=> array,
358
	 * 		readonlys	=> array,
359
	 * 		preserv		=> array,
360
	 * )
361
	 */
362
	public function get_options_etpl(importexport_definition &$definition = NULL)
363
	{
364
		return false;
365
	}
366
367
	/**
368
	 * returns slectors of this plugin via xajax
369
	 *
370
	 */
371
	public function get_selectors_etpl() {
372
		return array(
373
			'name'		=> 'importexport.export_csv_selectors',
374
		);
375
	}
376
377
	/**
378
	* Convert some internal data to something with more meaning
379
	*
380
	* Dates, times, user IDs, category IDs
381
	*/
382
	public static function convert(addressbook_egw_record &$record, $options) {
383
384
		if ($record->tel_prefer) {
0 ignored issues
show
Bug Best Practice introduced by
The property tel_prefer does not exist on addressbook_egw_record. Since you implemented __get, consider adding a @property annotation.
Loading history...
385
			$field = $record->tel_prefer;
386
			$record->tel_prefer = $record->$field;
0 ignored issues
show
Bug Best Practice introduced by
The property tel_prefer does not exist on addressbook_egw_record. Since you implemented __set, consider adding a @property annotation.
Loading history...
387
		}
388
389
		if(!is_array($options['explode_multiselects']))
390
		{
391
			return;
392
		}
393
		foreach((array)$options['explode_multiselects'] as $field => $explode_settings) {
394
			if(!is_array($record->$field)) $record->$field = explode(',', $record->$field);
395
			foreach((array)$explode_settings['values'] as $value => $settings) {
396
				$field_name = "$field-$value";
397
				$record->$field_name = array();
398
				if(is_array($record->$field) && in_array($value, $record->$field) || $record->$field == $value) {
399
					if($explode_settings['explode'] != self::MAIN_CATS) {
400
						$record->$field_name = $options['convert'] ? lang('Yes') : true;
401
					} elseif($options['convert']) {
402
						// 3 part assign due to magic get method
403
						$record_value = $record->$field_name;
404
						$record_value[] = $settings['label'];
405
						$record->$field_name = $record_value;
406
					} else {
407
						$record->$field_name = $value;
408
					}
409
				}
410
				if($explode_settings['explode'] == self::MAIN_CATS && count(array_intersect($record->$field, array_keys($settings['subs'])))) {
411
					// 3 part assign due to magic get method
412
					$record_value = $record->$field_name;
413
					if(!is_array($record_value)) $record_value = array($record_value);
414
					foreach(array_intersect($record->$field, array_keys($settings['subs'])) as $sub_id) {
415
						$record_value[] = $options['convert'] ? $settings['subs'][$sub_id] : $sub_id;
416
					}
417
					$record->$field_name = $record_value;
418
				}
419
				if(is_array($record->$field_name)) $record->$field_name = implode(($options['convert'] ? ', ' : ','), $record->$field_name);
420
			}
421
		}
422
	}
423
424
425
	protected function get_selects()
426
	{
427
		$this->selects = array(
0 ignored issues
show
Bug Best Practice introduced by
The property selects does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
428
			'tid' => array('n' => 'Contact')
429
		);
430
		foreach($this->ui->content_types as $tid => $data)
431
		{
432
			$this->selects['tid'][$tid] = $data['name'];
433
		}
434
	}
435
	/**
436
	 * Get the class name for the egw_record to use while exporting
437
	 *
438
	 * @return string;
439
	 */
440
	public static function get_egw_record_class()
441
	{
442
		return 'addressbook_egw_record';
443
	}
444
445
	/**
446
	 * Adjust automatically generated filter fields
447
	 */
448
	public function get_filter_fields(Array &$filters)
449
    {
450
		unset($filters['last_event']);
451
		unset($filters['next_event']);
452
		foreach($filters as $field_name => &$settings)
453
		{
454
			if($this->selects[$field_name]) $settings['values'] = $this->selects[$field_name];
455
		}
456
		$filters['owner'] = array(
457
			'name'		=> 'owner',
458
			'label'		=> 'addressbook',
459
			'type'		=> 'select',
460
			'rows'		=> 5,
461
			'tags'		=> true,
462
			'values'	=> $this->ui->get_addressbooks(Acl::READ)
463
		);
464
	}
465
}
466