1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8//this script may only be included - so its better to die if called directly.
9if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) {
10	header("location: index.php");
11	exit;
12}
13
14include_once('lib/smarty_tiki/block.self_link.php');
15
16$toolbarPickerIndex = -1;
17
18abstract class Toolbar
19{
20	protected $wysiwyg;
21	protected $icon;
22	protected $iconname;
23	protected $label;
24	protected $type;
25
26	private $requiredPrefs = [];
27
28	public static function getTag($tagName, $wysiwyg = false, $is_html = false) // {{{
29	{
30		global $section;
31		//we detect sheet first because it has unique buttons
32		if ($section == 'sheet' && $tag = ToolbarSheet::fromName($tagName)) {
33			return $tag;
34		} elseif ($wysiwyg && $tag = ToolbarCkOnly::fromName($tagName, $is_html)) {
35			return $tag;
36		} elseif ($tag = Toolbar::getCustomTool($tagName)) {
37			return $tag;
38		} elseif ($tag = ToolbarInline::fromName($tagName)) {
39			return $tag;
40		} elseif ($tag = ToolbarBlock::fromName($tagName)) {
41			return $tag;
42		} elseif ($tag = ToolbarLineBased::fromName($tagName)) {
43			return $tag;
44		} elseif ($tag = ToolbarWikiplugin::fromName($tagName)) {
45			return $tag;
46		} elseif ($tag = ToolbarPicker::fromName($tagName)) {
47			return $tag;
48		} elseif ($tag = ToolbarDialog::fromName($tagName)) {
49			return $tag;
50		} elseif ($tagName == 'fullscreen') {
51			return new ToolbarFullscreen;
52		} elseif ($tagName == 'tikiimage') {
53			return new ToolbarFileGallery;
54		} elseif ($tagName == 'tikifile') {
55			return new ToolbarFileGalleryFile;
56		} elseif ($tagName == 'help') {
57			return new ToolbarHelptool;
58		} elseif ($tagName == 'switcheditor') {
59			return new ToolbarSwitchEditor;
60		} elseif ($tagName == 'admintoolbar') {
61			return new ToolbarAdmin;
62		} elseif ($tagName == '-') {
63			return new ToolbarSeparator;
64		} elseif ($tagName == '|') {
65			return new ToolbarSpacer;
66		}
67	} // }}}
68
69	public static function getList($include_custom = true) // {{{
70	{
71		global $tikilib;
72		$parserlib = TikiLib::lib('parser');
73		$plugins = $parserlib->plugin_get_list();
74
75		foreach ($plugins as & $name) {
76			$name = "wikiplugin_$name";
77		}
78
79		if ($include_custom) {
80			$custom = Toolbar::getCustomList();
81			$plugins = array_merge($plugins, $custom);
82		}
83
84		return array_unique(
85			array_merge(
86				[
87					'-',
88					'|',
89					'bold',
90					'italic',
91					'underline',
92					'strike',
93					'code',
94					'sub',
95					'sup',
96					'tikilink',
97					'link',
98					'anchor',
99					'color',
100					'bgcolor',
101					'center',
102					'table',
103					'rule',
104					'pagebreak',
105					'box',
106					'email',
107					'h1',
108					'h2',
109					'h3',
110					'titlebar',
111					'pastlink',
112					'toc',
113					'list',
114					'numlist',
115					'specialchar',
116					'smiley',
117					'templates',
118					'cut',
119					'copy',
120					'paste',
121					'pastetext',
122					'pasteword',
123					'print',
124					'spellcheck',
125					'undo',
126					'redo',
127					'find',
128					'replace',
129					'selectall',
130					'removeformat',
131					'showblocks',
132					'left',
133					'right',
134					'full',
135					'indent',
136					'outdent',
137					'unlink',
138					'style',
139					'fontname',
140					'fontsize',
141					'format',
142					'source',
143					'fullscreen',
144					'help',
145					'tikiimage',
146					'tikifile',
147					'switcheditor',
148					'autosave',
149					'admintoolbar',
150					'nonparsed',
151					'bidiltr',
152					'bidirtl',
153					'screencapture',
154					'image',
155
156					'sheetsave',	// spreadsheet ones
157					'addrow',
158					'addrowmulti',
159					'addrowbefore',
160					'deleterow',
161					'addcolumn',
162					'addcolumnbefore',
163					'deletecolumn',
164					'addcolumnmulti',
165					'sheetgetrange',
166					'sheetfind',
167					'sheetrefresh',
168					'sheetclose',
169
170					'objectlink',
171					'tikitable',
172				],
173				$plugins
174			)
175		);
176	} // }}}
177
178	public static function getCustomList()
179	{
180
181		global $prefs;
182		if (isset($prefs['toolbar_custom_list'])) {
183			$custom = @unserialize($prefs['toolbar_custom_list']);
184			sort($custom);
185		} else {
186			$custom = [];
187		}
188
189		return $custom;
190	}
191
192	public static function getCustomTool($name)
193	{
194		global $prefs;
195		if (isset($prefs["toolbar_tool_$name"])) {
196			$data = unserialize($prefs["toolbar_tool_$name"]);
197			$tag = Toolbar::fromData($name, $data);
198			return $tag;
199		} else {
200			return null;
201		}
202	}
203
204	public static function isCustomTool($name)
205	{
206		global $prefs;
207		return isset($prefs["toolbar_tool_$name"]);
208	}
209
210	public static function saveTool($name, $label, $icon = 'img/icons/shading.png', $token = '', $syntax = '', $type = 'Inline', $plugin = '')
211	{
212		global $tikilib;
213
214		$name = strtolower(TikiLib::remove_non_word_characters_and_accents($name));
215		$standard_names = Toolbar::getList(false);
216		$custom_list = Toolbar::getCustomList();
217		if (in_array($name, $standard_names)) {		// don't allow custom tools with the same name as standard ones
218			$c = 1;
219			while (in_array($name . '_' . $c, $custom_list)) {
220				$c++;
221			}
222			$name = $name . '_' . $c;
223		}
224
225		$prefName = "toolbar_tool_$name";
226		$data = ['name' => $name, 'label' => $label, 'token' => $token, 'syntax' => $syntax, 'type' => $type, 'plugin' => $plugin];
227
228		if (strpos($icon, 'img/icons/') !== false) {
229			$data['icon'] = $icon;
230		} else {
231			$data['iconname'] = $icon;
232		}
233
234		$tikilib->set_preference($prefName, serialize($data));
235
236		if (! in_array($name, $custom_list)) {
237			$custom_list[] = $name;
238			$tikilib->set_preference('toolbar_custom_list', serialize($custom_list));
239		}
240	}
241
242	public static function deleteTool($name)
243	{
244		global $prefs, $tikilib;
245
246		$name = strtolower($name);
247
248		$prefName = "toolbar_tool_$name";
249		if (isset($prefs[$prefName])) {
250			$tikilib->delete_preference($prefName);
251
252			$list = [];
253			if (isset($prefs['toolbar_custom_list'])) {
254				$list = unserialize($prefs['toolbar_custom_list']);
255			}
256			if (in_array($name, $list)) {
257				$list = array_diff($list, [$name]);
258				$tikilib->set_preference('toolbar_custom_list', serialize($list));
259			}
260		}
261	}
262
263	public static function deleteAllCustomTools()
264	{
265		$tikilib = TikiLib::lib('tiki');
266
267		$tikilib->query('DELETE FROM `tiki_preferences` WHERE `name` LIKE \'toolbar_tool_%\'');
268		$tikilib->delete_preference('toolbar_custom_list');
269	}
270
271
272	public static function fromData($tagName, $data)
273	{
274 // {{{
275
276		$tag = null;
277
278		switch ($data['type']) {
279			case 'Inline':
280				$tag = new ToolbarInline();
281				 $tag->setSyntax($data['syntax']);
282				break;
283			case 'Block':
284				$tag = new ToolbarBlock();
285				$tag->setSyntax($data['syntax']);
286				break;
287			case 'LineBased':
288				$tag = new ToolbarLineBased();
289				$tag->setSyntax($data['syntax']);
290				break;
291			case 'Picker':
292				$tag = new ToolbarPicker();
293				break;
294			case 'Separator':
295				$tag = new ToolbarSeparator();
296				break;
297			case 'Spacer':
298				$tag = new ToolbarSpacer();
299				break;
300			case 'CkOnly':
301				$tag = new ToolbarCkOnly($tagName);
302				break;
303			case 'Fullscreen':
304				$tag = new ToolbarFullscreen();
305				break;
306			case 'TextareaResize':
307				$tag = new ToolbarTextareaResize();
308				break;
309			case 'Helptool':
310				$tag = new ToolbarHelptool();
311				break;
312			case 'FileGallery':
313				$tag = new ToolbarFileGallery();
314				break;
315			case 'Wikiplugin':
316				if (! isset($data['plugin'])) {
317					$data['plugin'] = '';
318				}
319				$tag = ToolbarWikiplugin::fromName('wikiplugin_' . $data['plugin']);
320				if (empty($tag)) {
321					$tag = new ToolbarWikiplugin();
322				}
323				break;
324			default:
325				$tag = new ToolbarInline();
326				break;
327		}
328
329		$tag->setLabel($data['label'])
330			->setWysiwygToken($data['token'])
331			->setIconName(! empty($data['iconname']) ? $data['iconname'] : 'help')
332			->setIcon(! empty($data['icon']) ? $data['icon'] : 'img/icons/shading.png')
333			->setType($data['type']);
334
335		return $tag;
336	}	// }}}
337
338	abstract function getWikiHtml($areaId);
339
340	function isAccessible() // {{{
341	{
342		global $prefs;
343
344		foreach ($this->requiredPrefs as $prefName) {
345			if (! isset($prefs[$prefName]) || $prefs[$prefName] != 'y') {
346				return false;
347			}
348		}
349
350		return true;
351	} // }}}
352
353	protected function addRequiredPreference($prefName) // {{{
354	{
355		$this->requiredPrefs[] = $prefName;
356	} // }}}
357
358	protected function setIcon($icon) // {{{
359	{
360		$this->icon = $icon;
361
362		return $this;
363	} // }}}
364
365	protected function setIconName($iconname) // {{{
366	{
367		$this->iconname = $iconname;
368
369		return $this;
370	} // }}}
371
372	protected function setLabel($label) // {{{
373	{
374		$this->label = $label;
375
376		return $this;
377	} // }}}
378
379	protected function setWysiwygToken($token) // {{{
380	{
381		$this->wysiwyg = $token;
382
383		return $this;
384	} // }}}
385
386	protected function setSyntax($syntax) // {{{
387	{
388		return $this;
389	} // }}}
390
391	protected function setType($type) // {{{
392	{
393		$this->type = $type;
394
395		return $this;
396	} // }}}
397
398	function getIcon() // {{{
399	{
400		return $this->icon;
401	} // }}}
402
403	function getLabel() // {{{
404	{
405		return $this->label;
406	} // }}}
407
408	function getWysiwygToken($areaId) // {{{
409	{
410		return $this->wysiwyg;
411	} // }}}
412
413
414	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
415	{
416		return null;
417	} // }}}
418
419	function getSyntax($areaId) // {{{
420	{
421		return '';
422	} // }}}
423
424	function getType() // {{{
425	{
426		return $this->type;
427	} // }}}
428
429	function getIconHtml() // {{{
430	{
431		if (! empty($this->iconname)) {
432			$iname = $this->iconname;
433		} elseif (! empty($this->icon)) {
434			$headerlib = TikiLib::lib('header');
435			return '<img src="' . htmlentities($headerlib->convert_cdn($this->icon), ENT_QUOTES, 'UTF-8') . '" alt="'
436			. htmlentities($this->getLabel(), ENT_QUOTES, 'UTF-8') . '" title=":'
437			. htmlentities($this->getLabel(), ENT_QUOTES, 'UTF-8') . '" class="tips bottom icon">';
438		} else {
439			$iname = 'help';
440		}
441		$smarty = TikiLib::lib('smarty');
442		$smarty->loadPlugin('smarty_function_icon');
443		return smarty_function_icon(['name' => $iname, 'ititle' => ':'
444				. htmlentities($this->getLabel(), ENT_QUOTES, 'UTF-8'), 'iclass' => 'tips bottom'], $smarty->getEmptyInternalTemplate());
445	} // }}}
446
447	function getSelfLink($click, $title, $class)
448	{
449 // {{{
450		global $prefs;
451		$smarty = TikiLib::lib('smarty');
452		$params = [];
453		$params['_onclick'] = $click . (substr($click, strlen($click) - 1) != ';' ? ';' : '') . 'return false;';
454		$params['_class'] = 'toolbar btn btn-sm px-2 tips bottom' . (! empty($class) ? ' ' . $class : '');
455		$params['_ajax'] = 'n';
456		$content = $title;
457		if ($this->iconname) {
458			$params['_icon_name'] = $this->iconname;
459			$colon = $prefs['javascript_enabled'] === 'y' ? ':' : '';
460			$params['_title'] = $colon . $title;
461		} else {
462			$params['_icon'] = $this->icon;
463		}
464
465		if (strpos($class, 'qt-plugin') !== false && ($this->iconname == 'plugin'
466				|| $this->icon == 'img/icons/plugin.png')) {
467			$params['_menu_text'] = 'y';
468			$params['_menu_icon'] = 'y';
469		}
470		$smarty->loadPlugin('smarty_block_self_link');
471		return smarty_block_self_link($params, $content, $smarty->getEmptyInternalTemplate());
472	} // }}}
473
474	protected function setupCKEditorTool($js, $name, $label = '', $icon = '')
475	{
476		if (empty($label)) {
477			$label = $name;
478		}
479		$label = addcslashes($label, "'");
480		TikiLib::lib('header')->add_js(
481			<<< JS
482if (typeof window.CKEDITOR !== "undefined" && !window.CKEDITOR.plugins.get("{$name}")) {
483	window.CKEDITOR.config.extraPlugins += (window.CKEDITOR.config.extraPlugins ? ',{$name}' : '{$name}' );
484	window.CKEDITOR.plugins.add( '{$name}', {
485		init : function( editor ) {
486			var command = editor.addCommand( '{$name}', new window.CKEDITOR.command( editor , {
487				modes: { wysiwyg:1 },
488				exec: function (editor, data) {
489					{$js}
490				},
491				canUndo: false
492			}));
493			editor.ui.addButton( '{$name}', {
494				label : '{$label}',
495				command : '{$name}',
496				icon: editor.config._TikiRoot + '{$icon}'
497			});
498		}
499	});
500}
501JS
502			,
503			10
504		);
505	}
506}
507
508class ToolbarSeparator extends Toolbar
509{
510	function __construct() // {{{
511	{
512		$this->setWysiwygToken('-')
513			->setIcon('img/separator.gif')
514				->setType('Separator');
515	} // }}}
516
517	function getWikiHtml($areaId) // {{{
518	{
519		return '|';
520	} // }}}
521}
522
523class ToolbarSpacer extends Toolbar
524{
525	function __construct() // {{{
526	{
527		$this->setWysiwygToken('|')
528			->setIcon('img/trans.png')
529				->setType('Spacer');
530	} // }}}
531
532	function getWikiHtml($areaId) // {{{
533	{
534		return '||';
535	} // }}}
536}
537
538class ToolbarCkOnly extends Toolbar
539{
540	function __construct($token, $icon = '', $iconname = '') // {{{
541	{
542		if (empty($icon)) {
543			$img_path = 'img/ckeditor/' . strtolower($token) . '.png';
544			if (is_file($img_path)) {
545				$icon = $img_path;
546			} else {
547				$icon = 'img/icons/shading.png';
548			}
549		}
550		$this->setWysiwygToken($token)
551			->setIcon($icon)
552			->setIconName($iconname)
553			->setType('CkOnly');
554	} // }}}
555
556	public static function fromName($name, $is_html) // {{{
557	{
558		global $prefs;
559
560		switch ($name) {
561			case 'templates':
562				if ($prefs['feature_wiki_templates'] === 'y') {
563					return new self('Templates');
564				} else {
565					return null;
566				}
567			case 'cut':
568				return new self('Cut', null, 'scissors');
569			case 'copy':
570				return new self('Copy', null, 'copy');
571			case 'paste':
572				return new self('Paste', null, 'paste');
573			case 'pastetext':
574				return new self('PasteText', null, 'paste');
575			case 'pasteword':
576				return new self('PasteFromWord', null, 'paste');
577			case 'print':
578				return new self('Print', null, 'print');
579			case 'spellcheck':
580				return new self('SpellChecker', null, 'ok');
581			case 'undo':
582				return new self('Undo', null, 'undo');
583			case 'redo':
584				return new self('Redo', null, 'repeat');
585			case 'selectall':
586				return new self('SelectAll', null, 'selectall');
587			case 'removeformat':
588				return new self('RemoveFormat', null, 'erase');
589			case 'showblocks':
590				return new self('ShowBlocks', null, 'box');
591			case 'left':
592				return new self('JustifyLeft', null, 'align-left');
593			case 'right':
594				return new self('JustifyRight', null, 'align-right');
595			case 'full':
596				return new self('JustifyBlock', null, 'align-justify');
597			case 'indent':
598				return new self('Indent', null, 'indent');
599			case 'outdent':
600				return new self('Outdent', null, 'outdent');
601			case 'style':
602				return new self('Styles');
603			case 'fontname':
604				return new self('Font');
605			case 'fontsize':
606				return new self('FontSize');
607			case 'format':
608				return 	new self('Format');
609			case 'source':
610				global $tikilib, $user, $page;
611				$p = $prefs['wysiwyg_htmltowiki'] == 'y' ? 'tiki_p_wiki_view_source' : 'tiki_p_use_HTML';
612				if ($tikilib->user_has_perm_on_object($user, $page, 'wiki page', $p)) {
613					return new self('Source', null, 'code_file');
614				} else {
615					return null;
616				}
617			case 'autosave':
618				return new self('autosave', 'img/ckeditor/ajaxSaveDirty.gif', 'floppy');
619			case 'inlinesave':
620				return new self('Inline save', 'img/ckeditor/ajaxSaveDirty.gif');
621			case 'inlinecancel':
622				return new self('Inline cancel', 'img/icons/cross.png');
623			case 'sub':
624				return new self('Subscript', null, 'subscript');
625			case 'sup':
626				return new self('Superscript', null, 'subscript');
627			case 'anchor':
628				return new self('Anchor', null, 'anchor');
629			case 'bidiltr':
630				return new self('BidiLtr', null, 'arrow-right');
631			case 'bidirtl':
632				return new self('BidiRtl', null, 'arrow-left');
633			case 'image':
634				return new self('Image', null, 'image');
635			case 'table':
636				return $is_html ? new self('Table') : null;
637			case 'link':
638				return $is_html ? new self('Link') : null;
639			case 'unlink':
640				return new self('Unlink', null, 'unlink');
641		}
642	} // }}}
643
644	function getWikiHtml($areaId) // {{{
645	{
646		return null;
647	} // }}}
648
649	function getWysiwygToken($areaId)
650	{
651		if ($this->wysiwyg === 'Image') {	// cke's own image tool
652			global $prefs;
653			$headerlib = TikiLib::lib('header');
654			// can't do upload the cke way yet
655			$url = 'tiki-list_file_gallery.php?galleryId=' . $prefs['home_file_gallery'] . '&filegals_manager=fgal_picker';
656			$headerlib->add_js('if (typeof window.CKEDITOR !== "undefined") {window.CKEDITOR.config.filebrowserBrowseUrl = "' . $url . '"}', 5);
657		}
658		return $this->wysiwyg;
659	}
660
661	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
662	{
663		switch ($this->wysiwyg) {
664			case 'autosave':
665			case 'Copy':
666			case 'Cut':
667			case 'Format':
668			case 'JustifyLeft':
669			case 'Paste':
670			case 'PasteText':
671			case 'PasteFromWord':
672			case 'Redo':
673			case 'RemoveFormat':
674			case 'ShowBlocks':
675			case 'Source':
676			case 'Undo':
677			case 'Unlink':
678				return $this->wysiwyg;
679				break;
680			default:
681				return null;
682		}
683	} // }}}
684
685	function getLabel() // {{{
686	{
687		return $this->wysiwyg;
688	} // }}}
689
690	function getIconHtml() // {{{ for admin page
691	{
692
693		if (! empty($this->iconname)) {
694			$smarty = TikiLib::lib('smarty');
695			$smarty->loadPlugin('smarty_function_icon');
696			return smarty_function_icon(['name' => $this->iconname, 'ititle' => ':'
697					. htmlentities($this->getLabel(), ENT_QUOTES, 'UTF-8'), 'iclass' => 'tips bottom'], $smarty->getEmptyInternalTemplate());
698		}
699		if ((! empty($this->icon) && $this->icon !== 'img/icons/shading.png') || in_array($this->label, ['Autosave'])) {
700			return parent::getIconHtml();
701		}
702
703		global $prefs;
704		$skin = $prefs['wysiwyg_toolbar_skin'];
705		$headerlib = TikiLib::lib('header');
706		$headerlib->add_cssfile('vendor_bundled/vendor/ckeditor/ckeditor/skins/' . $skin . '/editor.css');
707		$cls = strtolower($this->wysiwyg);
708		$headerlib->add_css(
709			'span.cke_skin_' . $skin . ' {border: none;background: none;padding:0;margin:0;}' .
710			'.toolbars-admin .row li.toolbar > span.cke_skin_' . $skin . ' {display: inline-block;}'
711		);
712		return '<span class="cke_skin_' . $skin
713			. '"><a class="cke_button cke_ltr" style="margin-top:-5px"><span class="cke_button__'
714			. htmlentities($cls, ENT_QUOTES, 'UTF-8') . '_icon"' .
715			' title="' . htmlentities($this->getLabel(), ENT_QUOTES, 'UTF-8') . '">' .
716			'<span class="cke_icon"> </span>' .
717			'</span></a></span>';
718	} // }}}
719}
720
721class ToolbarInline extends Toolbar
722{
723	protected $syntax;
724
725	public static function fromName($tagName) // {{{
726	{
727		switch ($tagName) {
728			case 'bold':
729				$label = tra('Bold');
730				$icon = tra('img/icons/text_bold.png');
731				$iconname = 'bold';
732				$wysiwyg = 'Bold';
733				$syntax = '__text__';
734				break;
735			case 'italic':
736				$label = tra('Italic');
737				$icon = tra('img/icons/text_italic.png');
738				$iconname = 'italic';
739				$wysiwyg = 'Italic';
740				$syntax = "''text''";
741				break;
742			case 'underline':
743				$label = tra('Underline');
744				$icon = tra('img/icons/text_underline.png');
745				$iconname = 'underline';
746				$wysiwyg = 'Underline';
747				$syntax = "===text===";
748				break;
749			case 'strike':
750				$label = tra('Strikethrough');
751				$icon = tra('img/icons/text_strikethrough.png');
752				$iconname = 'strikethrough';
753				$wysiwyg = 'Strike';
754				$syntax = '--text--';
755				break;
756			case 'code':
757				$label = tra('Code');
758				$icon = tra('img/icons/page_white_code.png');
759				$iconname = 'code';
760				$wysiwyg = 'Code';
761				$syntax = '-+text+-';
762				break;
763			case 'nonparsed':
764				$label = tra('Non-parsed (wiki syntax does not apply)');
765				$icon = tra('img/icons/noparse.png');
766				$iconname = 'ban';
767				$wysiwyg = null;
768				$syntax = '~np~text~/np~';
769				break;
770			default:
771				return;
772		}
773
774		$tag = new self;
775		$tag->setLabel($label)
776			->setWysiwygToken($wysiwyg)
777			->setIconName(! empty($iconname) ? $iconname : 'help')
778			->setIcon(! empty($icon) ? $icon : 'img/icons/shading.png')
779			->setSyntax($syntax)
780			->setType('Inline');
781
782		return $tag;
783	} // }}}
784
785	function getSyntax($areaId) // {{{
786	{
787		return $this->syntax;
788	} // }}}
789
790	function setSyntax($syntax) // {{{
791	{
792		$this->syntax = $syntax;
793
794		return $this;
795	} // }}}
796
797	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
798	{
799		return $this->getWysiwygToken($areaId);
800	} // }}}
801
802	function getWikiHtml($areaId) // {{{
803	{
804		if ($this->syntax == '~np~text~/np~') {	// closing ~/np~ tag breaks toolbar when inside nested plugins
805			return $this->getSelfLink(
806				'insertAt(\'' . $areaId . '\', \'~np~text~\'+\'/np~\');',
807				htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
808				'qt-inline'
809			);
810		} else {
811			return $this->getSelfLink(
812				'insertAt(\'' . $areaId . '\', \'' . addslashes(htmlentities($this->syntax, ENT_COMPAT, 'UTF-8')) . '\');',
813				htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
814				'qt-inline'
815			);
816		}
817	} // }}}
818}
819
820class ToolbarBlock extends ToolbarInline // Will change in the future
821{
822	protected $syntax;
823
824	public static function fromName($tagName) // {{{
825	{
826		global $prefs;
827
828		$label = null;
829		$wysiwyg = null;
830		$syntax = null;
831
832		switch ($tagName) {
833			case 'center':
834				$label = tra('Align Center');
835				$iconname = 'align-center';
836				$wysiwyg = 'JustifyCenter';
837				if ($prefs['feature_use_three_colon_centertag'] == 'y') {
838					$syntax = ":::text:::";
839				} else {
840					$syntax = "::text::";
841				}
842				break;
843			case 'rule':
844				$label = tra('Horizontal Bar');
845				$iconname = 'horizontal-rule';
846				$wysiwyg = 'HorizontalRule';
847				$syntax = '---';
848				break;
849			case 'pagebreak':
850				$label = tra('Page Break');
851				$iconname = 'page-break';
852				$wysiwyg = 'PageBreak';
853				$syntax = '...page...';
854				break;
855			case 'box':
856				$label = tra('Box');
857				$iconname = 'box';
858				$wysiwyg = 'Box';
859				$syntax = '^text^';
860				break;
861			case 'email':
862				$label = tra('Email');
863				$iconname = 'envelope';
864				$wysiwyg = null;
865				$syntax = '[mailto:email@example.com|text]';
866				break;
867			case 'h1':
868			case 'h2':
869			case 'h3':
870				$label = tra('Heading') . ' ' . $tagName{1};
871				$iconname = $tagName;
872				$wysiwyg = null;
873				$syntax = str_repeat('!', $tagName{1}) . ' text';
874				break;
875			case 'titlebar':
876				$label = tra('Title bar');
877				$iconname = 'title';
878				$wysiwyg = null;
879				$syntax = '-=text=-';
880				break;
881			case 'toc':
882				$label = tra('Table of contents');
883				$iconname = 'book';
884				$wysiwyg = 'TOC';
885				$syntax = '{maketoc}';
886				break;
887			default:
888				return;
889		}
890
891		$tag = new self;
892		$tag->setLabel($label)
893			->setWysiwygToken($wysiwyg)
894			->setIconName(! empty($iconname) ? $iconname : 'help')
895			->setSyntax($syntax)
896			->setType('Block');
897
898		return $tag;
899	} // }}}
900
901	function getWikiHtml($areaId) // {{{
902	{
903		if ($this->syntax == '...page...') {	// for some reason breaks toolbar when inside nested plugins
904			return $this->getSelfLink(
905				'insertAt(\'' . $areaId . '\', \'...\'+\'page\'+\'...\');',
906				htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
907				'qt-block'
908			);
909		} else {
910			return $this->getSelfLink(
911				'insertAt(\'' . $areaId . '\', \'' . addslashes(htmlentities($this->syntax, ENT_COMPAT, 'UTF-8')) . '\', true);',
912				htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
913				'qt-block'
914			);
915		}
916	} // }}}
917}
918
919class ToolbarLineBased extends ToolbarInline // Will change in the future
920{
921	protected $syntax;
922
923	public static function fromName($tagName) // {{{
924	{
925		switch ($tagName) {
926			case 'list':
927				$label = tra('Bullet List');
928				$iconname = 'list';
929				$wysiwyg = 'BulletedList';
930				$syntax = '* text';
931				break;
932			case 'numlist':
933				$label = tra('Numbered List');
934				$iconname = 'list-numbered';
935				$wysiwyg = 'NumberedList';
936				$syntax = '# text';
937				break;
938			case 'indent':
939				global $prefs;
940				return null;
941			break;
942			default:
943				return null;
944		}
945
946		$tag = new self;
947		$tag->setLabel($label)
948			->setWysiwygToken($wysiwyg)
949			->setIconName(! empty($iconname) ? $iconname : 'help')
950			->setSyntax($syntax)
951			->setType('LineBased');
952
953		return $tag;
954	} // }}}
955
956	function getWikiHtml($areaId) // {{{
957	{
958		return $this->getSelfLink(
959			'insertAt(\'' . $areaId . '\', \'' . addslashes(htmlentities($this->syntax, ENT_COMPAT, 'UTF-8')) . '\', true, true);',
960			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
961			'qt-line'
962		);
963	} // }}}
964}
965
966
967class ToolbarPicker extends Toolbar
968{
969	private $list;
970	private $name;
971
972	public static function fromName($tagName) // {{{
973	{
974		global $section, $prefs;
975		$headerlib = TikiLib::lib('header');
976
977		$tool_prefs = [];
978		$styleType = '';
979
980		switch ($tagName) {
981			case 'specialchar':
982				$wysiwyg = 'SpecialChar';
983				$label = tra('Special Characters');
984				$iconname = 'keyboard';
985				// Line taken from DokuWiki + some added chars for Tiki
986				$list = explode(' ', 'À à Á á  â à ã Ä ä Ǎ ǎ Ă ă Å å Ā ā Ą ą Æ æ Ć ć Ç ç Č č Ĉ ĉ Ċ ċ Ð đ ð Ď ď È è É é Ê ê Ë ë Ě ě Ē ē Ė ė Ę ę Ģ ģ Ĝ ĝ Ğ ğ Ġ ġ Ĥ ĥ Ì ì Í í Î î Ï ï Ǐ ǐ Ī ī İ ı Į į Ĵ ĵ Ķ ķ Ĺ ĺ Ļ ļ Ľ ľ Ł ł Ŀ ŀ Ń ń Ñ ñ Ņ ņ Ň ň Ò ò Ó ó Ô ô Õ õ Ö ö Ǒ ǒ Ō ō Ő ő Œ œ Ø ø Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ş ş Š š Ŝ ŝ Ţ ţ Ť ť Ù ù Ú ú Û û Ü ü Ǔ ǔ Ŭ ŭ Ū ū Ů ů ǖ ǘ ǚ ǜ Ų ų Ű ű Ŵ ŵ Ý ý Ÿ ÿ Ŷ ŷ Ź ź Ž ž Ż ż Þ þ ß Ħ ħ ¿ ¡ ¢ £ ¤ ¥ € ¦ § ª ¬ ¯ ° ± ÷ ‰ ¼ ½ ¾ ¹ ² ³ µ ¶ † ‡ · • º ∀ ∂ ∃ Ə ə ∅ ∇ ∈ ∉ ∋ ∏ ∑ ‾ − ∗ √ ∝ ∞ ∠ ∧ ∨ ∩ ∪ ∫ ∴ ∼ ≅ ≈ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊄ ⊆ ⊇ ⊕ ⊗ ⊥ ⋅ ◊ ℘ ℑ ℜ ℵ ♠ ♣ ♥ ♦ �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� ★ ☆ ☎ ☚ ☛ ☜ ☝ ☞ ☟ ☹ ☺ ✔ ✘ × „ “ ” ‚ ‘ ’ « » ‹ › — – … ← ↑ → ↓ ↔ ⇐ ⇑ ⇒ ⇓ ⇔ © ™ ® ′ ″ @ % ~ | [ ] { } * #');
987				$list = array_combine($list, $list);
988				break;
989			case 'smiley':
990				$wysiwyg = 'Smiley';
991				$label = tra('Smileys');
992				$iconname = 'smile';
993				$rawList = [ 'biggrin', 'confused', 'cool', 'cry', 'eek', 'evil', 'exclaim', 'frown', 'idea', 'lol', 'mad', 'mrgreen', 'neutral', 'question', 'razz', 'redface', 'rolleyes', 'sad', 'smile', 'surprised', 'twisted', 'wink', 'arrow', 'santa' ];
994				$tool_prefs[] = 'feature_smileys';
995
996				$list = [];
997				foreach ($rawList as $smiley) {
998					$tra = htmlentities(tra($smiley), ENT_QUOTES, 'UTF-8');
999					$list["(:$smiley:)"] = '<img src="' . $headerlib->convert_cdn('img/smiles/icon_' . $smiley . '.gif') . '" alt="' . $tra . '" title="' . $tra . '" width="15" height="15" />';
1000				}
1001
1002				break;
1003			case 'color':
1004				$wysiwyg = 'TextColor';
1005				$label = tra('Foreground color');
1006				$iconname = 'font-color';
1007				$rawList = [];
1008				$styleType = 'color';
1009
1010				$hex = ['0', '3', '6', '8', '9', 'C', 'F'];
1011				$count_hex = count($hex);
1012
1013				for ($r = 0; $r < $count_hex; $r += 2) { // red
1014					for ($g = 0; $g < $count_hex; $g += 2) { // green
1015						for ($b = 0; $b < $count_hex; $b += 2) { // blue
1016							$color = $hex[$r] . $hex[$g] . $hex[$b];
1017							$rawList[] = $color;
1018						}
1019					}
1020				}
1021
1022				$list = [];
1023				foreach ($rawList as $color) {
1024					$list["~~#$color:text~~"] = "<span style='background-color: #$color' title='#$color' />&nbsp;</span>";
1025				}
1026
1027				if ($section == 'sheet') {
1028					$list['reset'] = "<span title=':" . tra("Reset Colors") . "' class='toolbars-picker-reset' reset='true'>" . tra("Reset") . "</span>";
1029				}
1030
1031				break;
1032
1033			case 'bgcolor':
1034				$label = tra('Background Color');
1035				$iconname = 'background-color';
1036				$wysiwyg = 'BGColor';
1037				$styleType = 'background-color';
1038				$rawList = [];
1039
1040				$hex = ['0', '3', '6', '8', '9', 'C', 'F'];
1041				$count_hex = count($hex);
1042
1043				for ($r = 0; $r < $count_hex; $r += 2) { // red
1044					for ($g = 0; $g < $count_hex; $g += 2) { // green
1045						for ($b = 0; $b < $count_hex; $b += 2) { // blue
1046							$color = $hex[$r] . $hex[$g] . $hex[$b];
1047							$rawList[] = $color;
1048						}
1049					}
1050				}
1051
1052				$list = [];
1053				foreach ($rawList as $color) {
1054					$list["~~black,#$color:text~~"] = "<span style='background-color: #$color' title='#$color' />&nbsp;</span>";
1055				}
1056				if ($section == 'sheet') {
1057					$list['reset'] = "<span title='" . tra("Reset Colors") . "' class='toolbars-picker-reset' reset='true'>" . tra("Reset") . "</span>";
1058				}
1059
1060				break;
1061
1062			default:
1063				return;
1064		}
1065
1066		$tag = new self;
1067		$tag->setWysiwygToken($wysiwyg)
1068			->setLabel($label)
1069			->setIconName(! empty($iconname) ? $iconname : 'help')
1070			->setList($list)
1071			->setType('Picker')
1072			->setName($tagName)
1073			->setStyleType($styleType);
1074
1075		foreach ($tool_prefs as $pref) {
1076			$tag->addRequiredPreference($pref);
1077		}
1078
1079		global $toolbarPickerIndex;
1080		++$toolbarPickerIndex;
1081		$tag->index = $toolbarPickerIndex;
1082		ToolbarPicker::setupJs();
1083
1084		return $tag;
1085	} // }}}
1086
1087	function setName($name) // {{{
1088	{
1089		$this->name = $name;
1090
1091		return $this;
1092	} // }}}
1093
1094
1095	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
1096	{
1097		switch ($this->wysiwyg) {
1098			case 'BGColor':
1099			case 'TextColor':
1100			case 'SpecialChar':
1101				return $this->wysiwyg;
1102				break;
1103			default:
1104				return null;
1105		}
1106	} // }}}
1107
1108
1109	function setList($list) // {{{
1110	{
1111		$this->list = $list;
1112
1113		return $this;
1114	} // }}}
1115
1116	protected function setSyntax($syntax) // {{{
1117	{
1118		$this->syntax = $syntax;
1119
1120		return $this;
1121	} // }}}
1122
1123	public function getSyntax($areaId = '$areaId')
1124	{
1125		global $section;
1126		if ($section == 'sheet') {
1127			return 'displayPicker( this, \'' . $this->name . '\', \'' . $areaId . '\', true, \'' . $this->styleType . '\' )';	// is enclosed in double quotes later
1128		} else {
1129			return 'displayPicker( this, \'' . $this->name . '\', \'' . $areaId . '\' )';	// is enclosed in double quotes later
1130		}
1131	}
1132
1133	private static function setupJs()
1134	{
1135
1136		static $pickerAdded = false;
1137
1138		if (! $pickerAdded) {
1139			TikiLib::lib('header')->add_jsfile('lib/jquery_tiki/tiki-toolbars.js');
1140			$pickerAdded = true;
1141		}
1142	}
1143
1144	function getWikiHtml($areaId) // {{{
1145	{
1146		global $prefs;
1147		$headerlib = TikiLib::lib('header');
1148		$headerlib->add_js("if (! window.pickerData) { window.pickerData = {}; } window.pickerData['$this->name'] = " . str_replace('\/', '/', json_encode($this->list)) . ";");
1149
1150		return $this->getSelfLink(
1151			$this->getSyntax($areaId),
1152			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
1153			'qt-picker'
1154		);
1155	} // }}}
1156
1157	protected function setStyleType($type) // {{{
1158	{
1159		$this->styleType = $type;
1160
1161		return $this;
1162	} // }}}
1163}
1164
1165class ToolbarDialog extends Toolbar
1166{
1167	private $list;
1168	private $index;
1169	private $name;
1170
1171	public static function fromName($tagName) // {{{
1172	{
1173		global $prefs;
1174
1175		$tool_prefs = [];
1176
1177		switch ($tagName) {
1178			case 'tikilink':
1179				$label = tra('Wiki Link');
1180				$iconname = 'link';
1181				$icon = tra('img/icons/page_link.png');
1182				$wysiwyg = '';	// cke link dialog now adapted for wiki links
1183				$list = [tra("Wiki Link"),
1184						'<label for="tbWLinkDesc">' . tra("Show this text") . '</label>',
1185						'<input type="text" id="tbWLinkDesc" class="ui-widget-content ui-corner-all" style="width: 98%" />',
1186						'<label for="tbWLinkPage">' . tra("Link to this page") . '</label>',
1187						'<input type="text" id="tbWLinkPage" class="ui-widget-content ui-corner-all" style="width: 98%" />',
1188						$prefs['wikiplugin_alink'] == 'y' ? '<label for="tbWLinkAnchor">' . tra("Anchor") . ':</label>' : '',
1189						$prefs['wikiplugin_alink'] == 'y' ? '<input type="text" id="tbWLinkAnchor" class="ui-widget-content ui-corner-all" style="width: 98%" />' : '',
1190						$prefs['feature_semantic'] == 'y' ? '<label for="tbWLinkRel">' . tra("Semantic relation") . ':</label>' : '',
1191						$prefs['feature_semantic'] == 'y' ? '<input type="text" id="tbWLinkRel" class="ui-widget-content ui-corner-all" style="width: 98%" />' : '',
1192						'{"open": function () { dialogInternalLinkOpen(area_id); },
1193						"buttons": { "' . tra("Cancel") . '": function() { dialogSharedClose(area_id,this); },' .
1194									'"' . tra("Insert") . '": function() { dialogInternalLinkInsert(area_id,this); }}}'
1195					];
1196
1197				break;
1198			case 'objectlink':
1199				$types = TikiLib::lib('unifiedsearch')->getSupportedTypes();
1200				$options = '';
1201				foreach ($types as $type => $title) {
1202					$options .= '<option value="' . $type . '">' . $title . '</option>';
1203				}
1204				$label = tra('Object Link');
1205				$iconname = 'link-external-alt';
1206				$icon = tra('img/icons/page_link.png');
1207				$wysiwyg = 'Object Link';
1208
1209				$smarty = TikiLib::lib('smarty');
1210				$smarty->loadPlugin('smarty_function_object_selector');
1211				$object_selector = smarty_function_object_selector([
1212				'_id' => 'tbOLinkObjectSelector',
1213				'_class' => 'ui-widget-content ui-corner-all',
1214	//              '_format' => '{title}',
1215				'_filter' => ['type' => ''],
1216				'_parent' => 'tbOLinkObjectType',
1217				'_parentkey' => 'type',
1218				], $smarty->getEmptyInternalTemplate());
1219
1220				$list = [tra('Object Link'),
1221						'<label for="tbOLinkDesc">' . tra("Show this text") . '</label>',
1222						'<input type="text" id="tbOLinkDesc" />',
1223						'<label for="tbOLinkObjectType">' . tra("Types of object") . '</label>',
1224						'<select id="tbOLinkObjectType" class="ui-widget-content ui-corner-all" style="width: 98%">' .
1225							'<option value="*">' . tra('All') . '</option>' .
1226							$options .
1227						'</select>',
1228						'<label for="tbOLinkObjectSelector">' . tra("Link to this object") . '</label>',
1229						$object_selector,
1230	//                      '<input type="text" id="tbOLinkObjectSelector" class="ui-widget-content ui-corner-all" style="width: 98%" />',
1231						'{"open": function () { dialogObjectLinkOpen(area_id); },
1232						"buttons": { "' . tra("Cancel") . '": function() { dialogSharedClose(area_id,this); },' .
1233									'"' . tra("Insert") . '": function() { dialogObjectLinkInsert(area_id,this); }}}'
1234					];
1235
1236				break;
1237			case 'link':
1238				$wysiwyg = 'Link';
1239				$label = tra('External Link');
1240				$iconname = 'link-external';
1241				$icon = tra('img/icons/world_link.png');
1242				$list = [tra('External Link'),
1243						'<label for="tbLinkDesc">' . tra("Show this text") . '</label>',
1244						'<input type="text" id="tbLinkDesc" class="ui-widget-content ui-corner-all" style="width: 98%" />',
1245						'<label for="tbLinkURL">' . tra("link to this URL") . '</label>',
1246						'<input type="text" id="tbLinkURL" class="ui-widget-content ui-corner-all" style="width: 98%" />',
1247						'<label for="tbLinkRel">' . tra("Relation") . ':</label>',
1248						'<input type="text" id="tbLinkRel" class="ui-widget-content ui-corner-all" style="width: 98%" />',
1249						$prefs['cachepages'] == 'y' ? '<br /><label for="tbLinkNoCache" style="display:inline;">' . tra("No cache") . ':</label>' : '',
1250						$prefs['cachepages'] == 'y' ? '<input type="checkbox" id="tbLinkNoCache" class="ui-widget-content ui-corner-all" />' : '',
1251						'{"width": 300, "open": function () { dialogExternalLinkOpen( area_id ) },
1252						"buttons": { "' . tra("Cancel") . '": function() { dialogSharedClose(area_id,this); },' .
1253									'"' . tra("Insert") . '": function() { dialogExternalLinkInsert(area_id,this) }}}'
1254					];
1255				break;
1256
1257			case 'table':
1258			case 'tikitable':
1259				$iconname = 'table';
1260				$icon = tra('img/icons/table.png');
1261				$wysiwyg = 'Table';
1262				$label = tra('Table Builder');
1263				$list = [tra('Table Builder'),
1264						'{"open": function () { dialogTableOpen(area_id,this); },
1265						"width": 320, "buttons": { "' . tra("Cancel") . '": function() { dialogSharedClose(area_id,this); },' .
1266												  '"' . tra("Insert") . '": function() { dialogTableInsert(area_id,this); }}}'
1267					];
1268				break;
1269
1270			case 'find':
1271				$icon = tra('img/icons/find.png');
1272				$iconname = 'search';
1273				$wysiwyg = 'Find';
1274				$label = tra('Find Text');
1275				$list = [tra('Find Text'),
1276						'<label>' . tra("Search") . ':</label>',
1277						'<input type="text" id="tbFindSearch" class="ui-widget-content ui-corner-all" />',
1278						'<label for="tbFindCase" style="display:inline;">' . tra("Case Insensitivity") . ':</label>',
1279						'<input type="checkbox" id="tbFindCase" checked="checked" class="ui-widget-content ui-corner-all" />',
1280						'<p class="description">' . tra("Note: Uses regular expressions") . '</p>',	// TODO add option to not
1281						'{"open": function() { dialogFindOpen(area_id); },' .
1282						 '"buttons": { "' . tra("Close") . '": function() { dialogSharedClose(area_id,this); },' .
1283									  '"' . tra("Find") . '": function() { dialogFindFind(area_id); }}}'
1284					];
1285
1286				break;
1287
1288			case 'replace':
1289				$icon = tra('img/icons/text_replace.png');
1290				$iconname = 'repeat';
1291				$wysiwyg = 'Replace';
1292				$label = tra('Text Replace');
1293				$tool_prefs[] = 'feature_wiki_replace';
1294
1295				$list = [tra('Text Replace'),
1296						'<label for="tbReplaceSearch">' . tra("Search") . ':</label>',
1297						'<input type="text" id="tbReplaceSearch" class="ui-widget-content ui-corner-all" />',
1298						'<label for="tbReplaceReplace">' . tra("Replace") . ':</label>',
1299						'<input type="text" id="tbReplaceReplace" class="ui-widget-content ui-corner-all clearfix" />',
1300						'<label for="tbReplaceCase" style="display:inline;">' . tra("Case Insensitivity") . ':</label>',
1301						'<input type="checkbox" id="tbReplaceCase" checked="checked" class="ui-widget-content ui-corner-all" />',
1302						'<br /><label for="tbReplaceAll" style="display:inline;">' . tra("Replace All") . ':</label>',
1303						'<input type="checkbox" id="tbReplaceAll" checked="checked" class="ui-widget-content ui-corner-all" />',
1304						'<p class="description">' . tra("Note: Uses regular expressions") . '</p>',	// TODO add option to not
1305						'{"open": function() { dialogReplaceOpen(area_id); },' .
1306						 '"buttons": { "' . tra("Close") . '": function() { dialogSharedClose(area_id,this); },' .
1307									  '"' . tra("Replace") . '": function() { dialogReplaceReplace(area_id); }}}'
1308					];
1309
1310				break;
1311
1312			default:
1313				return;
1314		}
1315
1316		$tag = new self;
1317		$tag->name = $tagName;
1318		$tag->setWysiwygToken($wysiwyg)
1319			->setLabel($label)
1320			->setIconName(! empty($iconname) ? $iconname : 'help')
1321			->setIcon(! empty($icon) ? $icon : 'img/icons/shading.png')
1322			->setList($list)
1323			->setType('Dialog');
1324
1325		foreach ($tool_prefs as $pref) {
1326			$tag->addRequiredPreference($pref);
1327		}
1328
1329		global $toolbarDialogIndex;
1330		++$toolbarDialogIndex;
1331		$tag->index = $toolbarDialogIndex;
1332
1333		ToolbarDialog::setupJs();
1334
1335		return $tag;
1336	} // }}}
1337
1338	function setList($list) // {{{
1339	{
1340		$this->list = $list;
1341
1342		return $this;
1343	} // }}}
1344
1345	protected function setSyntax($syntax) // {{{
1346	{
1347		$this->syntax = $syntax;
1348
1349		return $this;
1350	} // }}}
1351
1352	public function getSyntax($areaId = '$areaId')
1353	{
1354		return 'displayDialog( this, ' . $this->index . ', \'' . $areaId . '\')';
1355	}
1356
1357	static function setupJs()
1358	{
1359		TikiLib::lib('header')->add_jsfile('lib/jquery_tiki/tiki-toolbars.js');
1360	}
1361
1362	function getWikiHtml($areaId) // {{{
1363	{
1364		$headerlib = TikiLib::lib('header');
1365		$headerlib->add_js("if (! window.dialogData) { window.dialogData = {}; } window.dialogData[$this->index] = "
1366			. json_encode($this->list) . ";", 1 + $this->index);
1367
1368		return $this->getSelfLink(
1369			$this->getSyntax($areaId),
1370			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
1371			'qt-picker'
1372		);
1373	} // }}}
1374
1375	function getWysiwygToken($areaId) // {{{
1376	{
1377		if (! empty($this->wysiwyg)) {
1378			TikiLib::lib('header')->add_js(
1379				"window.dialogData[$this->index] = " . json_encode($this->list) . ";",
1380				1 + $this->index
1381			);
1382			$syntax = str_replace('\'' . $areaId . '\'', 'editor.name', $this->getSyntax($areaId));
1383			$this->setupCKEditorTool($syntax, $this->wysiwyg, $this->label, $this->icon);
1384		}
1385		return $this->wysiwyg;
1386	} // }}}
1387
1388	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
1389	{
1390		switch ($this->name) {
1391			case 'tikilink':
1392				$this->wysiwyg = 'tikilink';
1393				break;
1394			case 'objectlink':
1395				$this->wysiwyg = 'objectlink';
1396				break;
1397			case 'table':
1398				$this->wysiwyg = 'tikitable';
1399				break;
1400			case 'link':
1401				$this->wysiwyg = 'externallink';
1402				break;
1403			default:
1404				return $this->wysiwyg;
1405		}
1406
1407		return $this->getWysiwygToken($areaId);
1408	} // }}}
1409}
1410
1411class ToolbarFullscreen extends Toolbar
1412{
1413	function __construct() // {{{
1414	{
1415		$this->setLabel(tra('Full-screen edit'))
1416			->setIconName('fullscreen')
1417			->setWysiwygToken('Maximize')
1418			->setType('Fullscreen');
1419	} // }}}
1420
1421	function getWikiHtml($areaId) // {{{
1422	{
1423
1424		return $this->getSelfLink(
1425			'toggleFullScreen(\'' . $areaId . '\');return false;',
1426			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
1427			'qt-fullscreen'
1428		);
1429	} // }}}
1430
1431	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
1432	{
1433		return $this->getWysiwygToken($areaId);
1434	} // }}}
1435}
1436
1437class ToolbarHelptool extends Toolbar
1438{
1439	function __construct() // {{{
1440	{
1441		$this->setLabel(tra('Wiki Help'))
1442			->setIcon('img/icons/help.png')
1443			->setType('Helptool');
1444	} // }}}
1445
1446	function getWikiHtml($areaId) // {{{
1447	{
1448		global $section;
1449
1450		$smarty = TikiLib::lib('smarty');
1451		$servicelib = TikiLib::lib('service');
1452
1453		$params = ['controller' => 'edit', 'action' => 'help', 'modal' => 1];
1454		$params['wiki'] = 1;
1455		$params['plugins'] = 1;
1456		$params['areaId'] = $areaId;
1457
1458		if ($GLOBALS['section'] == 'sheet') {
1459			$params['sheet'] = 1;
1460		}
1461
1462		$smarty->loadPlugin('smarty_function_icon');
1463		$icon = smarty_function_icon(['name' => 'help'], $smarty->getEmptyInternalTemplate());
1464		$url = $servicelib->getUrl($params);
1465		$help = tra('Help');
1466
1467		return "<a title=\":$help\" class=\"toolbar btn btn-sm px-2 qt-help tips bottom\" href=\"$url\" data-toggle=\"modal\" data-target=\"#bootstrap-modal\">$icon</a>";
1468	} // }}}
1469
1470	function getWysiwygToken($areaId) // {{{
1471	{
1472		global $section;
1473
1474		$servicelib = TikiLib::lib('service');
1475
1476		$params = ['controller' => 'edit', 'action' => 'help', 'modal' => 1];
1477		$params['wysiwyg'] = 1;
1478		$params['plugins'] = 1;
1479
1480		if ($section == 'sheet') {
1481			$params['sheet'] = 1;
1482		}
1483
1484		// multiple ckeditors share the same toolbar commands, so area_id (editor.name) must be added when clicked
1485		$params['areaId'] = '';	// this must be last param
1486
1487		$this->setLabel(tra('WYSIWYG Help'));
1488		$this->setIconName('help');
1489		$name = 'tikihelp';
1490
1491		$js = '$.openModal({show: true, remote: "' . $servicelib->getUrl($params) . '" + editor.name});';
1492
1493		$this->setupCKEditorTool($js, $name, $this->label, $this->icon);
1494
1495		return $name;
1496	}
1497
1498	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
1499	{
1500		return $this->getWysiwygToken($areaId);
1501	} // }}}
1502}
1503
1504class ToolbarFileGallery extends Toolbar
1505{
1506	private $name;
1507
1508	function __construct() // {{{
1509	{
1510		$this->setLabel(tra('Choose or upload images'))
1511			->setIconName('image')
1512			->setIcon(tra('img/icons/pictures.png'))
1513			->setWysiwygToken('tikiimage')
1514			->setType('FileGallery')
1515			->addRequiredPreference('feature_filegals_manager');
1516	} // }}}
1517
1518	function getSyntax($areaId)
1519	{
1520		global $prefs;
1521		$smarty = TikiLib::lib('smarty');
1522		if ($prefs['fgal_elfinder_feature'] !== 'y' || $prefs['fgal_elfinder_on_toolbar'] !== 'y') {
1523			$smarty->loadPlugin('smarty_function_filegal_manager_url');
1524			return 'openFgalsWindow(\'' . htmlentities(smarty_function_filegal_manager_url(['area_id' => $areaId], $smarty->getEmptyInternalTemplate())) . '\', true);';
1525		} else {
1526			TikiLib::lib('header')->add_jq_onready(
1527				'window.handleFinderInsertAt = function (file, elfinder, area_id) {
1528					$.getJSON($.service("file_finder", "finder"), { cmd: "tikiFileFromHash", hash: file.hash },
1529						function (data) {
1530							$(window).data("elFinderDialog").dialog("close");
1531							$($(window).data("elFinderDialog")).remove();
1532							$(window).data("elFinderDialog", null);
1533							window.insertAt(area_id, data.wiki_syntax);
1534							return false;
1535						}
1536					);
1537				};'
1538			);
1539			$smarty->loadPlugin('smarty_function_ticket');
1540			return '
1541			var area_id = (typeof editor === \'undefined\' ?  \'' . $areaId . '\' : editor.name);
1542			openElFinderDialog(
1543				this,
1544				{
1545					defaultGalleryId: ' . (empty($prefs['home_file_gallery']) ? $prefs['fgal_root_id'] : $prefs['home_file_gallery']) . ',
1546					deepGallerySearch: true,
1547					ticket: \'' . smarty_function_ticket(['mode' => 'get'], $smarty) . '\',
1548					getFileCallback: function(file,elfinder) {
1549							window.handleFinderInsertAt(file,elfinder,area_id);
1550						},
1551					eventOrigin:this,
1552					uploadCallback: function (data) {
1553							if (data.data.added.length === 1 && confirm(tr(\'Do you want to use this file in your page?\'))) {
1554								window.handleFinderInsertAt(data.data.added[0],window.elFinder,area_id);
1555							}
1556						}
1557				}
1558			);';
1559		}
1560	}
1561
1562	function getWikiHtml($areaId) // {{{
1563	{
1564		return $this->getSelfLink($this->getSyntax($areaId), htmlentities($this->label, ENT_QUOTES, 'UTF-8'), 'qt-filegal');
1565	} // }}}
1566
1567	function getWysiwygToken($areaId) // {{{
1568	{
1569		if (! empty($this->wysiwyg)) {
1570			$this->name = $this->wysiwyg;	// temp
1571			$exec_js = str_replace('&amp;', '&', $this->getSyntax($areaId));	// odd?
1572
1573			$this->setupCKEditorTool($exec_js, $this->name, $this->label, $this->icon);
1574		}
1575		return $this->wysiwyg;
1576	} // }}}
1577
1578	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
1579	{
1580		return $this->getWysiwygToken($areaId);
1581	} // }}}
1582
1583	function isAccessible() // {{{
1584	{
1585		return parent::isAccessible();
1586	} // }}}
1587}
1588
1589class ToolbarFileGalleryFile extends ToolbarFileGallery
1590{
1591
1592	function __construct() // {{{
1593	{
1594		$this->setLabel(tra('Choose or upload files'))
1595			->setIconName('upload')
1596			->setWysiwygToken('tikifile')
1597			->setType('FileGallery')
1598			->addRequiredPreference('feature_filegals_manager');
1599	} // }}}
1600
1601	function getSyntax($areaId)
1602	{
1603		$smarty = TikiLib::lib('smarty');
1604		$smarty->loadPlugin('smarty_function_filegal_manager_url');
1605		return 'openFgalsWindow(\'' . htmlentities(smarty_function_filegal_manager_url(['area_id' => $areaId], $smarty->getEmptyInternalTemplate()))
1606			. '&insertion_syntax=file\', true);';
1607	}
1608}
1609
1610class ToolbarSwitchEditor extends Toolbar
1611{
1612	private $name;
1613	function __construct() // {{{
1614	{
1615		$this->setLabel(tra('Switch Editor (wiki or WYSIWYG)'))
1616			->setIconName('pencil')
1617			->setIcon(tra('img/icons/pencil_go.png'))
1618			->setWysiwygToken('tikiswitch')
1619			->setType('SwitchEditor')
1620			->addRequiredPreference('feature_wysiwyg');
1621	} // }}}
1622
1623	function getWikiHtml($areaId) // {{{
1624	{
1625		return $this->getSelfLink(
1626			'switchEditor(\'wysiwyg\', $(this).parents(\'form\')[0]);',
1627			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
1628			'qt-switcheditor'
1629		);
1630	} // }}}
1631
1632	function getWysiwygToken($areaId) // {{{
1633	{
1634		global $prefs;
1635		if (! empty($this->wysiwyg)) {
1636			$this->name = $this->wysiwyg;	// temp
1637
1638			if ($prefs['feature_wysiwyg'] == 'y' && $prefs['wysiwyg_optional'] == 'y') {
1639				$js = "switchEditor('wiki', $('#$areaId').parents('form')[0]);";
1640				$this->setupCKEditorTool($js, $this->name, $this->label, $this->icon);
1641			}
1642		}
1643		return $this->wysiwyg;
1644	} // }}}
1645
1646
1647	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
1648	{
1649		return $this->getWysiwygToken($areaId);
1650	} // }}}
1651
1652
1653	function isAccessible() // {{{
1654	{
1655		global $tiki_p_edit_switch_mode;
1656
1657		return parent::isAccessible() &&
1658				! isset($_REQUEST['hdr']) &&		// or in section edit
1659				$tiki_p_edit_switch_mode === 'y';	// or no perm (new in 7.1)
1660	} // }}}
1661
1662/*	function getLabel() // {{{
1663	{
1664		return $this->label;
1665	} // }}}
1666*/
1667}
1668
1669
1670class ToolbarAdmin extends Toolbar
1671{
1672	private $name;
1673	function __construct() // {{{
1674	{
1675		$this->setLabel(tra('Admin Toolbars'))
1676			->setIconName('wrench')
1677			->setIcon(tra('img/icons/wrench.png'))
1678			->setWysiwygToken('admintoolbar')
1679			->setType('admintoolbar');
1680			//->addRequiredPreference('feature_wysiwyg');
1681	} // }}}
1682
1683	function getWikiHtml($areaId) // {{{
1684	{
1685		return $this->getSelfLink(
1686			'admintoolbar();',
1687			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
1688			'qt-admintoolbar'
1689		);
1690	} // }}}
1691
1692	function getWysiwygToken($areaId) // {{{
1693	{
1694		global $prefs;
1695		if (! empty($this->wysiwyg)) {
1696			$this->name = $this->wysiwyg;	// temp
1697
1698			if ($prefs['feature_wysiwyg'] == 'y') {
1699				$js = "admintoolbar();";
1700				$this->setupCKEditorTool($js, $this->name, $this->label, $this->icon);
1701			}
1702		}
1703		return $this->wysiwyg;
1704	} // }}}
1705
1706
1707	function getWysiwygWikiToken($areaId) // {{{ // wysiwyg_htmltowiki
1708	{
1709		return $this->getWysiwygToken($areaId);
1710	} // }}}
1711}
1712
1713class ToolbarWikiplugin extends Toolbar
1714{
1715	private $pluginName;
1716
1717	public static function fromName($name) // {{{
1718	{
1719		global $tikilib;
1720		$parserlib = TikiLib::lib('parser');
1721
1722		if (substr($name, 0, 11) == 'wikiplugin_') {
1723			$name = substr($name, 11);
1724			if ($info = $parserlib->plugin_info($name)) {
1725				$tag = new self;
1726				$tag->setLabel(str_ireplace('wikiplugin_', '', $info['name']))
1727					->setWysiwygToken(str_replace(' ', '_', $info['name']))
1728					->setPluginName($name)
1729					->setType('Wikiplugin');
1730
1731				if (! empty($info['iconname'])) {
1732					$tag->setIconName($info['iconname']);
1733				} elseif (! empty($info['icon'])) {
1734					$tag->setIcon($info['icon']);
1735				} else {
1736					$tag->setIcon('img/icons/plugin.png');
1737				}
1738
1739				TikiLib::lib('header')->add_jsfile('lib/jquery_tiki/tiki-toolbars.js');
1740
1741				return $tag;
1742			}
1743		}
1744	} // }}}
1745
1746	function setPluginName($name) // {{{
1747	{
1748		$this->pluginName = $name;
1749
1750		return $this;
1751	} // }}}
1752
1753	function getPluginName() // {{{
1754	{
1755		return $this->pluginName;
1756	} // }}}
1757
1758	function isAccessible() // {{{
1759	{
1760		global $tikilib;
1761		$parserlib = TikiLib::lib('parser');
1762		$dummy_output = '';
1763		return parent::isAccessible() && $parserlib->plugin_enabled($this->pluginName, $dummy_output);
1764	} // }}}
1765
1766	private static function getToken($name) // {{{
1767	{
1768		switch ($name) {
1769			case 'flash':
1770				return 'Flash';
1771		}
1772	} // }}}
1773
1774	function getWikiHtml($areaId) // {{{
1775	{
1776		return $this->getSelfLink(
1777			'popupPluginForm(\'' . $areaId . '\',\'' . $this->pluginName . '\')',
1778			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
1779			'qt-plugin'
1780		);
1781	} // }}}
1782	function getWysiwygToken($areaId, $add_js = true) // {{{
1783	{
1784		if (! empty($this->wysiwyg) && $add_js) {
1785			$js = "popupPluginForm(editor.name,'{$this->pluginName}');";
1786			//CKEditor needs image icons so get legacy plugin icons for the toolbar
1787			if (! $this->icon && ! empty($this->iconname)) {
1788				$iconsetlib = TikiLib::lib('iconset');
1789				$legacy = $iconsetlib->loadFile('themes/base_files/iconsets/legacy.php');
1790				if (array_key_exists($this->iconname, $legacy['icons'])) {
1791					$iconinfo = $legacy['icons'][$this->iconname];
1792				} elseif (in_array($this->iconname, $legacy['defaults'])) {
1793					$iconinfo['id'] = $this->iconname;
1794				}
1795				if (isset($iconinfo)) {
1796					$prepend = isset($iconinfo['prepend']) ? $iconinfo['prepend'] : 'img/icons/';
1797					$append = isset($iconinfo['append']) ? $iconinfo['append'] : '.png';
1798					$iconpath = $prepend . $iconinfo['id'] . $append;
1799				} else {
1800					$iconpath = 'img/icons/plugin.png';
1801				}
1802			}
1803			$this->setupCKEditorTool($js, $this->wysiwyg, $this->label, $iconpath);
1804		}
1805		return $this->wysiwyg;
1806	} // }}}
1807
1808	function getWysiwygWikiToken($areaId, $add_js = true) // {{{ // wysiwyg_htmltowiki
1809	{
1810		switch ($this->pluginName) {
1811			case 'img':
1812				$this->wysiwyg = 'wikiplugin_img';	// don't use ckeditor's html image dialog
1813				break;
1814			default:
1815		}
1816
1817		return $this->getWysiwygToken($areaId, $add_js);
1818	} // }}}
1819}
1820
1821class ToolbarSheet extends Toolbar
1822{
1823	protected $syntax;
1824
1825	public static function fromName($tagName) // {{{
1826	{
1827		switch ($tagName) {
1828			case 'sheetsave':
1829				$label = tra('Save Sheet');
1830				$iconname = 'floppy';
1831				$syntax = '
1832					$("#saveState").hide();
1833					$.sheet.saveSheet($.sheet.tikiSheet, function() {
1834						$.sheet.manageState($.sheet.tikiSheet, true);
1835					});';
1836				break;
1837			case 'addrow':
1838				$label = tra('Add row after selection or to the end if no selection');
1839				$icon = tra('img/icons/sheet_row_add.png');
1840				$syntax = 'sheetInstance.controlFactory.addRow();';	// add row after end to workaround bug in jquery.sheet.js 1.0.2
1841				break;														// TODO fix properly for 5.1
1842			case 'addrowmulti':
1843				$label = tra('Add multiple rows after selection or to the end if no selection');
1844				$icon = tra('img/icons/sheet_row_add_multi.png');
1845				$syntax = 'sheetInstance.controlFactory.addRowMulti();';
1846				break;
1847			case 'addrowbefore':
1848				$label = tra('Add row before selection or to end if no selection');
1849				$icon = tra('img/icons/sheet_row_add.png');
1850				$syntax = 'sheetInstance.controlFactory.addRow(null, true);';	// add row after end to workaround bug in jquery.sheet.js 1.0.2
1851				break;
1852			case 'deleterow':
1853				$label = tra('Delete selected row');
1854				$icon = tra('img/icons/sheet_row_delete.png');
1855				$syntax = 'sheetInstance.deleteRow();';
1856				break;
1857			case 'addcolumn':
1858				$label = tra('Add column after selection or to the end if no selection');
1859				$icon = tra('img/icons/sheet_col_add.png');
1860				$syntax = 'sheetInstance.controlFactory.addColumn();';	// add col before current or at end if none selected
1861				break;
1862			case 'deletecolumn':
1863				$label = tra('Delete selected column');
1864				$icon = tra('img/icons/sheet_col_delete.png');
1865				$syntax = 'sheetInstance.deleteColumn();';
1866				break;
1867			case 'addcolumnmulti':
1868				$label = tra('Add multiple columns after selection or to the end if no selection');
1869				$icon = tra('img/icons/sheet_col_add_multi.png');
1870				$syntax = 'sheetInstance.controlFactory.addColumnMulti();';
1871				break;
1872			case 'addcolumnbefore':
1873				$label = tra('Add column before selection or to the end if no selection');
1874				$icon = tra('img/icons/sheet_col_add.png');
1875				$syntax = 'sheetInstance.controlFactory.addColumn(null, true);';	// add col before current or at end if none selected
1876				break;
1877			case 'sheetgetrange':
1878				$label = tra('Get Cell Range');
1879				$icon = tra('img/icons/sheet_get_range.png');
1880				$syntax = 'sheetInstance.getTdRange(null, sheetInstance.obj.formula().val()); return false;';
1881				break;
1882			case 'sheetfind':
1883				$label = tra('Find');
1884				$iconname = 'search';
1885				$syntax = 'sheetInstance.cellFind();';
1886				break;
1887			case 'sheetrefresh':
1888				$label = tra('Refresh calculations');
1889				$iconname = 'refresh';
1890				$syntax = 'sheetInstance.calc();';
1891				break;
1892			case 'sheetclose':
1893				$label = tra('Finish editing');
1894				$iconname = 'delete';
1895				$syntax = '$.sheet.manageState(sheetInstance.obj.parent(), true);';	// temporary workaround TODO properly
1896				break;
1897			case 'bold':
1898				$label = tra('Bold');
1899				$iconname = 'bold';
1900				$wysiwyg = 'Bold';
1901				$syntax = 'sheetInstance.cellStyleToggle("styleBold");';
1902				break;
1903			case 'italic':
1904				$label = tra('Italic');
1905				$iconname = 'italic';
1906				$wysiwyg = 'Italic';
1907				$syntax = 'sheetInstance.cellStyleToggle("styleItalics");';
1908				break;
1909			case 'underline':
1910				$label = tra('Underline');
1911				$iconname = 'underline';
1912				$wysiwyg = 'Underline';
1913				$syntax = 'sheetInstance.cellStyleToggle("styleUnderline");';
1914				break;
1915			case 'strike':
1916				$label = tra('Strikethrough');
1917				$iconname = 'strikethrough';
1918				$wysiwyg = 'Strike';
1919				$syntax = 'sheetInstance.cellStyleToggle("styleLineThrough");';
1920				break;
1921			case 'center':
1922				$label = tra('Align Center');
1923				$iconname = 'align-center';
1924				$syntax = 'sheetInstance.cellStyleToggle("styleCenter");';
1925				break;
1926			default:
1927				return;
1928		}
1929
1930		$tag = new self;
1931		$tag->setLabel($label)
1932			->setSyntax($syntax)
1933			->setType('Sheet');
1934		if (! empty($iconname)) {
1935			$tag->setIconName(! empty($iconname) ? $iconname : 'help');
1936		}
1937		if (! empty($icon)) {
1938			$tag->setIcon(! empty($icon) ? $icon : 'img/icons/shading.png');
1939		}
1940
1941		return $tag;
1942	} // }}}
1943
1944	function getSyntax($areaId) // {{{
1945	{
1946		return $this->syntax;
1947	} // }}}
1948
1949	protected function setSyntax($syntax) // {{{
1950	{
1951		$this->syntax = $syntax;
1952
1953		return $this;
1954	} // }}}
1955
1956	function getWikiHtml($areaId) // {{{
1957	{
1958		return $this->getSelfLink(
1959			addslashes(htmlentities($this->syntax, ENT_COMPAT, 'UTF-8')),
1960			htmlentities($this->label, ENT_QUOTES, 'UTF-8'),
1961			'qt-sheet'
1962		);
1963	} // }}}
1964}
1965
1966
1967
1968class ToolbarsList
1969{
1970	private $lines = [];
1971	private $wysiwyg = false;
1972	private $is_html = false;
1973
1974	private function __construct()
1975	{
1976	}
1977
1978	/***
1979	 * @param array $params            params from smarty_function_toolbars
1980	 * @param array $tags_to_hide      list of tools not to show
1981	 * @return ToolbarsList
1982	 */
1983	public static function fromPreference($params, $tags_to_hide = []) // {{{
1984	{
1985		global $tikilib;
1986
1987		$global = $tikilib->get_preference('toolbar_global' . ($params['comments'] === 'y' ? '_comments' : ''));
1988		$local = $tikilib->get_preference('toolbar_' . $params['section'] . ($params['comments'] === 'y' ? '_comments' : ''), $global);
1989
1990		foreach ($tags_to_hide as $name) {
1991			$local = str_replace($name, '', $local);
1992		}
1993		if ($params['section'] === 'wysiwyg_plugin') {	// quick fix to prevent nested wysiwyg plugins (messy)
1994			$local = str_replace('wikiplugin_wysiwyg', '', $local);
1995		}
1996
1997		$local = str_replace([',,', '|,', ',|', ',/', '/,'], [',', '|', '|', '/', '/'], $local);
1998
1999		return self::fromPreferenceString($local, $params);
2000	} // }}}
2001
2002	public static function fromPreferenceString($string, $params) // {{{
2003	{
2004		global $toolbarPickerIndex;
2005		$toolbarPickerIndex = -1;
2006		$list = new self;
2007		$list->wysiwyg = (isset($params['_wysiwyg']) && $params['_wysiwyg'] === 'y');
2008		$list->is_html = ! empty($params['_is_html']);
2009
2010		$string = preg_replace('/\s+/', '', $string);
2011
2012		foreach (explode('/', $string) as $line) {
2013			$bits = explode('|', $line);
2014			if (count($bits) > 1) {
2015				$list->addLine(explode(',', $bits[0]), explode(',', $bits[1]));
2016			} else {
2017				$list->addLine(explode(',', $bits[0]));
2018			}
2019		}
2020
2021		return $list;
2022	} // }}}
2023
2024	public function addTag($name, $unique = false)
2025	{
2026		if ($unique && $this->contains($name)) {
2027			return false;
2028		}
2029		$this->lines[count($this->lines) - 1][0][0][] = Toolbar::getTag($name);
2030		return true;
2031	}
2032
2033	public function insertTag($name, $unique = false)
2034	{
2035		if ($unique && $this->contains($name)) {
2036			return false;
2037		}
2038		array_unshift($this->lines[0][0][0], Toolbar::getTag($name));
2039		return true;
2040	}
2041
2042	private function addLine(array $tags, array $rtags = []) // {{{
2043	{
2044		$elements = [];
2045		$j = count($rtags) > 0 ? 2 : 1;
2046
2047		for ($i = 0; $i < $j; $i++) {
2048			$group = [];
2049			$elements[$i] = [];
2050
2051			if ($i == 0) {
2052				$thetags = $tags;
2053			} else {
2054				$thetags = $rtags;
2055			}
2056			foreach ($thetags as $tagName) {
2057				if ($tagName === '-' || $tagName === '|') {
2058					if (count($group)) {
2059						$elements[$i][] = $group;
2060						$group = [];
2061					}
2062				} else {
2063					if (( $tag = Toolbar::getTag($tagName, $this->wysiwyg, $this->is_html) )
2064						&& $tag->isAccessible() ) {
2065						$group[] = $tag;
2066					}
2067				}
2068			}
2069
2070			if (count($group)) {
2071				$elements[$i][] = $group;
2072			}
2073		}
2074		if (count($elements)) {
2075			$this->lines[] = $elements;
2076		}
2077	} // }}}
2078
2079	function getWysiwygArray($areaId, $isHtml = true) // {{{
2080	{
2081		$lines = [];
2082		foreach ($this->lines as $line) {
2083			$lineOut = [];
2084
2085			foreach ($line as $bit) {
2086				foreach ($bit as $group) {
2087					$group_count = 0;
2088					foreach ($group as $tag) {
2089						if ($isHtml) {
2090							if ($token = $tag->getWysiwygToken($areaId)) {
2091								$lineOut[] = $token;
2092								$group_count++;
2093							}
2094						} else {
2095							if ($token = $tag->getWysiwygWikiToken($areaId)) {
2096								$lineOut[] = $token;
2097								$group_count++;
2098							}
2099						}
2100					}
2101					if ($group_count) { // don't add separators for empty groups
2102						$lineOut[] = '-';
2103					}
2104				}
2105			}
2106
2107			$lineOut = array_slice($lineOut, 0, -1);
2108
2109			if (count($lineOut)) {
2110				$lines[] = [$lineOut];
2111			}
2112		}
2113
2114		return $lines;
2115	} // }}}
2116
2117	function getWikiHtml($areaId, $comments = '') // {{{
2118	{
2119		global $tiki_p_admin, $tiki_p_admin_toolbars, $section, $prefs;
2120		$headerlib = TikiLib::lib('header');
2121		$smarty = TikiLib::lib('smarty');
2122		$html = '';
2123
2124		$c = 0;
2125		foreach ($this->lines as $line) {
2126			$lineHtml = '';
2127			$right = '';
2128			if (count($line) == 1) {
2129				$line[1] = [];
2130			}
2131
2132			// $line[0] is left part, $line[1] right floated section
2133			for ($bitx = 0, $bitxcount_line = count($line); $bitx < $bitxcount_line; $bitx++) {
2134				$lineBit = '';
2135
2136				/*if ($c == 0 && $bitx == 1 && ($tiki_p_admin == 'y' or $tiki_p_admin_toolbars == 'y')) {
2137					$params = array('_script' => 'tiki-admin_toolbars.php', '_onclick' => 'needToConfirm = true;', '_class' => 'toolbar', '_icon' => 'wrench', '_ajax' => 'n');
2138					if (isset($comments) && $comments == 'y')
2139						$params['comments'] = 'on';
2140					if (isset($section))
2141						$params['section'] = $section;
2142					$content = tra('Admin Toolbars');
2143					$right .= smarty_block_self_link($params, $content, $smarty);
2144				}*/
2145
2146				foreach ($line[$bitx] as $group) {
2147					$groupHtml = '';
2148					foreach ($group as $tag) {
2149						$groupHtml .= $tag->getWikiHtml($areaId);
2150					}
2151
2152					if (! empty($groupHtml)) {
2153						$param = ' class="toolbar-list"';
2154						$lineBit .= "<span$param>$groupHtml</span>";
2155					}
2156					if ($bitx == 1) {
2157						if (! empty($right)) {
2158							$right = '<span class="toolbar-list">' . $right . '</span>';
2159						}
2160						$lineHtml = "<div class='helptool-admin float-right'>$lineBit $right</div>" . $lineHtml;
2161					} else {
2162						$lineHtml = $lineBit;
2163					}
2164				}
2165
2166				// adding admin icon if no right part - messy - TODO better
2167				if ($c == 0 && empty($lineBit) && ! empty($right)) {
2168					$lineHtml .= "<div class='helptool-admin float-right'>$right</div>";
2169				}
2170			}
2171			if (! empty($lineHtml)) {
2172				$html .= "<div>$lineHtml</div>";
2173			}
2174			$c++;
2175		}
2176
2177		return $html;
2178	} // }}}
2179
2180	function contains($name)
2181	{
2182 // {{{
2183		foreach ($this->lines as $line) {
2184			foreach ($line as $group) {
2185				foreach ($group as $tags) {
2186					foreach ($tags as $tag) {
2187						if ($tag->getLabel() == $name) {
2188							return true;
2189						}
2190					}
2191				}
2192			}
2193		}
2194		return false;
2195	} // }}}
2196}
2197
2198
2199/**
2200 * Definition of the CKE Toolbar Combos
2201 */
2202class ToolbarCombos
2203{
2204
2205	/**
2206	 * Get the content of the format combo
2207	 *
2208	 * Valid toolbar types are:
2209	 * - 'html': WYSIWYG-HTML
2210	 * - 'wiki': Visual Wiki
2211	 *
2212	 * @param string $tb_type The CKE toolbar type
2213	 */
2214	static function getFormatTags($tb_type)
2215	{
2216		switch ($tb_type) {
2217			case 'wiki':
2218				return 'p;h1;h2;h3;h4;h5;h6';
2219				break;
2220			case 'html':
2221			default:
2222				return 'p;h1;h2;h3;h4;h5;h6;pre;address;div'; // CKE default
2223		}
2224	}
2225}
2226