qwc2
Version:
QGIS Web Client
960 lines (955 loc) • 47.3 kB
JavaScript
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);