extraheader = array('js'=>'', 'css'=>'', 'favicon'=>'', 'logo'=>'', 'logolink'=>'');
		$this->footerjs = array();
		$this->nonces = array();
	}
	/**
	 * Add javascript to an internal array which is output at the
	 * end of the page within a document.ready() function.
	 *
	 * @param string $script javascript to be added
	 */
	function addFooterJS($script) { /* {{{ */
		$this->footerjs[] = $script;
	} /* }}} */
	function htmlStartPage($title="", $bodyClass="", $base="", $httpheader=array()) { /* {{{ */
		if(1 || method_exists($this, 'js')) {
			/* We still need unsafe-eval, because printDocumentChooserHtml and
			 * printFolderChooserHtml will include a javascript file with ajax
			 * which is evaluated by jquery
			 * worker-src blob: is needed for cytoscape
			 * X-WebKit-CSP is deprecated, Chrome understands Content-Security-Policy
			 * since version 25+
			 * X-Content-Security-Policy is deprecated, Firefox understands
			 * Content-Security-Policy since version 23+
			 * 'worker-src blob:' is needed for cytoscape
			 * 'unsafe-inline' is needed for jquery 3.6.1 when loading the remote
			 * content of a modal box
			 */
			$csp_rules = [];
			$csp_rule = "script-src 'self' 'unsafe-eval' 'unsafe-inline'";
			if($this->nonces) {
				$csp_rule .= " 'nonce-".implode("' 'nonce-", $this->nonces)."'";
			}
			$csp_rules[] = $csp_rule;
			$csp_rules[] = "worker-src blob:";
			//$csp_rules[] = "style-src 'self'";
			/* Do not allow to embed myself into frames on foreigns pages */
			$csp_rules[] = "frame-ancestors 'self'";
			if($this->hasHook('getCspRules')) {
				$csp_rules = $this->callHook('getCspRules', $csp_rules);
			}
			foreach (array("X-WebKit-CSP", "X-Content-Security-Policy", "Content-Security-Policy") as $csp) {
				header($csp . ": " . implode('; ', $csp_rules).';');
			}
		}
		header('X-Content-Type-Options: nosniff');
		header('Strict-Transport-Security: max-age=15768000; includeSubDomains; preload');
		if($httpheader) {
			foreach($httpheader as $name=>$value) {
				header($name . ": " . $value);
			}
		}
		if($this->hasHook('startPage'))
				$this->callHook('startPage');
		echo "\n";
		echo "params['session'] && ($slang = $this->params['session']->getLanguage())) {
			echo str_replace('_', '-', $slang);
		} else {
			echo str_replace('_', '-', $this->params['settings']->_language);
		}
		echo "\">\n
\n";
		echo "".$flashmsg['msg']."
\n";
		}
		echo "
".getMLText('recent_uploads')." \n";
		if($this->hasHook('startBody'))
				$this->callHook('startBody');
	} /* }}} */
	function htmlAddHeader($head, $type='js') { /* {{{ */
		if($type == 'logo' || $type == 'favicon' || $type == 'logolink')
			$this->extraheader[$type] = $head;
		else
			$this->extraheader[$type] .= $head;
	} /* }}} */
	function htmlAddJsHeader($script) { /* {{{ */
		$nonce = createNonce();
		$this->nonces[] = $nonce;
		$this->extraheader['js'] .= ''."\n";
	} /* }}} */
	function htmlEndPage($nofooter=false) { /* {{{ */
		if(!$nofooter) {
			$html = $this->footNote();
			if($this->hasHook('footNote'))
				$html = $this->callHook('footNote', $html);
			echo $html;
			if($this->params['showmissingtranslations']) {
				$this->missingLanguageKeys();
			}
		}
		$parenttheme = 'bootstrap';
		echo ''."\n";
		//echo ''."\n";
		echo ''."\n";
		echo ''."\n";
		echo ''."\n";
		foreach(array('de', 'es', 'ar', 'el', 'bg', 'ru', 'hr', 'hu', 'ko', 'pl', 'ro', 'sk', 'tr', 'uk', 'ca', 'nl', 'fi', 'cs', 'it', 'fr', 'sv', 'sl', 'pt-BR', 'zh-CN', 'zh-TW') as $lang)
			echo ''."\n";
		echo ''."\n";
		echo ''."\n";
		parse_str($_SERVER['QUERY_STRING'], $tmp);
		$tmp['action'] = 'webrootjs';
		if(isset($tmp['formtoken']))
			unset($tmp['formtoken']);
		if(isset($tmp['referuri']))
			unset($tmp['referuri']);
		if(!empty($this->params['class']))
			echo ''."\n";
		echo ''."\n";
		if($this->params['enablemenutasks'] && isset($this->params['user']) && $this->params['user']) {
			$this->addFooterJS('SeedDMSTask.run();');
		}
		if($this->params['enabledropfolderlist'] && isset($this->params['user']) && $this->params['user']) {
			$this->addFooterJS("SeedDMSTask.add({name: 'dropfolder', interval: 30, func: function(){\$('#menu-dropfolder > div.ajax').trigger('update', {folderid: seeddms_folder});}});");
		}
		if($this->footerjs) {
			$jscode = "$(document).ready(function () {\n";
			foreach($this->footerjs as $script) {
				$jscode .= $script."\n";
			}
			$jscode .= "});\n";
			$hashjs = md5($jscode);
			if(!is_dir($this->params['cachedir'].'/js')) {
				SeedDMS_Core_File::makeDir($this->params['cachedir'].'/js');
			}
			if(is_dir($this->params['cachedir'].'/js')) {
				file_put_contents($this->params['cachedir'].'/js/'.$hashjs.'.js', $jscode);
			}
			$tmp['action'] = 'footerjs';
			$tmp['hashjs'] = $hashjs;
			echo ''."\n";
		}
		if(method_exists($this, 'js')) {
			parse_str($_SERVER['QUERY_STRING'], $tmp);
			$tmp['action'] = 'js';
			echo ''."\n";
		}
		echo "\n\n";
	} /* }}} */
	function webrootjs() { /* {{{ */
		header('Content-Type: application/javascript; charset=UTF-8');
		echo "var seeddms_absbaseprefix=\"".$this->params['absbaseprefix']."\";\n";
		echo "var seeddms_webroot=\"".$this->params['settings']->_httpRoot."\";\n";
		/* Place the current folder id in a js variable, just in case some js code
		 * needs it, e.g. for reloading parts of the page via ajax.
		 */
		if(!empty($_REQUEST['folderid']))
			echo "var seeddms_folder=".(int) $_REQUEST['folderid'].";\n";
		else
			echo "var seeddms_folder=0;\n";
	} /* }}} */
	function footerjs() { /* {{{ */
		header('Content-Type: application/javascript');
		if(file_exists($this->params['cachedir'].'/js/'.$_GET['hashjs'].'.js')) {
			readfile($this->params['cachedir'].'/js/'.$_GET['hashjs'].'.js');
		}
	} /* }}} */
	function missingLanguageKeys() { /* {{{ */
		global $MISSING_LANG, $LANG;
		if($MISSING_LANG) {
			echo ''."\n";
			$this->rowStart();
			$this->columnStart(12);
			echo $this->errorMsg("This page contains missing translations in the selected language. Please help to improve SeedDMS and provide the translation.");
			echo "
";
			echo "Key engl. Text Your translation ".htmlspecialchars($key)." ".(isset($LANG['en_GB'][$key]) ? $LANG['en_GB'][$key] : '')." 
";
			echo "
There are missing translations on this page! 
\n";
			echo "
\n";
		echo "
\n";
		echo "
\n";
		echo "\n";
		echo " \n";
		echo "
\n";
*/
		echo "\n";
		echo " \n";
	} /* }}} */
	function contentEnd() { /* {{{ */
		echo " 
\n";
		echo " \n";
/*
		echo " \n";
		echo "
\n";
		echo " extraheader['logolink']) ? $this->extraheader['logolink'] : $this->params['settings']->_httpRoot."out/out.ViewFolder.php")."\">".(!empty($this->extraheader['logo']) ? '".(strlen($this->params['sitename'])>0 ? $this->params['sitename'] : "")."  \n";
		echo " \n";
	} /* }}} */
	function globalNavigation($folder=null) { /* {{{ */
		$dms = $this->params['dms'];
		$accessobject = $this->params['accessobject'];
		echo "\n";
		echo " extraheader['logolink']) ? $this->extraheader['logolink'] : $this->params['settings']->_httpRoot."out/out.ViewFolder.php")."\">".(!empty($this->extraheader['logo']) ? '".(strlen($this->params['sitename'])>0 ? $this->params['sitename'] : "")."  \n";
		if(isset($this->params['user']) && $this->params['user']) {
			/* search form {{{ */
			echo "     \n";
			/* }}} End of search form */
			echo " \n";
			echo "   \n";
			echo " \n";
			echo "  
\n";
			$menuitems = array();
			/* calendar {{{ */
			if ($this->params['enablecalendar'] && $accessobject->check_view_access('Calendar')) $menuitems['calendar'] = array('link'=>$this->params['settings']->_httpRoot.'out/out.Calendar.php?mode='.$this->params['calendardefaultview'], 'label'=>getMLText("calendar"));
			if ($accessobject->check_view_access('AdminTools')) $menuitems['admintools'] = array('link'=>$this->params['settings']->_httpRoot.'out/out.AdminTools.php', 'label'=>getMLText("admin_tools"));
			if($this->params['enablehelp']) {
				$tmp = explode('.', basename($_SERVER['SCRIPT_FILENAME']));
				$menuitems['help'] = array('link'=>$this->params['settings']->_httpRoot.'out/out.Help.php?context='.$tmp[1], 'label'=>getMLText("help"));
			}
			/* }}} End of calendar */
			/* Check if hook exists because otherwise callHook() will override $menuitems */
			if($this->hasHook('globalNavigationBar'))
				$menuitems = $this->callHook('globalNavigationBar', $menuitems);
			foreach($menuitems as $menuitem) {
				if(!empty($menuitem['children'])) {
					echo "   \n";
					echo "     ".$menuitem['label']." \n";
					echo "     \n";
				} else {
					echo " ".$menuitem['label']."  \n";
			/* menu tasks {{{ */
			if($this->params['enablemenutasks'] && !$this->params['user']->isGuest()) {
				if($accessobject->check_view_access('Tasks', array('action'=>'menuTasks'))) {
					echo "   ";
				}
			}
			/* }}} End of menu tasks */
			/* drop folder dir {{{ */
			if($this->params['dropfolderdir'] && $this->params['enabledropfolderlist']) {
				echo "   ";
			}
			/* }}} End of drop folder dir */
			/* session list {{{ */
			if($this->params['enablesessionlist']) {
				echo "   ";
			}
			/* }}} End of session list */
			/* clipboard {{{ */
			if($this->params['enableclipboard']) {
				echo "   ";
			}
			/* }}} End of clipboard */
			/* user profile menu {{{ */
			echo "  
\n";
			/* }}} End of user profile menu */
			echo "    
 \n";
		return;
	} /* }}} */
	function getFolderPathHTML($folder, $tagAll=false, $document=null) { /* {{{ */
		$path = $folder->getPath();
		$txtpath = "";
		for ($i = 0; $i < count($path); $i++) {
			$txtpath .= "";
			if ($i+1 < count($path)) {
				$txtpath .= "params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=".$path[$i]->getID()."&showtree=".showtree()."\" data-droptarget=\"folder_".$path[$i]->getID()."\" rel=\"folder_".$path[$i]->getID()."\" data-name=\"".htmlspecialchars($path[$i]->getName())."\" class=\"table-row-folder droptarget\" data-uploadformtoken=\"".createFormKey('')."\" formtoken=\"".createFormKey('')."\">".
					htmlspecialchars($path[$i]->getName())." ";
			}
			else {
				$txtpath .= ($tagAll ? "params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=".$path[$i]->getID()."&showtree=".showtree()."\" data-droptarget=\"folder_".$path[$i]->getID()."\" rel=\"folder_".$path[$i]->getID()."\" data-name=\"".htmlspecialchars($path[$i]->getName())."\" class=\"table-row-folder droptarget\" data-uploadformtoken=\"".createFormKey('')."\" formtoken=\"".createFormKey('')."\">".htmlspecialchars($path[$i]->getName())." " : htmlspecialchars($path[$i]->getName()));
			}
		}
		if($document)
			$txtpath .= " params['settings']->_httpRoot."out/out.ViewDocument.php?documentid=".$document->getId()."\" class=\"table-document-row\" rel=\"document_".$document->getId()."\" data-name=\"".htmlspecialchars($document->getName())."\" formtoken=\"".createFormKey('')."\">".htmlspecialchars($document->getName())." '.$txtpath.' ";
			echo "
\n";
			echo ''.getMLText('nav_brand_'.$pageType).' ';
			echo "\n";
			echo "  \n";
			echo "\n";
			switch ($pageType) {
				case "view_folder":
					$this->folderNavigationBar($extra);
					break;
				case "view_document":
					$this->documentNavigationBar($extra);
					break;
				case "my_documents":
					$this->myDocumentsNavigationBar();
					break;
				case "my_account":
					$this->accountNavigationBar();
					break;
				case "admin_tools":
					$this->adminToolsNavigationBar();
					break;
				case "calendarold";
					$this->calendarOldNavigationBar($extra);
					break;
				case "calendar";
					$this->calendarNavigationBar($extra);
					break;
				default:
					if($this->hasHook('pageNavigationBar')) {
						$menubar = $this->callHook('pageNavigationBar', $pageType, $extra);
						if(is_string($menubar))
							echo $menubar;
					}
			}
			echo " 	
\n";
			echo " \n";
			if($pageType == "view_folder" || $pageType == "view_document")
				echo $pageTitle."\n";
//			echo "
".$pageTitle." \n";
		}
		return;
	} /* }}} */
	protected function showNavigationBar($menuitems, $options=array()) { /* {{{ */
		$content = '';
		$content .= "\n";
		foreach($menuitems as $menuitem) {
			if(!empty($menuitem['children'])) {
				$content .= "    \n";
				$content .= "     ".$menuitem['label']." \n";
				$content .= "     \n";
			} else {
				if(!empty($menuitem['divider'])) {
					$content .= "      
\n";
				} else {
					$content .= "     \n";
					$content .= " ";
				}
			}
		}
		$content .= " \n";
		echo $content;
	} /* }}} */
	protected function showNavigationListWithBadges($menuitems, $options=array()) { /* {{{ */
		$content = '';
		$content .= "\n";
		echo $content;
	} /* }}} */
	protected function showButtonwithMenu($button, $options=array()) { /* {{{ */
		$content = '';
		$content .= '
	
		
		'.$button['label'].'
		 
';
		if($button['menuitems']) {
			$content .= '
	
';
		}
		$content .= '
';
		echo $content;
	} /* }}} */
	protected function showPaneHeader($name, $title, $isactive) { /* {{{ */
		echo '
'.$title.' '."\n";
	} /* }}} */
	protected function showStartPaneContent($name, $isactive) { /* {{{ */
		echo '
';
	} /* }}} */
	protected function showEndPaneContent($name, $currentab) { /* {{{ */
		echo '
';
	} /* }}} */
	private function folderNavigationBar($folder) { /* {{{ */
		$dms = $this->params['dms'];
		$enableClipboard = $this->params['enableclipboard'];
		$accessobject = $this->params['accessobject'];
		if (!is_object($folder) || !$folder->isType('folder')) {
			self::showNavigationBar(array());
			return;
		}
		$accessMode = $folder->getAccessMode($this->params['user']);
		$folderID = $folder->getID();
		$menuitems = array();
		if ($accessMode == M_READ && !$this->params['user']->isGuest()) {
			if ($accessobject->check_controller_access('FolderNotify'))
				$menuitems['edit_folder_notify'] = array('link'=>$this->params['settings']->_httpRoot."out/out.FolderNotify.php?folderid=".$folderID."&showtree=".showtree(), 'label'=>getMLText('edit_folder_notify'));
		}
		else if ($accessMode >= M_READWRITE) {
			if ($accessobject->check_controller_access('AddSubFolder'))
				$menuitems['add_subfolder'] = array('link'=>$this->params['settings']->_httpRoot."out/out.AddSubFolder.php?folderid=". $folderID ."&showtree=".showtree(), 'label'=>getMLText('add_subfolder'));
			if ($accessobject->check_controller_access('AddDocument'))
				$menuitems['add_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.AddDocument.php?folderid=". $folderID ."&showtree=".showtree(), 'label'=>getMLText('add_document'));
			if(0 && $this->params['enablelargefileupload'])
				$menuitems['add_multiple_documents'] = array('link'=>$this->params['settings']->_httpRoot."out/out.AddMultiDocument.php?folderid=". $folderID ."&showtree=".showtree(), 'label'=>getMLText('add_multiple_documents'));
			if ($accessobject->check_controller_access('EditFolder')) {
				$menuitems['edit_folder_props'] = array('link'=>$this->params['settings']->_httpRoot."out/out.EditFolder.php?folderid=". $folderID ."&showtree=".showtree(), 'label'=>getMLText('edit_folder_props'));
			}
			if ($accessobject->check_controller_access('MoveFolder')) {
				if ($folderID != $this->params['rootfolderid'] && $folder->getParent())
					$menuitems['move_folder'] = array('link'=>$this->params['settings']->_httpRoot."out/out.MoveFolder.php?folderid=". $folderID ."&showtree=".showtree(), 'label'=>getMLText('move_folder'));
			}
			if ($accessMode == M_ALL) {
				if ($folderID != $this->params['rootfolderid'] && $folder->getParent())
					if ($accessobject->check_view_access('RemoveFolder'))
						$menuitems['rm_folder'] = array('link'=>$this->params['settings']->_httpRoot."out/out.RemoveFolder.php?folderid=". $folderID ."&showtree=".showtree(), 'label'=>getMLText('rm_folder'));
			}
			if ($accessMode == M_ALL) {
				if ($accessobject->check_view_access('FolderAccess'))
					$menuitems['edit_folder_access'] = array('link'=>$this->params['settings']->_httpRoot."out/out.FolderAccess.php?folderid=".$folderID."&showtree=".showtree(), 'label'=>getMLText('edit_folder_access'));
			}
			if ($accessobject->check_controller_access('FolderNotify'))
				$menuitems['edit_existing_notify'] = array('link'=>$this->params['settings']->_httpRoot."out/out.FolderNotify.php?folderid=". $folderID ."&showtree=". showtree(), 'label'=>getMLText('edit_existing_notify'));
		}
		if($enableClipboard) {
			$menuitems['add_to_clipboard'] = array('class'=>'addtoclipboard', 'attributes'=>array(['rel', 'F'.$folder->getId()], ['msg', getMLText('splash_added_to_clipboard')], ['title', getMLText("add_to_clipboard")]), 'label'=>getMLText("add_to_clipboard"));
		}
		if ($this->params['user']->isAdmin() && $this->params['enablefullsearch']) {
			$menuitems['index_folder'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Indexer.php?folderid=". $folderID."&showtree=".showtree(), 'label'=>getMLText('index_folder'));
		}
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'folderNavigationBar')) {
	      $menuitems = $hookObj->folderNavigationBar($this, $folder, $menuitems);
			}
		}
		self::showNavigationBar($menuitems);
	} /* }}} */
	private function documentNavigationBar($document)	{ /* {{{ */
		$accessobject = $this->params['accessobject'];
		$enableClipboard = $this->params['enableclipboard'];
		$accessMode = $document->getAccessMode($this->params['user']);
		$docid=".php?documentid=" . $document->getID();
		$menuitems = array();
		if ($accessMode >= M_READWRITE) {
			if (!$document->isLocked()) {
				if($accessobject->check_controller_access('UpdateDocument'))
					$menuitems['update_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.UpdateDocument".$docid, 'label'=>getMLText('update_document'));
				if($accessobject->check_controller_access('LockDocument'))
					$menuitems['lock_document'] = array('link'=>$this->params['settings']->_httpRoot."op/op.LockDocument".$docid."&formtoken=".createFormKey('lockdocument'), 'label'=>getMLText('lock_document'));
				if($document->isCheckedOut()) {
					if($accessobject->mayCheckIn($document)) {
						$menuitems['checkin_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.CheckInDocument".$docid, 'label'=>getMLText('checkin_document'));
					}
				} else {
					if($this->params['checkoutdir']) {
						$menuitems['checkout_document'] = array('link'=>$this->params['settings']->_httpRoot."op/op.CheckOutDocument".$docid, 'label'=>getMLText('checkout_document'));
					}
				}
				if($accessobject->check_controller_access('EditDocument'))
					$menuitems['edit_document_props'] = array('link'=>$this->params['settings']->_httpRoot."out/out.EditDocument".$docid , 'label'=>getMLText('edit_document_props'));
				if($accessobject->check_controller_access('MoveDocument'))
					$menuitems['move_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.MoveDocument".$docid, 'label'=>getMLText('move_document'));
			}
			else {
				$lockingUser = $document->getLockingUser();
				if (($lockingUser->getID() == $this->params['user']->getID()) || ($document->getAccessMode($this->params['user']) == M_ALL)) {
					if($accessobject->check_controller_access('UpdateDocument'))
						$menuitems['update_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.UpdateDocument".$docid, 'label'=>getMLText('update_document'));
					if($accessobject->check_controller_access('UnlockDocument'))
						$menuitems['unlock_document'] = array('link'=>$this->params['settings']->_httpRoot."op/op.UnlockDocument".$docid."&formtoken=".createFormKey('unlockdocument'), 'label'=>getMLText('unlock_document'));
					if($accessobject->check_controller_access('EditDocument'))
						$menuitems['edit_document_props'] = array('link'=>$this->params['settings']->_httpRoot."out/out.EditDocument".$docid, 'label'=>getMLText('edit_document_props'));
					if($accessobject->check_controller_access('MoveDocument'))
						$menuitems['move_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.MoveDocument".$docid, 'label'=>getMLText('move_document'));
				}
			}
			if($accessobject->maySetExpires($document)) {
				if ($accessobject->check_view_access('SetExpires'))
					$menuitems['expires'] = array('link'=>$this->params['settings']->_httpRoot."out/out.SetExpires".$docid, 'label'=>getMLText('expires'));
			}
		}
		if ($accessMode == M_ALL) {
			if ($accessobject->check_view_access('RemoveDocument'))
				$menuitems['rm_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.RemoveDocument".$docid, 'label'=>getMLText('rm_document'));
			if ($accessobject->check_view_access('DocumentAccess'))
				$menuitems['edit_document_access'] = array('link'=>$this->params['settings']->_httpRoot."out/out.DocumentAccess". $docid, 'label'=>getMLText('edit_document_access'));
		}
		if ($accessMode >= M_READ && !$this->params['user']->isGuest()) {
			if ($accessobject->check_view_access('DocumentNotify'))
				$menuitems['edit_existing_notify'] = array('link'=>$this->params['settings']->_httpRoot."out/out.DocumentNotify". $docid, 'label'=>getMLText('edit_existing_notify'));
		}
		if($enableClipboard) {
			$menuitems['add_to_clipboard'] = array('class'=>'addtoclipboard', 'attributes'=>array(['rel', 'D'.$document->getId()], ['msg', getMLText('splash_added_to_clipboard')], ['title', getMLText("add_to_clipboard")]), 'label'=>getMLText("add_to_clipboard"));
		}
		if ($accessobject->check_view_access('TransferDocument')) {
			$menuitems['transfer_document'] = array('link'=>$this->params['settings']->_httpRoot."out/out.TransferDocument". $docid, 'label'=>getMLText('transfer_document'));
		}
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'documentNavigationBar')) {
	      $menuitems = $hookObj->documentNavigationBar($this, $document, $menuitems);
			}
		}
		self::showNavigationBar($menuitems);
	} /* }}} */
	private function accountNavigationBar() { /* {{{ */
		$accessobject = $this->params['accessobject'];
		$menuitems = array();
		if ($accessobject->check_view_access('EditUserData') || !$this->params['disableselfedit'])
			$menuitems['edit_user_details'] = array('link'=>$this->params['settings']->_httpRoot."out/out.EditUserData.php", 'label'=>getMLText('edit_user_details'));
		
		if (!$this->params['user']->isAdmin()) 
			$menuitems['edit_default_keywords'] = array('link'=>$this->params['settings']->_httpRoot."out/out.UserDefaultKeywords.php", 'label'=>getMLText('edit_default_keywords'));
		if ($accessobject->check_view_access('ManageNotify'))
			$menuitems['edit_notify'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ManageNotify.php", 'label'=>getMLText('edit_existing_notify'));
		$menuitems['2_factor_auth'] = array('link'=>"../out/out.Setup2Factor.php", 'label'=>getMLText('2_factor_auth'));
		if ($this->params['enableusersview']){
			if ($accessobject->check_view_access('UsrView'))
				$menuitems['users'] = array('link'=>$this->params['settings']->_httpRoot."out/out.UsrView.php", 'label'=>getMLText('users'));
			if ($accessobject->check_view_access('GroupView'))
				$menuitems['groups'] = array('link'=>$this->params['settings']->_httpRoot."out/out.GroupView.php", 'label'=>getMLText('groups'));
		}		
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'accountNavigationBar')) {
	      $menuitems = $hookObj->accountNavigationBar($this, $menuitems);
			}
		}
		self::showNavigationBar($menuitems);
	} /* }}} */
	private function myDocumentsNavigationBar() { /* {{{ */
		$accessobject = $this->params['accessobject'];
		$menuitems = array();
		if ($accessobject->check_view_access('MyDocuments')) {
			$menuitems['inprocess'] = array('link'=>$this->params['settings']->_httpRoot."out/out.MyDocuments.php?inProcess=1", 'label'=>getMLText('documents_in_process'));
			$menuitems['all_documents'] = array('link'=>$this->params['settings']->_httpRoot."out/out.MyDocuments.php", 'label'=>getMLText('all_documents'));
		}
		if($this->params['workflowmode'] == 'traditional' || $this->params['workflowmode'] == 'traditional_only_approval') {
			if ($accessobject->check_view_access('ReviewSummary'))
				$menuitems['review_summary'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ReviewSummary.php", 'label'=>getMLText('review_summary'));
			if ($accessobject->check_view_access('ApprovalSummary'))
				$menuitems['approval_summary'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ApprovalSummary.php", 'label'=>getMLText('approval_summary'));
		} else {
			if ($accessobject->check_view_access('WorkflowSummary'))
				$menuitems['workflow_summary'] = array('link'=>$this->params['settings']->_httpRoot."out/out.WorkflowSummary.php", 'label'=>getMLText('workflow_summary'));
		}
		if ($accessobject->check_view_access('ReceiptSummary'))
		$menuitems['receipt_summary'] = array('link'=>"../out/out.ReceiptSummary.php", 'label'=>getMLText('receipt_summary'));
		if ($accessobject->check_view_access('RevisionSummary'))
		$menuitems['revision_summary'] = array('link'=>"../out/out.RevisionSummary.php", 'label'=>getMLText('revision_summary'));
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'mydocumentsNavigationBar')) {
	      $menuitems = $hookObj->mydocumentsNavigationBar($this, $menuitems);
			}
		}
		self::showNavigationBar($menuitems);
	} /* }}} */
	private function adminToolsNavigationBar() { /* {{{ */
		$accessobject = $this->params['accessobject'];
		$settings = $this->params['settings'];
		$menuitems = array();
		if($accessobject->check_view_access(array('UsrMgr', 'RoleMgr', 'GroupMgr', 'UserList', 'Acl'))) {
			$menuitems['user_group_management'] = array('link'=>"#", 'label'=>getMLText('user_group_management'));
			if ($accessobject->check_view_access('UsrMgr'))
				$menuitems['user_group_management']['children']['user_management'] = array('link'=>$this->params['settings']->_httpRoot."out/out.UsrMgr.php", 'label'=>getMLText('user_management'));
			if ($accessobject->check_view_access('RoleMgr'))
				$menuitems['user_group_management']['children']['role_management'] = array('link'=>$this->params['settings']->_httpRoot."out/out.RoleMgr.php", 'label'=>getMLText('role_management'));
			if ($accessobject->check_view_access('GroupMgr'))
				$menuitems['user_group_management']['children']['group_management'] = array('link'=>$this->params['settings']->_httpRoot."out/out.GroupMgr.php", 'label'=>getMLText('group_management'));
			if ($accessobject->check_view_access('UserList'))
				$menuitems['user_group_management']['children']['user_list'] = array('link'=>$this->params['settings']->_httpRoot."out/out.UserList.php", 'label'=>getMLText('user_list'));
			if ($accessobject->check_view_access('Acl'))
				$menuitems['user_group_management']['children']['access_control'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Acl.php", 'label'=>getMLText('access_control'));
			}
			if($accessobject->check_view_access(array('DefaultKeywords', 'Categories', 'AttributeMgr', 'WorkflowMgr', 'WorkflowStatesMgr', 'WorkflowActionsMgr'))) {
				$menuitems['definitions'] = array('link'=>"#", 'label'=>getMLText('definitions'));
			if ($accessobject->check_view_access('DefaultKeywords'))
				$menuitems['definitions']['children']['default_keywords'] = array('link'=>$this->params['settings']->_httpRoot."out/out.DefaultKeywords.php", 'label'=>getMLText('global_default_keywords'));
			if ($accessobject->check_view_access('Categories'))
				$menuitems['definitions']['children']['document_categories'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Categories.php", 'label'=>getMLText('global_document_categories'));
			if ($accessobject->check_view_access('AttributeMgr'))
				$menuitems['definitions']['children']['attribute_definitions'] = array('link'=>$this->params['settings']->_httpRoot."out/out.AttributeMgr.php", 'label'=>getMLText('global_attributedefinitions'));
			if($this->params['workflowmode'] == 'advanced') {
				if ($accessobject->check_view_access('WorkflowMgr'))
					$menuitems['definitions']['children']['workflows'] = array('link'=>$this->params['settings']->_httpRoot."out/out.WorkflowMgr.php", 'label'=>getMLText('global_workflows'));
				if ($accessobject->check_view_access('WorkflowStatesMgr'))
					$menuitems['definitions']['children']['workflow_states'] = array('link'=>$this->params['settings']->_httpRoot."out/out.WorkflowStatesMgr.php", 'label'=>getMLText('global_workflow_states'));
				if ($accessobject->check_view_access('WorkflowActionsMgr'))
					$menuitems['definitions']['children']['workflow_actions'] = array('link'=>$this->params['settings']->_httpRoot."out/out.WorkflowActionsMgr.php", 'label'=>getMLText('global_workflow_actions'));
			}
		}
		if($this->params['enablefullsearch']) {
			if($accessobject->check_view_access(array('Indexer', 'CreateIndex', 'IndexInfo'))) {
				$menuitems['fulltext'] = array('link'=>"#", 'label'=>getMLText('fullsearch'));
			if ($accessobject->check_view_access('Indexer'))
				$menuitems['fulltext']['children']['update_fulltext_index'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Indexer.php", 'label'=>getMLText('update_fulltext_index'));
			if ($accessobject->check_view_access('CreateIndex'))
				$menuitems['fulltext']['children']['create_fulltext_index'] = array('link'=>$this->params['settings']->_httpRoot."out/out.CreateIndex.php", 'label'=>getMLText('create_fulltext_index'));
			if ($accessobject->check_view_access('IndexInfo'))
				$menuitems['fulltext']['children']['fulltext_info'] = array('link'=>$this->params['settings']->_httpRoot."out/out.IndexInfo.php", 'label'=>getMLText('fulltext_info'));
			}
		}
		if($accessobject->check_view_access(array('BackupTools', 'LogManagement'))) {
			$menuitems['backup_log_management'] = array('link'=>"#", 'label'=>getMLText('backup_log_management'));
			if ($accessobject->check_view_access('BackupTools'))
				$menuitems['backup_log_management']['children'][] = array('link'=>$this->params['settings']->_httpRoot."out/out.BackupTools.php", 'label'=>getMLText('backup_tools'));
			if ($this->params['logfileenable'])
				if ($accessobject->check_view_access('LogManagement'))
					$menuitems['backup_log_management']['children'][] = array('link'=>$this->params['settings']->_httpRoot."out/out.LogManagement.php", 'label'=>getMLText('log_management'));
		}
		if($accessobject->check_view_access(array('ImportFS', 'ImportUsers', 'Statistic', 'Charts', 'Timeline', 'ObjectCheck', 'ExtensionMgr', 'Info'))) {
			$menuitems['misc'] = array('link'=>"#", 'label'=>getMLText('misc'));
			if ($accessobject->check_view_access('ImportFS'))
				$menuitems['misc']['children']['import_fs'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ImportFS.php", 'label'=>getMLText('import_fs'));
			if ($accessobject->check_view_access('ImportUsers'))
				$menuitems['misc']['children']['import_users'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ImportUsers.php", 'label'=>getMLText('import_users'));
			if ($accessobject->check_view_access('Statistic'))
				$menuitems['misc']['children']['folders_and_documents_statistic'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Statistic.php", 'label'=>getMLText('folders_and_documents_statistic'));
			if ($accessobject->check_view_access('Charts'))
				$menuitems['misc']['children']['charts'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Charts.php", 'label'=>getMLText('charts'));
			if ($accessobject->check_view_access('Timeline'))
				$menuitems['misc']['children']['timeline'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Timeline.php", 'label'=>getMLText('timeline'));
			if ($accessobject->check_view_access('SchedulerTaskMgr'))
				$menuitems['misc']['children']['schedulertaskmgr'] = array('link'=>$this->params['settings']->_httpRoot."out/out.SchedulerTaskMgr.php", 'label'=>getMLText('scheduler_task_mgr'));
			if ($accessobject->check_view_access('ObjectCheck'))
				$menuitems['misc']['children']['objectcheck'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ObjectCheck.php", 'label'=>getMLText('objectcheck'));
			if ($accessobject->check_view_access('ExpiredDocuments'))
				$menuitems['misc']['children']['documents_expired'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ExpiredDocuments.php", 'label'=>getMLText('documents_expired'));
			if ($accessobject->check_view_access('ExtensionMgr'))
				$menuitems['misc']['children']['extension_manager'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ExtensionMgr.php", 'label'=>getMLText('extension_manager'));
			if ($accessobject->check_view_access('ClearCache'))
				$menuitems['misc']['children']['clear_cache'] = array('link'=>$this->params['settings']->_httpRoot."out/out.ClearCache.php", 'label'=>getMLText('clear_cache'));
			if ($accessobject->check_view_access('Info'))
				$menuitems['misc']['children']['version_info'] = array('link'=>$this->params['settings']->_httpRoot."out/out.Info.php", 'label'=>getMLText('version_info'));
		}
		if ($settings->_enableDebugMode) {
			if($accessobject->check_view_access(array('Hooks', 'NotificationServices'))) {
				$menuitems['debug'] = array('link'=>"#", 'label'=>getMLText('debug'));
				if ($accessobject->check_view_access('Hooks'))
					$menuitems['debug']['children']['hooks'] = array('link'=>"../out/out.Hooks.php", 'label'=>getMLText('list_hooks'));
				if ($accessobject->check_view_access('NotificationServices'))
					$menuitems['debug']['children']['notification_services'] = array('link'=>"../out/out.NotificationServices.php", 'label'=>getMLText('list_notification_services'));
				if ($accessobject->check_view_access('ConversionServices'))
					$menuitems['debug']['children']['conversion_services'] = array('link'=>"../out/out.ConversionServices.php", 'label'=>getMLText('list_conversion_services'));
			}
		}
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'admintoolsNavigationBar')) {
	      $menuitems = $hookObj->admintoolsNavigationBar($this, $menuitems);
			}
		}
		self::showNavigationBar($menuitems);
	} /* }}} */
	
	private function calendarOldNavigationBar($d){ /* {{{ */
		$accessobject = $this->params['accessobject'];
		$ds="&day=".$d[0]."&month=".$d[1]."&year=".$d[2];
		echo "
params['settings']->_httpRoot."out/out.CalendarOld.php?mode=y\" class=\"brand\">".getMLText("calendar")." \n";
		echo "\n";
		return;
	
	} /* }}} */
	private function calendarNavigationBar($d){ /* {{{ */
		$accessobject = $this->params['accessobject'];
		$menuitems = array();
		if($accessobject->check_view_access(array('AddEvent')))
			$menuitems['addevent'] = array('link'=>$this->params['settings']->_httpRoot."out/out.AddEvent.php", 'label'=>getMLText('add_event'));
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'calendarNavigationBar')) {
	      $menuitems = $hookObj->calendarNavigationBar($this, $menuitems);
			}
		}
		self::showNavigationBar($menuitems);
	} /* }}} */
	function pageList($pageNumber, $totalPages, $baseURI, $params, $dataparams=[]) { /* {{{ */
		$maxpages = 25; // skip pages when more than this is shown
		$range = 2; // pages left and right of current page
		if (!is_numeric($pageNumber) || !is_numeric($totalPages) || $totalPages<2) {
			return;
		}
		// Construct the basic URI based on the $_GET array. One could use a
		// regular expression to strip out the pg (page number) variable to
		// achieve the same effect. This seems to be less haphazard though...
		$resultsURI = $baseURI;
		unset($params['pg']);
		$first=true;
		if($params) {
			$resultsURI .= '?'.http_build_query($params);
			$first=false;
		}
		$datastr = '';
		if($dataparams) {
			$datastr .= ' ';
			foreach($dataparams as $k=>$v)
				$datastr .= 'data-'.$k.'="'.$v.'"';
		}
		echo "";
		echo "";
		echo " ";
		return;
	} /* }}} */
	function contentContainer($content) { /* {{{ */
		echo "\n";
		echo $content;
		echo "
\n";
		return;
	} /* }}} */
	function contentContainerStart($class='', $id='') { /* {{{ */
		echo "\n";
		return;
	} /* }}} */
	function contentContainerEnd() { /* {{{ */
		echo "
\n";
		return;
	} /* }}} */
	function contentHeading($heading, $noescape=false) { /* {{{ */
		if($noescape)
			echo "".$heading." \n";
		else
			echo "".htmlspecialchars($heading)." \n";
		return;
	} /* }}} */
	function contentSubHeading($heading, $first=false) { /* {{{ */
//		echo "".htmlspecialchars($heading)."
\n";
		echo "".$heading." ";
		return;
	} /* }}} */
	function rowStart() { /* {{{ */
		echo "\n";
		return;
	} /* }}} */
	function rowEnd() { /* {{{ */
		echo "
\n";
		return;
	} /* }}} */
	function columnStart($width=6) { /* {{{ */
		echo "\n";
		return;
	} /* }}} */
	function columnEnd() { /* {{{ */
		echo "
\n";
		return;
	} /* }}} */
	function formField($title, $value, $params=array()) { /* {{{ */
		if($title !== null) {
			echo "";
		}
		return;
	} /* }}} */
	function formSubmit($value, $name='', $target='', $type='primary') { /* {{{ */
		switch($type) {
		case 'danger':
			$class = 'btn-danger';
			break;
		case 'secondary':
			$class = 'btn-secondary';
			break;
		case 'neutral':
			$class = '';
			break;
		case 'primary':
		default:
			$class = 'btn-primary';
		}
//		echo "\n";
		if(is_string($value)) {
			echo "".$value." \n";
		} else {
			if(is_array($value)) {
				foreach($value as $i=>$v)
					echo "".$v." \n";
			}
		}
//		echo "
\n";
	} /* }}} */
	function getMimeIcon($fileType) { /* {{{ */
		// for extension use LOWER CASE only
		$icons = array();
		$icons["txt"]  = "text-x-preview.svg";
		$icons["text"] = "text-x-preview.svg";
		$icons["tex"]  = "text-x-preview.svg";
		$icons["doc"]  = "office-document.svg";
		$icons["dot"]  = "office-document.svg";
		$icons["docx"] = "office-document.svg";
		$icons["dotx"] = "office-document.svg";
		$icons["rtf"]  = "office-document.svg";
		$icons["xls"]  = "office-spreadsheet.svg";
		$icons["xlt"]  = "office-spreadsheet.svg";
		$icons["xlsx"] = "office-spreadsheet.svg";
		$icons["xltx"] = "office-spreadsheet.svg";
		$icons["ppt"]  = "office-presentation.svg";
		$icons["pot"]  = "office-presentation.svg";
		$icons["pptx"] = "office-presentation.svg";
		$icons["potx"] = "office-presentation.svg";
		$icons["exe"]  = "executable.svg";
		$icons["html"] = "web.svg";
		$icons["htm"]  = "web.svg";
		$icons["gif"]  = "image.svg";
		$icons["jpg"]  = "image.svg";
		$icons["jpeg"] = "image.svg";
		$icons["bmp"]  = "image.svg";
		$icons["png"]  = "image.svg";
		$icons["tif"]  = "image.svg";
		$icons["tiff"] = "image.svg";
		$icons["log"]  = "text-x-preview.svg";
		$icons["midi"] = "audio.svg";
		$icons["pdf"]  = "gnome-mime-application-pdf.svg";
		$icons["wav"]  = "audio.svg";
		$icons["mp3"]  = "audio.svg";
		$icons["m4a"]  = "audio.svg";
		$icons["ogg"]  = "audio.svg";
		$icons["opus"]  = "audio.svg";
		$icons["c"]    = "text-x-preview.svg";
		$icons["cpp"]  = "text-x-preview.svg";
		$icons["h"]    = "text-x-preview.svg";
		$icons["java"] = "text-x-preview.svg";
		$icons["py"]   = "text-x-preview.svg";
		$icons["tar"]  = "package.svg";
		$icons["gz"]   = "package.svg";
		$icons["7z"]   = "package.svg";
		$icons["bz"]   = "package.svg";
		$icons["bz2"]  = "package.svg";
		$icons["tgz"]  = "package.svg";
		$icons["zip"]  = "package.svg";
		$icons["rar"]  = "package.svg";
		$icons["mpg"]  = "video.svg";
		$icons["mp4"]  = "video.svg";
		$icons["avi"]  = "video.svg";
		$icons["webm"]  = "video.svg";
		$icons["mkv"]  = "video.svg";
		$icons["ods"]  = "office-spreadsheet.svg";
		$icons["ots"]  = "office-spreadsheet.svg";
		$icons["sxc"]  = "office-spreadsheet.svg";
		$icons["stc"]  = "office-spreadsheet.svg";
		$icons["odt"]  = "office-document.svg";
		$icons["ott"]  = "office-document.svg";
		$icons["sxw"]  = "office-document.svg";
		$icons["stw"]  = "office-document.svg";
		$icons["odp"]  = "office-presentation.svg";
		$icons["otp"]  = "office-presentation.svg";
		$icons["sxi"]  = "office-presentation.svg";
		$icons["sti"]  = "office-presentation.svg";
		$icons["odg"]  = "office-drawing.svg";
		$icons["otg"]  = "office-drawing.svg";
		$icons["sxd"]  = "office-drawing.svg";
		$icons["std"]  = "office-drawing.svg";
		$icons["odf"]  = "ooo_formula.png";
		$icons["sxm"]  = "ooo_formula.png";
		$icons["smf"]  = "ooo_formula.png";
		$icons["mml"]  = "ooo_formula.png";
		$icons["folder"]  = "folder.svg";
		$icons["default"] = "text-x-preview.svg"; //"default.png";
		$ext = strtolower(substr($fileType, 1));
		if (isset($icons[$ext])) {
			return $this->imgpath.$icons[$ext];
		}
		else {
			return $this->imgpath.$icons["default"];
		}
	} /* }}} */
function getOverallStatusIcon($status) { /* {{{ */
	if (is_null($status)) {
		return '';
	} else {
		$icon = '';
		$color = '';
		switch($status) {
			case S_IN_WORKFLOW:
				$icon = 'fa fa-circle in-workflow';
				break;
			case S_DRAFT_REV:
				$icon = 'fa fa-circle in-workflow';
				break;
			case S_DRAFT_APP:
				$icon = 'fa fa-circle in-workflow';
				break;
			case S_RELEASED:
				$icon = 'fa-circle released';
				break;
			case S_REJECTED:
				$icon = 'fa-circle rejected';
				break;
			case S_OBSOLETE:
				$icon = 'fa-circle obsolete';
				break;
			case S_EXPIRED:
				$icon = 'fa-circle expired';
				break;
			case S_IN_REVISION:
				$icon = 'fa-refresh';
				break;
			case S_DRAFT:
				$icon = 'fa-circle-o';
				break;
			case S_NEEDS_CORRECTION:
				$icon = 'fa-circle in-workflow';
				break;
			default:
				$icon = 'fa fa-question';
				break;
		}
		return ''.getOverallStatusText($status).' 
';
	}
} /* }}} */
	/**
	 * Get attributes for a button opening a modal box
	 *
	 * @param array $config contains elements
	 *   target: id of modal box
	 *   remote: URL of data to be loaded into box
	 * @return string
	 */
	function getModalBoxLinkAttributes($config) { /* {{{ */
		$attrs = array();
		$attrs[] = array('data-target', '#'.$config['target']);
		if(isset($config['remote'])) {
			$attrs[] = array('href', $config['remote']);
			$attrs[] = array('data-remote', $config['remote']);
		}
		$attrs[] = array('data-toggle', 'modal');
		$attrs[] = array('role', 'button');
		if(isset($config['class'])) {
			if($config['class'])
				$attrs[] = array('class', $config['class']);
		} else
			$attrs[] = array('class', 'btn');
		return $attrs;
	} /* }}} */
	/**
	 * Get html for button opening a modal box
	 *
	 * @param array $config contains elements
	 *   target: id of modal box
	 *   remote: URL of data to be loaded into box
	 *   title: text on button
	 * @return string
	 */
	function getModalBoxLink($config) { /* {{{ */
//		$content = '';
//		$content .= "$attrval)
				$content .= ' '.$attrname.'="'.$attrval.'"';
		}
		$content .= ">".$config['title']." \n";
		return $content;
	} /* }}} */
	/**
	 * Get html for a modal box with buttons
	 *
	 * @param array $config contains elements
	 *   id: id of modal box (must match target of getModalBoxLink())
	 *   title: title of modal box
	 *   content: content to be shown in the body of the box. Can be left
	 *   empty if the body is loaded from the remote link passed to the button
	 *   to open this box.
	 *   buttons: array of buttons, each having a title and an optional id
	 * @return string
	 */
	function getModalBox($config) { /* {{{ */
		$content = '';
		$content .= '
	
		
			
			
';
		if(!empty($config['content']))
			$content .= $config['content'];
		else
			$content .= '
'.getMLText('data_loading').'
';
		$content .= '
			
 
	 
 
';
	return $content;
	} /* }}} */
	function printFileChooserJs() { /* {{{ */
?>
$(document).ready(function() {
/* do not use bootstrap4 custom form element because it is difficult to localize
	$(document).on('change', '.custom-file-input',function(){
		//get the file name
		var fileName = $(this).val().replace(/\\/g, '/').replace(/.*\//, '');
		//replace the "Choose a file" label
		$(this).next('.custom-file-label').html(fileName);
	})
*/
	/* Triggered after the file has been selected */
	$(document).on('change', '.btn-file :file', function() {
		var input = $(this),
		numFiles = input.get(0).files ? input.get(0).files.length : 1,
		label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
		input.trigger('fileselect', [numFiles, label]);
	});
	$(document).on('fileselect', '.upload-file .btn-file :file', function(event, numFiles, label) {
		var input = $(this).parents('.input-group').find(':text'),
		log = numFiles > 1 ? numFiles + ' files selected' : label;
		if( input.length ) {
			input.val(log);
		} else {
//			if( log ) alert(log);
		}
	});
});
  '.getMLText("browse").'…'.' 
 
';
		return $html;
 */
		$html = '
	
';
		return $html;
	} /* }}} */
	function printFileChooser($varname='userfile', $multiple=false, $accept='') { /* {{{ */
		echo self::getFileChooserHtml($varname, $multiple, $accept);
	} /* }}} */
	function printDateChooser($defDate = '', $varName, $lang='', $dateformat='', $startdate='', $enddate='', $weekstart=null) { /* {{{ */
		echo self::getDateChooser($defDate, $varName, $lang, $dateformat, $startdate, $enddate, $weekstart);
	} /* }}} */
	function getDateChooser($defDate = '', $varName, $lang='', $dateformat='', $startdate='', $enddate='', $weekstart=null, $placeholder='', $nogroup=false) { /* {{{ */
		if(!$dateformat)
			$dateformat = getConvertDateFormat();
		$content = '';
		if(!$nogroup)
			$content .= '';
		}
		return $content;
	} /* }}} */
	function __printDateChooser($defDate = -1, $varName) { /* {{{ */
	
		if ($defDate == -1)
			$defDate = mktime();
		$day   = date("d", $defDate);
		$month = date("m", $defDate);
		$year  = date("Y", $defDate);
		print "\n";
		for ($i = 1; $i <= 31; $i++)
		{
			print "" . $i . " \n";
		}
		print "  \n";
		print "\n";
		for ($i = 1; $i <= 12; $i++)
		{
			print "" . $i . " \n";
		}
		print "  \n";
		print "\n";	
		for ($i = $year-5 ; $i <= $year+5 ; $i++)
		{
			print "" . $i . " \n";
		}
		print " ";
	} /* }}} */
	function printSequenceChooser($objArr, $keepID = -1) { /* {{{ */
		echo $this->getSequenceChooser($objArr, $keepID);
	} /* }}} */
	function getSequenceChooser($objArr, $keepID = -1) { /* {{{ */
		if (count($objArr) > 0) {
			$max = $objArr[count($objArr)-1]->getSequence() + 1;
			$min = $objArr[0]->getSequence() - 1;
		}
		else {
			$max = 1.0;
		}
		$content = "\n";
		if ($keepID != -1) {
			$content .= "  " . getMLText("seq_keep");
		}
		if($this->params['defaultposition'] != 'start')
			$content .= "   " . getMLText("seq_end");
		if (count($objArr) > 0) {
			$content .= "   " . getMLText("seq_start");
		}
		if($this->params['defaultposition'] == 'start')
			$content .= "   " . getMLText("seq_end");
		for ($i = 0; $i < count($objArr) - 1; $i++) {
			if (($objArr[$i]->getID() == $keepID) || (($i + 1 < count($objArr)) && ($objArr[$i+1]->getID() == $keepID))) {
				continue;
			}
			$index = ($objArr[$i]->getSequence() + $objArr[$i+1]->getSequence()) / 2;
			$content .= "   " . getMLText("seq_after", array("prevname" => htmlspecialchars($objArr[$i]->getName())));
		}
		$content .= "  ";
		return $content;
	} /* }}} */
	
	function getDocumentChooserHtml($form, $accessMode=M_READ, $exclude = -1, $default = false, $formname = '', $folder='', $partialtree=0, $skiptree=false) { /* {{{ */
		if(!$formname)
			$formname = "docid";
		$formid = md5($formname.$form);
		if(!$folder)
			$folderid = $this->params['dms']->getRootFolder()->getId();
		else
			$folderid = $folder->getID();
		$content = '';
		$content .= "'.htmlspecialchars($attr).' ';
			}
			return implode(''.htmlspecialchars($attr).' ';
			}
			return implode(''.htmlspecialchars($targetfolder->getName()).' ';
			}
			return implode(''.htmlspecialchars($targetdoc->getName()).' ';
			}
			return implode('
					 ';
			*/
			$content = '
';
			break;
		case SeedDMS_Core_AttributeDefinition::type_email:
			$objvalue = $attribute ? (is_object($attribute) ? $attribute->getValue() : $attribute) : '';
			$content .= "";
				if($allowempty)
					$content .= "getID()."\"";
					if(in_array($curuser->getID(), $objvalue))
						$content .= " selected";
					$content .= ">".htmlspecialchars($curuser->getLogin()." - ".$curuser->getFullName())." ";
				}
				$content .= " ";
			} else {
				$content .= getMLText('no_users');
			}
			break;
		case SeedDMS_Core_AttributeDefinition::type_group:
			$objvalue = $attribute ? (is_object($attribute) ? $attribute->getValueAsArray() : (is_string($attribute) ? [$attribute] : $attribute)) : array();
			$groups = $dms->getAllGroups();
			if($groups) {
				$allowempty = $attrdef->getMinValues() == 0;
				$allowmultiple = $attrdef->getMultipleValues() || $alwaysmultiple;
				$content .= "";
				if($allowempty)
					$content .= "getID()."\"";
					if(in_array($curgroup->getID(), $objvalue))
						$content .= " selected";
					$content .= ">".htmlspecialchars($curgroup->getName())." ";
				}
				$content .= " ";
			} else {
				$content .= getMLText('no_groups');
			}
			break;
		default:
			if($valueset = $attrdef->getValueSetAsArray()) {
				$content .= "getMultipleValues() || $alwaysmultiple) {
					$content .= "[]\" multiple";
				} else {
					$content .= "\" data-allow-clear=\"true\"";
				}
				$content .= "".((!$norequire && $attrdef->getMinValues() > 0) ? ' required="required"' : '')." class=\"form-control chzn-select\" data-placeholder=\"".getMLText("select_value")."\">";
				if(!$attrdef->getMultipleValues() && !$alwaysmultiple) {
					$content .= "";
					}
				}
				$content .= "  ";
			} else {
				$objvalue = $attribute ? (is_object($attribute) ? $attribute->getValue() : $attribute) : '';
				if(strlen($objvalue) > 80) {
					$content .= "";
				} else {
					$content .= "\n";
		echo $msg;
		echo "
\n";
	} /* }}} */
	function warningMsg($msg) { /* {{{ */
		echo "\n";
		echo $msg;
		echo "
\n";
	} /* }}} */
	function errorMsg($msg) { /* {{{ */
		echo "\n";
		echo $msg;
		echo "
\n";
	} /* }}} */
	function successMsg($msg) { /* {{{ */
		echo "\n";
		echo $msg;
		echo "
\n";
	} /* }}} */
	function ___exitError($pagetitle, $error, $noexit=false, $plain=false) { /* {{{ */
		/* This is just a hack to prevent creation of js files in an error
		 * case, because they will contain this error page again. It would be much
		 * better, if there was extra error() function similar to show() and calling
		 * $view() after setting the action to 'error'. This would also allow to
		 * set separate error pages for each view.
		 */
		if(!$noexit && isset($_REQUEST['action'])) {
			if(in_array($_REQUEST['action'], array('js', 'footerjs'))) {
				exit;
			}
			if($_REQUEST['action'] == 'webrootjs') {
				$this->webrootjs();
				exit;
			}
		}
		if(!$plain) {	
			$this->htmlStartPage($pagetitle);
			$this->globalNavigation();
			$this->contentStart();
		}
		$html = '';
		$html .= "".getMLText('error')."! ";
		$html .= htmlspecialchars($error);
		$this->errorMsg($html);
		if(!$plain) {	
			print "".getMLText('back')." 
";
			$this->contentEnd();
			$this->htmlEndPage();
		}
		
		add_log_line(" UI::exitError error=".$error." pagetitle=".$pagetitle, PEAR_LOG_ERR);
		if($noexit)
			return;
		exit;	
	} /* }}} */
	function printNewTreeNavigation($folderid=0, $accessmode=M_READ, $showdocs=0, $formid='form1', $expandtree=0, $orderby='') { /* {{{ */
		$this->printNewTreeNavigationHtml($folderid, $accessmode, $showdocs, $formid, $expandtree, $orderby);
?>
		
\n";
		echo "
\n";
	} /* }}} */
	/**
	 * Create a tree of folders using jqtree.
	 *
	 * The tree can contain folders only or include documents.
	 *
	 * @param integer $folderid current folderid. If set the tree will be
	 *   folded out and the all folders in the path will be visible
	 * @param integer $accessmode use this access mode when retrieving folders
	 *   and documents shown in the tree
	 * @param boolean $showdocs set to true if tree shall contain documents
	 *   as well.
	 * @param integer $expandtree level to which the tree shall be opened
	 * @param boolean $partialtree set to true if the given folder is the start folder
	 */
	function printNewTreeNavigationJs($folderid=0, $accessmode=M_READ, $showdocs=0, $formid='form1', $expandtree=0, $orderby='', $partialtree=false) { /* {{{ */
		function jqtree($obj, $path, $folder, $user, $accessmode, $showdocs=1, $expandtree=0, $orderby='', $level=0) { /* {{{ */
			$orderdir = (isset($orderby[1]) ? ($orderby[1] == 'd' ? 'desc' : 'asc') : 'asc');
			if($path/* || $expandtree>=$level*/) {
				if($path)
					$pathfolder = array_shift($path);
				$children = array();
				if($expandtree) {
					$subfolders = $folder->getSubFolders(isset($orderby[0]) ? $orderby[0] : '', $orderdir);
					$subfolders = SeedDMS_Core_DMS::filterAccess($subfolders, $user, $accessmode);
				} else {
					$subfolders = array($pathfolder);
				}
				foreach($subfolders as $subfolder) {
					$node = array('label'=>$subfolder->getName(), 'id'=>$subfolder->getID(), 'load_on_demand'=>(1 && ($subfolder->hasSubFolders() || ($subfolder->hasDocuments() && $showdocs))) ? true : false, 'is_folder'=>true);
					/* if the subfolder is in the path then further unfold the tree. */
					if(/*$expandtree>=$level ||*/ $path && ($path[0]->getID() == $subfolder->getID())) {
						$node['children'] = jqtree($obj, $path, $subfolder, $user, $accessmode, $showdocs, $expandtree, $orderby, $level+1);
						if($showdocs) {
							$documents = $subfolder->getDocuments(isset($orderby[0]) ? $orderby[0] : '', $orderdir);
							$documents = SeedDMS_Core_DMS::filterAccess($documents, $user, $accessmode);
							if($obj->hasHook('filterTreeDocuments'))
								$documents = $obj->callHook('filterTreeDocuments', $folder, $documents);
							foreach($documents as $document) {
								$node2 = array('label'=>$document->getName(), 'id'=>$document->getID(), 'load_on_demand'=>false, 'is_folder'=>false);
								$node['children'][] = $node2;
							}
						}
					}
					$children[] = $node;
				}
				return $children;
			} else {
				$subfolders = $folder->getSubFolders(isset($orderby[0]) ? $orderby[0] : '', $orderdir);
				$subfolders = SeedDMS_Core_DMS::filterAccess($subfolders, $user, $accessmode);
				$children = array();
				foreach($subfolders as $subfolder) {
					$node = array('label'=>$subfolder->getName(), 'id'=>$subfolder->getID(), 'load_on_demand'=>($subfolder->hasSubFolders() || ($subfolder->hasDocuments() && $showdocs)) ? true : false, 'is_folder'=>true);
					$children[] = $node;
				}
				return $children;
			}
			return array();
		} /* }}} */
		$orderdir = (isset($orderby[1]) ? ($orderby[1] == 'd' ? 'desc' : 'asc') : 'asc');
		if($folderid && ($folder = $this->params['dms']->getFolder($folderid))) {
			if(!$partialtree) {
				$path = $folder->getPath();
				/* Get the first folder (root folder) of path */
				$folder = array_shift($path);
			}
			$node = array('label'=>$folder->getName(), 'id'=>$folder->getID(), 'load_on_demand'=>false, 'is_folder'=>true);
			if(!$folder->hasSubFolders()) {
				$node['load_on_demand'] = true;
				$node['children'] = array();
			} else {
				$node['children'] = jqtree($this, $path, $folder, $this->params['user'], $accessmode, $showdocs, 1 /*$expandtree*/, $orderby, 0);
				if($showdocs) {
					$documents = $folder->getDocuments(isset($orderby[0]) ? $orderby[0] : '', $orderdir);
					$documents = SeedDMS_Core_DMS::filterAccess($documents, $this->params['user'], $accessmode);
					if($this->hasHook('filterTreeDocuments'))
						$documents = $this->callHook('filterTreeDocuments', $folder, $documents);
					foreach($documents as $document) {
						$node2 = array('label'=>$document->getName(), 'id'=>$document->getID(), 'load_on_demand'=>false, 'is_folder'=>false);
						$node['children'][] = $node2;
					}
				}
			}
			/* Nasty hack to remove the highest folder */
			if(isset($this->params['remove_root_from_tree']) && $this->params['remove_root_from_tree']) {
				foreach($node['children'] as $n)
					$tree[] = $n;
			} else {
				$tree[] = $node;
			}
			
		} else {
			if($root = $this->params['dms']->getFolder($this->params['rootfolderid']))
				$tree = array(array('label'=>$root->getName(), 'id'=>$root->getID(), 'load_on_demand'=>false, 'is_folder'=>true));
			else
				$tree = array();
		}
?>
var data = ;
$(function() {
	const $tree = $('#jqtree');
	$tree.tree({
//		saveState: false,
		selectable: false,
		data: data,
		saveState: 'jqtree',
		openedIcon: $('params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=". $folderid."&showtree=0\"> ", true);
			$this->contentContainerStart();
?>
	
printNewTreeNavigation($folderid, M_READ, 0, '');
			$this->contentContainerEnd();
		} else {
			$this->contentHeading("params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=". $folderid."&showtree=1\"> ", true);
		}
	} /* }}} */
	/**
	 * Print clipboard in div container
	 *
	 * @param array clipboard
	 */
	function printClipboard($clipboard, $previewer){ /* {{{ */
		echo "\n";
		$this->contentHeading(getMLText("clipboard").'
', true);
		echo "
\n";
	} /* }}} */
	/**
	 * Wrap text in inline editing tags
	 *
	 * @param string text
	 */
	function printInlineEdit($text, $object){ /* {{{ */
		if(!empty($this->params['settings']->_inlineEditing)) {
			echo "
isType('document'))
				echo " data-document=\"".$object->getId()."\" data-formtoken=\"".createFormKey('setdocumentname')."\"";
			echo ">".$text;
			echo " \n";
		} else
			echo $text;
	} /* }}} */
	/**
	 * Print button with link for deleting a document
	 *
	 * This button is used in document listings (e.g. on the ViewFolder page)
	 * for deleting a document. In seeddms version < 4.3.9 this was just a
	 * link to the out/out.RemoveDocument.php page which asks for confirmation
	 * an than calls op/op.RemoveDocument.php. Starting with version 4.3.9
	 * the button just opens a small popup asking for confirmation and than
	 * calls the ajax command 'deletedocument'. The ajax call is called
	 * in the click function of 'button.removedocument'. That button needs
	 * to have two attributes: 'rel' for the id of the document, and 'msg'
	 * for the message shown by notify if the document could be deleted.
	 *
	 * @param object $document document to be deleted
	 * @param string $msg message shown in case of successful deletion
	 * @param boolean $return return html instead of printing it
	 * @return string html content if $return is true, otherwise an empty string
	 */
	function printDeleteDocumentButton($document, $msg, $return=false){ /* {{{ */
		$docid = $document->getID();
		$content = '';
		$content .= '
 $document->getName())), ENT_QUOTES).'" title="'.getMLText("delete").'"> ';
		if($return)
			return $content;
		else
			echo $content;
		return '';
	} /* }}} */
	function printDeleteDocumentButtonJs(){ /* {{{ */
		echo "
		$(document).ready(function () {
//			$('.delete-document-btn').click(function(ev) {
			$('body').on('click', 'a.delete-document-btn', function(ev){
				ev.stopPropagation();
				id = $(ev.currentTarget).attr('rel');
				confirmmsg = $(ev.currentTarget).attr('confirmmsg');
				msg = $(ev.currentTarget).attr('msg');
				formtoken = '".createFormKey('removedocument')."';
				bootbox.confirm({
					\"message\": confirmmsg,
					\"buttons\": {
						\"confirm\": {
							\"label\" : \"
 ".getMLText("rm_document")."\",
							\"className\" : \"btn-danger\",
						},
						\"cancel\": {
							\"label\" : \"".getMLText("cancel")."\",
							\"className\" : \"btn-secondary\",
						}
					},
					\"callback\": function(result) {
						if(result) {
							$.get('".$this->params['settings']->_httpRoot."op/op.Ajax.php',
								{ command: 'deletedocument', id: id, formtoken: formtoken },
								function(data) {
									if(data.success) {
										$('#table-row-document-'+id).hide('slow');
										noty({
											text: msg,
											type: 'success',
											dismissQueue: true,
											layout: 'topRight',
											theme: 'defaultTheme',
											timeout: 1500,
										});
									} else {
										noty({
											text: data.message,
											type: 'error',
											dismissQueue: true,
											layout: 'topRight',
											theme: 'defaultTheme',
											timeout: 3500,
										});
									}
								},
								'json'
							);
						}
					}
				});
			});
		});
		";
	} /* }}} */
	/**
	 * Print button with link for deleting a folder
	 *
	 * This button works like document delete button
	 * {@link SeedDMS_Bootstrap_Style::printDeleteDocumentButton()}
	 *
	 * @param object $folder folder to be deleted
	 * @param string $msg message shown in case of successful deletion
	 * @param boolean $return return html instead of printing it
	 * @return string html content if $return is true, otherwise an empty string
	 */
	function printDeleteFolderButton($folder, $msg, $return=false){ /* {{{ */
		$folderid = $folder->getID();
		$content = '';
		$content .= '
 $folder->getName())), ENT_QUOTES).'" title="'.getMLText("delete").'"> ';
		if($return)
			return $content;
		else
			echo $content;
		return '';
	} /* }}} */
	function printDeleteFolderButtonJs(){ /* {{{ */
		echo "
		$(document).ready(function () {
//			$('.delete-folder-btn').click(function(ev) {
			$('body').on('click', 'a.delete-folder-btn', function(ev){
				ev.stopPropagation();
				id = $(ev.currentTarget).attr('rel');
				confirmmsg = $(ev.currentTarget).attr('confirmmsg');
				msg = $(ev.currentTarget).attr('msg');
				formtoken = '".createFormKey('removefolder')."';
				bootbox.confirm({
					\"message\": confirmmsg,
					\"buttons\": {
						\"confirm\": {
							\"label\" : \"
 ".getMLText("rm_folder")."\",
							\"className\" : \"btn-danger\",
						},
						\"cancel\": {
							\"label\" : \"".getMLText("cancel")."\",
							\"className\" : \"btn-secondary\",
						}
					},
					\"callback\": function(result) {
						if(result) {
							$.get('".$this->params['settings']->_httpRoot."op/op.Ajax.php',
								{ command: 'deletefolder', id: id, formtoken: formtoken },
								function(data) {
									if(data.success) {
										$('#table-row-folder-'+id).hide('slow');
										noty({
											text: msg,
											type: 'success',
											dismissQueue: true,
											layout: 'topRight',
											theme: 'defaultTheme',
											timeout: 1500,
										});
									} else {
										noty({
											text: data.message,
											type: 'error',
											dismissQueue: true,
											layout: 'topRight',
											theme: 'defaultTheme',
											timeout: 3500,
										});
									}
								},
								'json'
							);
						}
					}
				});
			});
		});
		";
	} /* }}} */
	function printLockButton($document, $msglock, $msgunlock, $return=false) { /* {{{ */
		$accessobject = $this->params['accessobject'];
		$docid = $document->getID();
		if($document->isLocked()) {
			if(!$accessobject->check_controller_access('UnlockDocument'))
				return '';
			$icon = 'unlock';
			$msg = $msgunlock;
			$title = 'unlock_document';
		} else {
			if(!$accessobject->check_controller_access('LockDocument'))
				return '';
			$icon = 'lock';
			$msg = $msglock;
			$title = 'lock_document';
		}
		$content = '';
		$content .= '
';
		if($return)
			return $content;
		else
			echo $content;
		return '';
	} /* }}} */
	function printAccessButton($object, $return=false) { /* {{{ */
		$accessobject = $this->params['accessobject'];
		$content = '';
		$objid = $object->getId();
		if($object->isType('document')) {
			if($accessobject->check_view_access('DocumentAccess'))
				$content .= '
';
		} elseif($object->isType('folder')) {
			if($accessobject->check_view_access('FolderAccess'))
				$content .= '
';
		}
		if($return)
			return $content;
		else
			echo $content;
		return '';
	} /* }}} */
	/**
	 * Output left-arrow with link which takes over a number of ids into
	 * a select box.
	 *
	 * Clicking in the button will preset the comma seperated list of ids
	 * in data-ref as options in the select box with name $name
	 *
	 * @param string $name id of select box
	 * @param array $ids list of option values
	 */
	function getSelectPresetButtonHtml($name, $ids) { /* {{{ */
		return '
';
	} /* }}} */
	/**
	 * Output left-arrow with link which takes over a number of ids into
	 * a select box.
	 *
	 * Clicking in the button will preset the comma seperated list of ids
	 * in data-ref as options in the select box with name $name
	 *
	 * @param string $name id of select box
	 * @param array $ids list of option values
	 */
	function printSelectPresetButtonHtml($name, $ids) { /* {{{ */
		echo self::getSelectPresetButtonHtml($name, $ids);
	} /* }}} */
	/**
	 * Javascript code for select preset button
	 */
	function printSelectPresetButtonJs() { /* {{{ */
?>
$(document).ready( function() {
	$('.selectpreset_btn').click(function(ev){
		ev.preventDefault();
		if (typeof $(ev.currentTarget).data('ids') != 'undefined') {
			target = $(ev.currentTarget).data('ref');
			// Use attr() instead of data() because data() converts to int which cannot be split
			items = $(ev.currentTarget).attr('data-ids');
			arr = items.split(",");
			for(var i in arr) {
				$("#"+target+" option[value='"+arr[i]+"']").attr("selected", "selected");
			}
//			$("#"+target).trigger("chosen:updated");
			$("#"+target).trigger("change");
		}
	});
});
';
	} /* }}} */
	/**
	 * Output left-arrow with link which takes over a string into
	 * a input field.
	 *
	 * Clicking on the button will preset the string
	 * in data-ref the value of the input field with name $name
	 *
	 * @param string $name id of select box
	 * @param string $text text
	 */
	function printInputPresetButtonHtml($name, $text, $sep='') { /* {{{ */
		echo self::getInputPresetButtonHtml($name, $text, $sep);
	} /* }}} */
	/**
	 * Javascript code for input preset button
	 * This code workѕ for input fields and single select fields
	 */
	function printInputPresetButtonJs() { /* {{{ */
?>
$(document).ready( function() {
	$('.inputpreset_btn').click(function(ev){
		ev.preventDefault();
		if (typeof $(ev.currentTarget).data('text') != 'undefined') {
			target = $(ev.currentTarget).data('ref');
			value = $(ev.currentTarget).data('text');
			sep = $(ev.currentTarget).data('sep');
			if(sep) {
				// Use attr() instead of data() because data() converts to int which cannot be split
				arr = value.split(sep);
				for(var i in arr) {
					$("#"+target+" option[value='"+arr[i]+"']").attr("selected", "selected");
				}
			} else {
				$("#"+target).val(value);
			}
		}
	});
});
		return '
';
$(document).ready( function() {
	$('.checkboxpreset_btn').click(function(ev){
		ev.preventDefault();
		if (typeof $(ev.currentTarget).data('text') != 'undefined') {
			target = $(ev.currentTarget).data('ref');
			value = $(ev.currentTarget).data('text');
			if(value) {
				$("#"+target).attr('checked', '');
			} else {
				$("#"+target).removeAttribute('checked');
			}
		}
	});
});
getID().'" msg="'.getMLText($msg).'" attrvalue="'.htmlspecialchars($value, ENT_QUOTES).'" confirmmsg="'.htmlspecialchars(getMLText("confirm_rm_attr_value", array ("attrdefname" => $attrdef->getName())), ENT_QUOTES).'">
';
		if($return)
			return $content;
		else
			echo $content;
		return '';
	} /* }}} */
	function printDeleteAttributeValueButtonJs(){ /* {{{ */
		echo "
		$(document).ready(function () {
//			$('.delete-attribute-value-btn').click(function(ev) {
			$('body').on('click', 'a.delete-attribute-value-btn', function(ev){
				id = $(ev.currentTarget).attr('rel');
				confirmmsg = $(ev.currentTarget).attr('confirmmsg');
				attrvalue = $(ev.currentTarget).attr('attrvalue');
				msg = $(ev.currentTarget).attr('msg');
				formtoken = '".createFormKey('removeattrvalue')."';
				bootbox.confirm({
					\"message\": confirmmsg,
					\"buttons\": {
						\"confirm\": {
							\"label\" : \"
 ".getMLText("rm_attr_value")."\",
							\"className\" : \"btn-danger\",
						},
						\"cancel\": {
							\"label\" : \"".getMLText("cancel")."\",
							\"className\" : \"btn-secondary\",
						}
					},
					\"callback\": function(result) {
						if(result) {
							$.post('".$this->params['settings']->_httpRoot."op/op.AttributeMgr.php',
								{ action: 'removeattrvalue', attrdefid: id, attrvalue: attrvalue, formtoken: formtoken },
								function(data) {
									if(data.success) {
										$('#table-row-attrvalue-'+id).hide('slow');
										noty({
											text: msg,
											type: 'success',
											dismissQueue: true,
											layout: 'topRight',
											theme: 'defaultTheme',
											timeout: 1500,
										});
									} else {
										noty({
											text: data.message,
											type: 'error',
											dismissQueue: true,
											layout: 'topRight',
											theme: 'defaultTheme',
											timeout: 3500,
										});
									}
								},
								'json'
							);
						}
					}
				});
			});
		});
		";
	} /* }}} */
	function printClickDocumentJs() { /* {{{ */
		$onepage = $this->params['onepage'];
		if($onepage) {
?>
/* catch click on a document row in the list folders and documents */
$('body').on('click', '[id^=\"table-row-document\"] td:nth-child(2)', function(ev) {
	if(ev.shiftKey) {
		$(ev.currentTarget).parent().toggleClass('selected');
	} else {
		attr_id = $(ev.currentTarget).parent().attr('id').split('-')[3];
		window.location = '= $this->params['settings']->_httpRoot ?>out/out.ViewDocument.php?documentid=' + attr_id;
	}
});
params['onepage'];
		if($onepage) {
?>
/* catch click on a document row in the list folders and documents */
$('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) {
	if(ev.shiftKey) {
		$(ev.currentTarget).parent().toggleClass('selected');
	} else {
		attr_id = $(ev.currentTarget).parent().data('target-id');
		if(typeof attr_id == 'undefined')
			attr_id = $(ev.currentTarget).parent().attr('id').split('-')[3];
		window.location = '= $this->params['settings']->_httpRoot ?>out/out.ViewFolder.php?folderid=' + attr_id;
	}
});
getParent();
		if($folder) {
			$belowtitle .= "
".getMLText('in_folder').": /";
			$path = $folder->getPath();
			for ($i = 1; $i  < count($path); $i++) {
				$belowtitle .= htmlspecialchars($path[$i]->getName())."/";
			}
			$belowtitle .= " ";
		}
		return $belowtitle;
	} /* }}} */
	public function folderListHeaderImage() { /* {{{ */
		$folder = $this->getParam('folder');
		$onepage = $this->params['onepage'];
		$parent = ($folder && $onepage) ? $folder->getParent() : null;
		$headcol = ($parent ? '
' : '')."\n";	
		return $headcol;	
	} /* }}} */
	public function folderListHeaderName() { /* {{{ */
		$folder = $this->getParam('folder');
		$headcol = getMLText("name");
		if($folder) {
			$folderid = $folder->getId();
			$orderby = $this->params['orderby'];
			$orderdir = (isset($orderby[1]) ? ($orderby[1] == 'd' ? 'desc' : 'asc') : 'asc');
			$headcol .= " 
params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=". $folderid .($orderby=="n"||$orderby=="na"?"&orderby=nd":"&orderby=n")."\" data-orderby=\"".($orderby=="n"||$orderby=="na"?"nd":"n")."\"title=\"".getMLText("sort_by_name")."\">".($orderby=="n"||$orderby=="na"?'  ";
			$headcol .= " 
params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=". $folderid .($orderby=="s"||$orderby=="sa"?"&orderby=sd":"&orderby=s")."\" data-orderby=\"".($orderby=="s"||$orderby=="sa"?"sd":"s")."\" title=\"".getMLText("sort_by_sequence")."\">".($orderby=="s"||$orderby=="sa"?'  ";
			$headcol .= " 
params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=". $folderid .($orderby=="d"||$orderby=="da"?"&orderby=dd":"&orderby=d")."\" data-orderby=\"".($orderby=="d"||$orderby=="da"?"dd":"d")."\" title=\"".getMLText("sort_by_date")."\">".($orderby=="d"||$orderby=="da"?'  ";
		}
		return $headcol;	
	} /* }}} */
	public function folderListHeader() { /* {{{ */
		$content = "
";
		$content .= "\n\n";
		$headcols = array();
		$headcols['image'] = $this->folderListHeaderImage();	
		$headcols['name'] = $this->folderListHeaderName();
		if($ec = $this->callHook('folderListHeaderExtraColumns'))
				$headcols = array_merge($headcols, $ec);
		$headcols['status'] = getMLText("status");
		$headcols['action'] = getMLText("action");
		foreach($headcols as $headcol)
			$content .= "".$headcol." \n";
		$content .= " \n \n";
		return $content;
	} /* }}} */
	/**
	 * Start the row for a folder in list of documents and folders
	 *
	 * For a detailed description see
	 * {@link SeedDMS_Bootstrap_Style::folderListRowStart()}
	 */
	function documentListRowStart($document, $class='') { /* {{{ */
		if($class) {
			if($class == 'error')
				$class = 'table-danger';
			else
				$class = 'table-'.$class;
		}
		$docID = $document->getID();
		return "getName(), ENT_QUOTES)."\">";
	} /* }}} */
	function documentListRowEnd($document) { /* {{{ */
			return " \n";
	} /* }}} */
	function documentListRowStatus($latestContent) { /* {{{ */
		$user = $this->params['user'];
		$workflowmode = $this->params['workflowmode'];
		$document = $latestContent->getDocument();
		$status = $latestContent->getStatus();
		$attentionstr = '';
		if ( $document->isLocked() ) {
			$attentionstr .= "getLockingUser()->getFullName())."\">  ";
		}
		if($workflowmode == 'advanced') {
			$workflow = $latestContent->getWorkflow();
			if($workflow && $latestContent->needsWorkflowAction($user)) {
				$attentionstr .= "getName())."\">  ";
			}
		}
		$content = '';
		if($attentionstr)
			$content .= $attentionstr."";
		if(count($files))
			$content .= 'getName()) : '').'">'.($workflowstate ? htmlspecialchars($workflowstate->getName()) : '').' ';
		} else {
			$content .= $this->getOverallStatusIcon($status['status']);
		}
		$content .= "
";
		return $content;
	} /* }}} */
	function documentListRowAction($document, $previewer, $skipcont=false, $version=0, $extracontent=array()) { /* {{{ */
		$user = $this->params['user'];
		$enableClipboard = $this->params['enableclipboard'];
		$accessop = $this->params['accessobject'];
		$onepage = $this->params['onepage'];
		$content = '';
		$content .= "";
		$actions = array();
		if(!empty($extracontent['begin_action_list']))
			$content .= $extracontent['begin_action_list'];
		if($accessop->check_view_access('RemoveDocument')) {
			if($document->getAccessMode($user) >= M_ALL) {
				$actions['remove_document'] = $this->printDeleteDocumentButton($document, 'splash_rm_document', true);
			} else {
				$actions['remove_document'] = '
';
			}
		}
		$docID = $document->getID();
		if($document->getAccessMode($user) >= M_READWRITE) {
			$actions['edit_document'] = '
';
		} else {
			$actions['edit_document'] = '
';
		}
		if($document->getAccessMode($user) >= M_READWRITE) {
			$actions['lock_document'] = $this->printLockButton($document, 'splash_document_locked', 'splash_document_unlocked', true);
		}
		if($document->getAccessMode($user) >= M_READWRITE) {
			$actions['document_access'] = $this->printAccessButton($document, true);
		}
		if($enableClipboard) {
			$actions['add_to_clipboard'] = '
';
		}
		if($onepage)
			$actions['view_document'] = '
';
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'documentRowAction')) {
	      $actions = $hookObj->documentRowAction($this, $document, $actions);
			}
		}
		foreach($actions as $action) {
			if(is_string($action))
				$content .= $action;
		}
		if(!empty($extracontent['end_action_list']))
			$content .= $extracontent['end_action_list'];
		$content .= "
";
			if (file_exists($dms->contentDir . $latestContent->getPath())) {
				$previewhtml = $this->callHook('documentListPreview', $previewer, $document, $latestContent);
				if(is_string($previewhtml))
					$content .= $previewhtml;
				else {
					if($accessop->check_controller_access('Download', array('action'=>'version')))
						$content .= "params['settings']->_httpRoot."op/op.Download.php?documentid=".$docID."&version=".$version."\">";
					if($previewer->hasPreview($latestContent)) {
						$content .= " ";
				}
			} else
				$content .= " ";
			$content .= "";
			if($onepage)
				$content .= "".htmlspecialchars($document->getName()) . " ";
			else
				$content .= "params['settings']->_httpRoot."out/out.ViewDocument.php?documentid=".$docID."&showtree=".$showtree."\">" . htmlspecialchars($document->getName()) . " ";
			if(isset($extracontent['below_title']))
				$content .= $extracontent['below_title'];
			$content .= "".getMLText('owner').": ".htmlspecialchars($owner->getFullName())." , ".getMLText('creation_date').": ".getReadableDate($document->getDate())." , ".getMLText('version')." ".$version."  - ".getReadableDate($latestContent->getDate())." ".($document->expires() ? ", ".getMLText('expires').": ".getReadableDate($document->getExpires())." " : "")." ";
			if($comment) {
				$content .= "".htmlspecialchars($comment)." ";
			}
			if($categories = $document->getCategories()) {
				$content .= "".$category->getName()."  ";
				}
			}
			if(!empty($extracontent['bottom_title']))
				$content .= $extracontent['bottom_title'];
			$content .= " \n";
			if(!empty($extracontent['columns'])) {
				foreach($extracontent['columns'] as $col)
					$content .= ''.$col.' ';
			}
			$content .= "";
			$content .= $this->documentListRowStatus($latestContent);
			if($accessop->check_view_access($this, array('action'=>'receptionBar')) /*$owner->getID() == $user->getID()*/ && $receiptStatus = $latestContent->getReceiptStatus()) {
				$rstat = array('-1'=>0, '0'=>0, '1'=>0, '-2'=>0);
				$allcomments = array('-1'=>array(), '1'=>array());
				foreach ($receiptStatus as $r) {
					$rstat[''.$r['status']]++;
					if($r['comment']) {
//						$allcomments[''.$r['status']][] = htmlspecialchars($r['comment']);
						$m5 = md5(trim($r['comment']));
						if(isset($allcomments[''.$r['status']][$m5]))
							$allcomments[''.$r['status']][$m5]['n']++;
						else
							$allcomments[''.$r['status']][$m5] = array('n'=>1, 'c'=>htmlspecialchars(trim($r['comment'])));
					}
				}
				$totalreceipts = $rstat['-1'] + $rstat['0'] + $rstat['1'];
				if($totalreceipts) {
					$content .= "
".($rstat['1'] ? $rstat['1']."/".$totalreceipts : '').($allcomments['1'] ? " ".$this->printPopupBox('', implode('
	".($rstat['-1'] ? $rstat['-1']."/".$totalreceipts : '').($allcomments['-1'] ? " ".$this->printPopupBox('', implode('
 ";
				}
			}
			$content .= " ";
//				$content .= "".$version." ";
			$content .= "";
			$content .= $this->documentListRowAction($document, $previewer, $skipcont, $version, $extracontent);
			$content .= " ";
			if(!empty($extracontent['columns_last'])) {
				foreach($extracontent['columns_last'] as $col)
					$content .= ''.$col.' ';
			}
			if(!$skipcont)
				$content .= $this->documentListRowEnd($document);
		}
		return $content;
	} /* }}} */
	/**
	 * Start the row for a folder in list of documents and folders
	 *
	 * This method creates the starting tr tag for a new table row containing
	 * a folder list entry. The tr tag contains various attributes which are
	 * used for removing the table line and to make drap&drop work.
	 *
	 * id=table-row-folder- : used for identifying the row when removing the table
	 *   row after deletion of the folder by clicking on the delete button in that table
	 *   row.
	 * data-droptarget=folder_ : identifies the folder represented by this row
	 *   when it used as a target of the drag&drop operation.
	 *   If an element (either a file or a dragged item) is dropped on this row, the
	 *   data-droptarget will be evaluated to identify the underlying dms object.
	 *   Dropping a file on a folder will upload that file into the folder. Droping
	 *   an item (which is currently either a document or a folder) from the page will
	 *   move that item into the folder.
	 * rel=folder_ : This data is put into drag data when a drag starts. When the
	 *   item is dropped on some other item this data will identify the source object.
	 *   The attributes data-droptarget and rel are usually equal. At least there is
	 *   currently no scenario where they are different.
	 * formtoken= : token made of key 'movefolder'
	 *   formtoken is also placed in the drag data just like the value of attibute 'rel'.
	 *   This is always set to a value made of 'movefolder'.
	 * data-uploadformtoken= : token made of key 'adddocument'
	 * class=table-row-folder : The class must have a class named 'table-row-folder' in
	 *   order to be draggable and to extract the drag data from the attributes 'rel' and
	 *   'formtoken'
	 *
	 * @param object $folder
	 * @return string starting tr tag for a table
	 */
	function folderListRowStart($folder, $class='') { /* {{{ */
		return "getID()."\" draggable=\"true\" data-droptarget=\"folder_".$folder->getID()."\" rel=\"folder_".$folder->getID()."\" class=\"folder table-row-folder droptarget".($class ? ' '.$class : '')."\" data-uploadformtoken=\"".createFormKey('')."\" formtoken=\"".createFormKey('')."\" data-name=\"".htmlspecialchars($folder->getName(), ENT_QUOTES)."\">";
	} /* }}} */
	function folderListRowEnd($folder) { /* {{{ */
			return " \n";
	} /* }}} */
	function folderListRowStatus($subFolder) { /* {{{ */
		$dms = $this->params['dms'];
		$user = $this->params['user'];
		$showtree = $this->params['showtree'];
		$enableRecursiveCount = $this->params['enableRecursiveCount'];
		$maxRecursiveCount = $this->params['maxRecursiveCount'];
		$content = "";
		if($enableRecursiveCount) {
			if($user->isAdmin()) {
				/* No need to check for access rights in countChildren() for
				 * admin. So pass 0 as the limit.
				 */
				$cc = $subFolder->countChildren($user, 0);
				if($cc['folder_count'])
					$content .= '
";
		return $content;
	} /* }}} */
	function folderListRowAction($subFolder, $skipcont=false, $extracontent=array()) { /* {{{ */
		$dms = $this->params['dms'];
		$user = $this->params['user'];
//		$folder = $this->params['folder'];
		$showtree = $this->params['showtree'];
		$enableRecursiveCount = $this->params['enableRecursiveCount'];
		$maxRecursiveCount = $this->params['maxRecursiveCount'];
		$enableClipboard = $this->params['enableclipboard'];
		$accessop = $this->params['accessobject'];
		$onepage = $this->params['onepage'];
		$content = '';
		$content .= "";
		$actions = array();
		if(!empty($extracontent['begin_action_list']))
			$content .= $extracontent['begin_action_list'];
		$subFolderAccessMode = $subFolder->getAccessMode($user);
		if ($accessop->check_view_access('RemoveFolder')) {
			if($subFolderAccessMode >= M_ALL) {
				$actions['remove_folder'] = $this->printDeleteFolderButton($subFolder, 'splash_rm_folder', true);
			} else {
				$actions['remove_folder'] = '
';
			}
		}
		if ($accessop->check_view_access('EditFolder')) {
			if($subFolderAccessMode >= M_READWRITE) {
				$actions['edit_folder'] = '
';
			} else {
				$actions['edit_folder'] = '
';
			}
		}
		if($subFolderAccessMode >= M_READWRITE) {
			$actions['folder_access'] = $this->printAccessButton($subFolder, true);
		}
		if($enableClipboard) {
			$actions['add_to_clipboard'] = '
';
		}
		if($onepage)
			$actions['view_folder'] = '
';
		/* Do not use $this->callHook() because $menuitems must be returned by the the
		 * first hook and passed to next hook. $this->callHook() will just pass
		 * the menuitems to each single hook. Hence, the last hook will win.
		 */
		$hookObjs = $this->getHookObjects();
		foreach($hookObjs as $hookObj) {
			if (method_exists($hookObj, 'folderRowAction')) {
	      $actions = $hookObj->folderRowAction($this, $folder, $actions);
			}
		}
		foreach($actions as $action) {
			if(is_string($action))
				$content .= $action;
		}
		if(!empty($extracontent['end_action_list']))
			$content .= $extracontent['end_action_list'];
		$content .= "
params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=".$subFolder->getID()."&showtree=".$showtree."\"> " . "getId()."\">".htmlspecialchars($subFolder->getName())." ";
		else
			$content .= " params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=".$subFolder->getID()."&showtree=".$showtree."\">" . htmlspecialchars($subFolder->getName()) . " ";
		if(isset($extracontent['below_title']))
			$content .= $extracontent['below_title'];
		$content .= "".getMLText('owner').": ".htmlspecialchars($owner->getFullName())." , ".getMLText('creation_date').": ".date('Y-m-d', $subFolder->getDate())."  ";
		if($comment) {
			$content .= "".htmlspecialchars($comment)." ";
		}
		if(isset($extracontent['bottom_title']))
			$content .= $extracontent['bottom_title'];
		$content .= "".htmlspecialchars($owner->getFullName())." ";
		$content .= "";
		$content .= $this->folderListRowStatus($subFolder);
		$content .= " ";
		$content .= "";
		$content .= $this->folderListRowAction($subFolder, $skipcont, $extracontent);
		$content .= " ";
		if(!$skipcont)
			$content .= $this->folderListRowEnd($subFolder);
		return $content;
	} /* }}} */
	function show(){ /* {{{ */
		parent::show();
	} /* }}} */
	function error(){ /* {{{ */
		parent::error();
		$dms = $this->params['dms'];
		$user = $this->params['user'];
		$pagetitle = $this->params['pagetitle'];
		$errormsg = $this->params['errormsg'];
		$plain = $this->params['plain'];
		$noexit = $this->params['noexit'];
		if(!$plain) {	
			$this->htmlStartPage($pagetitle);
			$this->globalNavigation();
			$this->contentStart();
		}
		$html = '';
		$html .= "".getMLText('error')."! ";
		$html .= htmlspecialchars($errormsg);
		$this->errorMsg($html);
		print "".getMLText('back')." 
";
		
		$this->contentEnd();
		$this->htmlEndPage();
		
		add_log_line(" UI::exitError error=".$errormsg." pagetitle=".$pagetitle, PEAR_LOG_ERR);
		if($noexit)
			return;
		exit;	
	} /* }}} */
	/**
	 * Return HTML Template for jumploader
	 *
	 * @param string $uploadurl URL where post data is send
	 * @param integer $folderid id of folder where document is saved
	 * @param integer $maxfiles maximum number of files allowed to upload
	 * @param array $fields list of post fields
	 */
	function getFineUploaderTemplate() { /* {{{ */
		return '
';
	} /* }}} */
	/**
	 * Output HTML Code for Fine Uploader
	 *
	 * @param string $uploadurl URL where post data is send
	 * @param integer $folderid id of folder where document is saved
	 * @param integer $maxfiles maximum number of files allowed to upload
	 * @param array $fields list of post fields
	 */
	function printFineUploaderHtml($prefix='userfile') { /* {{{ */
		echo self::getFineUploaderHtml($prefix);
	} /* }}} */
	/**
	 * Get HTML Code for Fine Uploader
	 *
	 * @param string $uploadurl URL where post data is send
	 * @param integer $folderid id of folder where document is saved
	 * @param integer $maxfiles maximum number of files allowed to upload
	 * @param array $fields list of post fields
	 */
	function getFineUploaderHtml($prefix='userfile') { /* {{{ */
		$html = '
		
			,  ";
			echo "";
			switch ($rec["type"]) {
				case 0: // individual.
					$required = $dms->getUser($rec["required"]);
					if (!is_object($required)) {
						$reqName = getMLText("unknown_user")." '".$rec["required"]."'";
					} else {
						$reqName = htmlspecialchars($required->getFullName()." (".$required->getLogin().")");
					}
					break;
				case 1: // Approver is a group.
					$required = $dms->getGroup($rec["required"]);
					if (!is_object($required)) {
						$reqName = getMLText("unknown_group")." '".$rec["required"]."'";
					}
					else {
						$reqName = "".htmlspecialchars($required->getName())." ";
					}
					break;
			}
			echo $reqName;
			echo " ";
			echo "";
			echo "".getLongReadableDate($rec['date'])." - ";
			$updateuser = $dms->getUser($rec["userID"]);
			if(!is_object($updateuser))
				echo getMLText("unknown_user");
			else
				echo htmlspecialchars($updateuser->getFullName()." (".$updateuser->getLogin().")");
			echo " ";
			if($rec['comment'])
				echo "params['settings']->_httpRoot."op/op.Download.php?documentid=".$document->getID()."&reviewlogid=".$rec['reviewLogID']."\" class=\"btn btn-mini\"> ";
					}
				break;
			case "approval":
				if($accessop->check_controller_access('Download', array('action'=>'approval')))
					if($rec['file']) {
						echo "params['settings']->_httpRoot."op/op.Download.php?documentid=".$document->getID()."&approvelogid=".$rec['approveLogID']."\" class=\"btn btn-mini\"> ";
					}
				break;
			}
			echo " ";
			echo "";
			switch($type) {
			case "review":
				echo getReviewStatusText($rec["status"]);
				break;
			case "approval":
				echo getApprovalStatusText($rec["status"]);
				break;
			case "revision":
				echo getRevisionStatusText($rec["status"]);
				break;
			case "receipt":
				echo getReceiptStatusText($rec["status"]);
				break;
			default:
			}
			echo " ";
			echo " ";
		}
