Prechádzať zdrojové kódy

updated Storage wfs link for resolve, exported OBJ, added Tools

Piotr Labudda 8 rokov pred
rodič
commit
f791fdceb8

+ 0 - 20
SE/se-lib/Api/WfsServerBase.php

@@ -32,26 +32,6 @@ class Api_WfsServerBase {
 	 * @param string $typeName - 'p5_default_db:TEST_PERMS'
 	 * @param string $typeName - 'p5_default_db:TEST_PERMS'
 	*/
 	*/
 	public function getAclFromTypeName($typeName) {
 	public function getAclFromTypeName($typeName) {
-		if ('1' == V::get('root', '', $_GET) && User::isAdmin()) {// TODO: check byt CRM_CONFIG where key = 'root_access_acl__{$usrLogin}' and val = '{$namespace}'
-			$namespace = Api_WfsNs::namespaceFromTypeName($typeName);
-			DBG::log("getAclFromTypeName({$typeName}): ns='{$namespace}'");
-			Lib::loadClass('SchemaFactory');
-			$objectStorage = SchemaFactory::loadDefaultObject('SystemObject');
-			$item = $objectStorage->getItem($namespace, [
-				'propertyName' => '*,field'
-			]);
-			DBG::log($item, 'array', "acl item");
-			if (!$item['isStructInstalled']) throw new Api_WfsException("WARNING: acl '{$namespace}' has not struct installed - reinstall acl in Storage tool");
-			if (!$item['idZasob']) throw new Api_WfsException("WARNING: acl '{$namespace}' in not installed in Zasoby - add to Zasoby in Storage tool");
-			switch ($item['_type']) {
-				case 'AntAcl': {
-					Lib::loadClass('AntAclBase');
-					return AntAclBase::buildInstance($item['idZasob'], $item);
-				} break;
-				default: throw new Api_WfsException("WARNING: Not implemented '{$namespace}' type '{$item['_type']}'");
-			}
-		}
-
 		try { // TODO: use object cache `CRM_#CACHE_ACL_OBJECT`
 		try { // TODO: use object cache `CRM_#CACHE_ACL_OBJECT`
 			$namespace = str_replace([':', '__x3A__'], '/', $typeName);
 			$namespace = str_replace([':', '__x3A__'], '/', $typeName);
 			Lib::loadClass('SchemaFactory');
 			Lib::loadClass('SchemaFactory');

+ 33 - 426
SE/se-lib/Route/Storage.php

@@ -6,7 +6,6 @@ Lib::loadClass('Router');
 Lib::loadClass('Schema_TableFactory');
 Lib::loadClass('Schema_TableFactory');
 Lib::loadClass('Response');
 Lib::loadClass('Response');
 Lib::loadClass('UI');
 Lib::loadClass('UI');
-Lib::loadClass('OBJ');
 Lib::loadClass('SchemaFactory');
 Lib::loadClass('SchemaFactory');
 
 
 /*
 /*
@@ -67,25 +66,32 @@ class Route_Storage extends RouteBase {
 					, [
 					, [
 						[
 						[
 							'Nr zasobu' => '',
 							'Nr zasobu' => '',
-							'nazwa' => "Obiekty",
+							'nazwa' => "Narzędzia systemowe",
 							'opis' => "SystemObjects",
 							'opis' => "SystemObjects",
+							'config?' => '<span class="text text-muted">n/d</span>',
+							'obiekty' => UI::h('a', [ 'href' => Router::getRoute('Storage_Tools')->getLink() ], "narzędzia"),
+						],
+						[
+							'Nr zasobu' => '',
+							'nazwa' => '<span style="color:silver">' . "Obiekty" . '</span>',
+							'opis' => '<span style="color:silver">' . "SystemObjects" . '</span>',
 							'config?' => '<span class="text text-muted">brak</span>',
 							'config?' => '<span class="text text-muted">brak</span>',
 							'obiekty' => UI::h('a', [ 'href' => $this->getLink('systemObjects') ], "obiekty"),
 							'obiekty' => UI::h('a', [ 'href' => $this->getLink('systemObjects') ], "obiekty"),
 						],
 						],
 						[
 						[
 							'Nr zasobu' => '',
 							'Nr zasobu' => '',
-							'nazwa' => "Obiekty Test",
-							'opis' => "Obiekty podstawowe (test json)",
+							'nazwa' => '<span style="color:silver">' . "Obiekty Test" . '</span>',
+							'opis' => '<span style="color:silver">' .  "Obiekty podstawowe (test json)" . '</span>',
 							'config?' => '<span class="text text-muted">brak</span>',
 							'config?' => '<span class="text text-muted">brak</span>',
-							'obiekty' => UI::h('a', [ 'href' => $this->getLink('coreObjectList') ], "obiekty"),
-							'raw info' => UI::h('a', [ 'href' => $this->getLink('coreObjectParseAll') ], "raw info"),
+							'obiekty' => UI::h('a', [ 'href' => Router::getRoute('Storage_TestObj')->getLink('coreObjectList') ], "obiekty"),
+							'raw info' => UI::h('a', [ 'href' => Router::getRoute('Storage_TestObj')->getLink('coreObjectParseAll') ], "raw info"),
 						],
 						],
 						[
 						[
 							'Nr zasobu' => '',
 							'Nr zasobu' => '',
-							'nazwa' => "Obiekty Test",
-							'opis' => "Obiekty dla domeny '" . str_replace(array(".", "-"), '_', $_SERVER['SERVER_NAME']) . "' (test json)",
+							'nazwa' => '<span style="color:silver">' . "Obiekty Test" . '</span>',
+							'opis' => '<span style="color:silver">' . "Obiekty dla domeny '" . str_replace(array(".", "-"), '_', $_SERVER['SERVER_NAME']) . "' (test json)" . '</span>',
 							'config?' => '<span class="text text-muted">brak</span>',
 							'config?' => '<span class="text text-muted">brak</span>',
-							'obiekty' => UI::h('a', [ 'href' => $this->getLink('objectList') ], "obiekty"),
+							'obiekty' => UI::h('a', [ 'href' => Router::getRoute('Storage_TestObj')->getLink('objectList') ], "obiekty"),
 							// 'raw info' => UI::h('a', [ 'href' => $this->getLink('objectRawInfo') ], "raw info"),
 							// 'raw info' => UI::h('a', [ 'href' => $this->getLink('objectRawInfo') ], "raw info"),
 						],
 						],
 					]
 					]
@@ -100,363 +106,6 @@ class Route_Storage extends RouteBase {
 		UI::dol();
 		UI::dol();
 	}
 	}
 
 
-	public function checkObjectInstallAjaxAction() {
-		$response = array();
-
-		try {
-			$objectName = V::get('object', '', $_REQUEST, 'word');
-			if (empty($objectName)) throw new Exception("Missing Object name");
-			$response['object'] = $objectName;
-			$json = OBJ::getCoreObjectFromFile($objectName);
-
-			OBJ::checkInstall($json);
-
-			$response['type'] = 'success';
-			$response['msg'] = "OK - object installed";
-		} catch (Exception $e) {
-			$response['type'] = 'error';
-			$response['msg'] = $e->getMessage();
-			$response['code'] = $e->getCode();
-			$response['line'] = $e->getLine();
-		}
-		Response::sendJsonExit($response);
-	}
-
-	public function coreObjectStructAction() {
-		UI::gora();
-		UI::menu();
-		$this->navView();
-		try {
-
-			$objectName = V::get('object', '', $_REQUEST, 'word');
-			if (empty($objectName)) throw new Exception("Missing Object name");
-			$json = OBJ::getCoreObjectFromFile($objectName);
-
-			$label = OBJ::getLabel($json);
-			$parentList = OBJ::getParentList($json);
-			$linksParentList = array(); foreach ($parentList as $parentName) {
-				$parentLink = '<a href="index.php?_route=Storage&_task=coreObjectStruct&object=' . str_replace('/', '-', $parentName) . '">' . $parentName . '</a>';
-				array_unshift($linksParentList, $parentLink);
-			}
-
-			$ajaxCheckInstall = Request::getPathUri() . "index.php?_route=Storage&_task=checkObjectInstallAjax&object={$objectName}";
-			$onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Storage:checkObjectInstallAjax', { href: '{$ajaxCheckInstall}' })";
-			$btnCheckInstall = '<a onclick="'.$onClick.'" href="#" title="check install db tables"><i class="glyphicon glyphicon-cog"></i></a>';
-
-			UI::startContainer();
-			UI::tag('h1', [], "Obiekt <code>{$label}</code> <small>{$btnCheckInstall}</small>");
-			if (!empty($parentList)) {
-				UI::startTag('ol', ['class'=>"breadcrumb"]);
-					UI::tag('li', [], "Dziedziczy z:");
-					foreach ($linksParentList as $parentLink) {
-						UI::tag('li', [], $parentLink);
-					}
-					// <!-- <li class="active">Data</li> -->
-				UI::endTag('ol');
-				// <!-- <p> implode(", ", $parentList);</p> -->
-			}
-			// TODO: UI::table(['rows'=>array_map()]);
-?>
-	<table class="table table-bordered table-hovered">
-		<caption>Struktura:</caption>
-		<thead>
-			<tr>
-				<th>nazwa</th>
-				<th>typ</th>
-				<th>label</th>
-				<th>json</th>
-			</tr>
-		</thead>
-		<tbody>
-			<?php foreach (OBJ::getFields($json) as $fieldName => $field) : ?>
-				<tr>
-					<td><?php echo $fieldName; ?></td>
-					<td><?php echo $field['type']; ?></td>
-					<td><?php echo $field['label']; ?></td>
-					<td><?php echo json_encode($field); ?></td>
-				</tr>
-			<?php endforeach; ?>
-		</tbody>
-	</table>
-	<a href="index.php?_route=Storage&_task=coreObjectConnect&object=<?php echo $objectName; ?>" class="btn btn-primary">Przypisz rekordy do klasy</a>
-	<?php
-		$mainTable = OBJ::getMainTableName($json);
-		$sqlFields = OBJ::getTableFields($json);
-		$this->showTableWidget($mainTable, $sqlFields);
-	?>
-</div>
-<script>
-jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:click', function(e, n, payload) {
-	console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:click', n, payload);
-});
-jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', function(e, n, payload) {
-	console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', n, payload);
-	if ('success' == payload.type) {
-		// jQuery(n).parents('td').text(payload.body.id);
-	}
-	jQuery.notify(payload.msg, payload.type);
-});
-</script>
-<?php
-			DBG::_(true, true, "json", $json, __CLASS__, __FUNCTION__, __LINE__);
-		} catch (Exception $e) {
-			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
-		}
-		UI::dol();
-	}
-
-	public function coreObjectConnectAction() {
-		UI::gora();
-		UI::menu();
-		$this->navView();
-		try {
-
-			$objectName = V::get('object', '', $_REQUEST, 'word');
-			if (empty($objectName)) throw new Exception("Missing Object name");
-			$json = OBJ::getCoreObjectFromFile($objectName);
-
-			$label = OBJ::getLabel($json);
-			$parentList = OBJ::getParentList($json);
-			$linksParentList = array(); foreach ($parentList as $parentName) {
-				$parentLink = '<a href="index.php?_route=Storage&_task=coreObjectStruct&object=' . str_replace('/', '-', $parentName) . '">' . $parentName . '</a>';
-				array_unshift($linksParentList, $parentLink);
-			}
-
-			$ajaxCheckInstall = Request::getPathUri() . "index.php?_route=Storage&_task=checkObjectInstallAjax&object={$objectName}";
-			$onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Storage:checkObjectInstallAjax', { href: '{$ajaxCheckInstall}' })";
-			$btnCheckInstall = '<a onclick="'.$onClick.'" href="#" title="check install db tables"><i class="glyphicon glyphicon-cog"></i></a>';
-
-?>
-<div class="container">
-	<h1>Obiekt <code><?php echo $label; ?></code> <small><?php echo $btnCheckInstall; ?></small></h1>
-	<?php if (!empty($parentList)) : ?>
-		<ol class="breadcrumb">
-		  <li>Dziedziczy z:</li>
-			<?php foreach ($linksParentList as $parentLink) : ?>
-			  <li><?php echo $parentLink; ?></li>
-			<?php endforeach; ?>
-		  <!-- <li class="active">Data</li> -->
-		</ol>
-		<!-- <p> <?php echo implode(", ", $parentList); ?></p> -->
-	<?php endif; ?>
-	<?php
-		$tableOne = array();
-		$tableOne['caption'] = 'Klasy';
-		$tableOne['rows'] = array();
-		$parentList = OBJ::getParentList($json);
-		$tableOne['rows'][] = array('__primary_key' => $objectName, 'name' => $objectName);
-		$linksParentList = array(); foreach ($parentList as $parentName) {
-			// $parentLink = '<a href="index.php?_route=Storage&_task=coreObjectStruct&object=' . str_replace('/', '-', $parentName) . '">' . $parentName . '</a>';
-			// array_unshift($linksParentList, $parentLink);
-			$tableOne['rows'][] = array('__primary_key' => $parentName, 'name' => $parentName);
-		}
-
-		$tableTwo = array();
-		$tableTwo['caption'] = 'Rekordy w tabeli głównej';
-		$mainTable = OBJ::getMainTableName($json);
-		$sqlFields = OBJ::getTableFields($json);
-		$tableTwo['rows'] = $this->getTableRows($mainTable, $sqlFields);
-		foreach ($tableTwo['rows'] as $idx => $row) {
-			// $tableTwo['rows'][] = array('__primary_key' => $objectName, 'name' => $objectName);
-			$tableTwo['rows'][$idx]['__primary_key'] = $row['ID'];
-		}
-		echo '<hr>';
-		// TODO: filter connected rows
-		// TODO: filter not connected rows
-		// TODO: action connect rows - args(schema, rows_primary_key_list)
-		// TODO: action un connect rows - args(schema, rows_primary_key_list)
-		$this->showConnectSchemaToTableWidget(compact('tableOne', 'tableTwo'));
-	?>
-</div>
-<script>
-jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:click', function(e, n, payload) {
-	console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:click', n, payload);
-});
-jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', function(e, n, payload) {
-	console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', n, payload);
-	if ('success' == payload.type) {
-		// jQuery(n).parents('td').text(payload.body.id);
-	}
-	jQuery.notify(payload.msg, payload.type);
-});
-</script>
-<?php
-			DBG::_(true, true, "json", $json, __CLASS__, __FUNCTION__, __LINE__);
-		} catch (Exception $e) {
-			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
-		}
-		UI::dol();
-	}
-
-	public function showConnectSchemaToTableWidget($params) {
-		if (empty($params['tableOne'])) throw new Exception("Missing tableOne in Connect widget");
-		if (empty($params['tableTwo'])) throw new Exception("Missing tableTwo in Connect widget");
-		$tableOne = $params['tableOne'];
-		$tableTwo = $params['tableTwo'];
-		// TODO: add p5BtnAjax to table filters button
-		// TODO: add p5BtnAjax to conn button
-		$jsEventNamespace = 'ConnectTableWidget' . time();
-		$htmlIdWrap = "{$jsEventNamespace}-wrap";
-
-		$stateLogId = "{$jsEventNamespace}-state-log";
-		$stateSelectedTotalId = "{$jsEventNamespace}-state-selected";
-		$stateClearSelectedBtnId = "{$jsEventNamespace}-state-clear-selected-btn";
-		foreach ($tableOne['rows'] as $idx => $r) {
-			$tableOne['rows'][$idx]['@onClick'] = "return p5UI__Clickable(this, '{$jsEventNamespace}:tableOne', { primary_key: '{$r['__primary_key']}' });";
-		}
-		foreach ($tableTwo['rows'] as $idx => $r) {
-			$tableTwo['rows'][$idx]['@onClick'] = "return p5UI__Clickable(this, '{$jsEventNamespace}:tableTwo', { primary_key: '{$r['__primary_key']}' });";
-		}
-		$tableOne['hidden_cols'] = $tableTwo['hidden_cols'] = array('__primary_key', '@onClick', '__html_id');
-		$tableTwo['__html_id'] = "{$jsEventNamespace}-table-two";
-?>
-<div id="<?php echo $htmlIdWrap; ?>">
-	<?php UI::table($tableOne); ?>
-	<div class="btn-group">
-		<button class="btn btn-default" title="Wybierz rekordy wg instancji"><i class="glyphicon glyphicon-question-sign"></i></button>
-		<button class="btn btn-default conn-btn-filter-yes">Przypisani</button>
-		<button class="btn btn-default conn-btn-filter-no active">Nieprzypisani</button>
-		<button class="btn btn-default conn-btn-filter-clear" title="Wszystkie"><i class="glyphicon glyphicon-remove"></i></button>
-	</div>
-	<div class="btn-group">
-		<span style="padding-left:30px;">Zaznaczono
-			<span class="conn-selected-total">0</span>
-			<button class="conn-btn-clear-selected btn btn-link"
-							onClick="return p5UI__Clickable(this, '<?php echo $jsEventNamespace; ?>:clearSelection', {});"
-							style="display:none"><i class="glyphicon glyphicon-remove" style="color:red"></i></button>
-		</span>
-	</div>
-	<?php UI::jsAjaxTable($tableTwo); ?>
-	<pre class="conn-log"></pre>
-</div>
-<script>
-(function(){
-
-	var state = {
-		selectedTableOne: null,
-		selectedTableTwo: [],
-		filterSelected: 'no'
-	}
-	var props = {
-		jsEventNamespace: '<?php echo $jsEventNamespace; ?>',
-		htmlIdWrap: '<?php echo $htmlIdWrap; ?>',
-		tableTwoId: '#<?php echo $tableTwo['__html_id']; ?>',
-		stateSelectedTotalId: '#<?php echo $stateSelectedTotalId; ?>',
-		stateClearSelectedBtnId: '#<?php echo $stateClearSelectedBtnId; ?>',
-		stateLogId: '#<?php echo $stateLogId; ?>'
-	}
-	var wrap = jQuery('#' + props.htmlIdWrap);
-	var jqNodes = {
-		wrap: wrap,
-		filter_yes: wrap.find('.conn-btn-filter-yes'),
-		filter_no: wrap.find('.conn-btn-filter-no'),
-		filter_clear: wrap.find('.conn-btn-filter-clear'),
-		total_selected: wrap.find('.conn-selected-total'),
-		btn_clear_selected: wrap.find('.conn-btn-clear-selected'),
-		state_log: wrap.find('.conn-log')
-	}
-
-	function render() {
-		jqNodes['total_selected'].text(state.selectedTableTwo.length);
-		if (state.selectedTableTwo.length > 0) {
-			jQuery(props.stateClearSelectedBtnId).show();
-		} else {
-			jQuery(props.stateClearSelectedBtnId).hide();
-		}
-		jqNodes['state_log'].text(JSON.stringify(state));
-
-		switch (state.filterSelected) {
-			case 'yes':
-				jqNodes['filter_yes'].addClass('active');
-				jqNodes['filter_no'].removeClass('active');
-				jqNodes['filter_clear'].removeClass('active');
-				break;
-			case 'no':
-				jqNodes['filter_yes'].removeClass('active');
-				jqNodes['filter_no'].addClass('active');
-				jqNodes['filter_clear'].removeClass('active');
-				break;
-			case 'clear':
-				jqNodes['filter_yes'].removeClass('active');
-				jqNodes['filter_no'].removeClass('active');
-				jqNodes['filter_clear'].addClass('active');
-				break;
-		}
-	}
-	function action__selectTableOneRow(pk) {
-		state.selectedTableOne = pk;
-		render();
-	}
-	function action__unselectTableOneRow() {
-		state.selectedTableOne = null;
-		render();
-	}
-	function action__selectTableTwoRow(pk) {
-		var idx = state.selectedTableTwo.indexOf(pk);
-		if (-1 === idx) state.selectedTableTwo.push(pk);
-		else console.log("Error selectedTableTwo - should be not set");
-		render();
-	}
-	function action__unselectTableTwoRow(pk) {
-		var idx = state.selectedTableTwo.indexOf(pk);
-		if (-1 === idx) state.selectedTableTwo.push(pk);
-		else state.selectedTableTwo.splice(idx, 1);
-		render();
-	}
-	function action__clearSelection() {
-		state.selectedTableTwo = [];
-		jQuery(props.tableTwoId).find('tbody').children('.info').removeClass('info');
-		render();
-	}
-	function action__filterSet(fltr) {
-		state.filterSelected = fltr;
-		render();
-		// TODO: ajax load tableTwo based on filter
-	}
-
-	jQuery(document).on(props.jsEventNamespace + ':tableOne:click', function(e, n, payload) {
-		var n$ = jQuery(n);
-		console.log('event '+props.jsEventNamespace+':tableOne:click', n, payload);
-		jQuery.notify('tableOne row clicked [' + payload.props.primary_key + ']', 'info');
-		if (n$.hasClass('info')) {
-			n$.removeClass('info');
-			action__unselectTableOneRow();
-		} else {
-			n$.parent().children('.info').removeClass('info');
-			n$.addClass('info');
-			action__selectTableOneRow(payload.props.primary_key);
-		}
-	});
-	jQuery(document).on(props.jsEventNamespace + ':tableTwo:click', function(e, n, payload) {
-		var n$ = jQuery(n);
-		console.log('event '+props.jsEventNamespace+':tableTwo:click', n, payload);
-		jQuery.notify('tableTwo row clicked [' + payload.props.primary_key + ']', 'info');
-		if (n$.hasClass('info')) {
-			n$.removeClass('info');
-			action__unselectTableTwoRow(payload.props.primary_key);
-		} else {
-			n$.addClass('info');
-			action__selectTableTwoRow(payload.props.primary_key);
-		}
-	});
-	jQuery(document).on(props.jsEventNamespace + ':clearSelection:click', function(e, n, payload) {
-		action__clearSelection();
-	});
-	jqNodes['filter_yes'].on('click', function(e) {
-		action__filterSet('yes');
-	});
-	jqNodes['filter_no'].on('click', function(e) {
-		action__filterSet('no');
-	});
-	jqNodes['filter_clear'].on('click', function(e) {
-		action__filterSet('clear');
-	});
-
-})();
-</script>
-<?php
-	}
-
 	public function getTableRows($tblName, $fields) {
 	public function getTableRows($tblName, $fields) {
 		$sqlFields = array();
 		$sqlFields = array();
 		foreach ($fields as $fldName) {
 		foreach ($fields as $fldName) {
@@ -477,56 +126,6 @@ jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', fun
 		UI::table(array('caption' => "table({$tblName})", 'rows' => $rows));
 		UI::table(array('caption' => "table({$tblName})", 'rows' => $rows));
 	}
 	}
 
 
-	public function coreObjectParseAllAction() {
-		UI::gora();
-		UI::menu();
-		$this->navView();
-		try {
-			OBJ::parseAll();
-		} catch (Exception $e) {
-			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
-		}
-		UI::dol();
-	}
-
-	public function coreObjectListAction() {
-		UI::gora();
-		UI::menu();
-		$this->navView();
-		try {
-			$coreObjlist = OBJ::getCoreObjectList();
-			$objectList = array();
-			foreach ($coreObjlist as $objName) {
-				$objItem = array();
-				$objItem['name'] = $objName;
-				$objItem['struktura'] = '<a href="index.php?_route=Storage&_task=coreObjectStruct&object=' . $objName . '">' . "struct" . '</a>';
-				// $objItem['label'] = "";// TODO: read from json
-				$objectList[] = $objItem;
-			}
-			usort($objectList, function($rowA, $rowB) {
-				$a = $rowA['nazwa']; $b = $rowB['nazwa'];
-				if ($a == $b) return 0;
-				return ($a < $b) ? -1 : 1;
-			});
-
-			DBG::table("objectList", $objectList, __CLASS__, __FUNCTION__, __LINE__);
-		} catch (Exception $e) {
-			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
-		}
-		UI::dol();
-	}
-
-	public function objectListAction() {
-		UI::gora();
-		UI::menu();
-		try {
-
-		} catch (Exception $e) {
-			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
-		}
-		UI::dol();
-	}
-
 	public function tableListAction() {
 	public function tableListAction() {
 		UI::gora();
 		UI::gora();
 		UI::menu();
 		UI::menu();
@@ -630,11 +229,7 @@ jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', fun
 								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=DescribeFeatureTypeAdvanced&TYPENAME={$typeName}" ], "wfs DescribeFeatureTypeAdvanced"),
 								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=DescribeFeatureTypeAdvanced&TYPENAME={$typeName}" ], "wfs DescribeFeatureTypeAdvanced"),
 								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=GetFeature&TYPENAME={$typeName}&MAXFEATURES=10" ], "wfs GetFeature (max: 10)"),
 								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=GetFeature&TYPENAME={$typeName}&MAXFEATURES=10" ], "wfs GetFeature (max: 10)"),
 								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=GetFeatureAdvanced&TYPENAME={$typeName}&MAXFEATURES=10" ], "wfs GetFeatureAdvanced (max: 10)"),
 								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=GetFeatureAdvanced&TYPENAME={$typeName}&MAXFEATURES=10" ], "wfs GetFeatureAdvanced (max: 10)"),
-
-								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=DescribeFeatureType&TYPENAME={$typeName}&root=1" ], "wfs root DescribeFeatureType"),
-								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=DescribeFeatureTypeAdvanced&TYPENAME={$typeName}&root=1" ], "wfs root DescribeFeatureTypeAdvanced"),
-								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=GetFeature&TYPENAME={$typeName}&MAXFEATURES=10&root=1" ], "wfs root GetFeature (max: 10)"),
-								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=GetFeatureAdvanced&TYPENAME={$typeName}&MAXFEATURES=10&root=1" ], "wfs root GetFeatureAdvanced (max: 10)"),
+								UI::h('a', [ 'href' => "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&SRSNAME=EPSG:3003&REQUEST=GetFeature&TYPENAME={$typeName}&MAXFEATURES=3&resolve=all&resolveDepth=3" ], "wfs GetFeatureAdvanced (max: 3, resolveDepth: 3)"),
 							])
 							])
 						]),
 						]),
 					];
 					];
@@ -1228,27 +823,39 @@ jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', fun
 			$namespace = V::get('namespace', '', $_REQUEST, 'word');
 			$namespace = V::get('namespace', '', $_REQUEST, 'word');
 			switch ($task) {
 			switch ($task) {
 				case 'obejctList':
 				case 'obejctList':
+					$backLabel = 'Storage';
+					$backLink = $this->getLink();
+					$currentLink = $this->getLink('obejctList', ['idStorage' => $idStorage]);
+					break;
 				case 'tableList':
 				case 'tableList':
+					$backLabel = 'Storage';
+					$backLink = $this->getLink();
+					$currentLink = $this->getLink('tableList', ['idStorage' => $idStorage]);
+					break;
 				case 'viewList':
 				case 'viewList':
+					$backLabel = 'Storage';
+					$backLink = $this->getLink();
+					$currentLink = $this->getLink('viewList', ['idStorage' => $idStorage]);
+					break;
 				case 'rawInfo':
 				case 'rawInfo':
 					$backLabel = 'Storage';
 					$backLabel = 'Storage';
 					$backLink = $this->getLink();
 					$backLink = $this->getLink();
-					$currentLink = $this->getLink($task, ['idStorage' => $idStorage]);
+					$currentLink = $this->getLink('rawInfo', ['idStorage' => $idStorage]);
 					break;
 					break;
 				case 'tableStruct':
 				case 'tableStruct':
 					$backLabel = "Tabele [{$idStorage}]";
 					$backLabel = "Tabele [{$idStorage}]";
 					$backLink = $this->getLink('tableList', ['idStorage' => $idStorage]);
 					$backLink = $this->getLink('tableList', ['idStorage' => $idStorage]);
-					$currentLink = $this->getLink($task, ['idStorage' => $idStorage, 'table' => $tblName]);
+					$currentLink = $this->getLink('tableStruct', ['idStorage' => $idStorage, 'table' => $tblName]);
 					break;
 					break;
 				case 'objectStruct':
 				case 'objectStruct':
 					$backLabel = "Obiekty [{$idStorage}]";
 					$backLabel = "Obiekty [{$idStorage}]";
 					$backLink = $this->getLink('tableList', ['idStorage' => $idStorage]);// TODO: mv tableList to objectList
 					$backLink = $this->getLink('tableList', ['idStorage' => $idStorage]);// TODO: mv tableList to objectList
-					$currentLink = $this->getLink($task, ['idStorage' => $idStorage, 'namespace' => $namespace]);
+					$currentLink = $this->getLink('objectStruct', ['idStorage' => $idStorage, 'namespace' => $namespace]);
 					break;
 					break;
 				case 'coreObjectStruct':
 				case 'coreObjectStruct':
 					$backLabel = "Obiekty podstawowe";
 					$backLabel = "Obiekty podstawowe";
-					$backLink = $this->getLink('coreObjectList', ['idStorage' => $idStorage]);
-					$currentLink = $this->getLink($task, ['idStorage' => $idStorage, 'object' => $objName]);
+					$backLink = Router::getRoute('Storage_TestObj')->getLink('coreObjectList', ['idStorage' => $idStorage]);
+					$currentLink = $this->getLink('coreObjectStruct', ['idStorage' => $idStorage, 'object' => $objName]);
 					break;
 					break;
 			}
 			}
 			switch ($task) {
 			switch ($task) {

+ 401 - 0
SE/se-lib/Route/Storage/TestObj.php

@@ -0,0 +1,401 @@
+<?php
+
+Lib::loadClass('OBJ');
+Lib::loadClass('Response');
+Lib::loadClass('Router');
+
+class Route_Storage_TestObj extends RouteBase {
+
+	public function defaultAction() {
+
+	}
+
+	public function objectListAction() {
+		UI::gora();
+		UI::menu();
+		try {
+			throw new Exception("TODO...");
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function coreObjectListAction() {
+		UI::gora();
+		UI::menu();
+		Router::getRoute('Storage')->navView();
+		try {
+			$coreObjlist = OBJ::getCoreObjectList();
+			$objectList = array();
+			foreach ($coreObjlist as $objName) {
+				$objItem = array();
+				$objItem['name'] = $objName;
+				$objItem['struktura'] = UI::h('a', [ 'href' => $this->getLink('coreObjectStruct', [ 'object' => $objName ]) ], "struct");
+				// $objItem['label'] = "";// TODO: read from json
+				$objectList[] = $objItem;
+			}
+			usort($objectList, function($rowA, $rowB) {
+				$a = $rowA['nazwa']; $b = $rowB['nazwa'];
+				if ($a == $b) return 0;
+				return ($a < $b) ? -1 : 1;
+			});
+
+			DBG::table("objectList", $objectList, __CLASS__, __FUNCTION__, __LINE__);
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function checkObjectInstallAjaxAction() {
+		$response = array();
+
+		try {
+			$objectName = V::get('object', '', $_REQUEST, 'word');
+			if (empty($objectName)) throw new Exception("Missing Object name");
+			$response['object'] = $objectName;
+			$json = OBJ::getCoreObjectFromFile($objectName);
+
+			OBJ::checkInstall($json);
+
+			$response['type'] = 'success';
+			$response['msg'] = "OK - object installed";
+		} catch (Exception $e) {
+			$response['type'] = 'error';
+			$response['msg'] = $e->getMessage();
+			$response['code'] = $e->getCode();
+			$response['line'] = $e->getLine();
+		}
+		Response::sendJsonExit($response);
+	}
+
+	public function coreObjectConnectAction() {
+		UI::gora();
+		UI::menu();
+		Router::getRoute('Storage')->navView();
+		try {
+			$objectName = V::get('object', '', $_REQUEST, 'word');
+			if (empty($objectName)) throw new Exception("Missing Object name");
+			$json = OBJ::getCoreObjectFromFile($objectName);
+
+			$label = OBJ::getLabel($json);
+			$parentList = OBJ::getParentList($json);
+			$linksParentList = array(); foreach ($parentList as $parentName) {
+				$parentLink = UI::h('a', [ 'href' => $this->getLink('coreObjectStruct', [ 'object' => str_replace('/', '-', $parentName) ]) ], $parentName);
+				array_unshift($linksParentList, $parentLink);
+			}
+
+			$ajaxCheckInstall = Router::getRoute('Storage_TestObj')->getLink('checkObjectInstallAjax', [ 'object' => $objectName ]);
+			$onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Storage:checkObjectInstallAjax', { href: '{$ajaxCheckInstall}' })";
+			$btnCheckInstall = '<a onclick="'.$onClick.'" href="#" title="check install db tables"><i class="glyphicon glyphicon-cog"></i></a>';
+			UI::startContainer();
+			echo UI::h('h1', [], "Obiekt <code>{$label}</code> <small>{$btnCheckInstall}</small>");
+			if (!empty($parentList)) {
+				echo UI::h('ol', [ 'class' => "breadcrumb" ], array_merge([
+					'<li>Dziedziczy z:</li>',
+				], array_map(function ($parentLink) {
+					return "<li>{$parentLink}</li>";
+				}, $linksParentList)));
+				// <li class="active">Data</li>
+				// echo '<p>' . implode(", ", $parentList) . '</p>';
+			}
+			$tableOne = array();
+			$tableOne['caption'] = 'Klasy';
+			$tableOne['rows'] = array();
+			$parentList = OBJ::getParentList($json);
+			$tableOne['rows'][] = array('__primary_key' => $objectName, 'name' => $objectName);
+			$linksParentList = array();
+			foreach ($parentList as $parentName) {
+				// $parentLink = UI::h('a', [ 'href' => $this->getLink('coreObjectStruct', [ 'object' => str_replace('/', '-', $parentName) ]) ], $parentName);
+				// array_unshift($linksParentList, $parentLink);
+				$tableOne['rows'][] = array('__primary_key' => $parentName, 'name' => $parentName);
+			}
+
+			$tableTwo = array();
+			$tableTwo['caption'] = 'Rekordy w tabeli głównej';
+			$mainTable = OBJ::getMainTableName($json);
+			$sqlFields = OBJ::getTableFields($json);
+			$tableTwo['rows'] = $this->getTableRows($mainTable, $sqlFields);
+			foreach ($tableTwo['rows'] as $idx => $row) {
+				// $tableTwo['rows'][] = array('__primary_key' => $objectName, 'name' => $objectName);
+				$tableTwo['rows'][$idx]['__primary_key'] = $row['ID'];
+			}
+			echo '<hr>';
+			// TODO: filter connected rows
+			// TODO: filter not connected rows
+			// TODO: action connect rows - args(schema, rows_primary_key_list)
+			// TODO: action un connect rows - args(schema, rows_primary_key_list)
+			$this->showConnectSchemaToTableWidget(compact('tableOne', 'tableTwo'));
+
+			UI::endContainer();
+			echo UI::h('script', [], "
+				jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:click', function(e, n, payload) {
+					console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:click', n, payload);
+				});
+				jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', function(e, n, payload) {
+					console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', n, payload);
+					if ('success' == payload.type) {
+						// jQuery(n).parents('td').text(payload.body.id);
+					}
+					jQuery.notify(payload.msg, payload.type);
+				});
+			");
+			DBG::_(true, true, "json", $json, __CLASS__, __FUNCTION__, __LINE__);
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function coreObjectStructAction() {
+		UI::gora();
+		UI::menu();
+		Router::getRoute('Storage')->navView();
+		try {
+			$objectName = V::get('object', '', $_REQUEST, 'word');
+			if (empty($objectName)) throw new Exception("Missing Object name");
+			$json = OBJ::getCoreObjectFromFile($objectName);
+
+			$label = OBJ::getLabel($json);
+			$parentList = OBJ::getParentList($json);
+			$linksParentList = array(); foreach ($parentList as $parentName) {
+				$parentLink = UI::h('a', [ 'href' => $this->getLink('coreObjectStruct', [ 'object' => str_replace('/', '-', $parentName) ]) ], $parentName);
+				array_unshift($linksParentList, $parentLink);
+			}
+
+			$ajaxCheckInstall = $this->getLink('checkObjectInstallAjax', [ 'object' => $objectName ]);
+			$onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Storage:checkObjectInstallAjax', { href: '{$ajaxCheckInstall}' })";
+			$btnCheckInstall = '<a onclick="'.$onClick.'" href="#" title="check install db tables"><i class="glyphicon glyphicon-cog"></i></a>';
+
+			UI::startContainer();
+			UI::tag('h1', [], "Obiekt <code>{$label}</code> <small>{$btnCheckInstall}</small>");
+			if (!empty($parentList)) {
+				UI::startTag('ol', ['class'=>"breadcrumb"]);
+					UI::tag('li', [], "Dziedziczy z:");
+					foreach ($linksParentList as $parentLink) {
+						UI::tag('li', [], $parentLink);
+					}
+					// <!-- <li class="active">Data</li> -->
+				UI::endTag('ol');
+				// <!-- <p> implode(", ", $parentList);</p> -->
+			}
+			// TODO: UI::table(['rows'=>array_map()]);
+			$jsonFields = OBJ::getFields($json);
+			UI::table([
+				'caption' => "Struktura:",
+				'rows' => array_map(function ($field, $fieldName) {
+					return [
+						'nazwa' => $fieldName,
+						'typ' => $field['type'],
+						'label' => $field['label'],
+						'json' => json_encode($field),
+					];
+				}, $jsonFields, array_keys($jsonFields))
+			]);
+			echo UI::h('a', [ 'class' => "btn btn-primary", 'href' => $this->getLink('coreObjectConnect', [ 'object' => $objectName ]) ], "Przypisz rekordy do klasy");
+			$mainTable = OBJ::getMainTableName($json);
+			$sqlFields = OBJ::getTableFields($json);
+			$this->showTableWidget($mainTable, $sqlFields);
+			UI::endContainer();
+			echo UI::h('script', [], "
+				jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:click', function(e, n, payload) {
+					console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:click', n, payload);
+				});
+				jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', function(e, n, payload) {
+					console.log('event p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', n, payload);
+					if ('success' == payload.type) {
+						// jQuery(n).parents('td').text(payload.body.id);
+					}
+					jQuery.notify(payload.msg, payload.type);
+				});
+			");
+			DBG::_(true, true, "json", $json, __CLASS__, __FUNCTION__, __LINE__);
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function coreObjectParseAllAction() {
+		UI::gora();
+		UI::menu();
+		Router::getRoute('Storage')->navView();
+		try {
+			OBJ::parseAll();
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function showConnectSchemaToTableWidget($params) {
+		if (empty($params['tableOne'])) throw new Exception("Missing tableOne in Connect widget");
+		if (empty($params['tableTwo'])) throw new Exception("Missing tableTwo in Connect widget");
+		$tableOne = $params['tableOne'];
+		$tableTwo = $params['tableTwo'];
+		// TODO: add p5BtnAjax to table filters button
+		// TODO: add p5BtnAjax to conn button
+		$jsEventNamespace = 'ConnectTableWidget' . time();
+		$htmlIdWrap = "{$jsEventNamespace}-wrap";
+
+		$stateLogId = "{$jsEventNamespace}-state-log";
+		$stateSelectedTotalId = "{$jsEventNamespace}-state-selected";
+		$stateClearSelectedBtnId = "{$jsEventNamespace}-state-clear-selected-btn";
+		foreach ($tableOne['rows'] as $idx => $r) {
+			$tableOne['rows'][$idx]['@onClick'] = "return p5UI__Clickable(this, '{$jsEventNamespace}:tableOne', { primary_key: '{$r['__primary_key']}' });";
+		}
+		foreach ($tableTwo['rows'] as $idx => $r) {
+			$tableTwo['rows'][$idx]['@onClick'] = "return p5UI__Clickable(this, '{$jsEventNamespace}:tableTwo', { primary_key: '{$r['__primary_key']}' });";
+		}
+		$tableOne['hidden_cols'] = $tableTwo['hidden_cols'] = array('__primary_key', '@onClick', '__html_id');
+		$tableTwo['__html_id'] = "{$jsEventNamespace}-table-two";
+?>
+<div id="<?php echo $htmlIdWrap; ?>">
+	<?php UI::table($tableOne); ?>
+	<div class="btn-group">
+		<button class="btn btn-default" title="Wybierz rekordy wg instancji"><i class="glyphicon glyphicon-question-sign"></i></button>
+		<button class="btn btn-default conn-btn-filter-yes">Przypisani</button>
+		<button class="btn btn-default conn-btn-filter-no active">Nieprzypisani</button>
+		<button class="btn btn-default conn-btn-filter-clear" title="Wszystkie"><i class="glyphicon glyphicon-remove"></i></button>
+	</div>
+	<div class="btn-group">
+		<span style="padding-left:30px;">Zaznaczono
+			<span class="conn-selected-total">0</span>
+			<button class="conn-btn-clear-selected btn btn-link"
+							onClick="return p5UI__Clickable(this, '<?php echo $jsEventNamespace; ?>:clearSelection', {});"
+							style="display:none"><i class="glyphicon glyphicon-remove" style="color:red"></i></button>
+		</span>
+	</div>
+	<?php UI::jsAjaxTable($tableTwo); ?>
+	<pre class="conn-log"></pre>
+</div>
+<script>
+(function(){
+
+	var state = {
+		selectedTableOne: null,
+		selectedTableTwo: [],
+		filterSelected: 'no'
+	}
+	var props = {
+		jsEventNamespace: '<?php echo $jsEventNamespace; ?>',
+		htmlIdWrap: '<?php echo $htmlIdWrap; ?>',
+		tableTwoId: '#<?php echo $tableTwo['__html_id']; ?>',
+		stateSelectedTotalId: '#<?php echo $stateSelectedTotalId; ?>',
+		stateClearSelectedBtnId: '#<?php echo $stateClearSelectedBtnId; ?>',
+		stateLogId: '#<?php echo $stateLogId; ?>'
+	}
+	var wrap = jQuery('#' + props.htmlIdWrap);
+	var jqNodes = {
+		wrap: wrap,
+		filter_yes: wrap.find('.conn-btn-filter-yes'),
+		filter_no: wrap.find('.conn-btn-filter-no'),
+		filter_clear: wrap.find('.conn-btn-filter-clear'),
+		total_selected: wrap.find('.conn-selected-total'),
+		btn_clear_selected: wrap.find('.conn-btn-clear-selected'),
+		state_log: wrap.find('.conn-log')
+	}
+
+	function render() {
+		jqNodes['total_selected'].text(state.selectedTableTwo.length);
+		if (state.selectedTableTwo.length > 0) {
+			jQuery(props.stateClearSelectedBtnId).show();
+		} else {
+			jQuery(props.stateClearSelectedBtnId).hide();
+		}
+		jqNodes['state_log'].text(JSON.stringify(state));
+
+		switch (state.filterSelected) {
+			case 'yes':
+				jqNodes['filter_yes'].addClass('active');
+				jqNodes['filter_no'].removeClass('active');
+				jqNodes['filter_clear'].removeClass('active');
+				break;
+			case 'no':
+				jqNodes['filter_yes'].removeClass('active');
+				jqNodes['filter_no'].addClass('active');
+				jqNodes['filter_clear'].removeClass('active');
+				break;
+			case 'clear':
+				jqNodes['filter_yes'].removeClass('active');
+				jqNodes['filter_no'].removeClass('active');
+				jqNodes['filter_clear'].addClass('active');
+				break;
+		}
+	}
+	function action__selectTableOneRow(pk) {
+		state.selectedTableOne = pk;
+		render();
+	}
+	function action__unselectTableOneRow() {
+		state.selectedTableOne = null;
+		render();
+	}
+	function action__selectTableTwoRow(pk) {
+		var idx = state.selectedTableTwo.indexOf(pk);
+		if (-1 === idx) state.selectedTableTwo.push(pk);
+		else console.log("Error selectedTableTwo - should be not set");
+		render();
+	}
+	function action__unselectTableTwoRow(pk) {
+		var idx = state.selectedTableTwo.indexOf(pk);
+		if (-1 === idx) state.selectedTableTwo.push(pk);
+		else state.selectedTableTwo.splice(idx, 1);
+		render();
+	}
+	function action__clearSelection() {
+		state.selectedTableTwo = [];
+		jQuery(props.tableTwoId).find('tbody').children('.info').removeClass('info');
+		render();
+	}
+	function action__filterSet(fltr) {
+		state.filterSelected = fltr;
+		render();
+		// TODO: ajax load tableTwo based on filter
+	}
+
+	jQuery(document).on(props.jsEventNamespace + ':tableOne:click', function(e, n, payload) {
+		var n$ = jQuery(n);
+		console.log('event '+props.jsEventNamespace+':tableOne:click', n, payload);
+		jQuery.notify('tableOne row clicked [' + payload.props.primary_key + ']', 'info');
+		if (n$.hasClass('info')) {
+			n$.removeClass('info');
+			action__unselectTableOneRow();
+		} else {
+			n$.parent().children('.info').removeClass('info');
+			n$.addClass('info');
+			action__selectTableOneRow(payload.props.primary_key);
+		}
+	});
+	jQuery(document).on(props.jsEventNamespace + ':tableTwo:click', function(e, n, payload) {
+		var n$ = jQuery(n);
+		console.log('event '+props.jsEventNamespace+':tableTwo:click', n, payload);
+		jQuery.notify('tableTwo row clicked [' + payload.props.primary_key + ']', 'info');
+		if (n$.hasClass('info')) {
+			n$.removeClass('info');
+			action__unselectTableTwoRow(payload.props.primary_key);
+		} else {
+			n$.addClass('info');
+			action__selectTableTwoRow(payload.props.primary_key);
+		}
+	});
+	jQuery(document).on(props.jsEventNamespace + ':clearSelection:click', function(e, n, payload) {
+		action__clearSelection();
+	});
+	jqNodes['filter_yes'].on('click', function(e) {
+		action__filterSet('yes');
+	});
+	jqNodes['filter_no'].on('click', function(e) {
+		action__filterSet('no');
+	});
+	jqNodes['filter_clear'].on('click', function(e) {
+		action__filterSet('clear');
+	});
+
+})();
+</script>
+<?php
+	}
+
+}

+ 18 - 0
SE/se-lib/Route/Storage/Tools.php

@@ -0,0 +1,18 @@
+<?php
+
+Lib::loadClass('RouteBase');
+Lib::loadClass('Response');
+Lib::loadClass('UI');
+Lib::loadClass('DBG');
+
+class Route_Storage_Tools extends RouteBase {
+
+	public function defaultAction() {
+		UI::layout( [ $this, 'defaultView' ], [ 'showMenu' => false ] );
+	}
+	public function defaultView() {
+		echo $this->getLink();
+		throw new Exception("TODO...");
+	}
+
+}

+ 2 - 1
SE/se-lib/Route/WfsJsRequestPanel.php

@@ -221,9 +221,10 @@ class Route_WfsJsRequestPanel extends RouteBase {
 					var postBody = document.getElementById('wfsRequestBody').value.replace(/&lt;/g, '<').replace(/&gt;/g, '>')
 					var postBody = document.getElementById('wfsRequestBody').value.replace(/&lt;/g, '<').replace(/&gt;/g, '>')
 					console.log('wfsRequestBody:', postBody)
 					console.log('wfsRequestBody:', postBody)
 					var wfsParams = Object.assign({
 					var wfsParams = Object.assign({
-						'maxFeatures': 5,
+						'maxFeatures': 3,
 					}, (!postBody) ? {} : { 'ogc:Filter': postBody }
 					}, (!postBody) ? {} : { 'ogc:Filter': postBody }
 					)
 					)
+					console.log('p5WFS_GetFeature', featureTypeName, wfsParams)
 					p5WFS_GetFeature(featureTypeName, wfsParams).then(function (features) {
 					p5WFS_GetFeature(featureTypeName, wfsParams).then(function (features) {
 						console.log('features', features)
 						console.log('features', features)
 						document.getElementById('wfsResponse').innerHTML = JSON.stringify(features, null, 2)
 						document.getElementById('wfsResponse').innerHTML = JSON.stringify(features, null, 2)