GetFeature.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <?php
  2. Lib::loadClass('Api_WfsGeomTypeConverter');
  3. Lib::loadClass('Core_AclHelper');
  4. class Api_Wfs_GetFeature {
  5. public static function convertOgcPropertyListToFeatureQueryColsRecurseDBG(&$schemaCache, $ogcPropertyList, $aclOrSchema, $dbgLoopNr = 0) {
  6. if ($dbgLoopNr > 10) {
  7. DBG::log($ogcPropertyList, 'array', 'TODO: split $ogcPropertyList to acl fields and nested fields - DBG LOOP LIMIT 10');
  8. return [];
  9. }
  10. $acl = null;
  11. if (is_array($aclOrSchema) && !empty($aclOrSchema['namespace'])) {
  12. $acl = Core_AclHelper::getAclByNamespace($aclOrSchema['namespace'], false, $aclOrSchema);
  13. } else if ($aclOrSchema instanceof Core_AclBase) {
  14. $acl = $aclOrSchema;
  15. } else throw new Exception("Missing acl");
  16. if (empty($ogcPropertyList)) {
  17. // DBG::log($acl->getFields(), '', "TODO: \$ogcPropertyList = \$acl->getFields()");
  18. // DBG::log($acl->getFieldListByIdZasob(), 'array', "TODO: \$ogcPropertyList = \$acl->getFieldListByIdZasob()");
  19. $ogcPropertyList = array_values($acl->getFieldListByIdZasob());
  20. }
  21. DBG::log($ogcPropertyList, 'array', 'TODO: split $ogcPropertyList to acl fields and nested fields');
  22. $aclFields = array_filter($ogcPropertyList, function ($prop) { return ( false === strpos($prop, '/') ); });
  23. $nestedFields = array_reduce(
  24. array_filter($ogcPropertyList, function ($prop) { return ( false !== strpos($prop, '/') ); }),
  25. function ($ret, $propNested) {
  26. list($childName, $nestedPart) = explode('/', $propNested, 2);
  27. if (!array_key_exists($childName, $ret)) $ret[ $childName ] = [];
  28. $ret[ $childName ][] = $nestedPart;
  29. return $ret;
  30. },
  31. []
  32. );
  33. if (!empty($nestedFields)) {
  34. $aclFields = array_unique(array_merge($aclFields, array_keys($nestedFields)));
  35. }
  36. DBG::log([$aclFields, $nestedFields], 'array', 'TODO: splited $ogcPropertyList to acl fields and nested fields');
  37. // TODO: convert '*' to local fields
  38. // TODO: validate $aclFields is in $acl->getFields() or in $schemaCache[ nsLocal ]
  39. $contextFieldList = $aclFields;
  40. foreach ($nestedFields as $childName => $nestedProps) {
  41. $childNs = str_replace(['__x3A__', ':'], '/', $childName);
  42. if (!array_key_exists($childNs, $schemaCache)) {
  43. $schemaCache[$childNs] = SchemaFactory::loadDefaultObject('SystemObject')->getItem($childNs, [ 'propertyName' => '*,field' ]);
  44. }
  45. $contextFieldList = array_merge(
  46. $contextFieldList,
  47. array_map(function ($childCtxFld) use ($childName) {
  48. return "{$childName}/{$childCtxFld}";
  49. }, self::convertOgcPropertyListToFeatureQueryColsRecurseDBG($schemaCache, $nestedProps, $schemaCache[$childNs], $dbgLoopNr + 1))
  50. );
  51. }
  52. DBG::log($contextFieldList, 'array', 'TODO: splited $ogcPropertyList return $contextFieldList');
  53. return $contextFieldList;
  54. // $acl__getAllFieldNames = function ($listFields) {
  55. // return array_map(function ($field) {
  56. // return $field['fieldNamespace'];
  57. // }, $listFields);
  58. // };
  59. // $acl__getLocalFieldNames = function ($listFields) {
  60. // return array_map(function ($field) {
  61. // return $field['fieldNamespace'];
  62. // }, array_filter($listFields, function ($field) {
  63. // return $field['isLocal'];
  64. // }));
  65. // };
  66. }
  67. public static function convertOgcPropertyListToFeatureQueryCols(&$schemaCache, $ogcPropertyList, $acl) {
  68. $contextFieldList = [];
  69. $acl__getAllFieldNames = function ($listFields) {
  70. return array_map(function ($field) {
  71. return $field['fieldNamespace'];
  72. }, $listFields);
  73. };
  74. $acl__getLocalFieldNames = function ($listFields) {
  75. return array_map(function ($field) {
  76. return $field['fieldNamespace'];
  77. }, array_filter($listFields, function ($field) {
  78. return $field['isLocal'];
  79. }));
  80. };
  81. // DBG::log($acl->getFields(), 'array', "\$contextFieldList ACL fields");
  82. // if (empty($ogcPropertyList)) { // get all local fields
  83. // // $contextFieldList = $acl__getLocalFieldNames($acl->getFields());
  84. // $contextFieldList = $acl__getAllFieldNames($acl->getFields());
  85. // } else {
  86. // foreach ($ogcPropertyList as $fieldXPath) {
  87. // if ('*' === $fieldXPath) {
  88. // $contextFieldList = array_merge($contextFieldList, $acl__getLocalFieldNames($acl->getFields()));
  89. // } else if (false === strpos($fieldXPath, '/') && false === strpos($fieldXPath, ':')) {
  90. // $contextFieldList[] = $fieldXPath;
  91. // } else if (false === strpos($fieldXPath, '/') && false !== strpos($fieldXPath, ':')) {
  92. // $contextFieldList[] = $fieldXPath;
  93. // $fieldNs = str_replace(['__x3A__', ':'], '/', $fieldXPath);
  94. // $schemaCache[$fieldNs] = SchemaFactory::loadDefaultObject('SystemObject')->getItem($fieldNs, [ 'propertyName' => '*,field' ]);
  95. // DBG::log($schemaCache[$fieldNs], 'array', "\$schemaCache[{$fieldNs}]");
  96. // } else if ('/*' === substr($fieldXPath, -2) && false === strpos(substr($fieldXPath, 0, -2), '/')) {
  97. // $fieldName = substr($fieldXPath, 0, -2);
  98. // $contextFieldList[] = $fieldName;
  99. // $xsdType = $acl->getXsdFieldType($fieldName);
  100. // if ('ref:' !== substr($xsdType, 0, 4)) throw new Exception("Error Processing Request - field '{$fieldXPath}' type is not ref '/*' is not allowed");
  101. // $fieldNs = str_replace(['__x3A__', ':'], '/', substr($xsdType, 4));
  102. // if (!array_key_exists($fieldNs, $schemaCache)) {
  103. // $schemaCache[$fieldNs] = SchemaFactory::loadDefaultObject('SystemObject')->getItem($fieldNs, [ 'propertyName' => '*,field' ]);
  104. // DBG::log($schemaCache[$fieldNs], 'array', "\$schemaCache[{$fieldNs}]");
  105. // }
  106. // $fieldPrefix = "{$fieldName}";
  107. // $contextFieldList = array_merge($contextFieldList, array_map(function ($fieldName) use ($fieldPrefix) {
  108. // return "{$fieldPrefix}/{$fieldName}";
  109. // }, $acl__getLocalFieldNames($schemaCache[$fieldNs]['field'])));
  110. // } else {
  111. // $fieldName = trim($fieldXPath, '*/');
  112. // DBG::log(['$fieldXPath'=>$fieldXPath, '$fieldName'=>$fieldName], 'array', "\$contextFieldList TODO");
  113. // if (false !== strpos($fieldName, '/')) {
  114. // $xpathParts = explode('/', $fieldName);
  115. // DBG::log($xpathParts, 'array', "\$xpathParts recurse TODO");
  116. // $localFieldName = array_shift($xpathParts);
  117. // DBG::log($localFieldName, 'array', "\$xpathParts recurse TODO \$localFieldName");
  118. // if ('*' === end($xpathParts)) array_pop($xpathParts);
  119. // if (!empty($xpathParts)) {
  120. // foreach ($xpathParts as $part) {
  121. // if (false !== strpos($part, ':')) {
  122. // $fieldNs = str_replace(['__x3A__', ':'], '/', $part);
  123. // if (!array_key_exists($fieldNs, $schemaCache)) {
  124. // $schemaCache[$fieldNs] = SchemaFactory::loadDefaultObject('SystemObject')->getItem($fieldNs, [ 'propertyName' => '*,field' ]);
  125. // DBG::log($schemaCache[$fieldNs], 'array', "\$schemaCache[{$fieldNs}] recurse TODO");
  126. // }
  127. // }
  128. // }
  129. // $contextFieldList[] = $localFieldName . "/" . implode("/", $xpathParts) . "/*";
  130. // }
  131. // // 'default_db__x3A__CRM_PROCES:PROCES/ID',
  132. // // 'default_db__x3A__CRM_PROCES:PROCES/default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK/*',
  133. // }
  134. // }
  135. // }
  136. // }
  137. // DBG::log($contextFieldList, 'array', "\$contextFieldList");
  138. // DBG::log(array_keys($schemaCache), 'array', "\$schemaCache keys");
  139. return self::convertOgcPropertyListToFeatureQueryColsRecurseDBG($schemaCache, $ogcPropertyList, $acl);
  140. return $contextFieldList;
  141. }
  142. // TODO: add $contextAcl and context xpath to check for special perms by contextAcl
  143. public static function printXmlFeatureRecurse($xmlWriter, $acl, $item, $tagName, $attrs = [], $showAdvancedAttrs = false, $schemaCache = [], $printedFidLog = []) {
  144. $dbgFid = V::get('fid', 0, $attrs);
  145. if ($dbgFid) $printedFidLog[] = $dbgFid;
  146. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: printXmlFeatureRecurse... '{$tagName}'" . ( $dbgFid ? " fid='{$dbgFid}'" : "" )); // TODO: DBG
  147. DBG::log($acl, 'array', "DBG: printXmlFeatureRecurse( ... {$tagName}, \$acl)" . ( $dbgFid ? " fid='{$dbgFid}'" : "" ));
  148. DBG::log($item, 'array', "DBG: printXmlFeatureRecurse( ... {$tagName}, \$item)" . ( $dbgFid ? " fid='{$dbgFid}'" : "" ));
  149. DBG::log([$attrs, $showAdvancedAttrs, array_keys($schemaCache), $printedFidLog], 'array', "DBG: printXmlFeatureRecurse( ... {$tagName}, \$attrs, \$showAdvancedAttrs, keys(\$schemaCache), \$printedFidLog)");
  150. // $rootWfsNs = 'p5';
  151. list($itemPrefix, $localName) = explode(':', $tagName);
  152. if (1 === count($item) && !empty($item['xlink'])) {
  153. // @example 'xlink' => 'https://biuro.biall-net.pl/wfs/default_db/CRM_PROCES#PROCES.857'
  154. $xlink = $item['xlink'];
  155. list($xlinkUrl, $xlinkFid) = explode('#', $xlink);
  156. // } else if (1 == count($item[$fldName]) && !empty($item[$fldName][0]['xlink'])) {
  157. // $xmlWriter->writeComment("TODO: xlinks for '{$fldName}'"); // TODO: DBG
  158. DBG::log($item[$fldName], 'array', "TODO: xlinks for '{$tagName}'");
  159. // $xlink = $item[$fldName][0]['xlink'];
  160. // $xlinkParts = explode(':', $xlink);
  161. // if (2 != count($xlinkParts)) throw new Exception("Error Processing Request - wrong xlink format for ".$acl->getName().".{$itemKey}/{$fldName}");
  162. // $xlinkParts[0] = Api_WfsNs::getNsUri($xlinkParts[0]);
  163. // $xlink = implode('#', $xlinkParts);
  164. $xmlWriter->startElement($tagName);
  165. foreach ($attrs as $name => $value) {
  166. $xmlWriter->writeAttribute($name, $value);
  167. }
  168. if ($showAdvancedAttrs && !$acl->canReadObjectField($fldName, (object)$item)) {
  169. $xmlWriter->writeAttribute("p5:allow_read", "false");
  170. }
  171. if ($showAdvancedAttrs && $acl->canWriteObjectField($fldName, (object)$item)) {
  172. $xmlWriter->writeAttribute("p5:allow_write", "true");
  173. }
  174. $xmlWriter->writeAttribute('xlink:href', $xlink);
  175. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  176. return;
  177. }
  178. $xmlWriter->startElement($tagName);
  179. foreach ($attrs as $name => $value) {
  180. $xmlWriter->writeAttribute($name, $value);
  181. }
  182. $fldList = $acl->getRealFieldListByIdZasob();
  183. $geomFld = null;
  184. foreach ($fldList as $fldName) {
  185. if ($acl->isGeomField($fldName)) {
  186. $geomFld = $fldName;
  187. }
  188. }
  189. DBG::log($fldList, 'array', ">>> loop start fields(".count($fldList).")");
  190. foreach ($fldList as $idZasob => $fldName) {
  191. DBG::log(">>> loop {$idZasob} => {$fldName}...");
  192. $fldType = $acl->getXsdFieldType($fldName);
  193. DBG::log(">>> loop '{$fldName}' xsdType: '{$fldType}'");
  194. if (!$acl->canReadObjectField($fldName, (object)$item)) if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: skip - !canReadObjectField('{$fldName}')"); // TODO: DBG
  195. if (!$acl->canReadObjectField($fldName, (object)$item)) continue;
  196. DBG::log(">>> loop '{$fldName}' can read...");
  197. if ($geomFld != null && $fldName == $geomFld) {
  198. $xmlWriter->startElement("{$itemPrefix}:{$fldName}");
  199. if ($showAdvancedAttrs && !$acl->canReadObjectField($fldName, (object)$item)) {
  200. $xmlWriter->writeAttribute("p5:allow_read", "false");
  201. }
  202. if ($showAdvancedAttrs && $acl->canWriteObjectField($fldName, (object)$item)) {
  203. $xmlWriter->writeAttribute("p5:allow_write", "true");
  204. }
  205. (new Api_WfsGeomTypeConverter())->createGmlFromWkt_xmlWriter($item[$fldName], $xmlWriter);
  206. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  207. } else if (is_array($item[$fldName])) {// TODO: by struct - REF field
  208. DBG::log($item[$fldName], 'array', ">>> loop({$itemKey}) REF item[{$itemKey}][{$fldName}]");
  209. if (empty($item[$fldName])) { // SKIP empty fields
  210. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: skip empty field '{$fldName}'"); // TODO: DBG
  211. // $xmlWriter->h($fldName);
  212. } else {
  213. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: TODO: array field... '{$fldName}'"); // TODO: DBG
  214. // $xmlWriter->writeComment("TODO: ".$acl->getName().".{$itemKey}/{$fldName} ...");
  215. $fieldNs = str_replace(['__x3A__', ':'], '/', $fldName); // substr($xsdType, 4));
  216. if (!array_key_exists($fieldNs, $schemaCache)) {
  217. // maybe only xlinks - acl not needed
  218. $firstItem = reset($item[$fldName]);
  219. if (1 === count($firstItem) && !empty($firstItem['xlink'])) {
  220. foreach ($item[$fldName] as $childItem) {
  221. self::printXmlFeatureRecurse($xmlWriter, $childAcl = null, $childItem, $fldName, [], $showAdvancedAttrs, $schemaCache, $printedFidLog);
  222. }
  223. } else {
  224. DBG::log($schemaCache, 'array', "Error Processing Request - field is not ref or missing acl ".$acl->getName().".{$itemKey}/{$fldName}");
  225. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("Error Processing Request - field is not ref or missing acl ".$acl->getName().".{$itemKey}/{$fldName}");
  226. }
  227. } else {
  228. DBG::log($schemaCache[$fieldNs], 'array', "TODO: xxxxxxx ".$acl->getName().".{$itemKey}/{$fldName}");
  229. $childAcl = Core_AclHelper::getAclByNamespace($schemaCache[$fieldNs]['namespace'], false, $schemaCache[$fieldNs]);
  230. $childName = $schemaCache[$fieldNs]['name'];
  231. foreach ($item[$fldName] as $childItem) {
  232. $childPK = V::get($childAcl->getPrimaryKeyField(), '', $childItem);
  233. self::printXmlFeatureRecurse($xmlWriter, $childAcl, $childItem, $fldName, ($childPK) ? [ 'fid' => "{$childName}.{$childPK}" ] : [], $showAdvancedAttrs, $schemaCache, $printedFidLog);
  234. }
  235. // foreach ($item[$fldName] as $refItem) {
  236. // DBG::log($refItem, 'array', "\$refItem fld({$fldName})");
  237. // if (1 == count($refItem) && !empty($refItem['xlink'])) {
  238. // $xmlWriter->startElement($schemaCache[$fieldNs]['typeName']);
  239. // $xmlWriter->writeAttribute("xlink:href", $refItem['xlink']);
  240. // $xmlWriter->endElement();
  241. // } else {
  242. // $xmlWriter->writeComment("DBG: array field ref ... '{$fldName}'"); // TODO: DBG
  243. // $xmlWriter->startElement($schemaCache[$fieldNs]['typeName']);
  244. // foreach ($schemaCache[$fieldNs]['field'] as $field) {
  245. // if (array_key_exists($field['fieldNamespace'], $refItem)) {
  246. // $xmlWriter->writeComment("REF field ({$field['fieldNamespace']}) value({$refItem[$field['fieldNamespace']]}) TODO: get xsdType - TODO: recurse");
  247. // DBG::log($refItem[$field['fieldNamespace']], 'array', "REF field ({$field['fieldNamespace']}) TODO: get xsdType - TODO: recurse");
  248. // if (false !== strpos($field['fieldNamespace'], ':')) { // is ref - TODO: better check by xsdType
  249. // $xmlWriter->startElement($field['fieldNamespace']);
  250. // $xmlWriter->writeComment("TODO: recurse ...");
  251. // // $xmlWriter->text($refItem[$field['fieldNamespace']]);
  252. // $xmlWriter->endElement();
  253. // } else {
  254. // $xmlWriter->startElement("{$schemaCache[$fieldNs]['nsPrefix']}:{$field['fieldNamespace']}");
  255. // $xmlWriter->text($refItem[$field['fieldNamespace']]);
  256. // $xmlWriter->endElement();
  257. // }
  258. // }
  259. // }
  260. // $xmlWriter->endElement();
  261. // }
  262. // }
  263. }
  264. }
  265. } else if ('xsd:base64Binary' === $acl->getXsdFieldType($fldName)) {
  266. if (empty($item[$fldName]) && '0' !== $item[$fldName]) continue;
  267. $xmlWriter->startElement("{$itemPrefix}:{$fldName}");
  268. if ($showAdvancedAttrs && !$acl->canReadObjectField($fldName, (object)$item)) {
  269. $xmlWriter->writeAttribute("p5:allow_read", "false");
  270. }
  271. if ($showAdvancedAttrs && $acl->canWriteObjectField($fldName, (object)$item)) {
  272. $xmlWriter->writeAttribute("p5:allow_write", "true");
  273. }
  274. $xmlWriter->text(base64_encode($item[$fldName]));
  275. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  276. } else {
  277. $value = str_replace('&', '&amp;', $item[$fldName]);
  278. if (empty($value) && '0' !== $value) {
  279. continue;
  280. } else {
  281. $xmlWriter->startElement("{$itemPrefix}:{$fldName}");
  282. if ($showAdvancedAttrs && !$acl->canReadObjectField($fldName, (object)$item)) {
  283. $xmlWriter->writeAttribute("p5:allow_read", "false");
  284. }
  285. if ($showAdvancedAttrs && $acl->canWriteObjectField($fldName, (object)$item)) {
  286. $xmlWriter->writeAttribute("p5:allow_write", "true");
  287. }
  288. $xmlWriter->text($value);
  289. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  290. }
  291. }
  292. }
  293. $xmlWriter->endElement();
  294. }
  295. }