Html::fckEditor()   B
last analyzed

Complexity

Conditions 7
Paths 9

Size

Total Lines 135
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 44
nc 9
nop 10
dl 0
loc 135
rs 8.2826
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * EGroupware API: generates html with methods representing html-tags or higher widgets
4
 *
5
 * @link http://www.egroupware.org
6
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> complete rewrite in 6/2006 and earlier modifications
7
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
8
 * @author RalfBecker-AT-outdoor-training.de
9
 * @copyright 2001-2016 by [email protected]
10
 * @package api
11
 * @subpackage html
12
 * @version $Id$
13
 */
14
15
namespace EGroupware\Api;
16
use EGroupware\Api\Header\ContentSecurityPolicy;
17
/**
18
 * Generates html with methods representing html-tags or higher widgets
19
 *
20
 * The class has only static methods now, so there's no need to instanciate as object anymore!
21
 */
22
class Html
23
{
24
	/**
25
	 * Automatically turn on enhanced selectboxes if there's more than this many options
26
	 */
27
	const SELECT_ENHANCED_ROW_COUNT = 12;
28
29
	/**
30
	 * activates URLs in a text, URLs get replaced by html-links
31
	 *
32
	 * @param string $content text containing URLs
33
	 * @return string html with activated links
34
	 */
35
	static function activate_links($content)
36
	{
37
		if (!$content || strlen($content) < 20) return $content;	// performance
38
39
		// Exclude everything which is already a link
40
		$NotAnchor = '(?<!"|href=|href\s=\s|href=\s|href\s=)';
41
42
		// spamsaver emailaddress
43
		$result = preg_replace('/'.$NotAnchor.'mailto:([a-z0-9._-]+)@([a-z0-9_-]+)\.([a-z0-9._-]+)/i',
44
			"<a href=\"mailto:$1@$2.$3\" target=\"_blank\">$1 AT $2 DOT $3</a>",
45
			$content);
46
47
		//  First match things beginning with http:// (or other protocols)
48
		$optBracket0 = '(<|&lt;)';
49
		$Protocol = '(http:\/\/|(ftp:\/\/|https:\/\/))';	// only http:// gets removed, other protocolls are shown
50
		$Domain = '([\w-]+\.[\w\-.]+)';
51
		$Subdir = '([\w\-\.,@?^=%&;:\/~\+#]*[\w\-\@?^=%&\/~\+#])?';
52
		$optBracket = '(>|&gt;)';
53
		$Expr = '/' .$optBracket0. $NotAnchor . $Protocol . $Domain . $Subdir . $optBracket . '/i';
54
		// use preg_replace_callback as we experienced problems with https links
55
		$result2 = preg_replace_callback($Expr, function ($match)
56
		{
57
			return $match[1]."<a href=\"".($match[2]&&!$match[3]?$match[2]:'').($match[3]?$match[3]:'').$match[4].$match[5]."\" target=\"_blank\">".$match[4].$match[5]."</a>".$match[6];
58
		}, $result);
59
60
		if (true)	// hack to keep IDE from complaing about double assignments
61
		{
62
			//  First match things beginning with http:// (or other protocols)
63
			$Protocol = '(http:\/\/|(ftp:\/\/|https:\/\/))';	// only http:// gets removed, other protocolls are shown
64
			$Domain = '([\w-]+\.[\w\-.]+)';
65
			$Subdir = '([\w\-\.,@?^=%&;:\/~\+#]*[\w\-\@?^=%&\/~\+#])?';
66
			$optStuff = '(&quot;|&quot|;)?';
67
			$Expr = '/' . $NotAnchor . $Protocol . $Domain . $Subdir . $optStuff . '/i';
68
			// use preg_replace_callback as we experienced problems with https links
69
			$result3 = preg_replace_callback($Expr, function ($match)
70
			{
71
				$additionalQuote="";//at the end, ...
72
				// only one &quot at the end is found. chance is, it is not belonging to the URL
73
				if ($match[5]==';' && (strlen($match[4])-6) >=0 && strpos($match[4],'&quot',strlen($match[4])-6)!==false && strpos(substr($match[4],0,strlen($match[4])-6),'&quot')===false)
74
				{
75
					$match[4] = substr($match[4],0,strpos($match[4],'&quot',strlen($match[4])-6));
76
					$additionalQuote = "&quot;";
77
				}
78
				// if there is quoted stuff within the URL then we have at least one more &quot; in match[4], so chance is the last &quot is matched by the one within
79
				if ($match[5]==';' && (strlen($match[4])-6) >=0 && strpos($match[4],'&quot',strlen($match[4])-6)!==false && strpos(substr($match[4],0,strlen($match[4])-6),'&quot')!==false)
80
				{
81
					$match[4] .= $match[5];
82
				}
83
				if ($match[5]==';'&&$match[4]=="&quot")
84
				{
85
					$match[4] ='';
86
					$additionalQuote = "&quot;";
87
				}
88
				//error_log(__METHOD__.__LINE__.array2string($match));
89
				return "<a href=\"".($match[1]&&!$match[2]?$match[1]:'').($match[2]?$match[2]:'').$match[3].$match[4]."\" target=\"_blank\">".$match[3].$match[4]."</a>$additionalQuote";
90
			}, $result2);
91
92
			//  Now match things beginning with www.
93
			$optBracket0 = '(<|&lt;)?';
94
			$NotHTTP = '(?<!:\/\/|" target=\"_blank\">)';	//	avoid running again on http://www links already handled above
95
			$Domain2 = 'www(\.[\w\-.]+)';
96
			$Subdir2 = '([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?';
97
			$optBracket = '(>|&gt;|&gt|;)?';
98
			$Expr = '/' .$optBracket0. $NotAnchor . $NotHTTP . $Domain2 . $Subdir2 .$optBracket. '/i';
99
			//$Expr = '/' . $NotAnchor . $NotHTTP . $Domain . $Subdir . $optBracket . '/i';
100
			// use preg_replace_callback as we experienced problems with links such as <www.example.tld/pfad/zu/einer/pdf-Datei.pdf>
101
			$result4 = preg_replace_callback( $Expr, function ($match) {
102
					//error_log(__METHOD__.__LINE__.array2string($match));
103
					if ($match[4]==';' && (strlen($match[3])-4) >=0 && strpos($match[3],'&gt',strlen($match[3])-4)!==false)
104
					{
105
						$match[3] = substr($match[3],0,strpos($match[3],'&gt',strlen($match[3])-4));
106
						$match[4] = "&gt;";
107
					}
108
					if ($match[4]==';'&&$match[3]=="&gt")
109
					{
110
						$match[3] ='';
111
						$match[4] = "&gt;";
112
					}
113
					//error_log(__METHOD__.__LINE__.array2string($match));
114
					return $match[1]."<a href=\"http://www".$match[2].$match[3]."\" target=\"_blank\">"."www".$match[2].$match[3]."</a>".$match[4];
115
				}, $result3 );
116
		}
117
		return $result4;
118
	}
119
120
	/**
121
	 * escapes chars with special meaning in html as entities
122
	 *
123
	 * Allows to use and char in the html-output and prevents XSS attacks.
124
	 * Some entities are allowed and get NOT escaped: -> prevented by 4th param = doubleencode=false
125
	 * - &# some translations (AFAIK: the arabic ones) need this;
126
	 * - &nbsp; &lt; &gt; for convenience -> should not happen anymore, as we do not doubleencode anymore (20101020)
127
	 *
128
	 * @param string $str string to escape
129
	 * @param boolean $double_encoding =false do we want double encoding or not, default no
130
	 * @return string
131
	 */
132
	static function htmlspecialchars($str, $double_encoding=false)
133
	{
134
		return htmlspecialchars($str,ENT_COMPAT,Translation::charset(),$double_encoding);
135
	}
136
137
	/**
138
	 * allows to show and select one item from an array
139
	 *
140
	 * @param string $name	string with name of the submitted var which holds the key of the selected item form array
141
	 * @param string|array $key key(s) of already selected item(s) from $arr, eg. '1' or '1,2' or array with keys
142
	 * @param array $arr array with items to select, eg. $arr = array ( 'y' => 'yes','n' => 'no','m' => 'maybe');
143
	 * @param boolean $no_lang NOT run the labels of the options through lang(), default false=use lang()
144
	 * @param string $options additional options (e.g. 'width')
145
	 * @param int $multiple number of lines for a multiselect, default 0 = no multiselect, < 0 sets size without multiple
146
	 * @param boolean $enhanced Use enhanced selectbox with search.  Null for default yes if more than 12 options.
147
	 * @return string to set for a template or to echo into html page
148
	 */
149
	static function select($name, $key, $arr=0,$no_lang=false,$options='',$multiple=0,$enhanced=null)
150
	{
151
		if(is_null($enhanced)) $enhanced = false;	//disabled by default (count($arr) > self::SELECT_ENHANCED_ROW_COUNT);
152
153
		if (!is_array($arr))
154
		{
155
			$arr = array('no','yes');
156
		}
157
		if ((int)$multiple > 0)
158
		{
159
			$options .= ' multiple="1" size="'.(int)$multiple.'"';
160
			if (substr($name,-2) != '[]')
161
			{
162
				$name .= '[]';
163
			}
164
		}
165
		elseif($multiple < 0)
166
		{
167
			$options .= ' size="'.abs($multiple).'"';
168
		}
169
		// fix width for MSIE < 9 in/for selectboxes
170
		if (Header\UserAgent::type() == 'msie' && Header\UserAgent::version() < 9)
171
		{
172
			if (stripos($options,'onfocus="') === false)
173
			{
174
				$options .= ' onfocus="window.dropdown_menu_hack(this);" ';
175
			}
176
			else
177
			{
178
				$options = str_ireplace('onfocus="','onfocus="window.dropdown_menu_hack(this);',$options);
179
			}
180
		}
181
		$out = "<select name=\"$name\" $options>\n";
182
183
		if (!is_array($key))
184
		{
185
			// explode on ',' only if multiple values expected and the key contains just numbers and commas
186
			$key = $multiple > 0 && preg_match('/^[,0-9]+$/',$key) ? explode(',',$key) : array($key);
187
		}
188
		foreach($arr as $k => $data)
189
		{
190
			if (!is_array($data) || count($data) == 2 && isset($data['label']) && isset($data['title']))
191
			{
192
				$out .= self::select_option($k,is_array($data)?$data['label']:$data,$key,$no_lang,
193
					is_array($data)?$data['title']:'');
194
			}
195
			else
196
			{
197
				if (isset($data['lable']))
198
				{
199
					$k = $data['lable'];
200
					unset($data['lable']);
201
				}
202
				$out .= '<optgroup label="'.self::htmlspecialchars($no_lang || $k == '' ? $k : lang($k))."\">\n";
203
204
				foreach($data as $k => $label)
0 ignored issues
show
Comprehensibility Bug introduced by
$k is overwriting a variable from outer foreach loop.
Loading history...
205
				{
206
					$out .= self::select_option($k,is_array($label)?$label['label']:$label,$key,$no_lang,
207
						is_array($label)?$label['title']:'');
208
				}
209
				$out .= "</optgroup>\n";
210
			}
211
		}
212
		$out .= "</select>\n";
213
214
		if($enhanced) {
215
			Framework::includeJS('/api/js/jquery/chosen/chosen.jquery.js');
216
			Framework::includeCSS('/api/js/jquery/chosen/chosen.css',null,false);
217
			$out .= "<script>var lab = egw_LAB || \$LAB; lab.wait(function() {jQuery(function() {if(jQuery().chosen) jQuery('select[name=\"$name\"]').chosen({width: '100%'});});})</script>\n";
218
		}
219
		return $out;
220
	}
221
222
	/**
223
	 * emulating a multiselectbox using checkboxes
224
	 *
225
	 * Unfortunaly this is not in all aspects like a multi-selectbox, eg. you cant select options via javascript
226
	 * in the same way. Therefor I made it an extra function.
227
	 *
228
	 * @param string $name	string with name of the submitted var which holds the key of the selected item form array
229
	 * @param string|array $key key(s) of already selected item(s) from $arr, eg. '1' or '1,2' or array with keys
230
	 * @param array $arr array with items to select, eg. $arr = array ( 'y' => 'yes','n' => 'no','m' => 'maybe');
231
	 * @param boolean $no_lang NOT run the labels of the options through lang(), default false=use lang()
232
	 * @param string $options additional options (e.g. 'width')
233
	 * @param int $multiple number of lines for a multiselect, default 3
234
	 * @param boolean $selected_first show the selected items before the not selected ones, default true
235
	 * @param string $style ='' extra style settings like "width: 100%", default '' none
236
	 * @return string to set for a template or to echo into html page
237
	 */
238
	static function checkbox_multiselect($name, $key, $arr=0,$no_lang=false,$options='',$multiple=3,$selected_first=true,$style='',$enhanced = null)
239
	{
240
		//echo "<p align=right>checkbox_multiselect('$name',".array2string($key).",".array2string($arr).",$no_lang,'$options',$multiple,$selected_first,'$style')</p>\n";
241
		if(is_null($enhanced)) $enhanced = (count($arr) > self::SELECT_ENHANCED_ROW_COUNT);
242
243
		if (!is_array($arr))
244
		{
245
			$arr = array('no','yes');
246
		}
247
		if ((int)$multiple <= 0) $multiple = 1;
248
249
		if (substr($name,-2) != '[]')
250
		{
251
			$name .= '[]';
252
		}
253
		$base_name = substr($name,0,-2);
254
255
		if($enhanced) return self::select($name, $key, $arr,$no_lang,$options." style=\"$style\" ",$multiple,$enhanced);
256
257
		if (!is_array($key))
258
		{
259
			// explode on ',' only if multiple values expected and the key contains just numbers and commas
260
			$key = preg_match('/^[,0-9]+$/',$key) ? explode(',',$key) : array($key);
261
		}
262
		$html = '';
263
		$options_no_id = preg_replace('/id="[^"]+"/i','',$options);
264
265
		if ($selected_first)
266
		{
267
			$selected = $not_selected = array();
268
			foreach($arr as $val => $label)
269
			{
270
				if (in_array((string)$val,$key))
271
				{
272
					$selected[$val] = $label;
273
				}
274
				else
275
				{
276
					$not_selected[$val] = $label;
277
				}
278
			}
279
			$arr = $selected + $not_selected;
280
		}
281
		$max_len = 0;
282
		foreach($arr as $val => $label)
283
		{
284
			if (is_array($label))
285
			{
286
				$title = $label['title'];
287
				$label = $label['label'];
288
			}
289
			else
290
			{
291
				$title = '';
292
			}
293
			if ($label && !$no_lang) $label = lang($label);
294
			if ($title && !$no_lang) $title = lang($title);
295
296
			if (strlen($label) > $max_len) $max_len = strlen($label);
297
298
			$html .= self::label(self::checkbox($name,in_array((string)$val,$key),$val,$options_no_id.
299
				' id="'.$base_name.'['.$val.']'.'"').self::htmlspecialchars($label),
300
				$base_name.'['.$val.']','',($title ? 'title="'.self::htmlspecialchars($title).'" ':''))."<br />\n";
301
		}
302
		if ($style && substr($style,-1) != ';') $style .= '; ';
303
		if (strpos($style,'height')===false) $style .= 'height: '.(1.7*$multiple).'em; ';
304
		if (strpos($style,'width')===false)  $style .= 'width: '.(4+$max_len*($max_len < 15 ? 0.65 : 0.6)).'em; ';
305
		$style .= 'background-color: white; overflow: auto; border: lightgray 2px inset; text-align: left;';
306
307
		return self::div($html,$options,'',$style);
308
	}
309
310
	/**
311
	 * generates an option-tag for a selectbox
312
	 *
313
	 * @param string $value value
314
	 * @param string $label label
315
	 * @param mixed $selected value or array of values of options to mark as selected
316
	 * @param boolean $no_lang NOT running the label through lang(), default false=use lang()
317
	 * @param string $extra extra text, e.g.: style="", default: ''
318
	 * @return string html
319
	 */
320
	static function select_option($value,$label,$selected,$no_lang=0,$title='',$extra='')
321
	{
322
		// the following compares strict as strings, to archive: '0' == 0 != ''
323
		// the first non-strict search via array_search, is for performance reasons, to not always search the whole array with php
324
		if (($found = ($key = array_search($value,$selected)) !== false) && (string) $value !== (string) $selected[$key])
325
		{
326
			$found = false;
327
			foreach($selected as $sel)
328
			{
329
				if (($found = (((string) $value) === ((string) $selected[$key])))) break;
330
			}
331
			unset($sel);
332
		}
333
		return '<option value="'.self::htmlspecialchars($value).'"'.($found  ? ' selected="selected"' : '') .
334
			($title ? ' title="'.self::htmlspecialchars($no_lang ? $title : lang($title)).'"' : '') .
335
			($extra ? ' ' . $extra : '') . '>'.
336
			self::htmlspecialchars($no_lang || $label == '' ? $label : lang($label)) . "</option>\n";
337
	}
338
339
	/**
340
	 * generates a div-tag
341
	 *
342
	 * @param string $content of a div, or '' to generate only the opening tag
343
	 * @param string $options to include in the tag, default ''=none
344
	 * @param string $class css-class attribute, default ''=none
345
	 * @param string $style css-styles attribute, default ''=none
346
	 * @return string html
347
	 */
348
	static function div($content,$options='',$class='',$style='')
349
	{
350
		if ($class) $options .= ' class="'.$class.'"';
351
		if ($style) $options .= ' style="'.$style.'"';
352
353
		return "<div $options>\n".($content ? "$content</div>\n" : '');
354
	}
355
356
	/**
357
	 * generate one or more hidden input tag(s)
358
	 *
359
	 * @param array|string $vars var-name or array with name / value pairs
360
	 * @param string $value value if $vars is no array, default ''
361
	 * @param boolean $ignore_empty if true all empty, zero (!) or unset values, plus filer=none
362
	 * @param string html
363
	 */
364
	static function input_hidden($vars,$value='',$ignore_empty=True)
365
	{
366
		if (!is_array($vars))
367
		{
368
			$vars = array( $vars => $value );
369
		}
370
		foreach($vars as $name => $value)
371
		{
372
			if (is_array($value))
373
			{
374
				$value = json_encode($value);
375
			}
376
			if (!$ignore_empty || $value && !($name == 'filter' && $value == 'none'))	// dont need to send all the empty vars
377
			{
378
				$html .= "<input type=\"hidden\" name=\"$name\" value=\"".self::htmlspecialchars($value)."\" />\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $html seems to be never defined.
Loading history...
379
			}
380
		}
381
		return $html;
382
	}
383
384
	/**
385
	 * generate a textarea tag
386
	 *
387
	 * @param string $name name attr. of the tag
388
	 * @param string $value default
389
	 * @param boolean $ignore_empty if true all empty, zero (!) or unset values, plus filer=none
390
	 * @param boolean $double_encoding =false do we want double encoding or not, default no
391
	 * @param string html
392
	 */
393
	static function textarea($name,$value='',$options='',$double_encoding=false)
394
	{
395
		return "<textarea name=\"$name\" $options>".self::htmlspecialchars($value,$double_encoding)."</textarea>\n";
396
	}
397
398
	/**
399
	 * Checks if HTMLarea (or an other richtext editor) is availible for the used browser
400
	 *
401
	 * @return boolean
402
	 */
403
	static function htmlarea_availible()
404
	{
405
		// this one is for testing how it will turn out, if you do not have the device or agent ready at your fingertips
406
		// if (stripos($_SERVER[HTTP_USER_AGENT],'mozilla') !== false) return false;
407
		return true;
408
	}
409
410
	/**
411
	 * compability static function for former used htmlarea. Please use static function fckeditor now!
412
	 *
413
	 * creates a textarea inputfield for the htmlarea js-widget (returns the necessary html and js)
414
	 */
415
	static function htmlarea($name,$content='',$style='',$base_href=''/*,$plugins='',$custom_toolbar='',$set_width_height_in_config=false*/)
416
	{
417
		/*if (!self::htmlarea_availible())
418
		{
419
			return self::textarea($name,$content,'style="'.$style.'"');
420
		}*/
421
		return self::fckEditor($name, $content, $style, array('toolbar_expanded' =>'true'), '400px', '100%', $base_href);
422
	}
423
424
	/**
425
	* this static function is a wrapper for fckEditor to create some reuseable layouts
426
	*
427
	* @param string $_name name and id of the input-field
428
	* @param string $_content of the tinymce (will be run through htmlspecialchars !!!), default ''
429
	* @param string $_mode display mode of the tinymce editor can be: simple, extended or advanced
430
	* @param array  $_options (toolbar_expanded true/false)
431
	* @param string $_height ='400px'
432
	* @param string $_width ='100%'
433
	* @param string $_start_path ='' if passed activates the browser for image at absolute path passed
434
	* @param boolean $_purify =true run $_content through htmlpurifier before handing it to fckEditor
435
	* @param mixed (boolean/string) $_focusToBody=false USED only for CKEDIOR true means yes, focus on top, you may specify TOP or BOTTOM (to focus on the end of the editor area)
0 ignored issues
show
Documentation Bug introduced by
The doc comment (boolean/string) at position 1 could not be parsed: Unknown type name 'boolean/string' at position 1 in (boolean/string).
Loading history...
436
	* @param string $_executeJSAfterInit ='' Javascript to be executed after InstanceReady of CKEditor
437
	* @return string the necessary html for the textarea
438
	*/
439
	static function fckEditor($_name, $_content, $_mode, $_options=array('toolbar_expanded' =>'true'),
440
		$_height='400px', $_width='100%',$_start_path='',$_purify=true, $_focusToBody=false, $_executeJSAfterInit='')
441
	{
442
		//not used anymore but defined in function signature
443
		unset ($_options);
444
445
		if (!self::htmlarea_availible() || $_mode == 'ascii')
446
		{
447
			return self::textarea($_name,$_content,'style="width: '.$_width.'; height: '.$_height.';" id="'.htmlspecialchars($_name).'"');
448
		}
449
450
		//include the ckeditor js file
451
		Framework::includeJS('/vendor/tinymce/tinymce/tinymce.min.js');
452
453
		// run content through htmlpurifier
454
		if ($_purify && !empty($_content))
455
			$_content = self::purify($_content);
456
457
		// User preferences
458
		$font = $GLOBALS['egw_info']['user']['preferences']['common']['rte_font'];
459
		$font_size = $GLOBALS['egw_info']['user']['preferences']['common']['rte_font_size'];
460
		$font_size_unit = $GLOBALS['egw_info']['user']['preferences']['common']['rte_font_unit'];
461
		$rte_menubar = $GLOBALS['egw_info']['user']['preferences']['common']['rte_menubar'];
462
		$focusToBody = $_focusToBody ? "tinymce" : false;
463
		ContentSecurityPolicy::add('script-src', 'unsafe-inline');
464
		// we need to enable double encoding here, as ckEditor has to undo one level of encoding
465
		// otherwise < and > chars eg. from html markup entered in regular (not source) input, will turn into html!
466
		//error_log(__METHOD__.__LINE__.' '.Header\UserAgent::type().','.Header\UserAgent::version());
467
		return self::textarea($_name,$_content,'id="'.htmlspecialchars($_name).'"',true).	// true = double encoding
468
'
469
<script type="text/javascript">
470
471
egw_LAB.wait(function() {
472
473
var imageUpload = egw.ajaxUrl("EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_htmlarea_upload")+"&type=htmlarea";
474
imageUpload = imageUpload.substr(egw.webserverUrl.length+1);
475
var font_size_formats = {
476
	pt: "8pt 10pt 12pt 14pt 18pt 24pt 36pt 48pt 72pt",
477
	px:"8px 10px 12px 14px 18px 24px 36px 48px 72px"
478
};
479
var  language_code = {
480
	bg: "bg_BG", ca: "ca",	cs: "cs", da: "da", de: "de",	en:"en_CA",
481
	el:"el", "es-es":"es",	et: "et", eu: "eu" , fa: "fa_IR", fi: "fi",
482
	fr: "fr_FR", hi:"",	hr:"hr", hu:"hu_HU", id: "id", it: "it", iw: "",
483
	ja: "ja", ko: "ko_KR", lo: "", lt: "lt", lv: "lv",	nl: "nl", no: "nb_NO",
484
	pl: "pl", pt: "pt_PT", "pt-br": "pt_BR", ru: "ru", sk: "sk", sl: "sl_SI",
485
	sv: "sv_SE", th: "th_TH", tr: "tr_TR", uk: "en_GB", vi: "vi_VN", zh: "zh_CN",
486
	"zh-tw": "zh_TW"
487
};
488
var name = "#"+"'.$_name.'".replace( /(:|\.|\[|\]|,|=|@)/g, "\\\$1" );
489
var height = "'.$_height.'";
490
var width = "'.$_width.'";
491
var value = jQuery(name).val();
492
/**
493
 * language code represention for TinyMCE lang code
494
 */
495
var language_code = {
496
	bg: "bg_BG", ca: "ca",	cs: "cs", da: "da", de: "de",	en:"en_CA",
497
	el:"el", "es-es":"es",	et: "et", eu: "eu" , fa: "fa_IR", fi: "fi",
498
	fr: "fr_FR", hi:"",	hr:"hr", hu:"hu_HU", id: "id", it: "it", iw: "",
499
	ja: "ja", ko: "ko_KR", lo: "", lt: "lt", lv: "lv",	nl: "nl", no: "nb_NO",
500
	pl: "pl", pt: "pt_PT", "pt-br": "pt_BR", ru: "ru", sk: "sk", sl: "sl_SI",
501
	sv: "sv_SE", th: "th_TH", tr: "tr_TR", uk: "en_GB", vi: "vi_VN", zh: "zh_CN",
502
	"zh-tw": "zh_TW"
503
};
504
tinymce.init({
505
			selector: name,
506
			menubar: parseInt('. $rte_menubar.')? true : false,
507
			branding: false,
508
			resize: false,
509
			height: height.match(/%/) ? height : parseInt(height),
510
			width: width.match(/%/) ? width : parseInt(width),
511
			min_height: 200,
512
			auto_focus: "'.$focusToBody.'",
0 ignored issues
show
Bug introduced by
Are you sure $focusToBody of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

512
			auto_focus: "'./** @scrutinizer ignore-type */ $focusToBody.'",
Loading history...
513
			language: language_code["'. $GLOBALS['egw_info']['user']['preferences']['common']['lang'].'"],
514
			language_url: egw.webserverUrl+"/api/js/tinymce/langs/"+language_code[egw.preference("lang", "common")]+".js",
515
			browser_spellcheck: true,
516
			contextmenu: false,
517
			file_picker_callback: function(_callback, _value, _meta){
518
				var callback = _callback;
519
				var attrs = {
520
					menuaction: "filemanager.filemanager_select.select",
521
					mode: "open",
522
					method: "download_url",
523
					path: "'. $_start_path.'"
524
				};
525
526
				// Open the filemanager select in a popup
527
				var popup = egw(window).open_link(
528
					egw().link("/index.php", attrs),
529
					"link_existing",
530
					"680x400"
531
				);
532
				if(popup)
533
				{
534
					// Safari and IE lose reference to global variables after window close
535
					// Try to get updated data before window is closed then later we trigger
536
					// change event on widget
537
					egw().window.setTimeout(function(){
538
						jQuery(popup).bind("unload",function(){
539
							callback(this.selected_files, {alt:this.selected_files});
540
						});
541
					},1000);
542
				}
543
			},
544
			init_instance_callback : function(_editor){
545
				console.log(_editor);
546
				_editor.execCommand("fontName", true,"'.$font.'");
547
				_editor.execCommand("fontSize",	true,"'.$font_size.$font_size_unit.'");
548
				_editor.setContent(value);
549
			},
550
			plugins: [
551
				"print searchreplace autolink directionality "+
552
				"visualblocks visualchars image link media template "+
553
				"codesample table charmap hr pagebreak nonbreaking anchor toc "+
554
				"insertdatetime advlist lists textcolor wordcount imagetools "+
555
				"colorpicker textpattern help paste code searchreplace"
556
			],
557
			toolbar: "undo redo | formatselect | fontselect fontsizeselect | bold italic strikethrough forecolor backcolor | "+
558
					"link | alignleft aligncenter alignright alignjustify  | numlist "+
559
					"bullist outdent indent  | removeformat code| image | searchreplace",
560
			block_formats: "Paragraph=p;Heading 1=h1;Heading 2=h2;Heading 3=h3;"+
561
					"Heading 4=h4;Heading 5=h5;Heading 6=h6;Preformatted=pre",
562
			font_formats: "Andale Mono=andale mono,times;Arial=arial,helvetica,"+
563
					"sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book "+
564
					"antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;"+
565
					"Courier New=courier new,courier;Georgia=georgia,palatino;"+
566
					"Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;"+
567
					"Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,"+
568
					"monaco;Times New Roman=times new roman,times;Trebuchet "+
569
					"MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;"+
570
					"Wingdings=wingdings,zapf dingbats",
571
			fontsize_formats:font_size_formats["'. $font_size_unit.'"],
572
		});
573
		'.($_executeJSAfterInit?$_executeJSAfterInit:'').'
574
});
575
</script>
576
';
577
	}
578
579
	/**
580
	* this static function is a wrapper for tinymce to create some reuseable layouts
581
	*
582
	* Please note: if you did not run init_tinymce already you this static function need to be called before the call to phpgw_header() !!!
583
	*
584
	* @param string $_name name and id of the input-field
585
	* @param string $_mode display mode of the tinymce editor can be: simple, extended or advanced
586
	* @param string $_content ='' of the tinymce (will be run through htmlspecialchars !!!), default ''
587
	* @param string $_height ='400px'
588
	* @param string $_width ='100%'
589
	* @param boolean $_purify =true
590
	* @param string $_border ='0px' NOT used for CKEditor
591
	* @param mixed (boolean/string) $_focusToBody=false USED only for CKEDIOR true means yes, focus on top, you may specify TOP or BOTTOM (to focus on the end of the editor area)
0 ignored issues
show
Documentation Bug introduced by
The doc comment (boolean/string) at position 1 could not be parsed: Unknown type name 'boolean/string' at position 1 in (boolean/string).
Loading history...
592
	* @param string $_executeJSAfterInit ='' Javascript to be executed after InstanceReady of CKEditor
593
	* @return string the necessary html for the textarea
594
	*/
595
	static function fckEditorQuick($_name, $_mode, $_content='', $_height='400px', $_width='100%',$_purify=true, $_border='0px',$_focusToBody=false,$_executeJSAfterInit='')
596
	{
597
		if (!self::htmlarea_availible() || $_mode == 'ascii')
598
		{
599
			//TODO: use self::textarea
600
			return "<textarea name=\"$_name\" style=\"".($_width?" width:".$_width.';':" width:100%;").($_height?" height:".$_height.';':" height:400px;").($_border?" border:".$_border.';':" border:0px;")."\">$_content</textarea>";
601
		}
602
		else
603
		{
604
			return self::fckEditor($_name, $_content, $_mode, array(), $_height, $_width,'',$_purify,$_focusToBody,$_executeJSAfterInit);
605
		}
606
	}
607
608
	/**
609
	 * represents html's input tag
610
	 *
611
	 * @param string $name name
612
	 * @param string $value default value of the field
613
	 * @param string $type type, default ''=not specified = text
614
	 * @param string $options attributes for the tag, default ''=none
615
	 */
616
	static function input($name,$value='',$type='',$options='' )
617
	{
618
		switch ((string)$type)
619
		{
620
			case '';
621
				break;
622
			default:
623
				$type = 'type="'.htmlspecialchars($type).'"';
624
		}
625
		return "<input $type name=\"$name\" value=\"".self::htmlspecialchars($value)."\" $options />\n";
626
	}
627
628
	static protected $default_background_images = array(
629
		'save'   => '/save(&|\]|$)/',
630
		'apply'  => '/apply(&|\]|$)/',
631
		'cancel' => '/cancel(&|\]|$)/',
632
		'delete' => '/delete(&|\]|$)/',
633
		'edit'   => '/edit(&|\]|$)/',
634
		'next'   => '/(next|continue)(&|\]|$)/',
635
		'finish' => '/finish(&|\]|$)/',
636
		'back'   => '/(back|previous)(&|\]|$)/',
637
		'copy'   => '/copy(&|\]|$)/',
638
		'more'   => '/more(&|\]|$)/',
639
		'check'  => '/(yes|check)(&|\]|$)/',
640
		'cancelled' => '/no(&|\]|$)/',
641
		'ok'     => '/ok(&|\]|$)/',
642
		'close'  => '/close(&|\]|$)/',
643
		'add'    => '/(add(&|\]|$)|create)/',	// customfields use create*
644
	);
645
646
	static protected $default_classes = array(
647
		'et2_button_cancel'   => '/cancel(&|\]|$)/',	// yellow
648
		'et2_button_question' => '/(yes|no)(&|\]|$)/',	// yellow
649
		'et2_button_delete'   => '/delete(&|\]|$)/'		// red
650
	);
651
652
	/**
653
	 * represents html's button (input type submit or input type button or image)
654
	 *
655
	 * @param string $name name
656
	 * @param string $label label of the button
657
	 * @param string $onClick javascript to call, when button is clicked
658
	 * @param boolean $no_lang NOT running the label through lang(), default false=use lang()
659
	 * @param string $options attributes for the tag, default ''=none
660
	 * @param string $image to show instead of the label, default ''=none
661
	 * @param string $app app to search the image in
662
	 * @param string $buttontype which type of html button (button|submit), default ='submit'
663
	 * @return string html
664
	 */
665
	static function submit_button($name,$label,$onClick='',$no_lang=false,$options='',$image='',$app='phpgwapi', $buttontype='submit')
666
	{
667
		// workaround for idots and IE button problem (wrong cursor-image)
668
		if (Header\UserAgent::type() == 'msie')
669
		{
670
			$options .= ' style="cursor: pointer;"';
671
		}
672
		// add et2_classes to "old" buttons
673
		$classes = array('et2_button');
674
675
		if ($image != '')
676
		{
677
			$image = str_replace(array('.gif','.GIF','.png','.PNG'),'',$image);
678
679
			if (!($path = Image::find($app, $image)))
680
			{
681
				$path = $image;		// name may already contain absolut path
682
			}
683
			$image = ' src="'.$path.'"';
684
			$classes[] = 'image_button';
685
		}
686
		if (!$no_lang)
687
		{
688
			$label = lang($label);
689
		}
690
		if (($accesskey = @strstr($label,'&')) && $accesskey[1] != ' ' &&
691
			(($pos = strpos($accesskey,';')) === false || $pos > 5))
692
		{
693
			$label_u = str_replace('&'.$accesskey[1],'<u>'.$accesskey[1].'</u>',$label);
694
			$label = str_replace('&','',$label);
695
			$options .= ' accesskey="'.$accesskey[1].'" '.$options;
696
		}
697
		else
698
		{
699
			$accesskey = '';
700
			$label_u = $label;
701
		}
702
		if ($onClick) $options .= ' onclick="'.str_replace('"','\\"',$onClick).'"';
703
704
		// add default background-image to get et2 like buttons
705
		foreach(self::$default_background_images as $img => $reg_exp)
706
		{
707
			if (preg_match($reg_exp, $name) && ($url = Image::find($GLOBALS['egw_info']['flags']['currentapp'], $img)))
708
			{
709
				$options .= ' style="background-image: url('.$url.');"';
710
				$classes[] = 'et2_button_with_image et2_button_text';
711
				break;
712
			}
713
		}
714
		// add default class for cancel, delete or yes/no buttons
715
		foreach(self::$default_classes as $class => $reg_exp)
716
		{
717
			if (preg_match($reg_exp, $name))
718
			{
719
				$classes[] = $class;
720
				break;
721
			}
722
		}
723
		if (strpos($options, 'class="') !== false)
724
		{
725
			$options = str_replace('class="', 'class="'.implode(' ', $classes).' ', $options);
726
		}
727
		else
728
		{
729
			$options .= ' class="'.implode(' ', $classes).'"';
730
		}
731
732
		return '<button type="'.$buttontype.'" name="'.htmlspecialchars($name).
733
			'" value="'.htmlspecialchars($label).
734
			'" '.$options.'>'.
735
			($image != '' ? '<img'.$image.' title="'.self::htmlspecialchars($label).'"> ' : '').
736
			($image == '' || $accesskey ? self::htmlspecialchars($label_u) : '').'</button>';
737
	}
738
739
	/**
740
	 * creates an absolut link + the query / get-variables
741
	 *
742
	 * Example link('/index.php?menuaction=infolog.uiinfolog.get_list',array('info_id' => 123))
743
	 *	gives 'http://domain/phpgw-path/index.php?menuaction=infolog.uiinfolog.get_list&info_id=123'
744
	 *
745
	 * @param string $_url egw-relative link, may include query / get-vars
746
	 * @param array|string $vars query or array ('name' => 'value', ...) with query
747
	 * @return string absolut link already run through $phpgw->link
748
	 */
749
	static function link($_url,$vars='')
750
	{
751
		if (!is_array($vars))
752
		{
753
			parse_str($vars,$vars);
754
		}
755
		list($url,$v) = explode('?', $_url);	// url may contain additional vars
756
		if ($v)
757
		{
758
			parse_str($v,$v);
759
			$vars += $v;
760
		}
761
		return Framework::link($url,$vars);
762
	}
763
764
	/**
765
	 * represents html checkbox
766
	 *
767
	 * @param string $name name
768
	 * @param boolean $checked box checked on display
769
	 * @param string $value value the var should be set to, default 'True'
770
	 * @param string $options attributes for the tag, default ''=none
771
	 * @return string html
772
	 */
773
	static function checkbox($name,$checked=false,$value='True',$options='')
774
	{
775
		return '<input type="checkbox" name="'.$name.'" value="'.self::htmlspecialchars($value).'"' .($checked ? ' checked="1"' : '') . "$options />\n";
776
	}
777
778
	/**
779
	 * represents a html form
780
	 *
781
	 * @param string $content of the form, if '' only the opening tag gets returned
782
	 * @param array $hidden_vars array with name-value pairs for hidden input fields
783
	 * @param string $_url eGW relative URL, will be run through the link function, if empty the current url is used
784
	 * @param string|array $url_vars parameters for the URL, send to link static function too
785
	 * @param string $name name of the form, defaul ''=none
786
	 * @param string $options attributes for the tag, default ''=none
787
	 * @param string $method method of the form, default 'POST'
788
	 * @return string html
789
	 */
790
	static function form($content,$hidden_vars,$_url,$url_vars='',$name='',$options='',$method='POST')
791
	{
792
		$url = $_url ? self::link($_url, $url_vars) : $_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING'];
793
		$html = "<form method=\"$method\" ".($name != '' ? "name=\"$name\" " : '')."action=\"$url\" $options>\n";
794
		$html .= self::input_hidden($hidden_vars);
795
796
		if ($content)
797
		{
798
			$html .= $content;
799
			$html .= "</form>\n";
800
		}
801
		return $html;
802
	}
803
804
	/**
805
	 * represents a html form with one button
806
	 *
807
	 * @param string $name name of the button
808
	 * @param string $label label of the button
809
	 * @param array $hidden_vars array with name-value pairs for hidden input fields
810
	 * @param string $url eGW relative URL, will be run through the link function
811
	 * @param string|array $url_vars parameters for the URL, send to link static function too
812
	 * @param string $options attributes for the tag, default ''=none
813
	 * @param string $form_name name of the form, defaul ''=none
814
	 * @param string $method method of the form, default 'POST'
815
	 * @return string html
816
	 */
817
	static function form_1button($name,$label,$hidden_vars,$url,$url_vars='',$form_name='',$method='POST')
818
	{
819
		return self::form(self::submit_button($name,$label),$hidden_vars,$url,$url_vars,$form_name,' style="display: inline-block"',$method);
820
	}
821
822
	const THEAD = 1;
823
	const TFOOT = 2;
824
	const TBODY = 3;
825
	static $part2tag = array(
826
		self::THEAD => 'thead',
827
		self::TFOOT => 'tfoot',
828
		self::TBODY => 'tbody',
829
	);
830
831
	/**
832
	 * creates table from array of rows
833
	 *
834
	 * abstracts the html stuff for the table creation
835
	 * Example: $rows = array (
836
	 *  'h1' => array(	// optional header row(s)
837
	 *  ),
838
	 *  'f1' => array(	// optional footer row(s)
839
	 *  ),
840
	 *	'1'  => array(
841
	 *		1 => 'cell1', '.1' => 'colspan=3',
842
	 *		2 => 'cell2',
843
	 *		3 => 'cell3', '.3' => 'width="10%"'
844
	 *	),'.1' => 'BGCOLOR="#0000FF"' );
845
	 *	table($rows,'width="100%"') = '<table width="100%"><tr><td colspan=3>cell1</td><td>cell2</td><td width="10%">cell3</td></tr></table>'
846
	 *
847
	 * @param array $rows with rows, each row is an array of the cols
848
	 * @param string $options options for the table-tag
849
	 * @param boolean $no_table_tr dont return the table- and outmost tr-tabs, default false=return table+tr
850
	 * @return string with html-code of the table
851
	 */
852
	static function table($rows,$options = '',$no_table_tr=False)
853
	{
854
		$html = $no_table_tr ? '' : "<table $options>\n";
855
856
		$part = 0;
857
		foreach($rows as $key => $row)
858
		{
859
			if (!is_array($row))
860
			{
861
				continue;					// parameter
862
			}
863
			// get the current part from the optional 'h' or 'f' prefix of the key
864
			$p = $key[0] == 'h' ? self::THEAD : ($key[0] == 'f' ? self::TFOOT : self::TBODY);
865
			if ($part < $p && ($part || $p < self::TBODY))	// add only allowed and neccessary transitions
866
			{
867
				if ($part) $html .= '</'.self::$part2tag[$part].">\n";
868
				$html .= '<'.self::$part2tag[$part=$p].">\n";
869
			}
870
			$html .= $no_table_tr && $key == 1 ? '' : "\t<tr ".$rows['.'.$key].">\n";
871
872
			foreach($row as $key => $cell)
0 ignored issues
show
Comprehensibility Bug introduced by
$key is overwriting a variable from outer foreach loop.
Loading history...
873
			{
874
				if ($key[0] == '.')
875
				{
876
					continue;				// parameter
877
				}
878
				$table_pos = strpos($cell,'<table');
879
				$td_pos = strpos($cell,'<td');
880
				if ($td_pos !== False && ($table_pos === False || $td_pos < $table_pos))
881
				{
882
					$html .= $cell;
883
				}
884
				else
885
				{
886
					$html .= "\t\t<td ".$row['.'.$key].">$cell</td>\n";
887
				}
888
			}
889
			$html .= "\t</tr>\n";
890
		}
891
		if (!is_array($rows))
0 ignored issues
show
introduced by
The condition is_array($rows) is always true.
Loading history...
892
		{
893
			echo "<p>".function_backtrace()."</p>\n";
894
		}
895
		if ($part)	// close current part
896
		{
897
			$html .= "</".self::$part2tag[$part].">\n";
898
		}
899
		$html .= "</table>\n";
900
901
		if ($no_table_tr)
902
		{
903
			$html = substr($html,0,-16);
904
		}
905
		return $html;
906
	}
907
908
	/**
909
	 * changes a selectbox to submit the form if it gets changed, to be used with the sbox-class
910
	 *
911
	 * @param string $sbox html with the select-box
912
	 * @param boolean $no_script if true generate a submit-button if javascript is off
913
	 * @return string html
914
	 */
915
	static function sbox_submit( $sbox,$no_script=false )
916
	{
917
		$html = str_replace('<select','<select onchange="this.form.submit()" ',$sbox);
918
		if ($no_script)
919
		{
920
			$html .= '<noscript>'.self::submit_button('send','>').'</noscript>';
921
		}
922
		return $html;
923
	}
924
925
	/**
926
	 * html-widget showing progessbar with a view div's (html4 only, textual percentage otherwise)
927
	 *
928
	 * @param mixed $_percent percent-value, gets casted to int
929
	 * @param string $_title title for the progressbar, default ''=the percentage itself
930
	 * @param string $options attributes for the outmost div (may include onclick="...")
931
	 * @param string $width width, default 30px
932
	 * @param string $color color, default '#D00000' (dark red)
933
	 * @param string $height height, default 5px
934
	 * @return string html
935
	 */
936
	static function progressbar($_percent, $_title='',$options='',$width='',$color='',$height='' )
937
	{
938
		$percent = (int)$_percent;
939
		if (!$width) $width = '30px';
940
		if (!$height)$height= '5px';
941
		if (!$color) $color = '#D00000';
942
		$title = $_title ? self::htmlspecialchars($_title) : $percent.'%';
943
944
		return '<div class="onlyPrint">'.$title.'</div><div class="noPrint" title="'.$title.'" '.$options.
945
			' style="height: '.$height.'; width: '.$width.'; border: 1px solid black; padding: 1px; text-align: left;'.
946
			(@stristr($options,'onclick="') ? ' cursor: pointer;' : '').'">'."\n\t".
947
			'<div style="height: '.$height.'; width: '.$percent.'%; background: '.$color.';"></div>'."\n</div>\n";
948
	}
949
950
	/**
951
	 * representates a html img tag, output a picture
952
	 *
953
	 * If the name ends with a '%' and the rest is numeric, a progressionbar is shown instead of an image.
954
	 * The vfs:/ pseudo protocoll allows to access images in the vfs, eg. vfs:/home/ralf/me.png
955
	 * Instead of a name you specify an array with get-vars, it is passed to eGW's link function.
956
	 * This way session-information gets passed, eg. $name=array('menuaction'=>'myapp.class.image','id'=>123).
957
	 *
958
	 * @param string $app app-name to search the image
959
	 * @param string|array $name image-name or URL (incl. vfs:/) or array with get-vars
960
	 * @param string $title tooltip, default '' = none
961
	 * @param string $options further options for the tag, default '' = none
962
	 * @return string the html
963
	 */
964
	static function image( $app,$name,$title='',$options='' )
965
	{
966
		if (is_array($name))	// menuaction and other get-vars
967
		{
968
			$name = $GLOBALS['egw']->link('/index.php',$name);
969
		}
970
		if (substr($name,0,5) == 'vfs:/')	// vfs pseudo protocoll
971
		{
972
			$name = Framework::link(Vfs::download_url(substr($name,4)));
973
		}
974
		if ($name[0] == '/' || substr($name,0,7) == 'http://' || substr($name,0,8) == 'https://' || stripos($name,'api/thumbnail.php') )
975
		{
976
			if (!($name[0] == '/' || substr($name,0,7) == 'http://' || substr($name,0,8) == 'https://')) $name = '/'.$name;
977
			$url = $name;
978
		}
979
		else	// no URL, so try searching the image
980
		{
981
			$name = str_replace(array('.gif','.GIF','.png','.PNG'),'',$name);
982
983
			if (!($url = Image::find($app,$name)))
984
			{
985
				$url = $name;		// name may already contain absolut path
986
			}
987
			if($GLOBALS['egw_info']['server']['webserver_url'])
988
			{
989
				list(,$path) = explode($GLOBALS['egw_info']['server']['webserver_url'],$url);
990
991
				if (!is_null($path)) $path = EGW_SERVER_ROOT.$path;
0 ignored issues
show
introduced by
The condition is_null($path) is always false.
Loading history...
992
			}
993
			else
994
			{
995
				$path = EGW_SERVER_ROOT.$url;
996
			}
997
998
			if (is_null($path) || (!@is_readable($path) && stripos($path,'webdav.php')===false))
999
			{
1000
				// if the image-name is a percentage, use a progressbar
1001
				if (substr($name,-1) == '%' && is_numeric($percent = substr($name,0,-1)))
1002
				{
1003
					return self::progressbar($percent,$title);
1004
				}
1005
				return $title;
1006
			}
1007
		}
1008
		if ($title)
1009
		{
1010
			$options .= ' title="'.self::htmlspecialchars($title).'"';
1011
		}
1012
1013
		// This block makes pngfix.js useless, adding a check on disable_pngfix to have pngfix.js do its thing
1014
		if (Header\UserAgent::type() == 'msie' && Header\UserAgent::version() < 7.0 && substr($url,-4) == '.png' && ($GLOBALS['egw_info']['user']['preferences']['common']['disable_pngfix'] || !isset($GLOBALS['egw_info']['user']['preferences']['common']['disable_pngfix'])))
1015
		{
1016
			$extra_styles = "display: inline-block; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='$url',sizingMethod='image'); width: 1px; height: 1px;";
1017
			if (false!==strpos($options,'style="'))
1018
			{
1019
				$options = str_replace('style="','style="'.$extra_styles, $options);
1020
			}
1021
			else
1022
			{
1023
				$options .= ' style="'.$extra_styles.'"';
1024
			}
1025
			return "<span $options></span>";
1026
		}
1027
		return "<img src=\"$url\" $options />";
1028
	}
1029
1030
	/**
1031
	 * representates a html link
1032
	 *
1033
	 * @param string $content of the link, if '' only the opening tag gets returned
1034
	 * @param string $url eGW relative URL, will be run through the link function
1035
	 * @param string|array $vars parameters for the URL, send to link static function too
1036
	 * @param string $options attributes for the tag, default ''=none
1037
	 * @return string the html
1038
	 */
1039
	static function a_href( $content,$url,$vars='',$options='')
1040
	{
1041
		if (is_array($url))
0 ignored issues
show
introduced by
The condition is_array($url) is always false.
Loading history...
1042
		{
1043
			$vars = $url;
1044
			$url = '/index.php';
1045
		}
1046
		elseif (strpos($url,'/')===false &&
1047
			count(explode('.',$url)) >= 3 &&
1048
			!(strpos($url,'mailto:')!==false ||
1049
			strpos($url,'://')!==false ||
1050
			strpos($url,'javascript:')!==false))
1051
		{
1052
			$url = "/index.php?menuaction=$url";
1053
		}
1054
		if ($url[0] == '/')		// link relative to eGW
1055
		{
1056
			$url = self::link($url,$vars);
1057
		}
1058
		//echo "<p>self::a_href('".self::htmlspecialchars($content)."','$url',".print_r($vars,True).") = ".self::link($url,$vars)."</p>";
1059
		return '<a href="'.self::htmlspecialchars($url).'" '.$options.'>'.$content.'</a>';
1060
	}
1061
1062
	/**
1063
	 * representates a b tag (bold)
1064
	 *
1065
	 * @param string $content of the link, if '' only the opening tag gets returned
1066
	 * @return string the html
1067
	 */
1068
	static function bold($content)
1069
	{
1070
		return '<b>'.($content?$content.'</b>':'');
1071
	}
1072
1073
	/**
1074
	 * representates a hr tag (horizontal rule)
1075
	 *
1076
	 * @param string $width default ''=none given
1077
	 * @param string $options attributes for the tag, default ''=none
1078
	 * @return string the html
1079
	 */
1080
	static function hr($width='',$options='')
1081
	{
1082
		if ($width) $options .= " width=\"$width\"";
1083
1084
		return "<hr $options />\n";
1085
	}
1086
1087
	/**
1088
	 * formats option-string for most of the above functions
1089
	 *
1090
	 * Example: formatOptions('100%,,1','width,height,border') = ' width="100%" border="1"'
1091
	 *
1092
	 * @param mixed $options String (or Array) with option-values eg. '100%,,1'
1093
	 * @param mixed $names String (or Array) with the option-names eg. 'WIDTH,HEIGHT,BORDER'
1094
	 * @return string with options/attributes
1095
	 */
1096
	static function formatOptions($options,$names)
1097
	{
1098
		if (!is_array($options)) $options = explode(',',$options);
1099
		if (!is_array($names))   $names   = explode(',',$names);
1100
1101
		foreach($options as $n => $val)
1102
		{
1103
			if ($val != '' && $names[$n] != '')
1104
			{
1105
				$html .= ' '.strtolower($names[$n]).'="'.$val.'"';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $html seems to be never defined.
Loading history...
1106
			}
1107
		}
1108
		return $html;
1109
	}
1110
1111
	/**
1112
	 * html style tag (incl. type)
1113
	 *
1114
	 * @param string $styles css-style definitions
1115
	 * @return string html
1116
	 */
1117
	static function style($styles)
1118
	{
1119
		return $styles ? "<style type=\"text/css\">\n<!--\n$styles\n-->\n</style>" : '';
1120
	}
1121
1122
	/**
1123
	 * html label tag
1124
	 *
1125
	 * @param string $content the label
1126
	 * @param string $id for the for attribute, default ''=none
1127
	 * @param string $accesskey accesskey, default ''=none
1128
	 * @param string $options attributes for the tag, default ''=none
1129
	 * @return string the html
1130
	 */
1131
	static function label($content,$id='',$accesskey='',$options='')
1132
	{
1133
		if ($id != '')
1134
		{
1135
			$id = " for=\"$id\"";
1136
		}
1137
		if ($accesskey != '')
1138
		{
1139
			$accesskey = " accesskey=\"$accesskey\"";
1140
		}
1141
		return "<label$id$accesskey $options>$content</label>";
1142
	}
1143
1144
	/**
1145
	 * html fieldset, eg. groups a group of radiobuttons
1146
	 *
1147
	 * @param string $content the content
1148
	 * @param string $legend legend / label of the fieldset, default ''=none
1149
	 * @param string $options attributes for the tag, default ''=none
1150
	 * @return string the html
1151
	 */
1152
	static function fieldset($content,$legend='',$options='')
1153
	{
1154
		$html = "<fieldset $options>".($legend ? '<legend>'.self::htmlspecialchars($legend).'</legend>' : '')."\n";
1155
1156
		if ($content)
1157
		{
1158
			$html .= $content;
1159
			$html .= "\n</fieldset>\n";
1160
		}
1161
		return $html;
1162
	}
1163
1164
	/**
1165
	 * tree widget using dhtmlXtree
1166
	 *
1167
	 * Code inspired by Lars's Felamimail uiwidgets::createFolderTree()
1168
	 *
1169
	 * @author Lars Kneschke <lars-AT-kneschke.de> original code in felamimail
1170
	 * @param array $_folders array of folders: pairs path => node (string label or array with keys: label, (optional) image, (optional) title, (optional) checked)
1171
	 * @param string $_selected path of selected folder
1172
	 * @param mixed $_topFolder =false node of topFolder or false for none
1173
	 * @param string $_onNodeSelect ='alert' js static function to call if node gets selected
1174
	 * @param string $tree ='foldertree' id of the div and name of the variable containing the tree object
1175
	 * @param string $_divClass ='' css class of the div
1176
	 * @param string $_leafImage ='' default image of a leaf-node, ''=default of foldertree, set it eg. 'folderClosed.gif' to show leafs as folders
1177
	 * @param boolean|string $_onCheckHandler =false string with handler-name to display a checkbox for each folder, or false (default), 'null' switches checkboxes on without an handler!
1178
	 * @param string $delimiter ='/' path-delimiter, default /
1179
	 * @param string $folderImageDir =null string path to the tree menu images, null uses default path
1180
	 * @param string|array $autoLoading =null EGw relative path or array with get parameter, both send through Framework::link
1181
	 * @param string $dataMode ='JSON' data type for autoloading: XML, JSON, CSV
1182
	 * @param boolean $dragndrop =false true to enable drag-n-drop (must be before autoloading get enabled!)
1183
	 *
1184
	 * @return string the html code, to be added into the template
1185
	 */
1186
	static function tree($_folders,$_selected,$_topFolder=false,$_onNodeSelect="null",$tree='foldertree',$_divClass='',
1187
		$_leafImage='',$_onCheckHandler=false,$delimiter='/',$folderImageDir=null,$autoLoading=null,$dataMode='JSON',
1188
		$dragndrop=false)
1189
	{
1190
		$webserver_url = $GLOBALS['egw_info']['server']['webserver_url'];
1191
		if (empty($folderImageDir))
1192
		{
1193
			$folderImageDir = $webserver_url.'/phpgwapi/templates/default/images';
1194
		}
1195
		// check if we have template-set specific image path
1196
		$image_path = $folderImageDir;
1197
		if ($webserver_url && $webserver_url != '/')
1198
		{
1199
			list(,$image_path) = explode($webserver_url, $image_path, 2);
1200
		}
1201
		$templated_path = strtr($image_path, array(
1202
			'/phpgwapi/templates/default' => $GLOBALS['egw']->framework->template_dir,
1203
			'/default/' => '/'.$GLOBALS['egw']->framework->template.'/',
1204
		));
1205
		if (file_exists(EGW_SERVER_ROOT.$templated_path.'/dhtmlxtree'))
1206
		{
1207
			$folderImageDir = ($webserver_url != '/' ? $webserver_url : '').$templated_path;
1208
			//error_log(__METHOD__."() setting templated image-path: $folderImageDir");
1209
		}
1210
1211
		static $tree_initialised=false;
1212
		if (!$tree_initialised)
1213
		{
1214
			Framework::includeCSS('/api/js/dhtmlxtree/codebase/dhtmlxtree.css');
1215
			Framework::includeJS('/api/js/dhtmlxtree/codebase/dhtmlxcommon.js');
1216
			Framework::includeJS('/api/js/dhtmlxtree/sources/dhtmlxtree.js');
1217
			if ($autoLoading && $dataMode != 'XML') Framework::includeJS('/api/js/dhtmlxtree/sources/ext/dhtmlxtree_json.js');
1218
			$tree_initialised = true;
1219
			if (!$_folders && !$autoLoading) return null;
0 ignored issues
show
Bug Best Practice introduced by
The expression $_folders of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1220
		}
1221
		$html = self::div("\n",'id="'.$tree.'"',$_divClass).$html;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $html seems to be never defined.
Loading history...
1222
		$html .= "<script type='text/javascript'>\n";
1223
		$html .= "var $tree;";
1224
		$html .= "egw_LAB.wait(function() {";
1225
		$html .= "$tree = new"." dhtmlXTreeObject('$tree','100%','100%',0);\n";
1226
		$html .= "$tree.parentObject.style.overflow='auto';\n";	// dhtmlXTree constructor has hidden hardcoded
1227
		if (Translation::charset() == 'utf-8') $html .= "if ($tree.setEscapingMode) $tree.setEscapingMode('utf8');\n";
1228
		$html .= "$tree.setImagePath('$folderImageDir/dhtmlxtree/');\n";
1229
1230
		if($_onCheckHandler)
1231
		{
1232
			$html .= "$tree.enableCheckBoxes(1);\n";
1233
			$html .= "$tree.setOnCheckHandler('$_onCheckHandler');\n";
1234
		}
1235
1236
		if ($dragndrop) $html .= "$tree.enableDragAndDrop(true);\n";
1237
1238
		if ($autoLoading)
1239
		{
1240
			$autoLoading = is_array($autoLoading) ?
1241
				Framework::link('/index.php',$autoLoading) : Framework::link($autoLoading);
1242
			$html .= "$tree.setXMLAutoLoading('$autoLoading');\n";
1243
			if ($dataMode != 'XML') $html .= "$tree.setDataMode('$dataMode');\n";
1244
1245
			// if no folders given, use xml url to load root, incl. setting of selected folder
1246
			if (!$_folders)
0 ignored issues
show
Bug Best Practice introduced by
The expression $_folders of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1247
			{
1248
				if ($_selected) $autoLoading .= '&selected='.urlencode($_selected);
1249
				unset($_selected);
1250
				$html .= "$tree.loadXML('$autoLoading');\n";
1251
				$html .= "});";
1252
				return $html."</script>\n";
1253
			}
1254
		}
1255
1256
		$top = 0;
1257
		if ($_topFolder)
1258
		{
1259
			$top = '--topfolder--';
1260
			$topImage = '';
1261
			if (is_array($_topFolder))
1262
			{
1263
				$label = $_topFolder['label'];
1264
				if (isset($_topFolder['image']))
1265
				{
1266
					$topImage = $_topFolder['image'];
1267
				}
1268
			}
1269
			else
1270
			{
1271
				$label = $_topFolder;
1272
			}
1273
			$html .= "\n$tree.insertNewItem(0,'$top','".addslashes($label)."',$_onNodeSelect,'$topImage','$topImage','$topImage','CHILD,TOP');\n";
1274
1275
			if (is_array($_topFolder) && isset($_topFolder['title']))
1276
			{
1277
				$html .= "$tree.setItemText('$top','".addslashes($label)."','".addslashes($_topFolder['title'])."');\n";
1278
			}
1279
		}
1280
		if (is_string($_folders))
0 ignored issues
show
introduced by
The condition is_string($_folders) is always false.
Loading history...
1281
		{
1282
			switch($dataMode)
1283
			{
1284
				case 'JSON':
1285
					$html .= "$tree.loadJSONObject($_folders);\n"; break;
1286
				case 'XML':
1287
					$html .= "$tree.loadXMLString('$_folders');\n"; break;
1288
			}
1289
		}
1290
		else
1291
		{
1292
			// evtl. remove leading delimiter
1293
			if ($_selected[0] == $delimiter) $_selected = substr($_selected,1);
1294
1295
			$n = 0;
1296
			foreach($_folders as $path => $data)
1297
			{
1298
				if (!is_array($data))
1299
				{
1300
					$data = array('label' => $data);
1301
				}
1302
				$image1 = $image2 = $image3 = '0';
1303
1304
				// if _leafImage given, set it only for leaves, not for folders containing children
1305
				if ($_leafImage)
1306
				{
1307
					$image1 = $image2 = $image3 = "'".$_leafImage."'";
1308
					if (($next_item = array_slice($_folders, $n+1, 1, true)))
1309
					{
1310
						$next_path = key($next_item);
1311
						if (substr($next_path,0,strlen($path)+1) == $path.'/')
1312
						{
1313
							$image1 = $image2 = $image3 = '0';
1314
						}
1315
					}
1316
				}
1317
				if (isset($data['image']))
1318
				{
1319
					$image1 = $image2 = $image3 = "'".$data['image']."'";
1320
				}
1321
				// evtl. remove leading delimiter
1322
				if ($path[0] == $delimiter) $path = substr($path,1);
1323
				$folderParts = explode($delimiter,$path);
1324
1325
				//get rightmost folderpart
1326
				$label = array_pop($folderParts);
1327
				if (isset($data['label'])) $label = $data['label'];
1328
1329
				// the rest of the array is the name of the parent
1330
				$parentName = implode((array)$folderParts,$delimiter);
0 ignored issues
show
Unused Code introduced by
The call to implode() has too many arguments starting with $delimiter. ( Ignorable by Annotation )

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

1330
				$parentName = /** @scrutinizer ignore-call */ implode((array)$folderParts,$delimiter);

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...
1331
				if(empty($parentName)) $parentName = $top;
1332
1333
				$entryOptions = !isset($data['child']) || $data['child'] ? 'CHILD' : '';
1334
				if ($_onCheckHandler && $_selected)	// check selected items on multi selection
1335
				{
1336
					if (!is_array($_selected)) $_selected = explode(',',$_selected);
1337
					if (array_search("$path",$_selected)!==false) $entryOptions .= ',CHECKED';
1338
					//echo "<p>path=$path, _selected=".print_r($_selected,true).": $entryOptions</p>\n";
1339
				}
1340
				// highlight current item
1341
				elseif ((string)$_selected === (string)$path)
1342
				{
1343
					$entryOptions .= ',SELECT';
1344
				}
1345
				$html .= "$tree.insertNewItem('".addslashes($parentName)."','".addslashes($path)."','".addslashes($label).
1346
					"',$_onNodeSelect,$image1,$image2,$image3,'$entryOptions');\n";
1347
				if (isset($data['title']))
1348
				{
1349
					$html .= "$tree.setItemText('".addslashes($path)."','".addslashes($label)."','".addslashes($data['title'])."');\n";
1350
				}
1351
				++$n;
1352
			}
1353
		}
1354
		$html .= "$tree.closeAllItems(0);\n";
1355
		if ($_selected)
1356
		{
1357
			foreach(is_array($_selected)?$_selected:array($_selected) as $path)
1358
			{
1359
				$html .= "$tree.openItem('".addslashes($path)."');\n";
1360
			}
1361
		}
1362
		else
1363
		{
1364
				$html .= "$tree.openItem('$top');\n";
1365
		}
1366
		$html .= "});";
1367
		$html .= "</script>\n";
1368
1369
		return $html;
1370
	}
1371
1372
	/**
1373
	 * Runs HTMLPurifier over supplied html to remove malicious code
1374
	 *
1375
	 * @param string $html
1376
	 * @param array|string $config =null - config to influence the behavior of current purifying engine
1377
	 * @param array|string $spec =null - spec to influence the behavior of current purifying engine
1378
	 *		The $spec argument can be used to disallow an otherwise legal attribute for an element,
1379
	 *		or to restrict the attribute's values
1380
	 * @param boolean $_force =null - force the config passed to be used without merging to the default
1381
	 */
1382
	static function purify($html,$config=null,$spec=array(),$_force=false)
1383
	{
1384
		return Html\HtmLawed::purify($html, $config, $spec, $_force);
1385
	}
1386
}
1387