?>
				
 $value) {
			$used = (int) ($value/$max*100.0+0.5);
			$free = 100-$used;
		} else {
			$free = 0;
			$used = 100;
		}
		$html = '
		';
		return $html;
	} /* }}} */
	/**
	 * Output a timeline for a document
	 *
	 * @param object $document document
	 */
	protected function printTimelineJs($timelineurl, $height=300, $start='', $end='', $skip=array(), $onselect="") { /* {{{ */
		if(!$timelineurl)
			return;
?>
		var timeline;
		var data;
		// specify options
		var options = {
			'width':  '100%',
			'height': '100%',
			'editable': false,
			'selectable': true,
			'style': 'box',
			'locale': 'params['session']->getLanguage() ?>'
		};
		$(document).ready(function () {
		// Instantiate our timeline object.
		timeline = new links.Timeline(document.getElementById('timeline'), options);
		links.events.addListener(timeline, 'select', = $onselect ?>);
		$.getJSON(
			'', 
			function(data) {
				$.each( data, function( key, val ) {
					val.start = new Date(val.start);
				});
				timeline.draw(data);
			}
		);
		});
	
\n";
		$this->printTimelineJs($timelineurl, $height, $start, $end, $skip);
		echo "";
		$this->printTimelineHtml($height);
	} /* }}} */
	public function printPopupBox($title, $content, $ret=false) { /* {{{ */
		$id = md5(uniqid());
		/*
		$this->addFooterJS('
$("body").on("click", "span.openpopupbox", function(e) {
	$(""+$(e.target).data("href")).toggle();
//	$("div.popupbox").toggle();
});
');
		 */
		$html = '
		
		';
		if($ret)
			return $html;
		else
			echo $html;
	} /* }}} */
	public function printAccordion($title, $content, $open=false) { /* {{{ */
		$id = substr(md5(uniqid()), 0, 4);
?>