UNPKG

qwc2

Version:
960 lines (955 loc) 47.3 kB
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /** * Copyright 2017-2024 Sourcepole AG * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ import React from 'react'; import { connect } from 'react-redux'; import clone from 'clone'; import isEmpty from 'lodash.isempty'; import PropTypes from 'prop-types'; import { v4 as uuidv4 } from 'uuid'; import { setEditContext, clearEditContext } from '../actions/editing'; import { refreshLayer } from '../actions/layers'; import { setCurrentTaskBlocked } from '../actions/task'; import ConfigUtils from '../utils/ConfigUtils'; import CoordinatesUtils from '../utils/CoordinatesUtils'; import { getFeatureTemplate, parseExpressionsAsync } from '../utils/EditingUtils'; import LocaleUtils from '../utils/LocaleUtils'; import AutoEditForm from './AutoEditForm'; import LinkFeatureForm from './LinkFeatureForm'; import QtDesignerForm from './QtDesignerForm'; import ButtonBar from './widgets/ButtonBar'; import ReCaptchaWidget from './widgets/ReCaptchaWidget'; import './style/AttributeForm.css'; var AttributeForm = /*#__PURE__*/function (_React$Component) { function AttributeForm(props) { var _this; _classCallCheck(this, AttributeForm); _this = _callSuper(this, AttributeForm, [props]); _defineProperty(_this, "state", { busy: false, deleteClicked: false, childEdit: null, relationTables: {}, formValid: true, captchaResponse: null }); _defineProperty(_this, "render", function () { var captchaRequired = ConfigUtils.getConfigProp("editServiceCaptchaSiteKey") && !ConfigUtils.getConfigProp("username"); var captchaPending = captchaRequired && !_this.state.captchaResponse; var commitBar = null; if (_this.props.editContext.changed) { var commitButtons = [{ key: 'Commit', icon: _this.state.formValid ? 'ok' : 'warning', label: _this.state.formValid ? LocaleUtils.tr("editing.commit") : LocaleUtils.tr("editing.invalidform"), extraClasses: _this.state.formValid ? "button-accept" : "button-warning", type: "submit", disabled: !_this.state.formValid || captchaPending }, { key: 'Discard', icon: 'remove', label: LocaleUtils.tr("editing.discard"), extraClasses: "button-reject" }]; commitBar = /*#__PURE__*/React.createElement(ButtonBar, { buttons: commitButtons, onClick: _this.onDiscard }); /* submit is handled via onSubmit in the form */ } var editConfig = _this.props.editContext.editConfig; var editPermissions = editConfig.permissions || {}; var readOnly = _this.props.readOnly || editPermissions.updatable === false && _this.props.editContext.action === 'Pick'; var deleteBar = null; if (!_this.props.hideDelete && _this.props.editContext.action === 'Pick' && _this.props.editContext.feature && !_this.props.editContext.changed && editPermissions.deletable !== false && !_this.props.readOnly) { // Delete button bar will appear by default if no permissions are defined in editConfig or when deletable permission is set if (!_this.state.deleteClicked) { var _this$props$deleteLab; var deleteButtons = [{ key: 'Delete', icon: 'trash', label: (_this$props$deleteLab = _this.props.deleteLabel) !== null && _this$props$deleteLab !== void 0 ? _this$props$deleteLab : LocaleUtils.tr("editing.delete") }]; deleteBar = /*#__PURE__*/React.createElement(ButtonBar, { buttons: deleteButtons, onClick: _this.deleteClicked }); } else { var _deleteButtons = [{ key: 'Yes', icon: 'ok', label: LocaleUtils.tr("editing.reallydelete"), extraClasses: "button-accept", disabled: captchaPending }, { key: 'No', icon: 'remove', label: LocaleUtils.tr("editing.canceldelete"), extraClasses: "button-reject" }]; deleteBar = /*#__PURE__*/React.createElement(ButtonBar, { buttons: _deleteButtons, onClick: _this.deleteFeature }); } } var busyDiv = null; if (_this.state.busy) { busyDiv = /*#__PURE__*/React.createElement("div", { className: "attrib-form-busy" }); } var childAttributeForm = null; if (_this.state.childEdit) { childAttributeForm = /*#__PURE__*/React.createElement("div", { className: "link-feature-form-container" }, /*#__PURE__*/React.createElement(LinkFeatureForm, _extends({}, _this.state.childEdit, { finished: _this.state.childEdit.finishCallback, iface: _this.props.iface, mapPrefix: _this.props.editContext.mapPrefix, pickFilter: _this.props.childPickFilter, readOnly: _this.props.readOnly, translations: _this.props.translations }))); } var captchaButton = null; if (captchaRequired && (_this.props.editContext.changed || _this.state.deleteClicked)) { captchaButton = /*#__PURE__*/React.createElement(ReCaptchaWidget, { onChange: function onChange(value) { return _this.setState({ captchaResponse: value }); }, sitekey: ConfigUtils.getConfigProp("editServiceCaptchaSiteKey") }); } var readOnlyMsg = null; if (!readOnly && _this.props.editContext.geomReadOnly) { readOnlyMsg = LocaleUtils.tr("editing.geomreadonly"); } else if (!readOnly && _this.props.editContext.geomNonZeroZ) { readOnlyMsg = LocaleUtils.tr("editing.geomnonzeroz"); } return /*#__PURE__*/React.createElement("div", { className: "AttributeForm" }, readOnlyMsg ? /*#__PURE__*/React.createElement("div", { className: "attrib-form-geom-readonly" }, readOnlyMsg) : null, /*#__PURE__*/React.createElement("form", { action: "", onChange: function onChange(ev) { return _this.formChanged(ev); }, onSubmit: _this.onSubmit, ref: _this.setupChangedObserver }, editConfig.form ? /*#__PURE__*/React.createElement(QtDesignerForm, { addRelationRecord: _this.addRelationRecord, editConfig: editConfig, editRelationRecord: _this.editRelationRecord, feature: _this.props.editContext.feature, iface: _this.props.iface, mapCrs: _this.props.map.projection, mapPrefix: _this.props.editContext.mapPrefix, readOnly: readOnly, removeRelationRecord: _this.removeRelationRecord, reorderRelationRecord: _this.reorderRelationRecord, report: _this.props.report, setFormBusy: _this.setFormBusy, setRelationTables: _this.setRelationTables, switchEditContext: _this.startChildEdit, translations: _this.props.translations, updateField: _this.updateField, updateRelationField: _this.updateRelationField }) : /*#__PURE__*/React.createElement(AutoEditForm, { editLayerId: editConfig.editDataset, fields: editConfig.fields, iface: _this.props.iface, readOnly: readOnly, touchFriendly: _this.props.touchFriendly, updateField: _this.updateField, values: _this.props.editContext.feature.properties }), captchaButton, commitBar), deleteBar, busyDiv, childAttributeForm); }); _defineProperty(_this, "setFormBusy", function (busy) { _this.setState({ busy: busy }); }); _defineProperty(_this, "updateField", function (key, value) { var newProperties = _objectSpread(_objectSpread({}, _this.props.editContext.feature.properties), {}, _defineProperty({}, key, value)); var newFeature = _objectSpread(_objectSpread({}, _this.props.editContext.feature), {}, { properties: newProperties }); _this.props.setEditContext(_this.props.editContext.id, { feature: newFeature, changed: true }); _this.validateFieldConstraints(newFeature); }); _defineProperty(_this, "setRelationTables", function (relationTables) { _this.setState({ relationTables: relationTables }); }); _defineProperty(_this, "loadRelationValues", function (feature, callback) { if (!isEmpty(_this.state.relationTables)) { if (feature.id) { var relTables = Object.entries(_this.state.relationTables).map(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), name = _ref2[0], entry = _ref2[1]; if (entry.sortcol) { return name + ":" + entry.fk + ":" + entry.sortcol; } else { return name + ":" + entry.fk; } }).join(","); var mapEditConfigs = _this.props.editConfigs[_this.props.editContext.mapPrefix]; _this.props.iface.getRelations(_this.props.editContext.editConfig, feature.id, _this.props.map.projection, relTables, mapEditConfigs, function (relationValues) { var newFeature = _objectSpread(_objectSpread({}, feature), {}, { relationValues: relationValues }); callback(newFeature); }); } else { var relationValues = _objectSpread(_objectSpread({}, Object.entries(_this.state.relationTables).reduce(function (res, _ref3) { var _ref4 = _slicedToArray(_ref3, 2), name = _ref4[0], entry = _ref4[1]; return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, name, { fk: entry.fk, features: [] })); }, {})), feature.relationValues); var newFeature = _objectSpread(_objectSpread({}, feature), {}, { relationValues: relationValues }); callback(newFeature); } } }); _defineProperty(_this, "addRelationRecord", function (table) { var newRelationValues = _objectSpread({}, _this.props.editContext.feature.relationValues); var editConfig = _this.props.editConfigs[_this.props.editContext.mapPrefix][table.split('.').slice(-1)]; getFeatureTemplate(editConfig, { type: "Feature", properties: {} }, _this.props.iface, _this.props.editContext.mapPrefix, _this.props.map.projection, function (newRelFeature) { newRelFeature.__status__ = "empty"; if (editConfig.geomType === null) { newRelFeature.geometry = null; } // If feature id is known, i.e. not when drawing new feature, set foreign key if (_this.props.editContext.action !== "Draw") { newRelFeature.properties[_this.state.relationTables[table].fk] = _this.props.editContext.feature.id; } newRelationValues[table] = _objectSpread({}, newRelationValues[table]); newRelationValues[table].features = newRelationValues[table].features.concat([newRelFeature]); var newFeature = _objectSpread(_objectSpread({}, _this.props.editContext.feature), {}, { relationValues: newRelationValues }); _this.props.setEditContext(_this.props.editContext.id, { feature: newFeature, changed: true }); }); }); _defineProperty(_this, "reorderRelationRecord", function (table, idx, dir) { var nFeatures = _this.props.editContext.feature.relationValues[table].features.length; if (dir < 0 && idx === 0 || dir > 0 && idx >= nFeatures - 1) { return; } var newRelationValues = _objectSpread({}, _this.props.editContext.feature.relationValues); newRelationValues[table] = _objectSpread({}, newRelationValues[table]); var newFeatures = newRelationValues[table].features.slice(0); var offset = dir < 0 ? 0 : 1; newFeatures.splice(idx - 1 + offset, 2, newFeatures[idx + offset], newFeatures[idx - 1 + offset]); newFeatures[idx - 1 + offset].properties = _objectSpread({}, newFeatures[idx - 1 + offset].properties); newFeatures[idx + offset].properties = _objectSpread({}, newFeatures[idx + offset].properties); newFeatures[idx - 1 + offset].__status__ = ["new", "empty"].includes(newFeatures[idx - 1 + offset].__status__) ? "new" : "changed"; newFeatures[idx + offset].__status__ = ["new", "empty"].includes(newFeatures[idx + offset].__status__) ? "new" : "changed"; newRelationValues[table].features = newFeatures; var newFeature = _objectSpread(_objectSpread({}, _this.props.editContext.feature), {}, { relationValues: newRelationValues }); _this.props.setEditContext(_this.props.editContext.id, { feature: newFeature, changed: true }); }); _defineProperty(_this, "removeRelationRecord", function (table, idx) { var newRelationValues = _objectSpread({}, _this.props.editContext.feature.relationValues); newRelationValues[table] = _objectSpread({}, newRelationValues[table]); newRelationValues[table].features = newRelationValues[table].features.slice(0); var fieldStatus = newRelationValues[table].features[idx].__status__ || ""; // If field was new, delete it directly, else mark it as deleted if (["new", "empty"].includes(fieldStatus)) { newRelationValues[table].features.splice(idx, 1); } else { newRelationValues[table].features[idx] = _objectSpread(_objectSpread({}, newRelationValues[table].features[idx]), {}, { __status__: fieldStatus.startsWith("deleted") ? fieldStatus.substr(8) : "deleted:" + fieldStatus }); } var newFeature = _objectSpread(_objectSpread({}, _this.props.editContext.feature), {}, { relationValues: newRelationValues }); _this.props.setEditContext(_this.props.editContext.id, { feature: newFeature, changed: true }); }); _defineProperty(_this, "updateRelationField", function (table, idx, key, value) { var newRelationValues = _objectSpread({}, _this.props.editContext.feature.relationValues); newRelationValues[table] = _objectSpread({}, newRelationValues[table]); newRelationValues[table].features = newRelationValues[table].features.slice(0); newRelationValues[table].features[idx] = _objectSpread(_objectSpread({}, newRelationValues[table].features[idx]), {}, { properties: _objectSpread(_objectSpread({}, newRelationValues[table].features[idx].properties), {}, _defineProperty({}, key, value)), __status__: ["new", "empty"].includes(newRelationValues[table].features[idx].__status__) ? "new" : "changed" }); var newFeature = _objectSpread(_objectSpread({}, _this.props.editContext.feature), {}, { relationValues: newRelationValues }); _this.props.setEditContext(_this.props.editContext.id, { feature: newFeature, changed: true }); }); _defineProperty(_this, "editRelationRecord", function (action, layer, table, idx, displayField) { var editConfig = _this.props.editConfigs[_this.props.editContext.mapPrefix][table.split('.').slice(-1)]; var feature = _this.props.editContext.feature.relationValues[table].features[idx]; var childEdit = { action: action, editConfig: editConfig, editContextId: ':' + layer, dataset: table, idx: idx, feature: feature, finishCallback: _this.finishEditRelationRecord, displayField: displayField, hideDelete: true }; _this.setState({ childEdit: childEdit }); }); _defineProperty(_this, "finishEditRelationRecord", function (feature) { _this.props.clearEditContext(_this.state.childEdit.editContextId, _this.props.editContext.id); if (feature) { var table = _this.state.childEdit.dataset; var idx = _this.state.childEdit.idx; var newRelationValues = _objectSpread({}, _this.props.editContext.feature.relationValues); newRelationValues[table] = _objectSpread({}, newRelationValues[table]); newRelationValues[table].features = newRelationValues[table].features.slice(0); newRelationValues[table].features[idx] = _objectSpread(_objectSpread({}, feature), {}, { properties: _objectSpread({}, feature.properties) }); // If feature id is known, i.e. not when drawing new feature, set foreign key var changed = _this.props.editContext.changed; var fk = _this.state.relationTables[table].fk; if (_this.props.editContext.action !== "Draw" && feature.properties[fk] !== _this.props.editContext.feature.id) { newRelationValues[table].features[idx].properties[fk] = _this.props.editContext.feature.id; newRelationValues[table].features[idx].__status__ = "changed"; changed = true; } var newFeature = _objectSpread(_objectSpread({}, _this.props.editContext.feature), {}, { relationValues: newRelationValues }); _this.props.setEditContext(_this.props.editContext.id, { feature: newFeature, changed: changed }); } _this.setState({ childEdit: null }); }); _defineProperty(_this, "onDiscard", function (action) { if (action === "Discard") { _this.props.setCurrentTaskBlocked(false); if (!_this.props.onDiscard || !_this.props.onDiscard()) { if (_this.props.editContext.action === 'Pick') { // Re-query the original feature _this.setState({ busy: true }); _this.props.iface.getFeatureById(_this.props.editContext.editConfig, _this.props.editContext.feature.id, _this.props.map.projection, function (feature) { _this.setState({ busy: false }); if (!isEmpty(_this.state.relationTables)) { // Re-load relation values _this.loadRelationValues(feature, function (newFeature) { _this.props.setEditContext(_this.props.editContext.id, { feature: newFeature, changed: false }); }); // Re-validate feature field constraints _this.validateFieldConstraints(feature); } else { _this.props.setEditContext(_this.props.editContext.id, { feature: feature, changed: false }); } }); } else { var featureSkel = { type: "Feature", properties: {} }; getFeatureTemplate(_this.props.editContext.editConfig, featureSkel, _this.props.iface, _this.props.editContext.mapPrefix, _this.props.map.projection, function (feature) { _this.props.setEditContext(_this.props.editContext.id, { feature: feature, changed: false }); }); } } } }); _defineProperty(_this, "setupChangedObserver", function (form) { _this.form = form; if (form) { form.observer = new MutationObserver(function () { _this.setState({ formValid: form.checkValidity() }); }); form.observer.observe(form, { subtree: true, childList: true, attributes: true }); } }); _defineProperty(_this, "formChanged", function (ev) { var _ev$target; var form = ev.currentTarget; if ((_ev$target = ev.target) !== null && _ev$target !== void 0 && _ev$target.setCustomValidity) { ev.target.setCustomValidity(""); } if (form) { _this.setState({ formValid: form.checkValidity() }); _this.props.setEditContext(_this.props.editContext.id, { changed: true }); } }); _defineProperty(_this, "validateFieldConstraints", function (feature) { var validCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var invalidCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var constraintExpressions = _this.props.editContext.editConfig.fields.reduce(function (res, cur) { var _cur$constraints; if ((_cur$constraints = cur.constraints) !== null && _cur$constraints !== void 0 && _cur$constraints.expression) { return [].concat(_toConsumableArray(res), [{ field: cur.id, expression: cur.constraints.expression }]); } return res; }, []); parseExpressionsAsync(constraintExpressions, feature, _this.props.editContext.editConfig, _this.props.iface, _this.props.editContext.mapPrefix, _this.props.map.projection, false).then(function (result) { var valid = true; var reasons = []; Object.entries(result).forEach(function (_ref5) { var _ref6 = _slicedToArray(_ref5, 2), key = _ref6[0], value = _ref6[1]; var element = _this.form.elements.namedItem(key); if (element) { if (value === false) { var _this$props$editConte, _this$props$editConte2; valid = false; var reason = (_this$props$editConte = (_this$props$editConte2 = _this.props.editContext.editConfig.fields.find(function (field) { return field.id === key; })) === null || _this$props$editConte2 === void 0 || (_this$props$editConte2 = _this$props$editConte2.constraints) === null || _this$props$editConte2 === void 0 ? void 0 : _this$props$editConte2.placeholder) !== null && _this$props$editConte !== void 0 ? _this$props$editConte : LocaleUtils.tr("editing.contraintviolation"); reasons.push(reason); element.setCustomValidity(reason); } else { element.setCustomValidity(""); } } }); if (!valid) { _this.setState({ formValid: false }); if (invalidCallback) { invalidCallback(reasons); } } else { if (validCallback) { validCallback(); } } }); }); _defineProperty(_this, "onSubmit", function (ev) { ev.preventDefault(); _this.validateFieldConstraints(_this.props.editContext.feature, _this.doSubmit, function (reasons) { /* eslint-disable-next-line */ alert(LocaleUtils.tr("editing.contraintviolation") + ":\n" + reasons.join("\n")); }); }); _defineProperty(_this, "doSubmit", function () { _this.setState({ busy: true }); var feature = _this.props.editContext.feature; // Ensure properties is not null feature = _objectSpread(_objectSpread({}, feature), {}, { type: "Feature", properties: _objectSpread({}, feature.properties || {}), crs: { type: "name", properties: { name: CoordinatesUtils.toOgcUrnCrs(_this.props.map.projection) } } }); // Omit geometry if it is read-only if (_this.props.editContext.geomReadOnly) { delete feature.geometry; } var editConfig = _this.props.editContext.editConfig; var textNullValue = ConfigUtils.getConfigProp("editTextNullValue"); // Keep relation values separate var relationValues = clone(feature.relationValues || {}); delete feature.relationValues; var relationUploads = {}; var featureUploads = {}; // Collect all values from form fields var fieldnames = Array.from(_this.form.elements).map(function (element) { return element.name; }).filter(function (x) { return x && x !== "g-recaptcha-response"; }); fieldnames.forEach(function (name) { var element = _this.form.elements.namedItem(name); if (element) { var parts = name.split("__"); var value = element.type === "radio" || element.type === "checkbox" ? element.checked : element.value; var nullElements = ["date", "number", "radio"]; var nullFieldTypes = ["date", "number"]; if (parts.length >= 3) { var _relEditConfig$fields, _relEditConfig$fields2, _relEditConfig$fields3; // Relation value // Usually <table>__<field>__<index>, but <field> might also contain __ (i.e. upload__user) var tablename = parts[0]; var datasetname = _this.props.editContext.mapPrefix + "." + tablename; var field = parts.slice(1, parts.length - 1).join("__"); var index = parseInt(parts[parts.length - 1], 10); var relEditConfig = _this.props.editConfigs[_this.props.editContext.mapPrefix][tablename]; var nrelFieldConfig = (_relEditConfig$fields = (_relEditConfig$fields2 = relEditConfig.fields) === null || _relEditConfig$fields2 === void 0 || (_relEditConfig$fields3 = _relEditConfig$fields2.find) === null || _relEditConfig$fields3 === void 0 ? void 0 : _relEditConfig$fields3.call(_relEditConfig$fields2, function (f) { return f.id === field; })) !== null && _relEditConfig$fields !== void 0 ? _relEditConfig$fields : {}; var nrelFieldDataType = nrelFieldConfig.type; if (nrelFieldConfig.expression) { // Skip virtual fields delete relationValues[datasetname].features[index][field]; return; } if ((element instanceof RadioNodeList || nullElements.includes(element.type) || nullFieldTypes.includes(nrelFieldDataType)) && element.value === "") { // Set empty value to null instead of empty string value = null; } if (nrelFieldDataType === "text" && textNullValue !== undefined && element.value === textNullValue) { // Convert text NULL to null value = null; } // relationValues for table must exist as rows are either pre-existing or were added if (!(field in relationValues[datasetname].features[index].properties)) { relationValues[datasetname].features[index].defaultedProperties = [].concat(_toConsumableArray(relationValues[datasetname].features[index].defaultedProperties || []), [field]); } relationValues[datasetname].features[index].properties[field] = value; if (relationValues[datasetname].features[index].__status__ === "empty") { relationValues[datasetname].features[index].__status__ = "new"; } if (element.type === "file" && element.files.length > 0) { relationUploads[name] = element.files[0]; relationValues[datasetname].features[index].properties[field] = ""; } else if (element.type === "hidden" && element.value.startsWith("data:")) { var filename = element.dataset.filename; var type = element.value.match(/image\/\w+/)[0]; if (!filename) { var ext = type.split("/")[1]; filename = uuidv4() + "." + ext; } relationUploads[name] = new File([_this.dataUriToBlob(element.value)], filename, { type: type }); relationValues[datasetname].features[index].properties[field] = ""; } } else { var fieldConfig = (editConfig.fields || []).find(function (field) { return field.id === name; }) || {}; if (fieldConfig.expression) { // Skip virtual fields delete feature.properties[name]; return; } var dataType = fieldConfig.type; if ((element instanceof RadioNodeList || nullElements.includes(element.type) || nullFieldTypes.includes(dataType)) && element.value === "") { // Set empty value to null instead of empty string value = null; } if (dataType === "text" && textNullValue !== undefined && element.value === textNullValue) { // Convert text NULL to null value = null; } if (!(name in feature.properties)) { feature.defaultedProperties = [].concat(_toConsumableArray(feature.defaultedProperties || []), [name]); } feature.properties[name] = value; if (element.type === "file" && element.files.length > 0) { featureUploads[name] = element.files[0]; feature.properties[name] = ""; } else if (element.type === "hidden" && element.value.startsWith("data:")) { var _filename = element.dataset.filename; var _type = element.value.match(/image\/\w+/)[0]; if (!_filename) { var _ext = _type.split("/")[1]; _filename = uuidv4() + "." + _ext; } featureUploads[name] = new File([_this.dataUriToBlob(element.value)], _filename, { type: _type }); feature.properties[name] = ""; } } } }); // Remove expression fields Object.keys(feature.properties).forEach(function (key) { var fieldConfig = editConfig.fields.find(function (field) { return field.id === key; }) || {}; if (fieldConfig !== null && fieldConfig !== void 0 && fieldConfig.expression) { delete feature.properties[key]; } }); Object.entries(relationValues).forEach(function (_ref7) { var _ref8 = _slicedToArray(_ref7, 2), dataset = _ref8[0], entry = _ref8[1]; var _dataset$split = dataset.split(".", 2), _dataset$split2 = _slicedToArray(_dataset$split, 2), mapName = _dataset$split2[0], layerName = _dataset$split2[1]; var relEditConfig = _this.props.editConfigs[mapName][layerName]; entry.features.forEach(function (f) { Object.keys(f.properties).forEach(function (key) { var fieldConfig = relEditConfig.fields.find(function (field) { return field.id === key; }) || {}; if (fieldConfig !== null && fieldConfig !== void 0 && fieldConfig.expression) { delete f.properties[key]; } }); }); }); // Set relation values CRS and sort index if necessary Object.keys(relationValues).forEach(function (relTable) { relationValues[relTable].features = relationValues[relTable].features.filter(function (relFeature) { return relFeature.__status__ !== "empty"; }).map(function (relFeature, idx) { var newRelFeature = _objectSpread(_objectSpread({}, relFeature), {}, { crs: { type: "name", properties: { name: CoordinatesUtils.toOgcUrnCrs(_this.props.map.projection) } } }); var sortcol = _this.state.relationTables[relTable].sortcol; var noreorder = _this.state.relationTables[relTable].noreorder; if (sortcol && !noreorder) { newRelFeature.__status__ = feature.__status__ || (newRelFeature.properties[sortcol] !== idx ? "changed" : ""); newRelFeature.properties[sortcol] = idx; } return newRelFeature; }); }); feature.relationValues = relationValues; var featureData = new FormData(); featureData.set('feature', JSON.stringify(feature)); Object.entries(featureUploads).forEach(function (_ref9) { var _ref10 = _slicedToArray(_ref9, 2), key = _ref10[0], value = _ref10[1]; return featureData.set('file:' + key, value); }); Object.entries(relationUploads).forEach(function (_ref11) { var _ref12 = _slicedToArray(_ref11, 2), key = _ref12[0], value = _ref12[1]; return featureData.set('relfile:' + _this.props.editContext.mapPrefix + "." + key, value); }); if (_this.state.captchaResponse) { featureData.set('g-recaptcha-response', _this.state.captchaResponse); } if (_this.props.editContext.action === "Draw") { if (_this.props.iface.addFeatureMultipart) { _this.props.iface.addFeatureMultipart(editConfig, _this.props.map.projection, featureData, function (success, result) { return _this.featureCommited(success, result); }); } else { _this.props.iface.addFeature(editConfig.editDataset, feature, _this.props.map.projection, function (success, result) { return _this.featureCommited(success, result); }); } } else if (_this.props.editContext.action === "Pick") { if (_this.props.iface.editFeatureMultipart) { _this.props.iface.editFeatureMultipart(editConfig, _this.props.map.projection, feature.id, featureData, function (success, result) { return _this.featureCommited(success, result); }); } else { _this.props.iface.editFeature(editConfig.editDataset, feature, _this.props.map.projection, function (success, result) { return _this.featureCommited(success, result); }); } } }); _defineProperty(_this, "featureCommited", function (success, result) { if (!success) { _this.commitFinished(false, result); return; } // Check for relation records which failed to commit var relationValueErrors = Object.values(result.relationValues || []).find(function (entry) { return (entry.features || []).find(function (f) { return f.error; }); }) !== undefined; if (relationValueErrors) { // Relation values commit failed, switch to pick to avoid adding feature again on next attempt _this.commitFinished(false, LocaleUtils.tr("editing.relationcommitfailed")); _this.props.setEditContext(_this.props.editContext.id, { action: "Pick", feature: result, changed: true }); } else { _this.commitFinished(true, result); } }); _defineProperty(_this, "deleteClicked", function () { _this.setState({ deleteClicked: true }); _this.props.setCurrentTaskBlocked(true, LocaleUtils.tr("editing.unsavedchanged")); }); _defineProperty(_this, "deleteFeature", function (action) { if (action === 'Yes') { _this.setState({ busy: true }); var recaptchaResponse = null; if (_this.state.captchaResponse) { recaptchaResponse = _this.state.captchaResponse; } _this.props.iface.deleteFeature(_this.props.editContext.editConfig, _this.props.editContext.feature.id, _this.deleteFinished, recaptchaResponse); } else { _this.setState({ deleteClicked: false }); _this.props.setCurrentTaskBlocked(false); } }); _defineProperty(_this, "commitFinished", function (success, result) { _this.setState({ busy: false }); if (success) { _this.props.refreshLayer(function (layer) { return layer.wms_name === _this.props.editContext.mapPrefix; }); _this.props.setCurrentTaskBlocked(false); if (!_this.props.onCommit || !_this.props.onCommit(result)) { if (!isEmpty(_this.state.relationTables)) { // Re-load relation values _this.loadRelationValues(result, function (newFeature) { _this.props.setEditContext(_this.props.editContext.id, { action: 'Pick', feature: newFeature, changed: false }); }); // Re-validate feature field constraints _this.validateFieldConstraints(result); } else { _this.props.setEditContext(_this.props.editContext.id, { action: 'Pick', feature: result, changed: false }); } } } else { // eslint-disable-next-line alert(result); } }); _defineProperty(_this, "deleteFinished", function (success, result) { _this.setState({ busy: false }); if (success) { _this.setState({ deleteClicked: false }); _this.props.setCurrentTaskBlocked(false); _this.props.refreshLayer(function (layer) { return layer.wms_name === _this.props.editContext.mapPrefix; }); if (!_this.props.onDelete || !_this.props.onDelete(result)) { _this.props.setEditContext(_this.props.editContext.id, { feature: null, changed: false }); } } else { // eslint-disable-next-line alert(result); } }); _defineProperty(_this, "dataUriToBlob", function (dataUri) { var parts = dataUri.split(','); var byteString = parts[0].indexOf('base64') >= 0 ? atob(parts[1]) : decodeURI(parts[1]); var mimeString = parts[0].split(':')[1].split(';')[0]; var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], { type: mimeString }); }); _defineProperty(_this, "startChildEdit", function (action, layer, featureId, updateField, displayField) { var editConfig = _this.props.editConfigs[_this.props.editContext.mapPrefix][layer]; if (!editConfig) { // eslint-disable-next-line console.warn("No edit config found for linked edit layer " + layer); } else { var childEdit = { action: action, editConfig: editConfig, editContextId: ':' + layer, displayField: displayField, featureId: featureId, updateField: updateField, finishCallback: _this.finishChildEdit }; _this.setState({ childEdit: childEdit }); } }); _defineProperty(_this, "finishChildEdit", function (feature) { _this.props.clearEditContext(_this.state.childEdit.editContextId, _this.props.editContext.id); if (feature && feature.id !== _this.state.childEdit.featureId) { _this.state.childEdit.updateField(feature.id); } _this.setState({ childEdit: null }); }); _this.form = null; return _this; } _inherits(AttributeForm, _React$Component); return _createClass(AttributeForm, [{ key: "componentDidUpdate", value: function componentDidUpdate(prevProps, prevState) { var _this2 = this; if (prevProps.editContext.changed !== this.props.editContext.changed) { this.props.setCurrentTaskBlocked(this.props.editContext.changed === true, LocaleUtils.tr("editing.unsavedchanged")); } if ((!this.props.editContext.feature || this.props.editContext.changed) && this.state.deleteClicked) { this.setState({ deleteClicked: false }); } // Reload relation values if necessary var feature = this.props.editContext.feature; var prevFeature = prevProps.editContext.feature; if ((!this.props.editContext.changed || !feature.relationValues) && (this.state.relationTables !== prevState.relationTables || feature.id !== (prevFeature || {}).id)) { this.loadRelationValues(this.props.editContext.feature, function (newFeature) { _this2.props.setEditContext(_this2.props.editContext.id, { feature: newFeature }); }); // Re-validate feature field constraints this.validateFieldConstraints(this.props.editContext.feature); } } }]); }(React.Component); _defineProperty(AttributeForm, "propTypes", { childPickFilter: PropTypes.func, clearEditContext: PropTypes.func, deleteLabel: PropTypes.string, editConfigs: PropTypes.object, editContext: PropTypes.object, hideDelete: PropTypes.bool, iface: PropTypes.object, map: PropTypes.object, onCommit: PropTypes.func, onDelete: PropTypes.func, onDiscard: PropTypes.func, readOnly: PropTypes.bool, refreshLayer: PropTypes.func, report: PropTypes.bool, setCurrentTaskBlocked: PropTypes.func, setEditContext: PropTypes.func, theme: PropTypes.object, touchFriendly: PropTypes.bool, translations: PropTypes.object }); _defineProperty(AttributeForm, "defaultProps", { touchFriendly: true }); export default connect(function (state) { return { map: state.map, editConfigs: state.layers.editConfigs, theme: state.theme.current }; }, { clearEditContext: clearEditContext, setEditContext: setEditContext, setCurrentTaskBlocked: setCurrentTaskBlocked, refreshLayer: refreshLayer })(AttributeForm);