[ '@namespace' => 'default_db/ZALICZKA/Zaliczka', '@primaryKey' => 'ID', 'ID' => 'xsd:integer', // short syntax - define only simpleType 'KWOTA' => [ // long syntax - define type with another params like restrictions '@type' => 'xsd:decimal' ], 'worker' => 'ref:Worker' // short syntax - define only type = ref 'pozycja' => [ // long syntax - define ref with maxOccurs, TODO: use more xsd attributes '@ref' => 'ZaliczkaPozycja', '@maxOccurs' => 'unbounded' ] ], 'Worker' => [ '@namespace' => 'default_objects/AccessOwner', ... ], 'ZaliczkaPozycja' => [ '@namespace' => 'default_db/ZALICZKA_POZYCJA/ZaliczkaPozycja', ... ] 'YT_LINK' => [ '@type' => 'p5:typeSpecialSimpleLink', '@label' => "Youtube link", '@@params' => [// $acl->getXsdFieldParam($col, 'format'); 'format' => '', 'aliasMap' => [ 'LINK' => 'LINK' ] ] ], ] */ public $_simpleSchema = array(); public $_xsdTypes = null;// set by parseXsdTypes() public $_name = ''; public $_namespace = ''; public $_primaryKey = ''; public $_rootTableName = ''; public $_sourceNamespace = ''; public $_xmlnsMap = []; public function __construct($simpleSchema = null) { if ($simpleSchema) $this->_simpleSchema = $simpleSchema; if (!$this->_simpleSchema) throw new Exception("Missing simpleSchema"); if (empty($this->_simpleSchema['root'])) throw new Exception("Wrong simpleSchema syntax"); if (empty($this->_simpleSchema['root']['@namespace'])) throw new Exception("Missing @namespace in simpleSchema"); $this->_namespace = $this->_simpleSchema['root']['@namespace']; $ns = explode('/', $this->_namespace); $this->_name = end($ns); if (empty($this->_rootTableName)) { if (count($ns) < 3) throw new Exception("Wrong @namespace syntax in simpleSchema ns({$this->_simpleSchema['root']['@namespace']})"); $this->_rootTableName = $ns[1]; } { $this->_sourceNamespace = explode('/', $this->_namespace); array_pop($this->_sourceNamespace);// remove name $this->_sourceNamespace = implode('__x3A__', $this->_sourceNamespace); } if (empty($this->_simpleSchema['root']['@primaryKey'])) $this->_simpleSchema['root']['@primaryKey'] = 'ID';// TODO: throw new Exception("Missing @primaryKey in simpleSchema '{$this->_name}'"); $this->_primaryKey = $this->_simpleSchema['root']['@primaryKey'];// TODO: check if field exists {// validate and fix _simpleSchema: // - convert field scalar to [ '@type' => ... ] // - check required @namespace attribute foreach ($this->_simpleSchema as $keySchema => $schema) { foreach ($schema as $fieldName => $params) { if ('@' == substr($fieldName, 0, 1)) continue;// skip params if (is_scalar($params)) { $this->_simpleSchema[ $keySchema ][ $fieldName ] = [ '@type' => $params ]; } else if (!is_array($params)) { throw new Exception("Parse error - simpleSchema field type"); } } if (empty($schema['@namespace'])) throw new Exception("Missing @namespace in schema for '{$keySchema}'"); $ns = explode('/', $schema['@namespace']); $name = end($ns); if (count($ns) < 2) throw new Exception("Wrong @namespace syntax in schema for '{$keySchema}'"); } } // $this->parseXsdTypes();// parse xsdTypes $this->_xsdTypes = array(); { $generatedIdZasob = 10000;// fake zasob id foreach ($this->_simpleSchema['root'] as $key => $value) { if ('@' == substr($key, 0, 1)) continue;// skip attributes if (is_array($value)) { $fieldName = $key; $field = [ 'name' => $fieldName, 'perms' => '', 'idZasob' => $generatedIdZasob ]; if (!empty($value['@label'])) $field['label'] = $value['@label']; if (!empty($value['@type'])) $field['xsdType'] = "{$value['@type']}"; else if (!empty($value['@ref'])) { $field['xsdType'] = (false !== strpos($value['@ref'], '/')) ? "ref_uri:{$value['@ref']}" : "ref:{$value['@ref']}"; } else throw new Exception("StorageAcl - field type not defined '{$key}'"); if (!empty($value['@maxOccurs'])) $field['maxOccurs'] = $value['@maxOccurs']; $this->_xsdTypes[$fieldName] = $field; } else if (is_scalar($value)) {// short syntax: $fieldName => $xsdType $fieldName = $key; $field = [ 'name' => $fieldName, 'perms' => '', 'idZasob' => $generatedIdZasob ]; $field['xsdType'] = $value; $this->_xsdTypes[$fieldName] = $field; } else { throw new Exception("StorageAcl - TODO: Unimplemented value type in simpleSchema: " . json_encode($value)); } $generatedIdZasob++; } } // TODO: fix 'ref:*' types - use Core_AclHelper::parseNamespaceUrl($namespace) } public function __toString() { $out = "xsd @prefix(default_db__x3A__{$this->_rootTableName})" . "\n"; $aliasRefUri = array(); foreach ($this->_simpleSchema as $objectName => $schema) { if ('root' == $objectName) $objectName = $this->_name; $out .= "\t" . "{$objectName}"; $out .= " @namespace({$schema['@namespace']})"; $out .= "\n"; foreach ($schema as $fieldName => $field) { if ('@' == substr($fieldName, 0, 1)) continue;// skip tags $out .= "\t\t" . "{$fieldName}"; if (!empty($field['@type'])) { $out .= " @type({$field['@type']})"; if (!empty($field['@alias'])) $out .= " @alias({$field['@alias']})"; } else if (!empty($field['@ref'])) { $out .= " @ref({$field['@ref']})"; if (false !== strpos($field['@ref'], '/')) $aliasRefUri[ $fieldName ] = $field['@ref']; } else { $out .= " @BUG('missing @type or @ref')"; } // TODO: maxOccurs, nillable, etc. $out .= "\n"; } foreach ($aliasRefUri as $fieldName => $nsUri) { $out .= "\t" . "{$objectName} @ref({$nsUri})" . "\n"; // TODO: maxOccurs, nillable, etc. } } return $out; } public function hasSimpleSchema() { return true; } public function getSimpleSchema() { return $this->_simpleSchema; } public function getSimpleSchemaTree() { $tree = array(); $tree['@namespace'] = $this->getNamespace(); foreach ($this->getXsdTypes() as $fieldName => $field) { $tree[$fieldName] = $field; if (is_array($field) && 'ref_uri:' == substr($field['xsdType'], 0, 8)) { $tree[$fieldName] = $this->_getSimpleSchemaTreeRec(substr($field['xsdType'], 8), $this->getNamespace()); } } return $tree; } public function _getSimpleSchemaTreeRec($ref, $namespacePath = '', $loopCount = 0) { if (++$loopCount > 100) die("Recurse limit in getSimpleSchemaTree for schema({$this->_namespace})"); // echo "

DBG: F._getSimpleSchemaTreeRec({$ref}) \$namespacePath[$namespacePath]

"; $tree = array(); if (!empty($this->_simpleSchema[$ref])) { $tree = array(); foreach ($this->_simpleSchema[$ref] as $fieldName => $field) { // echo "

DBG: F._getSimpleSchemaTreeRec({$ref}) \$namespacePath[$namespacePath] - 1/'{$fieldName}'

"; $tree[$fieldName] = $field; if (is_array($field) && 'ref_uri:' == substr($field['xsdType'], 0, 8)) { $nsField = substr($field['xsdType'], 8); if (in_array($nsField, explode(',', $namespacePath))) { $tree[$fieldName]['@recurse_namespace'] = true; continue; } $tree[$fieldName] = $this->_getSimpleSchemaTreeRec($nsField, "{$namespacePath},{$nsField}", $loopCount); } } } else { $acl = Core_AclHelper::getAclByNamespace($ref); $tree['@namespace'] = $acl->getNamespace(); foreach ($acl->getXsdTypes() as $fieldName => $field) { // echo "

DBG: F._getSimpleSchemaTreeRec({$ref}) \$namespacePath[$namespacePath] - 2/'{$fieldName}'

"; $tree[$fieldName] = $field; if (is_array($field) && 'ref_uri:' == substr($field['xsdType'], 0, 8)) { $nsField = substr($field['xsdType'], 8); if (in_array($nsField, explode(',', $namespacePath))) { $tree[$fieldName]['@recurse_namespace'] = true; continue; } $tree[$fieldName] = $this->_getSimpleSchemaTreeRec($nsField, "{$namespacePath},{$nsField}", $loopCount); } } } // echo'
F._getSimpleSchemaTreeRec('.$ref.') tree';print_r($tree);echo'
'; return $tree; } public function getName() { return $this->_name; } public function getRootTableName() { return $this->_rootTableName; } public function getXsdTypes() { return $this->_xsdTypes; } public function getNamespace() { return $this->_namespace; } public function getSourceName() { return $this->_sourceNamespace; } public function init($force = false) {} public function isInitialized() { return true; } public function getFieldListByIdZasob() { return $this->getRealFieldListByIdZasob(); } public function getVisibleFieldListByIdZasob() { return $this->getRealFieldListByIdZasob(); } public function getVirtualFieldListByIdZasob() { return array(); } public function getRealFieldListByIdZasob($force = false) { $cols = array(); foreach ($this->getFields() as $idField => $field) { $cols[$idField] = $field['name']; } return $cols; } public function getFields() {// @returns array - $this->_fields $fieldsById = array(); foreach ($this->getXsdTypes() as $fieldName => $field) { $field['name'] = $fieldName; $field['label'] = V::get('label', $fieldName, $field); if ('p5:www_link' == $field['xsdType']) $field['simpleType'] = 'p5:www_link'; $fieldsById[ $field['idZasob'] ] = $field; } return $fieldsById; } public function getFieldType($fieldName) { foreach ($this->getFields() as $field) { if ($fieldName == $field['name']) return $field; } return null; } // TODO: replace legacy functions: isAllowed, hasFieldPerm, getFieldIdByName public function canCreateField($fieldName) { return false; }// TODO: perms from Procesy public function canReadField($fieldName) { return true; }// TODO: perms from Procesy public function canReadObjectField($fieldName, $record) { return true; }// TODO: perms from Procesy public function canWriteField($fieldName) { return false; }// TODO: perms from Procesy public function canWriteObjectField($fieldName, $record) { return false; }// TODO: perms from Procesy public function getTotal($params = array()) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }// TODO: use ParseOgcQuery public function getItem($primaryKey, $params = []) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); } public function getItems($params = array()) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }// TODO: use ParseOgcQuery public function itemsFetchRefs(&$items) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }// TODO: , $fieldName = '' public function fetchItemFieldRefs($primaryKey, $fieldName) { $refTable = ACL::getRefTable($this->getNamespace(), $fieldName); $sqlPk = DB::getPDO()->quote($primaryKey, PDO::PARAM_STR); return array_map( function ($row) { return [ 'xlink' => "{$row['REMOTE_TYPENAME']}.{$row['REMOTE_PRIMARY_KEY']}" // TODO: ]; } , DB::getPDO()->fetchAll(" select r.REMOTE_PRIMARY_KEY, r.REMOTE_TYPENAME from `{$refTable}` r where r.PRIMARY_KEY = {$sqlPk} and r.A_STATUS != 'DELETED' ") ); } public function addItem($itemTodo) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); } public function updateItem($itemPatch) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); } public function getGeomFieldType($fieldName) { return null; } public function getPrimaryKeyField() { return $this->_primaryKey; } public function getSqlPrimaryKeyField() { return (!empty($this->_simpleSchema['root'][$this->_primaryKey]['@alias'])) ? $this->_simpleSchema['root'][$this->_primaryKey]['@alias'] : $this->_primaryKey; } public function getSqlFieldName($fieldName) { if (empty($this->_simpleSchema['root'][$fieldName])) throw new Exception("Missing field in schema '{$fieldName}'"); return (!empty($this->_simpleSchema['root'][$fieldName]['@alias'])) ? $this->_simpleSchema['root'][$fieldName]['@alias'] : $fieldName; } public function getID() { return 0; } public function getAttributesFromZasoby() { return array(); } public function isEnumerationField($fieldName) { return false; } public function getEnumerations($fieldName) { return null; } public function getXsdFieldType($fieldName) { $xsdTypes = $this->getXsdTypes(); if (empty($xsdTypes[$fieldName])) throw new Exception("Field '{$fieldName}' not exists"); return $xsdTypes[$fieldName]['xsdType']; } public function isGeomField($fieldName) { if ('File' == $fieldName) return false; if ('AccessGroupRead' == $fieldName) return false; if ('AccessGroupWrite' == $fieldName) return false; if ('AccessOwner' == $fieldName) return false; // if ('NestedObjectTest' == $fieldName) return false; // return $this->parentAcl->isGeomField($fieldName); } public function generateSqlSelectFromRootTable($prefix = 't') { $sqlSelect = []; foreach ($this->_simpleSchema['root'] as $key => $field) { if ('@' == substr($key, 0, 1)) continue;// skip attr if (!empty($field['@ref'])) continue;// skip ref if (empty($field['@type'])) continue;// skip wrong simpleType structure - BUG if ('xsd:' != substr($field['@type'], 0, 4)) continue;// skip non xsd types - eg. p5:typeSpecialSimpleLink $sqlField = (!empty($field['@alias'])) ? $field['@alias'] : $key; $sqlSelect[] = "{$prefix}.`{$sqlField}` as `$key`"; } return implode("\n, ", $sqlSelect); } public function getXsdFieldParam($fieldName, $paramKey) { if (empty($this->_simpleSchema['root'][$fieldName])) return null; if (empty($this->_simpleSchema['root'][$fieldName]['@@params'])) return null; if (empty($this->_simpleSchema['root'][$fieldName]['@@params'][$paramKey])) return null; return $this->_simpleSchema['root'][$fieldName]['@@params'][$paramKey]; } public function addP5Types(&$item) { DBG::_('DBG_ACL', '>1', "\$item", $item, __CLASS__, __FUNCTION__, __LINE__); $sqlSelect = []; foreach ($this->_simpleSchema['root'] as $key => $field) { if ('@' == substr($key, 0, 1)) continue;// skip attr if (empty($field['@type'])) continue;// skip ref if ('p5:' != substr($field['@type'], 0, 3)) continue;// skip non p5 types $fieldName = $key; $item[$fieldName] = ''; switch ($field['@type']) { case 'p5:typeSpecialSimpleLink': { // '@@params' => [// $acl->getXsdFieldParam($col, 'format'); // 'format' => '', // 'aliasMap' => [ // 'LINK' => 'LINK' // ] // ] $link = $this->getXsdFieldParam($fieldName, 'format'); // $.each(_fieldProps._tsSimpleLink.aliasMap, function(i, v) { // //console.log('simpleLink aliasMap columnName:', columnName, 'i:', i, 'v:', v, 'props['+v+']', props[v], 'val', val, 'typeof val', typeof val); // if (undefined !== row[v]) { // valLink = valLink.replace(new RegExp('\{' + i + '\}', 'g'), row[v]); // } // }); foreach ($this->getXsdFieldParam($fieldName, 'aliasMap') as $itemFieldName => $alias) { DBG::_('DBG_ACL', '>1', "aliasMap({$itemFieldName} => {$alias})", $item[$itemFieldName], __CLASS__, __FUNCTION__, __LINE__); $link = str_replace("{{$alias}}", $item[$itemFieldName], $link); } $item[$fieldName] = $link; } break; } } } public function getParamCols($params = []) { $filterCols = []; $cols = V::get('cols', [], $params);// wfs:propertyName if (empty($cols)) {// set default filter cols foreach ($this->_simpleSchema['root'] as $fieldName => $field) { if ('@' == substr($fieldName, 0, 1)) continue; if ('unbounded' == V::get('maxOccurs', '', $field)) continue;// TODO:?: default load only single value fields (skip maxOccurs="unbounded")? if (!empty($field['@type'])) { if ('xsd:' === substr($field['@type'], 0, 4)) $filterCols[$fieldName] = true; else if ('p5:' === substr($field['@type'], 0, 3)) $filterCols[$fieldName] = true; } else if (!empty($field['@ref'])) { $filterCols[$fieldName] = []; } else throw new Exception("Schema error for field '{$fieldName}' ns({$this->_namespace})"); } } else { foreach ($cols as $fieldXPath) { if ('@' == substr($fieldXPath, 0, 1)) {// attr if ('@instance' === $fieldXPath) { $filterCols['@instance'] = true; } else { throw new Exception("Not implemented attribute name '{$fieldXPath}' in '{$this->_namespace}'"); } } else if (false === strpos($fieldXPath, '/')) {// not xpath - field name if (!array_key_exists($fieldXPath, $this->_simpleSchema['root'])) throw new Exception("Field name '{$fieldXPath}' not exists in '{$this->_namespace}'"); $field = $this->_simpleSchema['root'][$fieldXPath]; if (!empty($field['@type'])) { $filterCols[$fieldXPath] = true; } else if (!empty($field['@ref'])) { $filterCols[$fieldXPath] = []; } } else {// is xpath list($fieldName, $subXPath) = explode('/', $fieldXPath, 2);// split only by first '/' $filterCols[$fieldXPath][$fieldName] = $subXPath; } } } return $filterCols; } public function buildFromSqlRow($row, $params = []) { $object = []; $filterCols = $this->getParamCols($params); // $object['_raw'] = $row; if (in_array('@instance', $filterCols)) { $instanceTable = Core_AclHelper::getInstanceTable($this->getRootTableName()); $sqlPkFieldName = $this->getSqlPrimaryKeyField(); if (empty($row[$sqlPkFieldName])) throw new Exception("Missing primary key in ({$this->_namespace})"); $sqlPk = DB::getPDO()->quote($row[$sqlPkFieldName], PDO::PARAM_STR); $object['@instance'] = DB::getPDO()->fetchValue(" select i.INSTANCE_NAME from `{$instanceTable}` i where i.PRIMARY_KEY = {$sqlPk} ");// TODO: where i.`INSTANCE_TYPE` = 'instance' -- (i.`INSTANCE_TYPE` != 'derived') } foreach ($this->_simpleSchema['root'] as $fieldName => $field) { if ('@' == substr($fieldName, 0, 1)) continue; if (!array_key_exists($fieldName, $filterCols)) continue;// only filter cols if (!empty($field['@type'])) { // UI::alert('warning', "TODO: field({$fieldName}) type({$field['@type']})"); if ('xsd:' === substr($field['@type'], 0, 4)) { $sqlFieldName = (!empty($field['@alias'])) ? $field['@alias'] : $fieldName; $object[$fieldName] = V::get($sqlFieldName, '', $row); } else if ('p5:enum' === $field['@type']) { $sqlFieldName = (!empty($field['@alias'])) ? $field['@alias'] : $fieldName; $object[$fieldName] = V::get($sqlFieldName, '', $row); } else if ('p5:' === substr($field['@type'], 0, 3)) { $object[$fieldName] = "TODO: generate value for type {$field['@type']} - field '{$fieldName}' ns({$this->_namespace})";// TODO: single field method like addP5Types } else throw new Exception("Not Implemented type for field '{$fieldName}' ns({$this->_namespace})"); } else if (!empty($field['@ref'])) { // $object[$fieldName] = $this->fetchItemFieldRefs($primaryKey, $fieldName); $refTable = ACL::getRefTable($this->getNamespace(), $fieldName); $primaryKey = $row['ID'];// TODO: get primary key $sqlPk = DB::getPDO()->quote($primaryKey, PDO::PARAM_STR); $remoteIds = array_map( function ($row) { return $row['REMOTE_PRIMARY_KEY']; } , DB::getPDO()->fetchAll(" select r.REMOTE_PRIMARY_KEY, r.REMOTE_TYPENAME from `{$refTable}` r where r.PRIMARY_KEY = {$sqlPk} and r.A_STATUS != 'DELETED' ") ); $object[$fieldName] = (!empty($remoteIds)) ? array_values(ACL::getAclByNamespace($field['@ref'])->getItems(['@primaryKey' => $remoteIds])) : []; } else throw new Exception("Schema error for field '{$fieldName}' ns({$this->_namespace})"); } return $object; } public function getChildHistTable($childName) { if (empty($childName)) throw new Exception("Missing childName ('{$this->_namespace}')"); if (!array_key_exists($childName, $this->_simpleSchema['root'])) throw new Exception("Field '{$childName}' not found in shema for '{$this->_namespace}'"); if ('@' == substr($childName, 0, 1)) throw new Exception("Child hist table for attribute '{$childName}' not supported ('{$this->_namespace}')"); // TODO: allow child for '@instance' $childSchema = $this->_simpleSchema['root'][$childName]; return Core_AclHelper::getChildHistTable($this->getRootTableName(), $childName, $childSchema); } public function getEnumValues($childName) { $childSchema = $this->_simpleSchema['root'][$childName]; DBG::log([ 'msg' => 'dbg acl', 'sql field name' => $this->getSqlFieldName($childName), 'xsdType' => $this->getXsdFieldType($childName), 'childSchema' => $childSchema, ]); if ('p5:enum' == $this->getXsdFieldType($childName)) { if (!empty($this->_simpleSchema['root'][$childName]['@aliasFieldValues'])) { $aliasConf = $this->_simpleSchema['root'][$childName]['@aliasFieldValues']; $acl = Core_AclHelper::getAclByNamespace($aliasConf['namespace']); $sqlFldName = DB::getPDO()->quote($acl->getSqlFieldName($aliasConf['childName']), PDO::PARAM_STR); $sqlTblName = DB::getPDO()->quote($acl->getRootTableName(), PDO::PARAM_STR); $sqlDbName = DB::getPDO()->quote(DB::getPDO()->getDatabaseName(), PDO::PARAM_STR); } else { $sqlFldName = DB::getPDO()->quote($this->getSqlFieldName($childName), PDO::PARAM_STR); $sqlTblName = DB::getPDO()->quote($this->getRootTableName(), PDO::PARAM_STR); $sqlDbName = DB::getPDO()->quote(DB::getPDO()->getDatabaseName(), PDO::PARAM_STR); } $optionsEnum = DB::getPDO()->fetchValue(" select COLUMN_TYPE from information_schema.COLUMNS c where c.COLUMN_NAME = {$sqlFldName} and c.TABLE_SCHEMA = {$sqlDbName} and c.TABLE_NAME = {$sqlTblName} "); if (!empty($optionsEnum)) { $optionsEnum = substr($optionsEnum, strlen('enum')); $optionsEnum = trim($optionsEnum, "()'"); DBG::log(['msg' => '$optionsEnum', '$optionsEnum' => $optionsEnum]); return explode("','", $optionsEnum); } DBG::log(['msg' => 'Error empty options from enum field', '$optionsEnum' => $optionsEnum]); return null; } return null; } }