1 | <?php |
||||
2 | /** |
||||
3 | * importexport plugin to import vCard files |
||||
4 | * |
||||
5 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||
6 | * @package importexport |
||||
7 | * @link http://www.egroupware.org |
||||
8 | * @author Nathan Gray |
||||
9 | * @copyright 2012 Nathan Gray |
||||
10 | * @version $Id$ |
||||
11 | */ |
||||
12 | |||||
13 | use EGroupware\Api; |
||||
14 | |||||
15 | /** |
||||
16 | * Plugin to import vCard files |
||||
17 | */ |
||||
18 | class addressbook_import_vcard implements importexport_iface_import_plugin { |
||||
19 | |||||
20 | private static $plugin_options = array( |
||||
21 | |||||
22 | 'contact_owner', |
||||
23 | 'charset' |
||||
24 | ); |
||||
25 | |||||
26 | /** |
||||
27 | * actions wich could be done to data entries |
||||
28 | */ |
||||
29 | protected static $actions = array('insert'); |
||||
30 | |||||
31 | /** |
||||
32 | * conditions for actions |
||||
33 | * |
||||
34 | * @var array |
||||
35 | */ |
||||
36 | protected static $conditions = array( ); |
||||
37 | |||||
38 | /** |
||||
39 | * @var definition |
||||
0 ignored issues
–
show
|
|||||
40 | */ |
||||
41 | private $definition; |
||||
42 | |||||
43 | /** |
||||
44 | * @var bocontacts |
||||
0 ignored issues
–
show
The type
bocontacts was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||
45 | */ |
||||
46 | private $bocontacts; |
||||
47 | |||||
48 | /** |
||||
49 | * For figuring out if a contact has changed |
||||
50 | */ |
||||
51 | protected $tracking; |
||||
52 | |||||
53 | /** |
||||
54 | * @var bool |
||||
55 | */ |
||||
56 | private $dry_run = false; |
||||
57 | private $preview_records = array(); |
||||
58 | |||||
59 | /** |
||||
60 | * @var bool is current user admin? |
||||
61 | */ |
||||
62 | private $is_admin = false; |
||||
63 | |||||
64 | /** |
||||
65 | * @var int |
||||
66 | */ |
||||
67 | private $user = null; |
||||
68 | |||||
69 | /** |
||||
70 | * List of import warnings |
||||
71 | */ |
||||
72 | protected $warnings = array(); |
||||
73 | |||||
74 | /** |
||||
75 | * List of import errors |
||||
76 | */ |
||||
77 | protected $errors = array(); |
||||
78 | |||||
79 | /** |
||||
80 | * List of actions, and how many times that action was taken |
||||
81 | */ |
||||
82 | protected $results = array(); |
||||
83 | |||||
84 | /** |
||||
85 | * imports entries according to given definition object. |
||||
86 | * @param resource $_stream |
||||
87 | * @param string $_charset |
||||
88 | * @param definition $_definition |
||||
89 | */ |
||||
90 | public function import( $_stream, importexport_definition $_definition ) { |
||||
91 | $this->definition = $_definition; |
||||
0 ignored issues
–
show
It seems like
$_definition of type importexport_definition is incompatible with the declared type definition of property $definition .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||||
92 | |||||
93 | // user, is admin ? |
||||
94 | $this->is_admin = isset( $GLOBALS['egw_info']['user']['apps']['admin'] ) && $GLOBALS['egw_info']['user']['apps']['admin']; |
||||
95 | $this->user = $GLOBALS['egw_info']['user']['account_id']; |
||||
96 | |||||
97 | // set contact owner |
||||
98 | $contact_owner = isset( $_definition->plugin_options['contact_owner'] ) ? |
||||
0 ignored issues
–
show
The property
plugin_options does not exist on importexport_definition . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||||
99 | $_definition->plugin_options['contact_owner'] : $this->user; |
||||
100 | // Import into importer's personal addressbook |
||||
101 | if($contact_owner == 'personal') |
||||
102 | { |
||||
103 | $contact_owner = $this->user; |
||||
104 | } |
||||
105 | |||||
106 | // dry run? |
||||
107 | $this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] : false; |
||||
108 | |||||
109 | // Needed for categories to work right |
||||
110 | $GLOBALS['egw_info']['flags']['currentapp'] = 'addressbook'; |
||||
111 | |||||
112 | // fetch the addressbook bo |
||||
113 | $this->bocontacts = new addressbook_vcal(); |
||||
0 ignored issues
–
show
It seems like
new addressbook_vcal() of type addressbook_vcal is incompatible with the declared type bocontacts of property $bocontacts .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..
Loading history...
|
|||||
114 | |||||
115 | $charset = $_definition->plugin_options['charset']; |
||||
116 | if($charset == 'user') $charset = $GLOBALS['egw_info']['user']['preferences']['addressbook']['vcard_charset']; |
||||
117 | |||||
118 | // Start counting successes |
||||
119 | $this->current = 0; |
||||
0 ignored issues
–
show
|
|||||
120 | $count = 0; |
||||
121 | $this->results = array(); |
||||
122 | |||||
123 | // Failures |
||||
124 | $this->errors = array(); |
||||
125 | |||||
126 | // Fix for Apple Addressbook |
||||
127 | $vCard = preg_replace('/item\d\.(ADR|TEL|EMAIL|URL)/', '\1', stream_get_contents($_stream)); |
||||
128 | |||||
129 | $contacts = new Api\CalDAV\IcalIterator($vCard, '', $charset, array($this, '_vcard'),array( |
||||
130 | // Owner (addressbook) |
||||
131 | $contact_owner |
||||
132 | )); |
||||
133 | $contacts->next(); |
||||
134 | while($contacts->valid()) { |
||||
135 | $this->current++; |
||||
136 | $contact = $contacts->current(); |
||||
137 | |||||
138 | $this->action('insert', $contact, $this->current); |
||||
139 | |||||
140 | // Stop if we have enough records for a preview |
||||
141 | if($this->dry_run) |
||||
142 | { |
||||
143 | $egw_record = new addressbook_egw_record(); |
||||
144 | $egw_record->set_record($contact); |
||||
145 | $this->preview_records[] = $egw_record; |
||||
146 | if($count >= $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']) break; |
||||
147 | } |
||||
148 | |||||
149 | $count++; |
||||
150 | $contacts->next(); |
||||
151 | } |
||||
152 | |||||
153 | return $count; |
||||
154 | } |
||||
155 | |||||
156 | /** |
||||
157 | * Changes a vcard object into egw data array |
||||
158 | */ |
||||
159 | public function _vcard($_vcard, $owner) |
||||
160 | { |
||||
161 | $charset = $this->definition->plugin_options['charset']; |
||||
162 | if($charset == 'user') $charset = $GLOBALS['egw_info']['user']['preferences']['addressbook']['vcard_charset']; |
||||
163 | $record = $this->bocontacts->vcardtoegw($_vcard,$charset); |
||||
164 | |||||
165 | $record['owner'] = $owner; |
||||
166 | |||||
167 | // Check that owner (addressbook) is allowed |
||||
168 | if(!array_key_exists($record['owner'], $this->bocontacts->get_addressbooks())) |
||||
169 | { |
||||
170 | $this->errors[$this->current] = lang("Unable to import into %1, using %2", |
||||
171 | Api\Accounts::username($record['owner']), |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with EGroupware\Api\Accounts:...rname($record['owner']) .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.
Loading history...
|
|||||
172 | Api\Accounts::username($this->user) |
||||
173 | ); |
||||
174 | $record['owner'] = $this->user; |
||||
175 | } |
||||
176 | |||||
177 | // Do not allow owner == 0 (accounts) without an account_id |
||||
178 | // It causes the contact to be filed as an account, and can't delete |
||||
179 | if(!$record['owner'] && !$record['account_id']) |
||||
180 | { |
||||
181 | $record['owner'] = $this->user; |
||||
182 | } |
||||
183 | |||||
184 | // Check & apply value overrides |
||||
185 | foreach((array)$this->definition->plugin_options['override_values'] as $field => $settings) |
||||
186 | { |
||||
187 | if($settings['value']) |
||||
188 | { |
||||
189 | $record[$field] = $settings['value']; |
||||
190 | } |
||||
191 | } |
||||
192 | if (is_array($record['cat_id'])) |
||||
193 | { |
||||
194 | $record['cat_id'] = implode(',',$this->bocontacts->find_or_add_categories($record['cat_id'], -1)); |
||||
195 | } |
||||
196 | // Make sure picture is loaded/updated |
||||
197 | if($record['jpegphoto']) |
||||
198 | { |
||||
199 | $record['photo_unchanged'] = false; |
||||
200 | } |
||||
201 | return $record; |
||||
202 | } |
||||
203 | |||||
204 | /** |
||||
205 | * perform the required action |
||||
206 | * |
||||
207 | * @param int $_action one of $this->actions |
||||
208 | * @param array $_data contact data for the action |
||||
209 | * @return bool success or not |
||||
210 | */ |
||||
211 | private function action ( $_action, $_data, $record_num = 0 ) { |
||||
212 | switch ($_action) { |
||||
213 | case 'none' : |
||||
214 | return true; |
||||
215 | case 'update' : |
||||
216 | // Only update if there are changes |
||||
217 | $old = $this->bocontacts->read($_data['id']); |
||||
218 | // if we get countrycodes as countryname, try to translate them -> the rest should be handled by bo classes. |
||||
219 | foreach(array('adr_one_', 'adr_two_') as $c_prefix) |
||||
220 | { |
||||
221 | if (strlen(trim($_data[$c_prefix.'countryname']))==2) |
||||
222 | { |
||||
223 | $_data[$c_prefix.'countryname'] = Api\Country::get_full_name(trim($_data[$c_prefix.'countryname']), true); |
||||
224 | } |
||||
225 | } |
||||
226 | // Don't change a user account into a contact |
||||
227 | if($old['owner'] == 0) { |
||||
228 | unset($_data['owner']); |
||||
229 | } elseif(!$this->definition->plugin_options['change_owner']) { |
||||
230 | // Don't change addressbook of an existing contact |
||||
231 | unset($_data['owner']); |
||||
232 | } |
||||
233 | |||||
234 | // Merge to deal with fields not in import record |
||||
235 | $_data = array_merge($old, $_data); |
||||
236 | $changed = $this->tracking->changed_fields($_data, $old); |
||||
237 | if(count($changed) == 0) { |
||||
238 | return true; |
||||
239 | } else { |
||||
240 | //error_log(__METHOD__.__LINE__.array2string($changed).' Old:'.$old['adr_one_countryname'].' ('.$old['adr_one_countrycode'].') New:'.$_data['adr_one_countryname'].' ('.$_data['adr_one_countryname'].')'); |
||||
241 | } |
||||
242 | |||||
243 | // Make sure n_fn gets updated |
||||
244 | unset($_data['n_fn']); |
||||
245 | |||||
246 | // Fall through |
||||
247 | case 'insert' : |
||||
248 | if($_action == 'insert') { |
||||
249 | // Addressbook backend doesn't like inserting with ID specified, it screws up the owner & etag |
||||
250 | unset($_data['id']); |
||||
251 | } |
||||
252 | if(!isset($_data['org_name'])) { |
||||
253 | // org_name is a trigger to update n_fileas |
||||
254 | $_data['org_name'] = ''; |
||||
255 | } |
||||
256 | |||||
257 | if ( $this->dry_run ) { |
||||
258 | //print_r($_data); |
||||
259 | $this->results[$_action]++; |
||||
260 | return true; |
||||
261 | } else { |
||||
262 | $result = $this->bocontacts->save( $_data, $this->is_admin); |
||||
263 | if(!$result) { |
||||
264 | $this->errors[$record_num] = $this->bocontacts->error; |
||||
265 | } else { |
||||
266 | $this->results[$_action]++; |
||||
267 | } |
||||
268 | return $result; |
||||
269 | } |
||||
270 | default: |
||||
271 | throw new Api\Exception('Unsupported action: '. $_action); |
||||
272 | |||||
273 | } |
||||
274 | } |
||||
275 | |||||
276 | public function preview( $_stream, importexport_definition $_definition ) |
||||
277 | { |
||||
278 | $rows = array('h1'=>array(),'f1'=>array(),'.h1'=>'class=th'); |
||||
279 | |||||
280 | // Set this so plugin doesn't do any data changes |
||||
281 | $_definition->plugin_options = (array)$_definition->plugin_options + array('dry_run' => true); |
||||
0 ignored issues
–
show
The property
plugin_options does not exist on importexport_definition . Since you implemented __get , consider adding a @property annotation.
Loading history...
The property
plugin_options does not exist on importexport_definition . Since you implemented __set , consider adding a @property annotation.
Loading history...
|
|||||
282 | |||||
283 | $this->import($_stream, $_definition); |
||||
284 | rewind($_stream); |
||||
285 | |||||
286 | // Get field labels |
||||
287 | $rows['h1'] = $labels = $this->bocontacts->contact_fields; |
||||
288 | |||||
289 | $record_class = get_class($this->preview_records[0]); |
||||
290 | |||||
291 | foreach($this->preview_records as $record) |
||||
292 | { |
||||
293 | // Convert to human-friendly |
||||
294 | importexport_export_csv::convert($record,$record_class::$types,$_definition->application); |
||||
0 ignored issues
–
show
The property
application does not exist on importexport_definition . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||||
295 | $record = $record->get_record_array(); |
||||
296 | $row = array(); |
||||
297 | foreach(array_keys($labels) as $field) |
||||
298 | { |
||||
299 | $row[$field] = $record[$field]; |
||||
300 | |||||
301 | // Don't scare users, do something with jpeg |
||||
302 | if($field == 'jpegphoto' && $row[$field]) |
||||
303 | { |
||||
304 | $row[$field] = '<img style="max-width:50px;max-height:50px;" src="data:image/jpeg;base64,'.$row[$field].'"/>'; |
||||
305 | } |
||||
306 | unset($record[$field]); |
||||
307 | } |
||||
308 | $row += $record; |
||||
309 | $rows[] = $row; |
||||
310 | } |
||||
311 | return Api\Html::table($rows); |
||||
312 | } |
||||
313 | |||||
314 | /** |
||||
315 | * returns translated name of plugin |
||||
316 | * |
||||
317 | * @return string name |
||||
318 | */ |
||||
319 | public static function get_name() { |
||||
320 | return lang('Addressbook vCard import'); |
||||
321 | } |
||||
322 | |||||
323 | /** |
||||
324 | * returns translated (user) description of plugin |
||||
325 | * |
||||
326 | * @return string descriprion |
||||
327 | */ |
||||
328 | public static function get_description() { |
||||
329 | return lang("Imports contacts into your Addressbook from a vCard File. "); |
||||
330 | } |
||||
331 | |||||
332 | /** |
||||
333 | * retruns file suffix(s) plugin can handle (e.g. csv) |
||||
334 | * |
||||
335 | * @return string suffix (comma seperated) |
||||
336 | */ |
||||
337 | public static function get_filesuffix() { |
||||
338 | return 'vcf'; |
||||
339 | } |
||||
340 | |||||
341 | /** |
||||
342 | * return etemplate components for options. |
||||
343 | * @abstract We can't deal with etemplate objects here, as an uietemplate |
||||
344 | * objects itself are scipt orientated and not "dialog objects" |
||||
345 | * |
||||
346 | * @return array ( |
||||
347 | * name => string, |
||||
348 | * content => array, |
||||
349 | * sel_options => array, |
||||
350 | * preserv => array, |
||||
351 | * ) |
||||
352 | */ |
||||
353 | public function get_options_etpl(importexport_definition &$definition=null) |
||||
354 | { |
||||
355 | $charset = $definition->plugin_options['charset']; |
||||
0 ignored issues
–
show
The property
plugin_options does not exist on importexport_definition . Since you implemented __get , consider adding a @property annotation.
Loading history...
|
|||||
356 | if($charset == 'user') $charset = $GLOBALS['egw_info']['user']['preferences']['addressbook']['vcard_charset']; |
||||
357 | return array( |
||||
358 | 'name' => 'addressbook.import_vcard', |
||||
359 | 'content' => array( |
||||
360 | 'file_type' => 'vcard,ical,vcf', |
||||
361 | 'charset' => $charset |
||||
362 | ), |
||||
363 | 'sel_options' => array( |
||||
364 | 'charset' => Api\Translation::get_installed_charsets() |
||||
365 | ), |
||||
366 | 'preserv' => array() |
||||
367 | ); |
||||
368 | } |
||||
369 | |||||
370 | /** |
||||
371 | * returns etemplate name for slectors of this plugin |
||||
372 | * |
||||
373 | * @return string etemplate name |
||||
374 | */ |
||||
375 | public function get_selectors_etpl() { |
||||
376 | // lets do it! |
||||
377 | } |
||||
378 | |||||
379 | /** |
||||
380 | * Returns warnings that were encountered during importing |
||||
381 | * Maximum of one warning message per record, but you can append if you need to |
||||
382 | * |
||||
383 | * @return Array ( |
||||
384 | * record_# => warning message |
||||
385 | * ) |
||||
386 | */ |
||||
387 | public function get_warnings() { |
||||
388 | return $this->warnings; |
||||
389 | } |
||||
390 | |||||
391 | /** |
||||
392 | * Returns errors that were encountered during importing |
||||
393 | * Maximum of one error message per record, but you can append if you need to |
||||
394 | * |
||||
395 | * @return Array ( |
||||
396 | * record_# => error message |
||||
397 | * ) |
||||
398 | */ |
||||
399 | public function get_errors() { |
||||
400 | return $this->errors; |
||||
401 | } |
||||
402 | |||||
403 | /** |
||||
404 | * Returns a list of actions taken, and the number of records for that action. |
||||
405 | * Actions are things like 'insert', 'update', 'delete', and may be different for each plugin. |
||||
406 | * |
||||
407 | * @return Array ( |
||||
408 | * action => record count |
||||
409 | * ) |
||||
410 | */ |
||||
411 | public function get_results() { |
||||
412 | return $this->results; |
||||
413 | } |
||||
414 | } |
||||
415 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths