Bläddra i källkod

updated Menu and Bookmarks

Piotr Labudda 8 år sedan
förälder
incheckning
101b2cf565

+ 251 - 300
SE/se-lib/ProcesMenu.php

@@ -309,45 +309,45 @@ jQuery(document).ready(function() {
 		$MojeTestyTitle = "Ilość Procesów: {$proces_cnt}, Aktualnych testów: {$testy_ok},  Teoretycznych: {$testy_teoretyczne}, Praktycznych: {$testy_praktyczne}";
 
 		$userAcl = User::getAcl();
-		$tbls = $userAcl->getTablesAcl();
-		$urls = $userAcl->getUrls();
-
+		$tbls = [];
+		$urls = [];
+		$outUrls = array();
 		$outMenus = array();// typeName => label (order by label)
-		$rawOutMenus = array();
-		$labelsOutMenus = array();
-		$outBtnsMenus = array();
 		$typeNameToIdZasob = array();// $typeName => $idZasob
-		if (!empty($tbls)) {
-			foreach ($tbls as $kZasobID => $vTblAcl) {
-				$tblName = $vTblAcl->getName();
-				$typeName = "p5_default_db:{$tblName}";
-				$labelsOutMenus[$typeName] = $vTblAcl->getLongLabel();
-				$rawOutMenus[$typeName] = strtolower($vTblAcl->getLongRawLabel());
-				$typeNameToIdZasob[$typeName] = $kZasobID;
-				if ($userAcl->getPermsFiltrProcesId()) {
-					$outBtnsMenus[$typeName] = $vTblAcl->getRawLabel();
-				}
-			}
-		}
-		asort($rawOutMenus);
-		foreach ($rawOutMenus as $typeName => $rawLongLabel) $outMenus[$typeName] = $labelsOutMenus[$typeName];
 		if ($userAcl->getPermsFiltrProcesId()) {
-			asort($outBtnsMenus);
-		}
+			$tbls = $userAcl->getTablesAcl();
+			$urls = $userAcl->getUrls();
 
-		$outUrls = array();
-		if (!empty($urls)) {
-			/**
-			 * 	[147] => Array(
-       *       [TYPE] => URL
-       *       [DESC] => ?MENU_INIT=DODAJ_REKORDY_MIESZKAN_FUNC
-       *       [OPIS] => Narzedzie do wprowadzania zasobow mieszkan indywidualnych
-			 */
-			foreach ($urls as $kZasobID => $vTitle) {
-				$outUrls[$kZasobID] = $vTitle;
+			if (!empty($urls)) {
+				foreach ($urls as $kZasobID => $vTitle) {
+					$outUrls[$kZasobID] = $vTitle;
+				}
+			}
+			asort($outUrls);
+
+			$rawOutMenus = array();
+			$labelsOutMenus = array();
+			$outBtnsMenus = array();
+			// DBG::nicePrint($tbls, '$tbls');
+			if (!empty($tbls)) {
+				foreach ($tbls as $kZasobID => $vTblAcl) {
+					if (null == $vTblAcl) continue;
+					$tblName = $vTblAcl->getName();
+					$typeName = "p5_default_db:{$tblName}";
+					$labelsOutMenus[$typeName] = $vTblAcl->getLongLabel();
+					$rawOutMenus[$typeName] = strtolower($vTblAcl->getLongRawLabel());
+					$typeNameToIdZasob[$typeName] = $kZasobID;
+					// if ($userAcl->getPermsFiltrProcesId()) {
+						$outBtnsMenus[$typeName] = $vTblAcl->getRawLabel();
+					// }
+				}
+			}
+			asort($rawOutMenus);
+			foreach ($rawOutMenus as $typeName => $rawLongLabel) $outMenus[$typeName] = $labelsOutMenus[$typeName]; // TODO: mv to localStorage
+			if ($userAcl->getPermsFiltrProcesId()) {
+				asort($outBtnsMenus);
 			}
 		}
-		asort($outUrls);
 
 		$active = '';
 		$script_name = V::get('SCRIPT_NAME', '', $_SERVER);
@@ -393,19 +393,6 @@ jQuery(document).ready(function() {
 /*
  * $_SESSION['USER_PROFILE'][section][key] = val;
  */
-		$userBookmarks = UserBookmarks::getInstance();
-
-		$bookmarksJson = array();
-		$bookmarks = $userBookmarks->getBookmarks();
-		foreach ($bookmarks as $kZasobID => $vClass) {
-			if (array_key_exists($kZasobID, $tbls)) {
-				$bookmarksJson[] = (object)array('id'=>$kZasobID, 'name'=>$tbls[$kZasobID]->getName(), 'label'=>$tbls[$kZasobID]->getRawLabel(), 'opis'=>$tbls[$kZasobID]->getOpis(), 'type'=>'menu', 'class'=>$vClass);
-			}
-			else if (array_key_exists($kZasobID, $urls)) {
-				$bookmarksJson[] = (object)array('id'=>$kZasobID, 'name'=>$urls[$kZasobID], 'type'=>'url', 'class'=>$vClass);
-			}
-		}
-
 		$userGroupIdsCSV = User::getGroupsIds();
 		$userGroupIdsCSV = implode(',', $userGroupIdsCSV);
 
@@ -437,35 +424,67 @@ jQuery(document).ready(function() {
 	<div class="container-fluid">
 		<div class="collapse navbar-collapse">
 			<ul class="nav navbar-nav">
-				<li class="dropdown<?php if ($active == 'menu') echo ' active'; ?>">
-					<a href="#" class="dropdown-toggle" data-toggle="dropdown">Menu <b class="caret"></b></a>
-					<ul class="dropdown-menu" id="SE-menu-tables">
-						<?php foreach ($outMenus as $typeName => $vName) : $kZasobID = $typeNameToIdZasob[$typeName]; ?>
-							<li>
-								<a href="index.php?_route=ViewTableAjax&typeName=<?= $typeName; ?>">
-									<i class="bookmark-item-add-<?= $kZasobID; ?> bookmark-item-add glyphicon glyphicon-star-empty" title="Add to favorites" data-zasobid="<?= $kZasobID; ?>"></i>
-									<i class="bookmark-item-rem-<?= $kZasobID; ?> bookmark-item-rem glyphicon glyphicon-star" style="display:none" title="Remove from favorites" data-zasobid="<?= $kZasobID; ?>"></i>
-									<?php echo $vName; ?>
-								</a>
-							</li>
-						<?php endforeach; ?>
-					</ul>
-				</li>
-				<li class="dropdown">
-					<a href="#" class="dropdown-toggle" data-toggle="dropdown">Narzędzia <b class="caret"></b></a>
-					<ul class="dropdown-menu">
-						<?php foreach ($outUrls as $kZasobID => $vTitle) : ?>
-							<li>
-								<a href="index.php?FUNCTION_INIT=URL_INIT&ZASOB_ID=<?php echo $kZasobID; ?>" target="_blank" title="<?php echo $vTitle; ?>">
-									<i class="bookmark-item-add-<?php echo $kZasobID; ?> bookmark-item-add glyphicon glyphicon-star-empty" title="Add to favorites" data-zasobid="<?php echo $kZasobID; ?>"></i>
-									<i class="bookmark-item-rem-<?php echo $kZasobID; ?> bookmark-item-rem glyphicon glyphicon-star" style="display:none" title="Remove from favorites" data-zasobid="<?php echo $kZasobID; ?>"></i>
-									<code><?php echo $kZasobID; ?></code>
-									<?php echo (mb_strlen($vTitle, 'utf-8') > 100)? mb_substr($vTitle, 0, 100, 'utf-8') . '...' : $vTitle; ?>
-								</a>
-							</li>
-						<?php endforeach; ?>
-					</ul>
-				</li>
+				<?php
+					echo UI::h('li', [ 'class' => "dropdown" . ($active == 'menu' ? ' active' : '') ], [
+						UI::h('a',
+							($userAcl->getPermsFiltrProcesId())
+								? [
+										'href' => "#",
+										'class' => "dropdown-toggle",
+										'data-toggle' => "dropdown",
+									]
+								: [
+									'href' => "#",
+									'class' => "dropdown-toggle",
+									'onClick' => "return initP5MainMenuDropdown(this, 'SE-menu-tables');",
+								],
+							"Menu ". '<b class="caret"></b>'
+						),
+						UI::h('ul', [
+							'class' => "dropdown-menu",
+							'id' => "SE-menu-tables",
+						], array_map(function ($vName, $typeName) use ($typeNameToIdZasob) {
+							$kZasobID = $typeNameToIdZasob[$typeName];
+							return UI::h('li', [], [
+								UI::h('a', ['href'=>"index.php?_route=ViewTableAjax&typeName={$typeName}"], [
+									UI::h('i', ['class'=>"bookmark-item-add glyphicon glyphicon-star-empty", 'title'=>"Add to favorites", 'data-zasobid'=>$kZasobID]),
+									UI::h('i', ['class'=>"bookmark-item-rem glyphicon glyphicon-star", 'style'=>"display:none", 'title'=>"Remove from favorites", 'data-zasobid'=>$kZasobID]),
+									" " . $vName
+								])
+							]);
+						}, array_values($outMenus), array_keys($outMenus))),
+					]);
+
+					echo UI::h('li', [ 'class' => "dropdown"], [
+						UI::h('a',
+							($userAcl->getPermsFiltrProcesId())
+							? [
+									'href' => "#",
+									'class' => "dropdown-toggle",
+									'data-toggle' => "dropdown",
+								]
+							: [
+								'href' => "#",
+								'class' => "dropdown-toggle",
+								'onClick' => "return initP5UrlsMenuDropdown(this, 'SE-menu-urls');",
+							],
+							"Narzędzia " . '<b class="caret"></b>'
+						),
+						UI::h('ul', [
+							'class' => "dropdown-menu",
+							'id' => "SE-menu-urls"
+						], array_map(function ($label, $idZasob) {
+							return UI::h('li', [], [
+								UI::h('a', [ 'href' => "index.php?FUNCTION_INIT=URL_INIT&ZASOB_ID={$idZasob}", 'target' => "_blank", 'title' => $label ], [
+									UI::h('i', [ 'class' => "bookmark-item-add glyphicon glyphicon-star-empty", 'title' => "Add to favorites", 'data-zasobid' => $idZasob ]),
+									UI::h('i', [ 'class' => "bookmark-item-rem glyphicon glyphicon-star", 'style' => "display:none", 'title' => "Remove from favorites", 'data-zasobid' => $idZasob ]),
+									UI::h('code', [ 'style' => "margin-left:6px; margin-right:6px" ], (string)$idZasob),
+									(mb_strlen($label, 'utf-8') > 100)? mb_substr($label, 0, 100, 'utf-8') . '...' : $label,
+								]),
+							]);
+						}, array_values($outUrls), array_keys($outUrls))),
+					]);
+				?>
 				<li class="dropdown <?php if ($active == 'procesy') echo "active"; ?>">
 					<a id="ProcesMenuProcesDropdownLink" href="#" class="dropdown-toggle" data-toggle="dropdown">Procesy <b class="caret"></b></a>
 					<ul class="dropdown-menu">
@@ -872,242 +891,174 @@ jQuery(document).ready(function() {
 -->
 		</div><!-- /.navbar-collapse -->
 	</nav>
-	<?php if ($userAcl->getPermsFiltrProcesId()) : ?>
-		<div id="SE-menu-sub" style="clear:both;">
-			<a class="btn btn-xs btn-danger" href="index.php?FUNCTION_INIT=MENU_SELECT_PROCES&_action=setPermsAll" title="Wyłącz filtr uprawnień dla procesu <?php echo $userAcl->getPermsFiltrProcesId(); ?>">Wyłącz filtr uprawnień: <?php echo $userAcl->getPermsFiltrProcesId(); ?></a>
-			<?php foreach ($outBtnsMenus as $typeName => $label) : ?>
-				<a class="btn btn-xs btn-default" href="index.php?_route=ViewTableAjax&typeName=<?php echo $typeName; ?>" title="<?php echo $label; ?>"><?php echo V::strShortUtf8($label, 20); ?></a>
-			<?php endforeach; ?>
-			<?php foreach ($outUrls as $kZasobID => $vTitle) : ?>
-				<a class="btn btn-xs btn-default" href="index.php?FUNCTION_INIT=URL_INIT&ZASOB_ID=<?php echo $kZasobID; ?>" target="_blank" title="<?php echo $vTitle; ?>"><?php echo V::strShortUtf8($vTitle, 20); ?></a>
-			<?php endforeach; ?>
-		</div>
-	<?php else : ?>
-		<div id="SE-menu-sub" style="clear:both;"></div>
-<script>
-(function ($, undefined) {
-	var UserBookmarks = function() {
-		var priv = {}; //private api
-		var publ = {}; //public api
-
-		priv.options = {};
-		var defaults = {
-			url: '',  //webservice url
-			urlInit: true, // try to load services on init
-			preloadData: null,
-			menuid: '',
-			debug: false
-		};
-
-		var _cont; // container holding table
-		var _menu; // container holding stars
-		var _stateEdit = false;
-
-		/*
-		 initialize the plugin.
-		 */
-		priv.init = function() {
-			_cont = $(priv.options.id);
-			_menu = $(priv.options.menuid);
-
-			_menu.find('.bookmark-item-add').click(function(e){
-				e.preventDefault();
-				e.stopPropagation();
-				var zasobid = jQuery(this).data('zasobid');
-				priv.update('add_bookmark', zasobid);
-
-				var item = $('#bookmark-item-' + zasobid);
-				if (item) {
-					item.addClass('has_bookmark');
+	<?php
+
+		UI::inlineJS(APP_PATH_WWW . '/static/p5UI/menuStore.js');
+
+		$idFiltrProcesID = $userAcl->getPermsFiltrProcesId();
+		if ($idFiltrProcesID > 0) {
+			echo UI::h('div', [ 'id' => "SE-menu-sub", 'style' => "clear:both;" ], array_merge(
+				[
+					UI::h('a', [ 'class' => "btn btn-xs btn-danger", 'href' => "index.php?FUNCTION_INIT=MENU_SELECT_PROCES&_action=setPermsAll", 'title' => "Wyłącz filtr uprawnień dla procesu {$idFiltrProcesID}" ], "Wyłącz filtr uprawnień: {$idFiltrProcesID}"),
+				],
+				array_map(
+					function ($label, $typeName) {
+						return UI::h('a', [ 'class' => "btn btn-xs btn-default", 'href' => "index.php?_route=ViewTableAjax&typeName={$typeName}", 'title' => $label ], V::strShortUtf8($label, 20));
+					}, array_values($outBtnsMenus), array_keys($outBtnsMenus)
+				),
+				array_map(
+					function ($label, $idZasob) {
+						return UI::h('a', [ 'class' => "btn btn-xs btn-default", 'href' => "index.php?FUNCTION_INIT=URL_INIT&ZASOB_ID={$idZasob}", 'target' => "_blank", 'title' => $label ], V::strShortUtf8($label, 20));
+					}, array_values($outUrls), array_keys($outUrls)
+				)
+			));
+		} else {
+			echo UI::h('div', [ 'id' => "SE-menu-sub", 'style' => "clear:both" ]);
+			UI::inlineJS(APP_PATH_WWW . '/static/p5UI/userBookmarks.js');
+			// url: 'index-ajax.php?_cls=UserBookmarks',
+			echo UI::h('script', [], "
+				(function (global) {
+					jQuery('#SE-menu-sub').UserBookmarks({
+						url: 'index.php?_route=P5Menu',
+						store: global.p5UI__MenuStore,
+						debug: false
+					});
+				})(window);
+			");
+		}
+		echo '</div>';
+
+		echo UI::h('script', [], "
+			(function (global) {
+				if (!global.p5UI__MenuStore) throw 'Missing global.p5UI__MenuStore'
+
+				function p5BookmarksAdd(e, id) {
+					e.preventDefault()
+					e.stopPropagation()
+					global.p5UI__MenuStore.remoteUpdate({
+						'_postTask': 'addBookmark',
+						'_zasobID': id,
+					})
 				}
-			});
-			_menu.find('.bookmark-item-rem').click(function(e){
-				e.preventDefault();
-				e.stopPropagation();
-				var zasobid = jQuery(this).data('zasobid');
-				priv.update('remove_bookmark', zasobid);
-			});
-
-			if (priv.options.urlInit) priv.update();
-			if (priv.options.preloadData) priv.setData(priv.options.preloadData);
-
-			_cont.sortable();
-			_cont.on('sortupdate', priv.sort);
-
-		};
-
-		priv.setData = function(data) {
-			_cont.empty();
-			$.each(data, function(ind, item){
-				if ('type' in item) {
-					var l = $('<a></a>');
-					l.data('id', item.id);
-					l.addClass('btn');
-					l.addClass('btn-xs');
-					var label = item.name, title = '';
-					if (item.hasOwnProperty('class') && item['class'] != '') {
-						l.addClass(item.class);
-					} else {
-						l.addClass('btn-default');
-					}
-					if (item.type == 'menu') {
-						l.attr('href', 'index.php?_route=ViewTableAjax&typeName=' + 'p5_default_db:' + item.name);
-						if ('label' in item && item.label.length > 0) {
-							label = item.label;
-							title = item.label + ' (' + item.name + ')';
-						}
-						else if ('opis' in item && item.opis.length > 0) {
-							label = item.opis;
-							title = item.opis + ' (' + item.name + ')';
-						}
-					} else if (item.type == 'url') {
-						l.attr('href', 'index.php?FUNCTION_INIT=URL_INIT&ZASOB_ID=' + item.id);
-						l.attr('target', '_blank');
-					}
-					if (label.length > 20) {
-						var pos = label.indexOf(' - ');
-						if (pos > 20 || pos < 5) {
-							pos = 20;
-							l.text(label.substring(0, pos) + ' ...');
-						} else {
-							l.text(label.substring(0, pos));
-						}
-					} else {
-						l.text(label);
+				function p5BookmarksRemove(e, id) {
+					e.preventDefault()
+					e.stopPropagation()
+					global.p5UI__MenuStore.remoteUpdate({
+						'_postTask': 'removeBookmark',
+						'_zasobID': id,
+					})
+				}
+
+				global.p5BookmarksAdd = p5BookmarksAdd
+				global.p5BookmarksRemove = p5BookmarksRemove
+			})(window)
+		");
+
+		echo UI::h('script', [], "
+			(function (global) {
+				if (!global.p5UI__MenuStore) throw 'Missing global.p5UI__MenuStore'
+
+				function initP5MainMenuDropdown( btnNode, idSubMenu ) {
+					if (!btnNode._initialized) {
+						var jqDropdownTrigger = jQuery(btnNode)
+						var jqDropdownMenu = jQuery('#' + idSubMenu)
+						var jqDropdownParent = jqDropdownMenu.parent()
+
+						global.p5UI__MenuStore.subscribe(
+							(function (global, idSubMenu) {
+								return function renderP5MainMenuDropdown(data) {
+									var jqDropdownMenu = jQuery('#' + idSubMenu)
+									jqDropdownMenu.empty()
+									jqDropdownMenu.append(data.objects.map(function (item) {
+										var star = (-1 !== data.idsBookmarks.indexOf(item.id))
+											? '<i class=\"bookmark-item-rem glyphicon glyphicon-star\" title=\"Usuń z ulubionych\" onClick=\"return p5BookmarksRemove(event, ' + item.id + ')\"></i>'
+											: '<i class=\"bookmark-item-add glyphicon glyphicon-star-empty\" title=\"Dodaj do ulubionych\" onClick=\"return p5BookmarksAdd(event, ' + item.id + ')\"></i>'
+										return jQuery('<li>' +
+											'<a href=\"index.php?_route=ViewTableAjax&namespace=' + item.namespace + '\">' +
+												star +
+												' ' + item.label +
+											'</a>' +
+										'</li>');
+									}))
+								}
+							})(global, idSubMenu)
+						)
+
+						jqDropdownTrigger.attr('data-toggle', 'dropdown') // is required by bootstrap dorpdown.js evenf if is called via js
+
+						global.p5UI__MenuStore.forceUpdate()
+
+						jQuery(btnNode).dropdown()
 					}
-					if (title == '') title = label;
-					l.attr('title', title);
-					l.appendTo(_cont);
+					btnNode._initialized = true
+					return true;
+				}
 
-					if (_stateEdit) {
-						priv.addEditBtns(l);
+				global.initP5MainMenuDropdown = initP5MainMenuDropdown
+			})(window)
+		");
+
+		echo UI::h('script', [], "
+			(function (global) {
+				if (!global.p5UI__MenuStore) throw 'Missing global.p5UI__MenuStore'
+
+				function initP5UrlsMenuDropdown( btnNode, idSubMenu ) {
+					if (!btnNode._initialized) {
+						var jqDropdownTrigger = jQuery(btnNode)
+						var jqDropdownMenu = jQuery('#' + idSubMenu)
+						var jqDropdownParent = jqDropdownMenu.parent()
+						global.p5UI__MenuStore.subscribe(
+							(function (global, idSubMenu) {
+								return function renderP5MainMenuDropdown(data) {
+									var jqDropdownMenu = jQuery('#' + idSubMenu)
+									jqDropdownMenu.empty()
+									jqDropdownMenu.append(data.urls.map(function (item) {
+										var star = (-1 !== data.idsBookmarks.indexOf(item.id))
+											? '<i class=\"bookmark-item-rem glyphicon glyphicon-star\" title=\"Usuń z ulubionych\" onClick=\"return p5BookmarksRemove(event, ' + item.id + ')\"></i>'
+											: '<i class=\"bookmark-item-add glyphicon glyphicon-star-empty\" title=\"Dodaj do ulubionych\" onClick=\"return p5BookmarksAdd(event, ' + item.id + ')\"></i>'
+										return jQuery('<li>' +
+											'<a href=\"index.php?FUNCTION_INIT=URL_INIT&ZASOB_ID=' + item.id + '\" target=\"_blank\" title=\"' + item.raw_label + '\">' +
+												star +
+												' ' + '<code>' + item.id + '</code>' +
+												' ' + item.label +
+											'</a>' +
+										'</li>');
+									}))
+								}
+							})(global, idSubMenu)
+						)
+
+						jqDropdownTrigger.attr('data-toggle', 'dropdown') // is required by bootstrap dorpdown.js evenf if is called via js
+
+						global.p5UI__MenuStore.forceUpdate()
+
+						jQuery(btnNode).dropdown()
 					}
+					btnNode._initialized = true
+					return true;
 				}
 
-				// stars visibility
-				$('.bookmark-item-rem-' + item.id).show();
-				$('.bookmark-item-add-' + item.id).hide();
-			});
+				global.initP5UrlsMenuDropdown = initP5UrlsMenuDropdown
+			})(window)
+		");
 
-			if (data.length > 0) {
-				var editBtn = $('<button class="btn btn-xs" style="float:right" title="Edit Bookmarks"><i class="glyphicon glyphicon-cog"></i></button>')
-				editBtn.on('click', priv.edit);
-				editBtn.prependTo(_cont);
-			}
-		};
-
-		priv.update = function(task, zasobID, argsAdd) {
-			task = task || '';
-			zasobID = zasobID || '';
-			argsAdd = argsAdd || '';
-			$.ajax({
-				url: priv.options.url + '&_task=' + task + '&_zasobID=' + zasobID + argsAdd,
-				type: 'GET',
-				dataType: 'json',
-				contentType: "application/json; charset=utf-8",
-				data: null,
-				async: true,
-				success: function (data) {
-					$('.bookmark-item-rem').hide();
-					$('.bookmark-item-add').show();
-
-					priv.setData(data);
-				},
-				error: function (err) {
-					console.log('Error');
-					console.log(err);
-				}
-			});
-		};
+		$args = [
+			'P5MENU_URL' => Router::getRoute('P5Menu')->getLink(),
+		];
+		echo UI::h('script', [], "
+			(function (global) {
+				if (!global.p5UI__MenuStore) throw 'Missing global.p5UI__MenuStore'
+				var P5MENU_URL = '{$args['P5MENU_URL']}'
 
-		priv.changed = function(e) {
-			if (priv.options.debug) console.log(e.data);
-			if (priv.options.debug) console.log('id(' + e.data.id + ') cls(' + e.data.cls + ')');
-			priv.update('change_bookmark', e.data.id, '&btnCls=' + e.data.cls);
-			return false;
-		};
+				global.p5UI__MenuStore.setRemoteUrl(P5MENU_URL)
 
-		priv.removed = function(e) {
-			if (priv.options.debug) console.log(e.data);
-			if (priv.options.debug) console.log('id(' + e.data.id + ')');
-			priv.update('remove_bookmark', e.data.id);
-			return false;
-		};
-
-		priv.sort = function(e, ui) {
-			var idsOrder = [];
-			_cont.find('a').each(function(ind, n){
-				idsOrder.push($(n).data('id'));
-			});
-			priv.update('sort_bookmarks', 0, '&ids[]=' + idsOrder.join('&ids[]='));
-			return true;
-		};
-
-		priv.addEditBtns = function(el) {
-			var next, btn;
-			el.wrap('<div></div>');
-			next = $('<span><em> Change color:</em> </span>');
-			$.each(['btn-default', 'btn-primary', 'btn-info', 'btn-success', 'btn-warning', 'btn-danger'], function(btnInd, btnClass){
-				btn = $('<button class="btn btn-xs ' + btnClass + '"> &nbsp; </button>');
-				btn.on('click', {id: el.data('id'), cls: btnClass}, priv.changed);
-				btn.appendTo(next);
-			});
-
-			btn = $('<button class="btn btn-xs"> remove </button>');
-			btn.on('click', {id: el.data('id')}, priv.removed);
-			btn.appendTo(next);
-
-			next.insertAfter(el);
-		};
-
-		priv.edit = function(e) {
-			_stateEdit = !_stateEdit;
-			var el;
-			_cont.find('a').each(function(ind, n){
-				if (priv.options.debug) console.log(n);
-				el = $(n);
-				if (_stateEdit) {
-					priv.addEditBtns(el);
+				if (global.p5UI__MenuStore.hasData()) {
+					global.p5UI__MenuStore.forceUpdate() // force update all subscribers
 				} else {
-					el.next().remove();
-					el.unwrap();
+					global.p5UI__MenuStore.remoteUpdate() // update from remote url @see setRemoteUrl
 				}
-			});
-		}
-
-		publ.init = function(options) {
-			if (priv.options.debug) console.log('UserBookmarks initialization...');
-			//merge supplied options with defaults
-			$.extend(priv.options, defaults, options);
-			priv.init();
-			return publ;
-		};
-		return publ;
-	};
-
-	$.fn.UserBookmarks = function(options) {
-		options = options || {};
-		return this.each(function() {
-			options.id = this;
-			$(this).data('UserBookmarks', new UserBookmarks().init(options));
-		});
-	};
-})(jQuery);
-</script>
-		<script>
-		jQuery(document).ready(function(){
-			jQuery('#SE-menu-sub').UserBookmarks({
-				url: 'index-ajax.php?_cls=UserBookmarks',
-				urlInit: false,
-				preloadData: <?php echo json_encode($bookmarksJson); ?>,
-				menuid: '#SE-menu',
-				debug: false
-			});
-		});
-		</script>
-	<?php endif; ?>
-</div>
-		<?php
+			})(window)
+		");
 	}
 
 	/**

+ 117 - 0
SE/se-lib/Route/P5Menu.php

@@ -0,0 +1,117 @@
+<?php
+
+Lib::loadClass('RouteBase');
+Lib::loadClass('UserBookmarks');
+Lib::loadClass('Response');
+
+class Route_P5Menu extends RouteBase {
+
+  public function defaultAction() {
+		Response::sendTryCatchJson(array($this, 'getMenuData'), $args = 'JSON_FROM_REQUEST_BODY');
+	}
+	public function getMenuData($args = []) {
+    if ($postTask = V::get('_postTask', '', $args)) {
+      DBG::log($args, 'array', "exec '{$postTask}'");
+      if (!method_exists($this, "{$postTask}PostTask")) throw new Exception("Post Task not exists!");
+      $this->{"{$postTask}PostTask"}($args);
+    }
+
+		$userAcl = User::getAcl();
+		$listObjects = $userAcl->getTablesAcl();
+		$listUrls = $userAcl->getUrls();
+		// DBG::log($listUrls, 'array', "\$listUrls");
+
+		$bookmarks = UserBookmarks::getInstance()->getBookmarks();
+
+		return [
+			'type' => 'success',
+			'msg' => "OK",
+			'body' => [
+
+				'objects' => array_map(function ($acl, $idZasob) {
+					if (!$acl) return [
+						'id' => $idZasob,
+						'TODO' => 'TODO'
+					];
+
+					return [
+						'id' => $acl->getID(),
+						'namespace' => $acl->getNamespace(),
+						'name' => $acl->getName(),
+						'label' => $acl->getLongLabel(),
+						'raw_label' => strtolower($acl->getLongRawLabel()),
+					];
+				}, array_values($listObjects), array_keys($listObjects)),
+
+				'urls' => array_map(function ($label, $idZasob) {
+					return [
+						'id' => $idZasob,
+						'label' => (mb_strlen($label, 'utf-8') > 100) ? mb_substr($label, 0, 100, 'utf-8') . '...' : $label,
+						'raw_label' => $label,
+					];
+				}, array_values($listUrls), array_keys($listUrls)),
+
+				'bookmarks' => array_map(function ($cls, $idZasob) use ($listObjects, $listUrls) {
+					if (array_key_exists($idZasob, $listObjects)) {
+            $acl = $listObjects[$idZasob];
+            return [
+              'id' => $idZasob,
+              'namespace' => $acl->getNamespace(),
+              'name' => $acl->getName(),
+              'label' => $acl->getRawLabel(),
+              'opis' => $acl->getOpis(),
+              'type' => 'menu',
+              'class' => $cls
+            ];
+					}
+					else if (array_key_exists($idZasob, $listUrls)) {
+            return [
+              'id' => $idZasob,
+              'name' => $listUrls[$idZasob],
+              'type' => 'url',
+              'class' => $cls
+            ];
+					}
+          return null;
+				}, array_values($bookmarks), array_keys($bookmarks)),
+
+				'idsBookmarks' => array_keys($bookmarks),
+
+			]
+		];
+	}
+
+  public function addBookmarkPostTask($args) {
+    $zasobID = V::get('_zasobID', 0, $args, 'int');
+    if ($zasobID <= 0) throw new Exception('Missing _zasobID');
+    UserBookmarks::getInstance()->addBookmark($zasobID);
+    User::saveProfile();
+  }
+
+  public function removeBookmarkPostTask($args) {
+    $zasobID = V::get('_zasobID', 0, $args, 'int');
+    if ($zasobID <= 0) throw new Exception('Missing _zasobID');
+    UserBookmarks::getInstance()->removeBookmark($zasobID);
+    User::saveProfile();
+    $this->getMenuData();
+  }
+
+  public function changeBookmarkPostTask($args) {
+    $zasobID = V::get('_zasobID', 0, $args, 'int');
+    if ($zasobID <= 0) throw new Exception('Missing _zasobID');
+    $btnCls = V::get('btnCls', '', $args);
+    if (empty($btnCls)) {
+      die('Error: no button class');
+    }
+    UserBookmarks::getInstance()->changeBookmark($zasobID, $btnCls);
+    User::saveProfile();
+  }
+
+  public function sortBookmarksPostTask($args) {
+    $idsOrdered = V::get('ids', array(), $args, 'array', array('V', 'filterPositiveInteger'));
+    if (empty($idsOrdered)) throw new Exception('Missing ids');
+    UserBookmarks::getInstance()->sortBookmarks($idsOrdered);
+    User::saveProfile();
+  }
+
+}

+ 12 - 7
SE/se-lib/Route/Users.php

@@ -10,6 +10,7 @@ Lib::loadClass('TypespecialVariable');
 Lib::loadClass('TableAjax');
 Lib::loadClass('UserActivity');
 Lib::loadClass('UI');
+Lib::loadClass('Response');
 
 class Route_Users extends RouteBase {
 
@@ -89,14 +90,18 @@ class Route_Users extends RouteBase {
 
 		UI::gora();
 		UI::menu();
-		?>
-		<div class="container">
-			<div class="alert alert-success" title="<?php echo number_format($fixAllPermsExecTime, 4); ?> s / <?php echo number_format($fixUserPermsExecTime, 4); ?> s">
-				Zaktualizowano uprawnienia
-			</div>
-		</div>
-		<?php
+		echo UI::h('div', [ 'class' => "container"], [
+			UI::h('div', [
+				'class' => "alert alert-success",
+				'title' => number_format($fixAllPermsExecTime, 4) . " s / " . number_format($fixUserPermsExecTime, 4) . " s"
+			], "Zaktualizowano uprawnienia"),
+		]);
 		UI::loadTemplate('defaultPage', $data);
+		echo UI::h('script', [], "
+			(function (global) {
+				if (global.p5UI__MenuStore) global.p5UI__MenuStore.remoteUpdate()
+			})(window)
+		");
 		UI::dol();
 	}
 

+ 30 - 7
SE/se-lib/User.php

@@ -245,6 +245,12 @@ class User {
 							Lib::loadClass('UI');
 							UI::gora();
 							UI::loadTemplate('logout', $data);
+							UI::inlineJS(APP_PATH_WWW . '/static/p5UI/menuStore.js');
+							echo UI::h('script', [], "
+								(function (global) {
+									if (global.p5UI__MenuStore) global.p5UI__MenuStore.clearCache()
+								})(window)
+							");
 							UI::dol();
 							exit;
 						}
@@ -264,6 +270,12 @@ class User {
 					Lib::loadClass('UI');
 					UI::gora();
 					UI::loadTemplate('logout', $data);
+					UI::inlineJS(APP_PATH_WWW . '/static/p5UI/menuStore.js');
+					echo UI::h('script', [], "
+						(function (global) {
+							if (global.p5UI__MenuStore) global.p5UI__MenuStore.clearCache()
+						})(window)
+					");
 					UI::dol();
 					exit;
 				}
@@ -295,13 +307,18 @@ class User {
 
 					UI::gora();
 					UI::menu();
-					?>
-					<div class="container">
-						<div class="alert alert-success" title="<?php echo number_format($fixAllPermsExecTime, 4); ?> s / <?php echo number_format($fixUserPermsExecTime, 4); ?> s">
-							Zaktualizowano uprawnienia
-						</div>
-					</div>
-					<?php
+					echo UI::h('div', [ 'class' => "container"], [
+						UI::h('div', [
+							'class' => "alert alert-success",
+							'title' => number_format($fixAllPermsExecTime, 4) . " s / " . number_format($fixUserPermsExecTime, 4) . " s"
+						], "Zaktualizowano uprawnienia"),
+					]);
+					UI::loadTemplate('defaultPage', $data);
+					echo UI::h('script', [], "
+						(function (global) {
+							if (global.p5UI__MenuStore) global.p5UI__MenuStore.remoteUpdate()
+						})(window)
+					");
 					UI::loadTemplate('defaultPage', $data);
 					UI::dol();
 					exit;
@@ -361,6 +378,12 @@ class User {
 			Lib::loadClass('UI');
 			UI::gora();
 			UI::loadTemplate('login', $data);
+			UI::inlineJS(APP_PATH_WWW . '/static/p5UI/menuStore.js');
+			echo UI::h('script', [], "
+				(function (global) {
+					if (global.p5UI__MenuStore) global.p5UI__MenuStore.clearCache()
+				})(window)
+			");
 			UI::dol();
 			exit;
 		}

+ 2 - 2
SE/se-lib/UserAcl.php

@@ -152,7 +152,7 @@ class UserAcl {
 			$tblIds = array();
 			foreach ($value as $idTable => $tableConfig) {
 				$tblIds[] = $idTable;
-				$vTableAcl = TableAcl::buildInstance($idTable, $tableConfig);
+				TableAcl::buildInstance($idTable, $tableConfig);
 			}
 			$value = $tblIds;
 		}
@@ -207,7 +207,7 @@ class UserAcl {
 
 	public function getTableAcl($tableID) {
 		$tblAcl = TableAcl::getInstance($tableID);
-		if (!$tblAcl) throw new Exception("Brak tabeli nr [{$tableID}]!");
+		if (!$tblAcl) throw new Exception("Brak obiektu nr [{$tableID}]!");
 		$tblAcl->init();
 		return $tblAcl;
 	}

+ 115 - 0
SE/static/p5UI/menuStore.js

@@ -0,0 +1,115 @@
+function p5UI__MenuStore() {
+
+  var _url = ''
+  var _subscribers = []
+
+  function _subscribe( fn ) { // fn( { bookmarks, idsBookmarks, objects, urls } )
+    _subscribers.push( fn )
+  }
+
+  function _update( data ) { // data: { bookmarks, idsBookmarks, objects, urls }
+
+    data.objects.sort(function (a, b) {
+      if (a.raw_label > b.raw_label) return 1
+      if (a.raw_label < b.raw_label) return -1
+      return 0
+    })
+    data.urls.sort(function (a, b) {
+      if (a.raw_label > b.raw_label) return 1
+      if (a.raw_label < b.raw_label) return -1
+      return 0
+    })
+
+    _set('bookmarks', data.bookmarks)
+    _set('idsBookmarks', data.idsBookmarks)
+    _set('objects', data.objects)
+    _set('urls', data.urls)
+
+    var data = _getData()
+    _subscribers.forEach(function (fn) {
+      fn(data)
+    })
+  }
+
+  function _forceUpdate() {
+    var data = _getData()
+    _subscribers.forEach(function (fn) {
+      fn(data)
+    })
+  }
+
+  function _getData() {
+    return {
+      bookmarks: _get('bookmarks'),
+      idsBookmarks: _get('idsBookmarks'),
+      objects: _get('objects'),
+      urls: _get('urls')
+    }
+  }
+
+  function _hasData() {
+    var data = _getData()
+    return (
+      null !== data.bookmarks
+      || null !== data.idsBookmarks
+      || null !== data.objects
+      || null !== data.urls
+    )
+  }
+
+  function _set(type, value) {
+    global.localStorage.setItem('p5Menu.' + type, JSON.stringify(value))
+  }
+
+  function _get(type, value) {
+    var val = global.localStorage.getItem('p5Menu.' + type)
+    return val ? JSON.parse(val) : null
+  }
+
+  function _setRemoteUrl(url) {
+    _url = url
+  }
+
+  function _remoteUpdate(postData) {
+    var options =
+    window.fetch(_url
+      , (!postData)
+        ? { method: 'GET',
+            headers: { 'Content-Type': 'application/json' },
+            credentials: 'same-origin',
+          }
+        : { method: 'POST',
+            headers: { 'Content-Type': 'application/json' },
+            credentials: 'same-origin',
+            body: JSON.stringify(postData)
+          }
+    ).then(function (response) {
+      return response.json()
+    }).then(function (response) {
+      if ('success' === response.type) {
+        _update(response.body)
+      } else {
+        // err...
+      }
+    })
+  }
+
+  function _clearCache() {
+    global.localStorage.removeItem('p5Menu.' + 'urls')
+    global.localStorage.removeItem('p5Menu.' + 'objects')
+    global.localStorage.removeItem('p5Menu.' + 'bookmarks')
+    global.localStorage.removeItem('p5Menu.' + 'idsBookmarks')
+  }
+
+  return {
+    subscribe: _subscribe,
+    update: _update,
+    forceUpdate: _forceUpdate,
+    setRemoteUrl: _setRemoteUrl,
+    remoteUpdate: _remoteUpdate,
+    getData: _getData,
+    hasData: _hasData,
+    clearCache: _clearCache,
+  }
+}
+global.p5UI__MenuStore = p5UI__MenuStore()

+ 168 - 0
SE/static/p5UI/userBookmarks.js

@@ -0,0 +1,168 @@
+var $ = global.jQuery
+var UserBookmarks = function() {
+  var priv = {}; //private api
+  var publ = {}; //public api
+
+  priv.options = {};
+  var defaults = {
+    url: '',  //webservice url
+    store: null, // @required
+    debug: false
+  };
+
+  var _cont; // container holding table
+  var _stateEdit = false;
+
+  /*
+   initialize the plugin.
+   */
+  priv.init = function() {
+    _cont = $(priv.options.id);
+
+    priv.options.store.subscribe(
+      (function (global, priv) {
+        return function renderUserBookmarks(data) {
+          priv.setData(data.bookmarks)
+        }
+      })(global, priv)
+    )
+
+    _cont.sortable();
+    _cont.on('sortupdate', priv.sort);
+  };
+
+  priv.setData = function(data) {
+    _cont.empty();
+    data.forEach(function(item) {
+      if ('type' in item) {
+        var l = $('<a></a>');
+        l.data('id', item.id);
+        l.addClass('btn');
+        l.addClass('btn-xs');
+        var label = item.name, title = '';
+        if (item.hasOwnProperty('class') && item['class'] != '') {
+          l.addClass(item.class);
+        } else {
+          l.addClass('btn-default');
+        }
+        if (item.type == 'menu') {
+          // l.attr('href', 'index.php?_route=ViewTableAjax&typeName=' + 'p5_default_db:' + item.name);
+          l.attr('href', 'index.php?_route=ViewTableAjax&namespace=' + item.namespace);
+          if ('label' in item && item.label.length > 0) {
+            label = item.label;
+            title = item.label + ' (' + item.name + ')';
+          }
+          else if ('opis' in item && item.opis.length > 0) {
+            label = item.opis;
+            title = item.opis + ' (' + item.name + ')';
+          }
+        } else if (item.type == 'url') {
+          l.attr('href', 'index.php?FUNCTION_INIT=URL_INIT&ZASOB_ID=' + item.id);
+          l.attr('target', '_blank');
+        }
+        if (label.length > 20) {
+          var pos = label.indexOf(' - ');
+          if (pos > 20 || pos < 5) {
+            pos = 20;
+            l.text(label.substring(0, pos) + ' ...');
+          } else {
+            l.text(label.substring(0, pos));
+          }
+        } else {
+          l.text(label);
+        }
+        if (title == '') title = label;
+        l.attr('title', title);
+        l.appendTo(_cont);
+
+        if (_stateEdit) {
+          priv.addEditBtns(l);
+        }
+      }
+    });
+
+    if (data.length > 0) {
+      var editBtn = $('<button class="btn btn-xs" style="float:right" title="Edit Bookmarks"><i class="glyphicon glyphicon-cog"></i></button>')
+      editBtn.on('click', priv.edit);
+      editBtn.prependTo(_cont);
+    }
+  };
+
+  priv.changed = function(e) {
+    global.p5UI__MenuStore.remoteUpdate({
+      '_postTask': 'changeBookmark',
+      '_zasobID': e.data.id,
+      btnCls: e.data.cls
+    })
+    return false;
+  };
+
+  priv.removed = function(e) {
+    global.p5UI__MenuStore.remoteUpdate({
+      '_postTask': 'removeBookmark',
+      '_zasobID': e.data.id,
+    })
+    return false;
+  };
+
+  priv.sort = function(e, ui) {
+    var idsOrder = _cont.find('a').map(function(ind, n){
+      return $(n).data('id');
+    });
+    global.p5UI__MenuStore.remoteUpdate({
+      '_postTask': 'sortBookmarks',
+      '_zasobID': 0,
+      ids: idsOrder,
+    })
+    return true;
+  };
+
+  priv.addEditBtns = function(el) {
+    var next, btn;
+    el.wrap('<div></div>');
+    next = $('<span><em> Change color:</em> </span>');
+    $.each(['btn-default', 'btn-primary', 'btn-info', 'btn-success', 'btn-warning', 'btn-danger'], function(btnInd, btnClass){
+      btn = $('<button class="btn btn-xs ' + btnClass + '"> &nbsp; </button>');
+      btn.on('click', {id: el.data('id'), cls: btnClass}, priv.changed);
+      btn.appendTo(next);
+    });
+
+    btn = $('<button class="btn btn-xs"> remove </button>');
+    btn.on('click', {id: el.data('id')}, priv.removed);
+    btn.appendTo(next);
+
+    next.insertAfter(el);
+  };
+
+  priv.edit = function(e) {
+    _stateEdit = !_stateEdit;
+    var el;
+    _cont.find('a').each(function(ind, n){
+      if (priv.options.debug) console.log(n);
+      el = $(n);
+      if (_stateEdit) {
+        priv.addEditBtns(el);
+      } else {
+        el.next().remove();
+        el.unwrap();
+      }
+    });
+  }
+
+  publ.init = function(options) {
+    if (priv.options.debug) console.log('UserBookmarks initialization...');
+    //merge supplied options with defaults
+    $.extend(priv.options, defaults, options);
+    priv.init();
+    return publ;
+  };
+  return publ;
+};
+
+$.fn.UserBookmarks = function(options) {
+  options = options || {};
+  return this.each(function() {
+    options.id = this;
+    $(this).data('UserBookmarks', new UserBookmarks().init(options));
+  });
+};