Explorar o código

+ remove AntAcl without build file - removed from filesystem

Piotr Labudda %!s(int64=7) %!d(string=hai) anos
pai
achega
9b9a8ad260

+ 1 - 1
SE/se-lib/Route/Status.php

@@ -124,7 +124,7 @@ class Route_Status extends RouteBase {
 		DBG::log("updateObjectCachePostTask...");
 		SchemaFactory::loadDefaultObject('SystemSource')->updateCache();
 		SchemaFactory::loadDefaultObject('SystemObject')->updateCache();
-		SchemaFactory::loadDefaultObject('SystemObjectField')->updateCache();
+		// SchemaFactory::loadDefaultObject('SystemObjectField')->updateCache(); // TODO: require loop by acl objects
 		UI::alert('info', "Lista obiketów zaktualizowana");
 	}
 	function updateRefTablesPostTask() {

+ 138 - 0
SE/se-lib/Route/Test/StorageUpdateCache.php

@@ -0,0 +1,138 @@
+<?php
+
+Lib::loadClass('RouteBase');
+
+class Route_Test_StorageUpdateCache extends RouteBase {
+
+	function defaultAction() { UI::layout([ $this, 'defaultView' ]); }
+	function defaultView() {
+		// $this->checkSourceListView();
+		// $this->checkAntAclPathListView();
+		$this->fixAntAclWithStructNotInstalledView();
+	}
+	function checkSourceListView() {
+		$sourceStorage = SchemaFactory::loadDefaultObject('SystemSource');
+		$listSourceToUpdate = $sourceStorage->getItems([ 'f_hasConfig' => 1 ]);
+		UI::table([ 'caption' => "\$listSourceToUpdate:", 'rows' => $listSourceToUpdate ]);
+	}
+	function checkAntAclPathListView() {
+		$basePath = APP_PATH_SCHEMA . "/ant-object";
+		foreach (glob("{$basePath}/*/*/build.xml", GLOB_NOSORT) as $buildXmlPath) {
+			try {
+				// $this->_updateAntObjectCache($buildXmlPath, $basePath);
+				$relativePath = substr($buildXmlPath, strlen("{$basePath}/")); // expected: "{prefix}.{rootTableName}/{objectName}/build.xml"
+				$ret = preg_match_all('/^([0-9a-zA-Z_]*)\.([0-9a-zA-Z_]*)\/([0-9a-zA-Z_-]*)\/build\.xml$/', $relativePath, $matches, PREG_SET_ORDER);
+				DBG::nicePrint($matches, "ret({$ret}) \$matches for({$relativePath})");
+
+				$sourceName = $matches[0][1];
+				$rootTableName = $matches[0][2];
+				$name = $matches[0][3];
+				DBG::nicePrint([$sourceName, $rootTableName], "\$name='{$name}' - [\$lowerSource, \$rootTableName]");
+
+			} catch (Exception $e) {
+				UI::alert('danger', $e->getMessage());
+			}
+		}
+	}
+	function fixAntAclWithStructNotInstalledView() {
+		Lib::loadClass('Schema_SystemObjectStorageAcl');
+
+		// Fix objects AntAcl which struct is not installed
+		$listAntAclObjectsToFix = DB::getPDO()->fetchAll("
+			select t.*
+			from `CRM_#CACHE_ACL_OBJECT` t
+			where t._type = 'AntAcl'
+				and t.idZasob is not NULL
+				and t.hasStruct = 1
+				and t.isStructInstalled = 0
+		");
+		if (!empty($listAntAclObjectsToFix)) {
+			UI::alert('info', "Fix AntAcl objects which is not installed (total: ".count($listAntAclObjectsToFix).")");
+			foreach ($listAntAclObjectsToFix as $antAclInfo) {
+				$namespace = $antAclInfo['namespace'];
+				DBG::nicePrint($antAclInfo, "\$antAclInfo ({$namespace})");
+				$zasobyStruct = DB::getPDO()->fetchAll("
+					select z.ID, z.`DESC`
+					from `CRM_LISTA_ZASOBOW` z
+					where z.PARENT_ID = :parent_id
+						and z.`TYPE` = 'KOMORKA'
+						and z.A_STATUS not in ('DELETED')
+				", [ ':parent_id' => $antAclInfo['idZasob'] ]);
+				DBG::nicePrint($zasobyStruct, "\$zasobyStruct ({$namespace})");
+
+				ob_start();
+				{
+					Lib::loadClass('Schema_SystemObjectFieldStorageAcl');
+
+					{ // TODO
+						$item = [];
+						$exNs = explode('/', $namespace);
+						$item['name'] = array_pop($exNs);
+						$item['nsPrefix'] = implode('__x3A__', $exNs);
+						$item['typeName'] = implode('__x3A__', $exNs) . ':' . $item['name'];
+
+						$antAclPath = Schema_SystemObjectFieldStorageAcl::getAntAclXsdBasePath($item['typeName']);
+						if (!file_exists("{$antAclPath}/build.xml")) {
+							UI::alert('danger', "Ant build file not exists for namespace '{$antAclInfo['namespace']}' - Removing AntAcl");
+							Schema_SystemObjectStorageAcl::deleteObjectFromCache($antAclInfo['idZasob'], $antAclInfo['namespace']);
+
+							throw new Exception("Ant build file not exists #TEST_OFF");
+							// continue;
+						}
+					} // TODO
+
+					$objFieldAcl = new Schema_SystemObjectFieldStorageAcl();
+					try {
+						$objFieldAcl->updateCache($namespace);
+					} catch (Exception $e) {
+						UI::alert('danger', $e->getMessage());
+						Schema_SystemObjectStorageAcl::deleteObjectFromCache($antAclInfo['idZasob'], $antAclInfo['namespace']);
+						continue;
+					}
+					$reinstallLog = ob_get_clean();
+				}
+				// DBG::nicePrint($reinstallLog, "\$reinstallLog ({$namespace})");
+
+				$fieldCacheStruct = DB::getPDO()->fetchAll("
+					select t.namespace, t.fieldNamespace
+					from `CRM_#CACHE_ACL_OBJECT_FIELD` t
+					where t.objectNamespace = :namespace
+						and t.idZasob is NULL
+				", [ ':namespace' => $namespace ]);
+				DBG::nicePrint($fieldCacheStruct, "\$fieldCacheStruct ({$namespace})");
+
+				$fieldsToFix = [];
+				foreach ($fieldCacheStruct as $cacheField) {
+					$fieldName = $cacheField['fieldNamespace'];
+					foreach ($zasobyStruct as $fieldZasob) {
+						if ($fieldZasob['DESC'] === $fieldName) {
+							$fieldsToFix[] = [
+								'idZasob' => $fieldZasob['ID'],
+								'namespace' => $cacheField['namespace'],
+							];
+						}
+					}
+				}
+				DBG::nicePrint($fieldsToFix, "\$fieldsToFix ({$namespace})");
+
+				foreach ($fieldsToFix as $fixField) {
+					$affected = SchemaFactory::loadDefaultObject('SystemObjectField')->updateItem([
+						'namespace' => $fixField['namespace'],
+						'idZasob' => $fixField['idZasob']
+					]);
+					if (!$affected) UI::alert('warning', "field ({$fixField['namespace']}) update idZasob failed");
+				}
+
+				$affected = SchemaFactory::loadDefaultObject('SystemObject')->updateItem([
+					'namespace' => $namespace,
+					'isObjectActive' => 1
+				]);
+				($affected)
+				?	UI::alert('success', "object ({$namespace}) activated")
+				:	UI::alert('warning', "object ({$namespace}) activation failed");
+			}
+		}
+
+	}
+
+}

+ 166 - 136
SE/se-lib/Schema/SystemObjectStorageAcl.php

@@ -44,129 +44,31 @@ class Schema_SystemObjectStorageAcl extends Core_AclSimpleSchemaBase {
 		DBG::simpleLog('schema', "SystemObject::updateCache...");
 		// DB::getPDO()->execSql(" drop table if exists `{$this->_rootTableName}` "); // TODO: DBG
 		DB::getPDO()->execSql(" update `{$this->_rootTableName}` set hasStruct = 0 ");
-		$idDefDB = DB::getPDO()->getZasobId();
 
 		$sourceStorage = SchemaFactory::loadDefaultObject('SystemSource');
-		foreach ($sourceStorage->getItems([ 'f_hasConfig' => 1 ]) as $source) {
+		$listSourceToUpdate = $sourceStorage->getItems([ 'f_hasConfig' => 1 ]);
+		foreach ($listSourceToUpdate as $source) {
 			if ('default_objects' == $source['nsPrefix']) {
-				$clsFiles = array_map(function ($clsFile) {
-					return substr($clsFile, strlen(APP_PATH_LIB . "/Schema/"), -1 * strlen('StorageAcl.php'));
-					// return str_replace('/', '_', substr($clsFile, strlen(APP_PATH_LIB . "/Schema/"), -1 * strlen('StorageAcl.php')));
-				}, array_merge(
-					glob(APP_PATH_LIB . "/Schema/*StorageAcl.php", GLOB_NOSORT),
-					glob(APP_PATH_LIB . "/Schema/*/*StorageAcl.php", GLOB_NOSORT)
-				));
-				DBG::log($clsFiles, 'array', "DBG glob default_objects");
-				foreach ($clsFiles as $clsName) {
-					try {
-						$acl = SchemaFactory::loadDefaultObject($clsName);
-						$namespace = $acl->getNamespace();
-						DB::getPDO()->insertOrUpdate($this->_rootTableName, [
-							'namespace' => $namespace,
-							'idDatabase' => $source['idZasob'],
-							'_type' => "StorageAcl",
-							'_rootTableName' => $acl->getRootTableName(),
-							'hasStruct' => 1
-						]);
-					} catch (Exception $e) {
-						UI::alert('danger', $e->getMessage());
-					}
-				}
-				DB::getPDO()->execSql("
-					insert into `{$this->_rootTableName}` (namespace, idZasob, idDatabase, description, hasStruct)
-					select concat('{$source['nsPrefix']}/', t.`DESC`)
-						, t.ID as idZasob
-						, '{$source['idZasob']}' as idDatabase
-						, t.`OPIS` as description
-						, 1 as hasStruct
-					from CRM_LISTA_ZASOBOW t
-					where t.`TYPE` = 'TABELA'
-						and t.A_STATUS in('NORMAL', 'WAITING')
-						and t.PARENT_ID = {$source['idZasob']}
-						and t.`DESC` not like '%/%'
-					on duplicate key update idZasob = t.ID
-											, hasStruct = 1
-				");
+				$this->_updateDefaultObjectsStorageAclCache($source['idZasob']);
 			}
 			else {
 				try {
-					$dbName = DB::getPDO($source['idZasob'])->getDatabaseName();
-					$dbType = DB::getPDO($source['idZasob'])->getType();
-					if ('mysql' == $dbType) { // TODO: if the same database DB::getPDO($source['idZasob'])->getID === DB::getPDO()->getID
-						UI::alert('warning', "TODO: { id: {$source['idZasob']}, nsPrefix: '{$source['nsPrefix']}', dbName: '{$dbName}', dbType: '{$dbType}'} ...");
-						// TODO: if another DB split select and insert
-						DB::getPDO()->execSql("
-							insert into `{$this->_rootTableName}` (namespace, idDatabase, _rootTableName, _type, description, hasStruct, isStructInstalled)
-							select concat('{$source['nsPrefix']}/', t.TABLE_NAME) as namespace
-								, '{$source['idZasob']}' as idDatabase
-								, t.TABLE_NAME as _rootTableName
-								, 'TableAcl' as _type
-								, t.TABLE_COMMENT as description
-								, 1 as hasStruct
-								, 1 as isStructInstalled
-							from INFORMATION_SCHEMA.TABLES t
-							where t.TABLE_SCHEMA = '{$dbName}'
-								and t.TABLE_NAME not like '%#%'
-							on duplicate key update _rootTableName = t.TABLE_NAME
-								, hasStruct = 1
-								, isStructInstalled = 1
-						");
-						DB::getPDO()->execSql("
-							insert into `{$this->_rootTableName}` (namespace, idZasob, idDatabase, description, hasStruct)
-							select IF(t.`DESC` like 'default_db/%',
-									t.`DESC`,
-									concat('{$source['nsPrefix']}/', t.`DESC`)
-								) as namespace
-								, t.ID as idZasob
-								, '{$source['idZasob']}' as idDatabase
-								, t.`OPIS` as description
-								, 1 as hasStruct
-							from CRM_LISTA_ZASOBOW t
-							where t.`TYPE` = 'TABELA'
-								and t.A_STATUS in('NORMAL', 'WAITING')
-								and t.PARENT_ID = {$source['idZasob']}
-							on duplicate key update idZasob = t.ID
-								, hasStruct = 1
-						");
-					// } else if ('pgsql' == $dbType) {// TODO: use pgsql @see Storage Pgsql getTables from information_schema
-					} else {
-						UI::alert('warning', "TODO: { id: {$source['idZasob']}, nsPrefix: '{$source['nsPrefix']}', dbName: '{$dbName}', dbType: <b>'{$dbType}'</b>} ...");
-					}
+					$this->_updateDatabaseCache($source);
+				} catch (AlertWarningException $e) {
+					UI::alert('warning', "Warning source '{$source['idZasob']}': " . $e->getMessage());
+					continue;
 				} catch (Exception $e) {
-					UI::alert('danger', "Error source '{$source['idZasob']}' " . $e->getMessage());
+					UI::alert('danger', "Error source '{$source['idZasob']}': " . $e->getMessage());
 					continue;
 				}
 			}
 		}
 
 		// Ant objects in: SE/schema/ant-object/
-		foreach (glob(APP_PATH_SCHEMA . "/ant-object/*/*/build.xml", GLOB_NOSORT) as $buildXmlPath) {
-			// SE/schema/ant-object/default_db.test_perms/TestPermsAnt/build.xml
-			$file = substr($buildXmlPath, strlen(APP_PATH_SCHEMA . '/ant-object/'), -1 * strlen('/build.xml'));
-			DBG::nicePrint($file, "file({$file})");
-			list($partSource, $name) = explode('/', $file);
-			list($sourceName, $rootTableName) = explode('.', $partSource);
-			DBG::nicePrint([$sourceName, $rootTableName], "\$name='{$name}' - [\$lowerSource, \$rootTableName]");
-			// $clsName = substr(basename($file), 0, -1 * strlen('StorageAcl.php'));
+		$basePath = APP_PATH_SCHEMA . "/ant-object";
+		foreach (glob("{$basePath}/*/*/build.xml", GLOB_NOSORT) as $buildXmlPath) {
 			try {
-				// Lib::loadClass('AntAclBase');
-				// $acl = AntAclBase::buildInstance(0, [
-				// 	'source' => $sourceName,
-				// 	'rootTableName' => $rootTableName,
-				// 	'name' => $name
-				// ]);
-				// $acl = SchemaFactory::loadDefaultObject($clsName);
-				// $namespace = $acl->getNamespace();
-				// $name = $acl->getName();
-				$idDatabase = DB::getPDO($sourceName)->getZasobId();
-				$namespace = "{$sourceName}/{$rootTableName}/{$name}";
-				DB::getPDO()->insertOrUpdate($this->_rootTableName, [
-					'namespace' => $namespace,
-					'idDatabase' => $idDatabase,
-					'_type' => "AntAcl",
-					'_rootTableName' => $rootTableName,
-					'hasStruct' => 1
-				]);
+				$this->_updateAntObjectCache($buildXmlPath, $basePath);
 			} catch (Exception $e) {
 				UI::alert('danger', $e->getMessage());
 			}
@@ -175,33 +77,9 @@ class Schema_SystemObjectStorageAcl extends Core_AclSimpleSchemaBase {
 		if ($activeProject = Config::getProjectPath()) {
 			$baseAntObjectPath = "{$activeProject}/schema/ant-object";
 			DBG::nicePrint($baseAntObjectPath, "\$baseAntObjectPath");
-			foreach (glob("{$baseAntObjectPath}/*/*/build.xml", GLOB_NOSORT) as $file) {
-				// SE/schema/ant-object/default_db.test_perms/TestPermsAnt/build.xml
-				$file = substr($file, strlen("{$baseAntObjectPath}/"), -1 * strlen('/build.xml'));
-				DBG::nicePrint($file, '$file');
-				list($partSource, $name) = explode('/', $file);
-				list($sourceName, $rootTableName) = explode('.', $partSource);
-				DBG::nicePrint([$sourceName, $rootTableName, $name], '[$lowerSource, $rootTableName, $name]');
-				// $clsName = substr(basename($file), 0, -1 * strlen('StorageAcl.php'));
+			foreach (glob("{$baseAntObjectPath}/*/*/build.xml", GLOB_NOSORT) as $buildXmlPath) {
 				try {
-					// Lib::loadClass('AntAclBase');
-					// $acl = AntAclBase::buildInstance(0, [
-					// 	'source' => $sourceName,
-					// 	'rootTableName' => $rootTableName,
-					// 	'name' => $name
-					// ]);
-					// $acl = SchemaFactory::loadDefaultObject($clsName);
-					// $namespace = $acl->getNamespace();
-					// $name = $acl->getName();
-					$idDatabase = DB::getPDO($sourceName)->getZasobId();
-					$namespace = "{$sourceName}/{$rootTableName}/{$name}";
-					DB::getPDO()->insertOrUpdate($this->_rootTableName, [
-						'namespace' => $namespace,
-						'idDatabase' => $idDatabase,
-						'_type' => "AntAcl",
-						'_rootTableName' => $rootTableName,
-						'hasStruct' => 1
-					]);
+					$this->_updateAntObjectCache($buildXmlPath, $baseAntObjectPath);
 				} catch (Exception $e) {
 					UI::alert('danger', $e->getMessage());
 				}
@@ -234,8 +112,32 @@ class Schema_SystemObjectStorageAcl extends Core_AclSimpleSchemaBase {
 				ob_start();
 				{
 					Lib::loadClass('Schema_SystemObjectFieldStorageAcl');
+
+					{ // check if build.xml file still eixsts
+						$item = [];
+						$exNs = explode('/', $namespace);
+						$item['name'] = array_pop($exNs);
+						$item['nsPrefix'] = implode('__x3A__', $exNs);
+						$item['typeName'] = implode('__x3A__', $exNs) . ':' . $item['name'];
+
+						$antAclPath = Schema_SystemObjectFieldStorageAcl::getAntAclXsdBasePath($item['typeName']);
+						if (!file_exists("{$antAclPath}/build.xml")) {
+							UI::alert('danger', "Ant build file not exists for namespace '{$antAclInfo['namespace']}' - Removing AntAcl");
+							Schema_SystemObjectStorageAcl::deleteObjectFromCache($antAclInfo['idZasob'], $antAclInfo['namespace']);
+
+							// throw new Exception("Ant build file not exists #TEST_OFF");
+							continue;
+						}
+					}
+
 					$objFieldAcl = new Schema_SystemObjectFieldStorageAcl();
-					$objFieldAcl->updateCache($namespace);
+					try {
+						$objFieldAcl->updateCache($namespace);
+					} catch (Exception $e) {
+						UI::alert('danger', $e->getMessage());
+						Schema_SystemObjectStorageAcl::deleteObjectFromCache($antAclInfo['idZasob'], $antAclInfo['namespace']);
+						continue;
+					}
 					$reinstallLog = ob_get_clean();
 				}
 				// DBG::nicePrint($reinstallLog, "\$reinstallLog ({$namespace})");
@@ -293,6 +195,114 @@ class Schema_SystemObjectStorageAcl extends Core_AclSimpleSchemaBase {
 
 		SchemaVersionUpgrade::fixSystemObjectCoreTablesStructInstalled();
 	}
+	function _updateDefaultObjectsStorageAclCache($idSource) {
+		$clsFiles = array_map(function ($clsFile) {
+			return substr($clsFile, strlen(APP_PATH_LIB . "/Schema/"), -1 * strlen('StorageAcl.php'));
+			// return str_replace('/', '_', substr($clsFile, strlen(APP_PATH_LIB . "/Schema/"), -1 * strlen('StorageAcl.php')));
+		}, array_merge(
+			glob(APP_PATH_LIB . "/Schema/*StorageAcl.php", GLOB_NOSORT),
+			glob(APP_PATH_LIB . "/Schema/*/*StorageAcl.php", GLOB_NOSORT)
+		));
+		DBG::log($clsFiles, 'array', "DBG glob default_objects");
+		foreach ($clsFiles as $clsName) {
+			try {
+				$acl = SchemaFactory::loadDefaultObject($clsName);
+				$namespace = $acl->getNamespace();
+				DB::getPDO()->insertOrUpdate($this->_rootTableName, [
+					'namespace' => $namespace,
+					'idDatabase' => $idSource,
+					'_type' => "StorageAcl",
+					'_rootTableName' => $acl->getRootTableName(),
+					'hasStruct' => 1
+				]);
+			} catch (Exception $e) {
+				UI::alert('danger', $e->getMessage());
+			}
+		}
+		DB::getPDO()->execSql("
+			insert into `{$this->_rootTableName}` (namespace, idZasob, idDatabase, description, hasStruct)
+			select concat('{$source['nsPrefix']}/', t.`DESC`)
+				, t.ID as idZasob
+				, '{$idSource}' as idDatabase
+				, t.`OPIS` as description
+				, 1 as hasStruct
+			from CRM_LISTA_ZASOBOW t
+			where t.`TYPE` = 'TABELA'
+				and t.A_STATUS in('NORMAL', 'WAITING')
+				and t.PARENT_ID = {$idSource}
+				and t.`DESC` not like '%/%'
+			on duplicate key update idZasob = t.ID
+									, hasStruct = 1
+		");
+	}
+	function _updateDatabaseCache($source) {
+		$dbName = DB::getPDO($source['idZasob'])->getDatabaseName();
+		$dbType = DB::getPDO($source['idZasob'])->getType();
+		switch ($dbType) {
+			case 'mysql': return $this->_updateDatabaseMysqlCache($source, $dbName, $dbType);
+			default: throw new AlertWarningException("Not imeplemented db type '{$dbType}': { id: {$source['idZasob']}, nsPrefix: '{$source['nsPrefix']}', dbName: '{$dbName}', dbType: <b>'{$dbType}'</b>}");
+		}
+	}
+	function _updateDatabaseMysqlCache($source, $dbName) {
+		// TODO: if another DB split select and insert
+		DB::getPDO()->execSql("
+			insert into `{$this->_rootTableName}` (namespace, idDatabase, _rootTableName, _type, description, hasStruct, isStructInstalled)
+			select concat('{$source['nsPrefix']}/', t.TABLE_NAME) as namespace
+				, '{$source['idZasob']}' as idDatabase
+				, t.TABLE_NAME as _rootTableName
+				, 'TableAcl' as _type
+				, t.TABLE_COMMENT as description
+				, 1 as hasStruct
+				, 1 as isStructInstalled
+			from INFORMATION_SCHEMA.TABLES t
+			where t.TABLE_SCHEMA = '{$dbName}'
+				and t.TABLE_NAME not like '%#%'
+			on duplicate key update _rootTableName = t.TABLE_NAME
+				, hasStruct = 1
+				, isStructInstalled = 1
+		");
+		DB::getPDO()->execSql("
+			insert into `{$this->_rootTableName}` (namespace, idZasob, idDatabase, description, hasStruct)
+			select IF(t.`DESC` like 'default_db/%',
+					t.`DESC`,
+					concat('{$source['nsPrefix']}/', t.`DESC`)
+				) as namespace
+				, t.ID as idZasob
+				, '{$source['idZasob']}' as idDatabase
+				, t.`OPIS` as description
+				, 1 as hasStruct
+			from CRM_LISTA_ZASOBOW t
+			where t.`TYPE` = 'TABELA'
+				and t.A_STATUS in('NORMAL', 'WAITING')
+				and t.PARENT_ID = {$source['idZasob']}
+			on duplicate key update idZasob = t.ID
+				, hasStruct = 1
+		");
+	}
+	function _updateAntObjectCache($buildXmlPath, $basePath) {
+		// format: "{$basePath}/{prefix}.{rootTableName}/{objectName}/build.xml"
+		// regex: ^([a-zA-Z_]*)\.([a-zA-Z_]*)\/([a-zA-Z_]*)$
+		// example: SE/schema/ant-object/default_db.test_perms/TestPermsAnt/build.xml
+		$relativePath = substr($buildXmlPath, strlen("{$basePath}/")); // expected: "{prefix}.{rootTableName}/{objectName}/build.xml"
+		$ret = preg_match_all('/^([0-9a-zA-Z_]*)\.([0-9a-zA-Z_]*)\/([0-9a-zA-Z_-]*)\/build\.xml$/', $relativePath, $matches, PREG_SET_ORDER);
+		if (!$ret) throw new Exception("Syntax error in ant acl path: {$relativePath}");
+		$antInfo = [
+			'sourceName' => $matches[0][1],
+			'rootTableName' => $matches[0][2],
+			'objectName' => $matches[0][3],
+		];
+		DBG::nicePrint($antInfo, "relativePath({$relativePath})");
+
+		$idDatabase = DB::getPDO($antInfo['sourceName'])->getZasobId();
+		$namespace = "{$antInfo['sourceName']}/{$antInfo['rootTableName']}/{$antInfo['objectName']}";
+		DB::getPDO()->insertOrUpdate($this->_rootTableName, [
+			'namespace' => $namespace,
+			'idDatabase' => $idDatabase,
+			'_type' => "AntAcl",
+			'_rootTableName' => $antInfo['rootTableName'],
+			'hasStruct' => 1
+		]);
+	}
 
 	public function _parseWhere($params = []) {
 		$sqlWhere = [];
@@ -463,4 +473,24 @@ class Schema_SystemObjectStorageAcl extends Core_AclSimpleSchemaBase {
 		return DB::getPDO()->update($this->_rootTableName, $pkField, $pk, $itemPatch);
 	}
 
+	static function deleteObjectFromCache($idZasob, $namespace) {
+		if (!$idZasob) throw new Exception("Missing idZasob in deleteObjectFromCache");
+		if (!$namespace) throw new Exception("Missing namespace in deleteObjectFromCache");
+		DB::getPDO()->execSql("
+			DELETE from `CRM_#CACHE_ACL_OBJECT` where idZasob = :id_zasob
+		", [
+			':id_zasob' => $idZasob,
+		]);
+		DB::getPDO()->execSql("
+			DELETE from `CRM_#CACHE_ACL_OBJECT_FIELD` where objectNamespace = :namespace
+		", [
+			':namespace' => $namespace,
+		]);
+		DB::getPDO()->update('CRM_LISTA_ZASOBOW', 'ID', $idZasob, [
+			'PARENT_ID' => -1,
+			'A_STATUS' => "DELETED",
+			'A_RECORD_UPDATE_AUTHOR' => "update-acl-cache",
+			'A_RECORD_UPDATE_DATE' => "NOW()",
+		]);
+	}
 }