Tomas Bures

Some fixes.

Added Import/Export to panel param settings.
... ... @@ -149,7 +149,7 @@ class Form extends Component {
class Fieldset extends Component {
static propTypes = {
id: PropTypes.string,
label: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
flat: PropTypes.bool,
className: PropTypes.string
... ...
... ... @@ -93,7 +93,7 @@ export class RestActionModalDialog extends Component {
const t = this.props.t;
return (
<ModalDialog hidden={!this.props.visible} title={this.props.title} onCloseAsync={() => this.hideModal(true)} buttons={[
<ModalDialog hidden={!this.props.visible} title={this.props.title} onCloseAsync={async () => this.hideModal(true)} buttons={[
{ label: t('no'), className: 'btn-primary', onClickAsync: async () => this.hideModal(true) },
{ label: t('yes'), className: 'btn-danger', onClickAsync: ::this.performAction }
]}>
... ...
... ... @@ -187,3 +187,10 @@
}
}
.fieldsetToolbar {
float: right;
:global .btn {
padding: 3px 10px;
}
}
\ No newline at end of file
... ...
... ... @@ -107,7 +107,7 @@ export default class FieldTypes {
populateFields(data, signals) {
for (const sigSpec of this.signalsVisibleForEdit) {
this.fieldTypes[sigSpec.type].populateFields(sigSpec, data, (signals && signals[sigSpec.cid]) || null, this.getFormId(sigSpec.cid));
this.fieldTypes[sigSpec.type].populateFields(sigSpec, data, signals && sigSpec.cid in signals ? signals[sigSpec.cid] : null, this.getFormId(sigSpec.cid));
}
}
... ...
... ... @@ -5,10 +5,11 @@ import PropTypes
from "prop-types";
import {
LinkButton,
requiresAuthenticatedUser,
requiresAuthenticatedUser, Toolbar,
withPageHelpers
} from "../../../lib/page";
import {
ACEEditor,
Button,
ButtonRow,
Dropdown,
... ... @@ -43,6 +44,91 @@ import ParamTypes
from "./ParamTypes"
import {withComponentMixins} from "../../../lib/decorator-helpers";
import {withTranslation} from "../../../lib/i18n";
import {ModalDialog} from "../../../lib/bootstrap-components";
import styles from "../../../lib/styles.scss"
@withComponentMixins([
withTranslation,
withForm
])
export class ImportExportModalDialog extends Component {
constructor(props) {
super(props);
this.state = {};
this.initForm();
}
static propTypes = {
visible: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
onImport: PropTypes.func.isRequired,
onExport: PropTypes.func.isRequired
}
localValidateFormValues(state) {
const t = this.props.t;
state.setIn(['code', 'error'], null);
const codeStr = state.getIn(['code', 'value']);
if (!codeStr) {
state.setIn(['code', 'error'], t('JSON code must not be empty'));
} else {
try {
const code = JSON.parse(codeStr);
} catch (err) {
state.setIn(['code', 'error'], t('Syntax error in the JSON code'));
}
}
}
doClose() {
this.props.onClose();
}
componentDidMount() {
this.populateFormValues({
code: this.props.visible ? this.props.onExport() : ''
});
}
componentDidUpdate(prevProps, prevState) {
if (!prevProps.visible && this.props.visible) {
const code = this.props.onExport();
this.updateFormValue('code', code);
}
}
doImport() {
if (this.isFormWithoutErrors()) {
const code = JSON.parse(this.getFormValue('code'));
this.props.onImport(code);
} else {
this.showFormValidation();
}
}
render() {
const t = this.props.t;
return (
<ModalDialog hidden={!this.props.visible} title={t('Import panel settings')} onCloseAsync={async () => this.doClose()} buttons={[
{ label: t('Close'), className: 'btn-primary', onClickAsync: async () => this.doClose() },
{ label: t('Import'), className: 'btn-danger', onClickAsync: async () => this.doImport() }
]}>
<Form stateOwner={this}>
<ACEEditor id="code" mode="json" format="wide"/>
</Form>
</ModalDialog>
);
}
}
@withComponentMixins([
withTranslation,
... ... @@ -55,7 +141,9 @@ export default class CUD extends Component {
constructor(props) {
super(props);
this.state = {};
this.state = {
importExportModalShown: false
};
this.initForm({
onChangeBeforeValidation: ::this.onChangeBeforeValidation,
... ... @@ -239,6 +327,23 @@ export default class CUD extends Component {
return (
<Panel title={isEdit ? t('Edit Panel') : t('Create Panel')}>
<ImportExportModalDialog
visible={this.state.importExportModalShown}
onClose={() => {
this.setState({importExportModalShown: false});
}}
onExport={() => {
const data = this.getFormValues();
const params = this.paramTypes.getParams(configSpec, data);
return JSON.stringify(params, null, 2);
}}
onImport={code => {
const data = {};
this.paramTypes.setFields(configSpec, code, data);
this.populateFormValues(data);
this.setState({importExportModalShown: false});
}}
/>
{canDelete &&
<DeleteModalDialog
stateOwner={this}
... ... @@ -262,9 +367,20 @@ export default class CUD extends Component {
{configSpec ?
params &&
<Fieldset label={t('Panel parameters')}>
{params}
</Fieldset>
<>
<Fieldset
label={
<div>
<Toolbar className={styles.fieldsetToolbar}>
<Button className="btn-primary" label={t('Import / Export')} onClickAsync={ async () => this.setState({importExportModalShown: true}) }/>
</Toolbar>
<span>{t('Panel parameters')}</span>
</div>
}
>
{params}
</Fieldset>
</>
:
this.getFormValue('template') &&
<div className="alert alert-info" role="alert">{t('Loading template...')}</div>
... ...
... ... @@ -13,6 +13,7 @@ const shares = require('./shares');
const signals = require('./signals');
const { IndexingStatus, IndexMethod } = require('../../shared/signals');
const {parseCardinality, getFieldsetPrefix, resolveAbs} = require('../../shared/templates');
const log = require('../lib/log');
const allowedKeysCreate = new Set(['cid', 'name', 'description', 'namespace', 'record_id_template']);
const allowedKeysUpdate = new Set(['name', 'description', 'namespace', 'record_id_template']);
... ... @@ -447,6 +448,7 @@ async function query(context, queries) {
for (const sigCid of signals) {
const sig = signalMap[sigCid];
if (!sig) {
log.verbose(`unknown signal ${sigSet.cid}.${sigCid}`);
shares.throwPermissionDenied();
}
... ...
... ... @@ -263,7 +263,7 @@ async function remove(context, id) {
const signalSet = await tx('signal_sets').where('id', existing.set).first();
if (RawSignalTypes.has(existing.type)) {
await updateSignalSetStatus(tx, signalSet, await signalStorage.removeField(signalSet.cid, existing.cid));
await updateSignalSetStatus(tx, signalSet, await signalStorage.removeField(signalSet, existing.id));
}
await tx('signals').where('id', id).del();
... ...
... ... @@ -4,7 +4,7 @@ const elasticsearch = require('../lib/elasticsearch');
const knex = require('../lib/knex');
const {getIndexName, getFieldName, createIndex} = require('../lib/indexers/elasticsearch-common');
const {getTableName, getColumnName} = require('../models/signal-storage');
const {IndexingStatus, deserializeFromDb, IndexMethod} = require('../../shared/signals');
const {IndexingStatus, deserializeFromDb, IndexMethod, RawSignalTypes} = require('../../shared/signals');
const log = require('../lib/log');
const signalSets = require('../models/signal-sets');
... ... @@ -118,7 +118,9 @@ async function index(cid, method) {
const esDoc = {};
for (const fieldCid in signalByCidMap) {
const field = signalByCidMap[fieldCid];
esDoc[getFieldName(field.id)] = deserializeFromDb[field.type](row[getColumnName(field.id)]);
if (RawSignalTypes.has(field.type)) {
esDoc[getFieldName(field.id)] = deserializeFromDb[field.type](row[getColumnName(field.id)]);
}
}
bulk.push(esDoc);
... ...