beforeSendToClient()   F
last analyzed

Complexity

Conditions 18
Paths 252

Size

Total Lines 101
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
cc 18
eloc 51
c 2
b 0
f 2
nc 252
nop 2
dl 0
loc 101
rs 3.3833

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * EGroupware - eTemplate serverside of owner list widget
4
 *
5
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
6
 * @package etemplate
7
 * @subpackage api
8
 * @link http://www.egroupware.org
9
 * @author Nathan Gray
10
 * @copyright 2016 Nathan Gray
11
 * @version $Id$
12
 */
13
14
use EGroupware\Api;
15
use EGroupware\Api\Link;
16
use EGroupware\Api\Framework;
17
use EGroupware\Api\Etemplate;
18
19
/**
20
 * eTemplate tag list widget
21
 *
22
 * The naming convention is <appname>_<subtype>_etemplate_widget
23
 */
24
class calendar_owner_etemplate_widget extends Etemplate\Widget\Taglist
25
{
26
27
	/**
28
	 *  Make sure all the needed select options are there
29
	 *
30
	 * @param string $cname
31
	 * @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
32
	 */
33
	public function beforeSendToClient($cname, array $expand=null)
34
	{
35
36
		Framework::includeJS('.','et2_widget_owner','calendar');
37
		Framework::includeCSS('calendar','calendar');
38
39
		$bo = new calendar_bo();
40
41
		$form_name = self::form_name($cname, $this->id, $expand);
42
43
		$value =& self::get_array(self::$request->content, $form_name);
44
45
		if (!is_array(self::$request->sel_options[$form_name]))
46
		{
47
			self::$request->sel_options[$form_name] = array();
48
		}
49
		$sel_options =& self::$request->sel_options[$form_name];
50
51
		// Get user accounts, formatted nicely for grouping and matching
52
		// the ajax call calendar_uiforms->ajax_owner() - users first
53
		$accounts = array();
54
		$list = array('accounts', 'owngroups');
55
		foreach($list as $type)
56
		{
57
			$account_options = array('account_type' => $type);
58
			$accounts_type = Api\Accounts::link_query('',$account_options);
59
			if($type == 'accounts')
60
			{
61
				$accounts_type = array_intersect_key($accounts_type, $GLOBALS['egw']->acl->get_grants('calendar'));
62
			}
63
			$accounts += $accounts_type;
64
		}
65
		$sel_options += array_map(
66
			function($account_id, $account_name)
67
			{
68
				$data = array(
69
					'value' => ''.$account_id,
70
					'label' => $account_name,
71
					'app' => lang('api-accounts'),
72
				);
73
				if ($account_id > 0)
74
				{
75
					$contact_obj = new Api\Contacts();
76
					if (($contact = $contact_obj->read('account:'.$account_id, true)))
77
					{
78
						$data['icon'] = Api\Framework::link('/api/avatar.php', array(
79
							'contact_id' => $contact['id'],
80
							'etag' => $contact['etag'] ? $contact['etag'] : 1
81
						));
82
					}
83
				}
84
				else
85
				{
86
					// Add in group memberships as strings
87
					$data['resources'] = array_map(function($a) { return ''.$a;},$GLOBALS['egw']->accounts->members($account_id, true));
88
				}
89
				return $data;
90
			},
91
			array_keys($accounts), $accounts
92
		);
93
94
		if(!is_array($value))
95
		{
96
			// set value with an empty string only if sel options are not
97
			// loaded, for example: setting calendar owner via URL when
98
			// calendar app is not yet loaded.
99
			$value = !empty($sel_options) ? array(): explode(',', $value);
100
		}
101
102
		// Add external owners that a select account widget will not find
103
		foreach($value as &$owner)
104
		{
105
			$label = self::get_owner_label($owner);
106
			$info = array();
107
			if(!is_numeric($owner))
108
			{
109
				$resource = $bo->resources[substr($owner, 0,1)];
110
				if($resource['info'] && !($info = $bo->resource_info($owner)))
111
				{
112
					continue;	// ignore that resource, we would get a PHP Fatal: Unsupported operand types
113
				}
114
			}
115
			else if (!in_array($owner, array_keys($accounts)))
116
			{
117
				$resource = array('app'=> 'api-accounts');
118
			}
119
			if ($resource && is_numeric ($owner) && (int)$owner < 0)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $resource seems to be defined later in this foreach loop on line 109. Are you sure it is defined here?
Loading history...
120
			{
121
				// Add in group memberships as strings
122
				$info['resources'] = array_map(function($a) { return ''.$a;},$GLOBALS['egw']->accounts->members($owner, true));
123
			}
124
125
			$option = array('value' => $owner, 'label' => $label, 'app' => lang($resource['app'])) + $info;
126
			$sel_option_index = $this->get_index($sel_options, 'value', $owner);
127
			if($sel_option_index === false)
128
			{
129
				$sel_options[] = $option;
130
			}
131
			else
132
			{
133
				$sel_options[$sel_option_index] = array_merge($sel_options[$sel_option_index], $option);
134
			}
135
		}
136
	}
137
138
	/**
139
	 * Get the index of an array (sel_options) containing the given value
140
	 *
141
	 * @param Array $array
142
	 * @param string $key key we're checking to match value
143
	 * @param string $value Value we're looking for
144
	 * @return boolean|int Returns index
145
	 */
146
	private function get_index(&$array, $key, $value)
147
	{
148
		foreach($array as $_key => $_value)
149
		{
150
			if($_value[$key] === $value) return $_key;
151
		}
152
		return false;
153
	}
154
	/**
155
	 * Validate input
156
	 *
157
	 * @param string $cname current namespace
158
	 * @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
159
	 * @param array $content
160
	 * @param array &$validated=array() validated content
161
	 */
162
	public function validate($cname, array $expand, array $content, &$validated=array())
163
	{
164
		$form_name = self::form_name($cname, $this->id, $expand);
165
166
		if (!$this->is_readonly($cname, $form_name))
167
		{
168
			$value = $value_in =& self::get_array($content, $form_name);
0 ignored issues
show
Unused Code introduced by
The assignment to $value_in is dead and can be removed.
Loading history...
169
			if(!is_array($value))
170
			{
171
				$value = Array($value);
172
			}
173
174
			$valid =& self::get_array($validated, $form_name, true);
175
			if (true) $valid = $value;
176
		}
177
	}
178
	/**
179
	 * Handle ajax searches for owner across all supported resources
180
	 *
181
	 * @return Array List of matching results
182
	 */
183
	public static function ajax_owner($id = null)
184
	{
185
		// Handle a request for a single ID
186
		if($id)
187
		{
188
			$label = self::get_owner_label($id);
189
			Api\Json\Response::get()->data($label);
190
			return $label;
191
		}
192
193
		$bo = new calendar_bo();
194
		$query = $_REQUEST['query'];
195
196
		// Arbitrarily limited to 50 / resource
197
		$options = array('start' => 0, 'num_rows' => 50,
198
			// Filter accounts out of addressbook
199
			'filter' => array('account_id' => null)) +
200
			array_diff_key($_REQUEST, array_flip(array('menuaction','query')));
201
		$results = array();
202
203
		// Contacts matching accounts the user does not have permission for cause
204
		// confusion as user selects the contact and there's nothing there, so
205
		// we remove those contacts
206
		$remove_contacts = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $remove_contacts is dead and can be removed.
Loading history...
207
208
		$resources = array_merge(array('' => $bo->resources['']),$bo->resources);
209
		$contacts_obj = new Api\Contacts();
210
		foreach($resources as $type => $data)
211
		{
212
			$mapped = array();
213
			$_results = array();
214
215
			// Handle Api\Accounts seperately
216
			if($type == '')
217
			{
218
				$account_options = $options + array('account_type' => 'both');
219
				$_results += $remove_contacts = Api\Accounts::link_query($query, $account_options);
220
				if (!empty($_REQUEST['checkgrants']))
221
				{
222
					$grants = $GLOBALS['egw']->acl->get_grants('calendar');
223
					$_results = array_intersect_key($_results, $grants);
224
				}
225
			}
226
			// App provides a custom search function
227
			else if ($data['app'] && $data['search'])
228
			{
229
				$_results = call_user_func_array($data['search'], array($query, $options));
230
			}
231
			// Use standard link registry
232
			else if ($data['app'] && Link::get_registry($data['app'], 'query'))
233
			{
234
				$_results = Link::query($data['app'], $query,$options);
235
			}
236
237
			// There are always special cases
238
			switch ($type)
239
			{
240
				case 'l':
241
					// Include mailing lists
242
					$lists = array_filter(
243
						$contacts_obj->get_lists(Api\Acl::READ),
244
						function($element) use($query) {
245
							return (stripos($element, $query) !== false);
246
						}
247
					);
248
					foreach($lists as $list_id => $list)
249
					{
250
						$_results[$list_id] = array(
251
							'label' => $list,
252
							'resources' => $bo->enum_mailing_list($type.$list_id)
253
						);
254
					}
255
					break;
256
			}
257
			if(!$_results)
258
			{
259
				continue;
260
			}
261
262
			foreach(array_unique($_results, SORT_REGULAR) as $id => $title)
263
			{
264
				if($id && $title)
265
				{
266
					// Magicsuggest uses id, not value.
267
					$value = array(
268
						'id' => $type.$id,
269
						'value'=> $type.$id,
270
						'label' => $title,
271
						'app'	=> lang($data['app'])
272
					);
273
					if(is_array($value['label']))
274
					{
275
						$value = array_merge($value, $value['label']);
276
					}
277
					switch($type)
278
					{
279
						case 'r':
280
							// TODO: fetch resources photo
281
							break;
282
						case 'c':
283
						case '':
284
							$contact = $contacts_obj->read($type === '' ? 'account:'.$id : $id, true);
285
							if (is_array($contact)) $value['icon'] = Api\Framework::link('/api/avatar.php', array(
286
								'contact_id' => $contact['id'],
287
								'etag' => $contact['etag'] ? $contact['etag'] : 1
288
							));
289
							if($id < 0)
290
							{
291
								$value['resources'] = $GLOBALS['egw']->accounts->members($id, true);
292
							}
293
							break;
294
						default :
295
							// do nothing
296
					}
297
					$mapped[] = $value;
298
				}
299
			}
300
			if(count($mapped))
301
			{
302
				$results = array_merge($results, $mapped);
303
			}
304
		}
305
306
		// switch regular JSON response handling off
307
		Api\Json\Request::isJSONRequest(false);
308
309
		header('Content-Type: application/json; charset=utf-8');
310
		echo json_encode($results);
311
		exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
312
	}
313
314
	/**
315
	 * Get just the label for a single owner
316
	 * @param string $id
317
	 */
318
	public static function get_owner_label($id)
319
	{
320
		static $bo=null;
321
		if(!$bo) $bo = new calendar_bo();
322
323
		$id = ''.$id;
324
		if(!is_numeric($id))
325
		{
326
			$resource = $bo->resources[substr($id, 0,1)];
327
			$label = Link::title($resource['app'], substr($id,1));
328
329
			// Could not get via link, try via resources info
330
			if($label === false)
331
			{
332
				$info = ExecMethod($resource['info'], substr($id,1));
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod() has been deprecated: use autoloadable class-names, instanciate and call method or use static methods ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

332
				$info = /** @scrutinizer ignore-deprecated */ ExecMethod($resource['info'], substr($id,1));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
333
				$label = $info[0]['name'];
334
			}
335
		}
336
		else
337
		{
338
			$label = Link::title('api-accounts',$id) ?: Api\Accounts::username($id);
339
		}
340
		return $label;
341
	}
342
}