Completed
Push — 16.1 ( 1e4888...9df24e )
by Ralf
12:06
created

Vfs   C

Complexity

Total Complexity 64

Size/Duplication

Total Lines 314
Duplicated Lines 7.01 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
dl 22
loc 314
rs 5.8364
c 0
b 0
f 0
wmc 64
lcom 1
cbo 9

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 2
D beforeSendToClient() 16 75 22
A ajax_upload() 0 8 3
C ajax_htmlarea_upload() 6 52 8
C fix_html_dragins() 0 25 8
A get_temp_dir() 0 6 2
D store_file() 0 44 9
B validate() 0 27 5
B get_vfs_path() 0 16 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Vfs often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Vfs, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * EGroupware - eTemplate serverside vfs 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 2011 Nathan Gray
11
 * @version $Id$
12
 */
13
14
namespace EGroupware\Api\Etemplate\Widget;
15
16
use EGroupware\Api\Etemplate;
17
use EGroupware\Api;
18
19
/**
20
 * eTemplate VFS widget
21
 * Deals with the Virtual File System
22
 */
23
class Vfs extends File
24
{
25
	// Legacy option for vfs-upload
26
	protected $legacy_options = "mime";
27
28
	public function __construct($xml='')
29
	{
30
		if($xml) parent::__construct($xml);
31
	}
32
33
	/**
34
	 * If widget is a vfs-file widget, and there are files in the specified directory,
35
	 * they should be displayed.
36
	 */
37
	public function beforeSendToClient($cname, $expand = array())
38
	{
39
		if($this->type == 'vfs-upload' || $this->attrs['type'] == 'vfs-upload')
40
		{
41
			$form_name = self::form_name($cname, $this->id, $expand ? $expand : array('cont'=>self::$request->content));
42
			if($this->attrs['path'])
43
			{
44
				$path = $this->attrs['path'];
45
			}
46
			else
47
			{
48
				$path = $this->id;
49
			}
50
51
			$this->setElementAttribute($form_name, 'path', $path);
52
			// ID maps to path - check there for any existing files
53
			list($app,$id,$relpath) = explode(':',$path,3);
54
			if($app && $id)
55
			{
56
				if(!is_numeric($id))
57
				{
58
					$_id = self::expand_name($id,0,0,0,0,self::$request->content);
59
					if($_id != $id)
60
					{
61
						$id = $_id;
62
						$form_name = "$app:$id:$relpath";
63
					}
64
				}
65
				$value =& self::get_array(self::$request->content, $form_name, true);
66
				$path = Api\Link::vfs_path($app,$id,'',true);
67
				if (!empty($relpath)) $path .= '/'.$relpath;
68
69
				if (true) $value = array();
70
71
				// Single file, already existing
72
				if (substr($path,-1) != '/' && Api\Vfs::file_exists($path) && !Api\Vfs::is_dir($path))
73
				{
74
					$file = Api\Vfs::stat($path);
75
					$file['path'] = $path;
76
					$file['name'] = Api\Vfs::basename($file['path']);
0 ignored issues
show
Bug introduced by
It seems like $file['path'] can also be of type boolean; however, EGroupware\Api\Vfs::basename() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
77
					$file['mime'] = Api\Vfs::mime_content_type($file['path']);
78
					$value = array($file);
79
				}
80
				// Single file, missing extension in path
81
				else if (substr($path, -1) != '/' && !Api\Vfs::file_exists($path) && $relpath && substr($relpath,-4,1) !== '.')
82
				{
83
					$find = Api\Vfs::find(substr($path,0, - strlen($relpath)), array(
84
						'type' => 'f',
85
						'maxdepth' => 1,
86
						'name' => $relpath . '*'
87
					));
88 View Code Duplication
					foreach($find as $file)
89
					{
90
						$file_info = Api\Vfs::stat($file);
91
						$file_info['path'] = $file;
92
						$file_info['name'] = Api\Vfs::basename($file_info['path']);
93
						$file_info['mime'] = Api\Vfs::mime_content_type($file_info['path']);
94
						$value[] = $file_info;
95
					}
96
				}
97
				else if (substr($path, -1) == '/' && Api\Vfs::is_dir($path))
98
				{
99
					$scan = Api\Vfs::scandir($path);
100 View Code Duplication
					foreach($scan as $file)
101
					{
102
						$file_info = Api\Vfs::stat("$path$file");
103
						$file_info['path'] = "$path$file";
104
						$file_info['name'] = Api\Vfs::basename($file_info['path']);
105
						$file_info['mime'] = Api\Vfs::mime_content_type($file_info['path']);
106
						$value[] = $file_info;
107
					}
108
				}
109
			}
110
		}
111
	}
112
113
	public static function ajax_upload()
114
	{
115
		parent::ajax_upload();
116
		foreach($_FILES as $file)
117
		{
118
			self::store_file($_REQUEST['path'] ? $_REQUEST['path'] : $_REQUEST['widget_id'], $file);
119
		}
120
	}
121
122
	/**
123
	 * Upload via dragging images into ckeditor
124
	 */
125
	public static function ajax_htmlarea_upload()
126
	{
127
		$request_id = urldecode($_REQUEST['request_id']);
128
		$widget_id = $_REQUEST['widget_id'];
129
		if(!self::$request = Etemplate\Request::read($request_id))
130
		{
131
			$error = lang("Could not read session");
132
		}
133 View Code Duplication
		elseif (!($template = Template::instance(self::$request->template['name'], self::$request->template['template_set'],
134
			self::$request->template['version'], self::$request->template['load_via'])))
135
		{
136
			// Can't use callback
137
			$error = lang("Could not get template for file upload, callback skipped");
138
		}
139
		elseif (!isset($_FILES['upload']))
140
		{
141
			$error = lang('No _FILES[upload] found!');
142
		}
143
		else
144
		{
145
			$data = self::$request->content[$widget_id];
146
			$path = self::store_file($path = (!is_array($data) && $data[0] == '/' ? $data :
147
				self::get_vfs_path($data['to_app'].':'.$data['to_id'])).'/', $_FILES['upload']);
148
149
			// store temp. vfs-path like links to be able to move it to correct location after entry is stored
150
			if (!$data['to_id'] || is_array($data['to_id']))
151
			{
152
				Api\Link::link($data['to_app'], $data['to_id'], Api\Link::VFS_APPNAME, array(
153
					'name' => $_FILES['upload']['name'],
154
					'type' => $_FILES['upload']['type'],
155
					'tmp_name' => Api\Vfs::PREFIX.$path,
156
				));
157
				self::$request->content = array_merge(self::$request->content, array($widget_id => $data));
158
			}
159
		}
160
		// switch regular JSON response handling off
161
		Api\Json\Request::isJSONRequest(false);
162
163
		$file = array(
164
			"uploaded" => (int)empty($error),
165
			"fileName" => Api\Html::htmlspecialchars($_FILES['upload']['name']),
166
			"url" => Api\Framework::link(Api\Vfs::download_url($path)),
167
			"error" => array(
168
				"message" => $error,
169
			)
170
		);
171
172
		header('Content-Type: application/json; charset=utf-8');
173
		echo json_encode($file);
174
175
		exit;
176
	}
177
178
	/**
179
	 * Fix source/url of dragged in images in html
180
	 *
181
	 * @param string $app
182
	 * @param int|string $id
183
	 * @param array $links
184
	 * @param string& $html
185
	 * @return boolean true if something was fixed and $html needs to be stored
186
	 */
187
	static function fix_html_dragins($app, $id, array $links, &$html)
188
	{
189
		$replace = $remove_dir = array();
190
		foreach($links as $link)
191
		{
192
			$matches = null;
193
			if (is_array($link) && preg_match('|^'.preg_quote(Api\Vfs::PREFIX,'|').'('.preg_quote(self::get_temp_dir($app, ''), '|').'[^/]+)/|', $link['id']['tmp_name'], $matches))
194
			{
195
				$replace[substr($link['id']['tmp_name'], strlen(Api\Vfs::PREFIX))] =
196
					Api\Link::vfs_path($app, $id, Api\Vfs::basename($link['id']['tmp_name']), true);
197
198
				if (!in_array($matches[1], $remove_dir)) $remove_dir[] = $matches[1];
199
			}
200
		}
201
		if ($replace)
202
		{
203
			$html = strtr($old = $html, $replace);
204
			// remove all dirs
205
			foreach($remove_dir as $dir)
206
			{
207
				Api\Vfs::remove($dir);
208
			}
209
		}
210
		return isset($old) && $old != $html;
211
	}
212
213
	/**
214
	 * Generate a temp. directory for htmlarea uploads: /home/$user/.tmp/$app_$postfix
215
	 *
216
	 * @param string $app app-name
217
	 * @param string $postfix =null default random id
218
	 * @return string vfs path
219
	 */
220
	static function get_temp_dir($app, $postfix=null)
221
	{
222
		if (!isset($postfix)) $postfix = md5(time().session_id());
223
224
		return '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp/'.$app.'_'.$postfix;
225
	}
226
227
	/**
228
	* Ajax callback to receive an incoming file
229
	*
230
	* The incoming file is automatically placed into the appropriate VFS location.
231
	* If the entry is not yet created, the file information is stored into the widget's value.
232
	* When the form is submitted, the information for all files uploaded is available in the returned
233
	* $content array and the application should deal with the file.
234
	*/
235
	public static function store_file($path, $file)
236
	{
237
		$name = $_REQUEST['widget_id'];
238
239
		// Find real path
240
		if($path[0] != '/')
241
		{
242
			$path = self::get_vfs_path($path);
243
		}
244
		$filename = $file['name'];
245
		if (substr($path,-1) != '/')
246
		{
247
			// add extension to path
248
			$parts = explode('.',$filename);
249
			if (($extension = array_pop($parts)) && Api\MimeMagic::ext2mime($extension))       // really an extension --> add it to path
250
			{
251
				$path .= '.'.$extension;
252
			}
253
		}
254
		else    // multiple upload with dir given (trailing slash)
255
		{
256
			$path .= Api\Vfs::encodePathComponent($filename);
257
		}
258
		if (!($dir = Api\Vfs::dirname($path)))
259
		{
260
			self::set_validation_error($name,lang('Error create parent directory %1!', "dirname('$path') === false"));
261
			return false;
262
		}
263
		if (!Api\Vfs::file_exists($dir) && !Api\Vfs::mkdir($dir,null,STREAM_MKDIR_RECURSIVE))
264
		{
265
			self::set_validation_error($name,lang('Error create parent directory %1!',Api\Vfs::decodePath($dir)));
266
			return false;
267
		}
268
		if (!copy($file['tmp_name'],Api\Vfs::PREFIX.$path))
269
		{
270
			self::set_validation_error($name,lang('Error copying uploaded file to vfs!'));
271
			return false;
272
		}
273
274
		// Try to remove temp file
275
		unlink($file['tmp_name']);
276
277
		return $path;
278
	}
279
280
	/**
281
	 * Validate input
282
	 * Merge any already uploaded files into the content array
283
	 *
284
	 * @param string $cname current namespace
285
	 * @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
286
	 * @param array $content
287
	 * @param array &$validated=array() validated content
288
	 */
289
	public function validate($cname, array $expand, array $content, &$validated=array())
290
	{
291
		// do not validate, as it would overwrite preserved values with null!
292
		if (in_array($this->type, array('vfs-size', 'vfs-uid', 'vfs-gid', 'vfs', 'vfs-mime')))
293
		{
294
			return;
295
		}
296
		$form_name = self::form_name($cname, $this->id, $expand);
297
		$value = $value_in = self::get_array($content, $form_name);
298
		$valid =& self::get_array($validated, $form_name, true);
299
300
		switch($this->type)
301
		{
302
			case 'vfs-upload':
303
				if(!is_array($value)) $value = array();
304
				/* Check & skip files that made it asyncronously
305
				list($app,$id,$relpath) = explode(':',$this->id,3);
306
				//...
307
				foreach($value as $tmp => $file)
308
				{
309
					if(Api\Vfs::file_exists(self::get_vfs_path($id) . $relpath)) {}
310
				}*/
311
				parent::validate($cname, $content, $validated);
312
				break;
313
		}
314
		if (true) $valid = $value;
315
	}
316
317
	/**
318
	 * Change an ID like app:id:relative/path to an actual VFS location
319
	 */
320
	public static function get_vfs_path($path)
321
	{
322
		list($app,$id,$relpath) = explode(':',$path,3);
323
		if (empty($id) || $id == 'undefined')
324
		{
325
			static $tmppath = array();      // static var, so all vfs-uploads get created in the same temporary dir
326
			if (!isset($tmppath[$app])) $tmppath[$app] = self::get_temp_dir ($app);
327
			$path = $tmppath[$app];
328
		}
329
		else
330
		{
331
			$path = Api\Link::vfs_path($app,$id,'',true);
332
		}
333
		if (!empty($relpath)) $path .= '/'.$relpath;
334
		return $path;
335
	}
336
}
337