GetFeature.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. Lib::loadClass('Api_WfsGeomTypeConverter');
  3. Lib::loadClass('Core_AclHelper');
  4. class Api_Wfs_GetFeature {
  5. public static function convertOgcPropertyListToFeatureQueryCols(&$schemaCache, $ogcPropertyList, $acl) {
  6. return self::convertOgcPropsRecurse($schemaCache, $ogcPropertyList, $acl);
  7. }
  8. public static function convertOgcPropsRecurse(&$schemaCache, $ogcPropertyList, $aclOrSchema, $dbgLoopNr = 0) {
  9. if ($dbgLoopNr > 10) {
  10. DBG::log($ogcPropertyList, 'array', 'convertOgcPropsRecurse: LOOP LIMIT 10! - return []');
  11. return [];
  12. }
  13. $acl = null;
  14. if (is_array($aclOrSchema) && !empty($aclOrSchema['namespace'])) {
  15. $acl = Core_AclHelper::getAclByNamespace($aclOrSchema['namespace'], false, $aclOrSchema);
  16. } else if ($aclOrSchema instanceof Core_AclBase) {
  17. $acl = $aclOrSchema;
  18. } else throw new Exception("Missing acl");
  19. if (empty($ogcPropertyList)) {
  20. $ogcPropertyList = array_values($acl->getFieldListByIdZasob());
  21. $ogcPropertyList = array_filter($ogcPropertyList, function ($fieldName) use ($acl) {
  22. return $acl->canReadField($fieldName); // TODO: || $acl->canReadObjectField();
  23. });
  24. }
  25. $aclFields = array_filter($ogcPropertyList, function ($prop) { return ( false === strpos($prop, '/') ); });
  26. $nestedFields = array_reduce(
  27. array_filter($ogcPropertyList, function ($prop) { return ( false !== strpos($prop, '/') ); }),
  28. function ($ret, $propNested) {
  29. list($childName, $nestedPart) = explode('/', $propNested, 2);
  30. if (!array_key_exists($childName, $ret)) $ret[ $childName ] = [];
  31. $ret[ $childName ][] = $nestedPart;
  32. return $ret;
  33. },
  34. []
  35. );
  36. if (!empty($nestedFields)) {
  37. $aclFields = array_unique(array_merge($aclFields, array_keys($nestedFields)));
  38. }
  39. if (in_array('*', $aclFields)) {
  40. $aclFields = array_filter($aclFields, function ($fieldName) { return ( '*' !== $fieldName); });
  41. $allAclFields = array_values($acl->getFieldListByIdZasob());
  42. $allAclFields = array_filter($allAclFields, function ($fieldName) use ($acl) {
  43. return $acl->canReadField($fieldName);
  44. });
  45. if ($allAclFields) $aclFields = array_unique(array_merge($aclFields, $allAclFields));
  46. }
  47. DBG::log([$aclFields, $nestedFields], 'array', 'convertOgcPropsRecurse: splited to acl fields and nested fields');
  48. foreach ($aclFields as $fieldName) {
  49. if (!$acl->canReadField($fieldName)) throw new Exception("Access Denied to read field '{$fieldName}' from '" . $acl->getNamespace() . "'");
  50. }
  51. foreach ($aclFields as $fieldName) {
  52. if (false !== strpos($fieldName, ':')) {
  53. $childNs = str_replace(['__x3A__', ':'], '/', $fieldName);
  54. $schemaCache[$childNs] = SchemaFactory::loadDefaultObject('SystemObject')->getItem($childNs, [ 'propertyName' => '*,field' ]);
  55. }
  56. }
  57. $contextFieldList = $aclFields;
  58. foreach ($nestedFields as $childName => $nestedProps) {
  59. $childNs = str_replace(['__x3A__', ':'], '/', $childName);
  60. if (!array_key_exists($childNs, $schemaCache)) {
  61. $schemaCache[$childNs] = SchemaFactory::loadDefaultObject('SystemObject')->getItem($childNs, [ 'propertyName' => '*,field' ]);
  62. }
  63. $contextFieldList = array_merge(
  64. $contextFieldList,
  65. array_map(function ($childCtxFld) use ($childName) {
  66. return "{$childName}/{$childCtxFld}";
  67. }, self::convertOgcPropsRecurse($schemaCache, $nestedProps, $schemaCache[$childNs], $dbgLoopNr + 1))
  68. );
  69. }
  70. DBG::log($contextFieldList, 'array', 'convertOgcPropsRecurse: return $contextFieldList');
  71. return $contextFieldList;
  72. }
  73. // TODO: add $contextAcl and context xpath to check for special perms by contextAcl
  74. public static function printXmlFeatureRecurse($xmlWriter, $acl, $item, $tagName, $attrs = [], $showAdvancedAttrs = false, $schemaCache = [], $printedFidLog = []) {
  75. $dbgFid = V::get('fid', 0, $attrs);
  76. if ($dbgFid) $printedFidLog[] = $dbgFid;
  77. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: printXmlFeatureRecurse... '{$tagName}'" . ( $dbgFid ? " fid='{$dbgFid}'" : "" )); // TODO: DBG
  78. DBG::log($acl, 'array', "DBG: printXmlFeatureRecurse( ... {$tagName}, \$acl)" . ( $dbgFid ? " fid='{$dbgFid}'" : "" ));
  79. DBG::log($item, 'array', "DBG: printXmlFeatureRecurse( ... {$tagName}, \$item)" . ( $dbgFid ? " fid='{$dbgFid}'" : "" ));
  80. DBG::log([$attrs, $showAdvancedAttrs, array_keys($schemaCache), $printedFidLog], 'array', "DBG: printXmlFeatureRecurse( ... {$tagName}, \$attrs, \$showAdvancedAttrs, keys(\$schemaCache), \$printedFidLog)");
  81. // $rootWfsNs = 'p5';
  82. list($itemPrefix, $localName) = explode(':', $tagName);
  83. if (1 === count($item) && !empty($item['xlink'])) {
  84. // @example 'xlink' => 'https://biuro.biall-net.pl/wfs/default_db/CRM_PROCES#PROCES.857'
  85. $xlink = $item['xlink'];
  86. list($xlinkUrl, $xlinkFid) = explode('#', $xlink);
  87. // } else if (1 == count($item[$fldName]) && !empty($item[$fldName][0]['xlink'])) {
  88. // $xmlWriter->writeComment("TODO: xlinks for '{$fldName}'"); // TODO: DBG
  89. DBG::log($item[$fldName], 'array', "TODO: xlinks for '{$tagName}'");
  90. // $xlink = $item[$fldName][0]['xlink'];
  91. // $xlinkParts = explode(':', $xlink);
  92. // if (2 != count($xlinkParts)) throw new Exception("Error Processing Request - wrong xlink format for ".$acl->getName().".{$itemKey}/{$fldName}");
  93. // $xlinkParts[0] = Api_WfsNs::getNsUri($xlinkParts[0]);
  94. // $xlink = implode('#', $xlinkParts);
  95. $xmlWriter->startElement($tagName);
  96. foreach ($attrs as $name => $value) {
  97. $xmlWriter->writeAttribute($name, $value);
  98. }
  99. if ($showAdvancedAttrs && !$acl->canReadObjectField($fldName, (object)$item)) {
  100. $xmlWriter->writeAttribute("p5:allow_read", "false");
  101. }
  102. if ($showAdvancedAttrs && $acl->canWriteObjectField($fldName, (object)$item)) {
  103. $xmlWriter->writeAttribute("p5:allow_write", "true");
  104. }
  105. $xmlWriter->writeAttribute('xlink:href', $xlink);
  106. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  107. return;
  108. }
  109. $xmlWriter->startElement($tagName);
  110. foreach ($attrs as $name => $value) {
  111. $xmlWriter->writeAttribute($name, $value);
  112. }
  113. $fldList = $acl->getRealFieldListByIdZasob();
  114. $geomFld = null;
  115. foreach ($fldList as $fldName) {
  116. if ($acl->isGeomField($fldName)) {
  117. $geomFld = $fldName;
  118. }
  119. }
  120. DBG::log($fldList, 'array', ">>> loop start fields(".count($fldList).")");
  121. foreach ($fldList as $idZasob => $fldName) {
  122. DBG::log(">>> loop {$idZasob} => {$fldName}...");
  123. $fldType = $acl->getXsdFieldType($fldName);
  124. DBG::log(">>> loop '{$fldName}' xsdType: '{$fldType}'");
  125. if (!$acl->canReadObjectField($fldName, (object)$item)) if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: skip - !canReadObjectField('{$fldName}')"); // TODO: DBG
  126. if (!$acl->canReadObjectField($fldName, (object)$item)) continue;
  127. DBG::log(">>> loop '{$fldName}' can read...");
  128. if ($geomFld != null && $fldName == $geomFld) {
  129. $xmlWriter->startElement("{$itemPrefix}:{$fldName}");
  130. if ($showAdvancedAttrs && !$acl->canReadObjectField($fldName, (object)$item)) {
  131. $xmlWriter->writeAttribute("p5:allow_read", "false");
  132. }
  133. if ($showAdvancedAttrs && $acl->canWriteObjectField($fldName, (object)$item)) {
  134. $xmlWriter->writeAttribute("p5:allow_write", "true");
  135. }
  136. (new Api_WfsGeomTypeConverter())->createGmlFromWkt_xmlWriter($item[$fldName], $xmlWriter);
  137. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  138. } else if (is_array($item[$fldName])) {// TODO: by struct - REF field
  139. DBG::log($item[$fldName], 'array', ">>> loop({$itemKey}) REF item[{$itemKey}][{$fldName}]");
  140. if (empty($item[$fldName])) { // SKIP empty fields
  141. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: skip empty field '{$fldName}'"); // TODO: DBG
  142. // $xmlWriter->h($fldName);
  143. } else {
  144. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("DBG: TODO: array field... '{$fldName}'"); // TODO: DBG
  145. // $xmlWriter->writeComment("TODO: ".$acl->getName().".{$itemKey}/{$fldName} ...");
  146. $fieldNs = str_replace(['__x3A__', ':'], '/', $fldName); // substr($xsdType, 4));
  147. if (!array_key_exists($fieldNs, $schemaCache)) {
  148. // maybe only xlinks - acl not needed
  149. $firstItem = reset($item[$fldName]);
  150. if (1 === count($firstItem) && !empty($firstItem['xlink'])) { // TODO: $schemaCache[$fieldNs] must exists for xlinks - xlmns is required
  151. foreach ($item[$fldName] as $childItem) {
  152. self::printXmlFeatureRecurse($xmlWriter, $childAcl = null, $childItem, $fldName, [], $showAdvancedAttrs, $schemaCache, $printedFidLog);
  153. }
  154. } else {
  155. DBG::log($schemaCache, 'array', "Error Processing Request - field is not ref or missing acl ".$acl->getName().".{$itemKey}/{$fldName}");
  156. if(V::get('DBG_XML', '', $_GET))$xmlWriter->writeComment("Error Processing Request - field is not ref or missing acl ".$acl->getName().".{$itemKey}/{$fldName}");
  157. }
  158. } else {
  159. DBG::log($schemaCache[$fieldNs], 'array', "TODO: xxxxxxx ".$acl->getName().".{$itemKey}/{$fldName}");
  160. $childAcl = Core_AclHelper::getAclByNamespace($schemaCache[$fieldNs]['namespace'], false, $schemaCache[$fieldNs]);
  161. $childName = $schemaCache[$fieldNs]['name'];
  162. foreach ($item[$fldName] as $childItem) {
  163. $childPK = V::get($childAcl->getPrimaryKeyField(), '', $childItem);
  164. self::printXmlFeatureRecurse($xmlWriter, $childAcl, $childItem, $fldName, ($childPK) ? [ 'fid' => "{$childName}.{$childPK}" ] : [], $showAdvancedAttrs, $schemaCache, $printedFidLog);
  165. }
  166. // foreach ($item[$fldName] as $refItem) {
  167. // DBG::log($refItem, 'array', "\$refItem fld({$fldName})");
  168. // if (1 == count($refItem) && !empty($refItem['xlink'])) {
  169. // $xmlWriter->startElement($schemaCache[$fieldNs]['typeName']);
  170. // $xmlWriter->writeAttribute("xlink:href", $refItem['xlink']);
  171. // $xmlWriter->endElement();
  172. // } else {
  173. // $xmlWriter->writeComment("DBG: array field ref ... '{$fldName}'"); // TODO: DBG
  174. // $xmlWriter->startElement($schemaCache[$fieldNs]['typeName']);
  175. // foreach ($schemaCache[$fieldNs]['field'] as $field) {
  176. // if (array_key_exists($field['fieldNamespace'], $refItem)) {
  177. // $xmlWriter->writeComment("REF field ({$field['fieldNamespace']}) value({$refItem[$field['fieldNamespace']]}) TODO: get xsdType - TODO: recurse");
  178. // DBG::log($refItem[$field['fieldNamespace']], 'array', "REF field ({$field['fieldNamespace']}) TODO: get xsdType - TODO: recurse");
  179. // if (false !== strpos($field['fieldNamespace'], ':')) { // is ref - TODO: better check by xsdType
  180. // $xmlWriter->startElement($field['fieldNamespace']);
  181. // $xmlWriter->writeComment("TODO: recurse ...");
  182. // // $xmlWriter->text($refItem[$field['fieldNamespace']]);
  183. // $xmlWriter->endElement();
  184. // } else {
  185. // $xmlWriter->startElement("{$schemaCache[$fieldNs]['nsPrefix']}:{$field['fieldNamespace']}");
  186. // $xmlWriter->text($refItem[$field['fieldNamespace']]);
  187. // $xmlWriter->endElement();
  188. // }
  189. // }
  190. // }
  191. // $xmlWriter->endElement();
  192. // }
  193. // }
  194. }
  195. }
  196. } else if ('xsd:base64Binary' === $acl->getXsdFieldType($fldName)) {
  197. if (empty($item[$fldName]) && '0' !== $item[$fldName]) continue;
  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. $xmlWriter->text(base64_encode($item[$fldName]));
  206. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  207. } else {
  208. $value = str_replace('&', '&amp;', $item[$fldName]);
  209. if (empty($value) && '0' !== $value) {
  210. continue;
  211. } else {
  212. $xmlWriter->startElement("{$itemPrefix}:{$fldName}");
  213. if ($showAdvancedAttrs && !$acl->canReadObjectField($fldName, (object)$item)) {
  214. $xmlWriter->writeAttribute("p5:allow_read", "false");
  215. }
  216. if ($showAdvancedAttrs && $acl->canWriteObjectField($fldName, (object)$item)) {
  217. $xmlWriter->writeAttribute("p5:allow_write", "true");
  218. }
  219. $xmlWriter->text($value);
  220. $xmlWriter->endElement();// {$itemPrefix}:{$fldName}
  221. }
  222. }
  223. }
  224. $xmlWriter->endElement();
  225. }
  226. }