Some small updates coming from IVIS
This commit is contained in:
parent
4943b22a51
commit
e85c707973
14 changed files with 4319 additions and 2975 deletions
|
@ -174,7 +174,7 @@ function createApp(trusted) {
|
|||
}));
|
||||
|
||||
if (trusted) {
|
||||
passport.setup(app);
|
||||
passport.setupRegularAuth(app);
|
||||
} else {
|
||||
app.use(passport.tryAuthByRestrictedAccessToken);
|
||||
}
|
||||
|
|
2871
client/package-lock.json
generated
2871
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
client/src/lib/bootstrap-components.js
vendored
22
client/src/lib/bootstrap-components.js
vendored
|
@ -97,7 +97,8 @@ class Button extends Component {
|
|||
|
||||
class DropdownMenu extends Component {
|
||||
static propTypes = {
|
||||
label: PropTypes.string,
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
noCaret: PropTypes.bool,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
|
@ -109,10 +110,17 @@ class DropdownMenu extends Component {
|
|||
className = className + ' ' + props.className;
|
||||
}
|
||||
|
||||
let label;
|
||||
if (this.props.noCaret) {
|
||||
label = props.label;
|
||||
} else {
|
||||
label = <span>{props.label}{' '}<span className="caret"></span></span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="btn-group">
|
||||
<button type="button" className={className} data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{props.label}{' '}<span className="caret"></span>
|
||||
{label}
|
||||
</button>
|
||||
<ul className="dropdown-menu">
|
||||
{props.children}
|
||||
|
@ -140,7 +148,15 @@ class DropdownMenuItem extends Component {
|
|||
|
||||
return (
|
||||
<li className={className}>
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{props.icon && <Icon icon={props.icon}/>}{props.label}{' '}<span className="caret"></span></a>
|
||||
{props.icon ?
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<Icon icon={props.icon}/>{' '}{props.label}{' '}<span className="caret"></span>
|
||||
</a>
|
||||
:
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
{props.label}{' '}<span className="caret"></span>
|
||||
</a>
|
||||
}
|
||||
<ul className="dropdown-menu">
|
||||
{props.children}
|
||||
</ul>
|
||||
|
|
|
@ -27,6 +27,7 @@ import styles from "./styles.scss";
|
|||
import moment from "moment";
|
||||
import {getUrl} from "./urls";
|
||||
|
||||
|
||||
const FormState = {
|
||||
Loading: 0,
|
||||
LoadingWithNotice: 1,
|
||||
|
@ -76,7 +77,7 @@ class Form extends Component {
|
|||
const statusMessageText = owner.getFormStatusMessageText();
|
||||
const statusMessageSeverity = owner.getFormStatusMessageSeverity();
|
||||
|
||||
let formClass = 'form-horizontal';
|
||||
let formClass = `form-horizontal ${styles.form} `;
|
||||
if (props.format === 'wide') {
|
||||
formClass = '';
|
||||
} else if (props.format === 'inline') {
|
||||
|
@ -111,6 +112,7 @@ class Fieldset extends Component {
|
|||
id: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
flat: PropTypes.bool
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
|
@ -123,7 +125,7 @@ class Fieldset extends Component {
|
|||
const id = this.props.id;
|
||||
const htmlId = 'form_' + id;
|
||||
|
||||
const className = id ? owner.addFormValidationClass('', id) : '';
|
||||
const className = id ? owner.addFormValidationClass('', id) : null;
|
||||
|
||||
let helpBlock = null;
|
||||
if (this.props.help) {
|
||||
|
@ -141,7 +143,7 @@ class Fieldset extends Component {
|
|||
return (
|
||||
<fieldset className={className}>
|
||||
{props.label ? <legend>{props.label}</legend> : null}
|
||||
<div className="fieldset-content">
|
||||
<div className={props.flat ? 'fieldset-content fieldset-content-flat' : 'fieldset-content'}>
|
||||
{props.children}
|
||||
{helpBlock}
|
||||
{validationBlock}
|
||||
|
@ -1081,6 +1083,49 @@ function withForm(target) {
|
|||
scheduleValidateForm(this);
|
||||
};
|
||||
|
||||
inst.updateForm = function(mutator) {
|
||||
this.setState(previousState => {
|
||||
const onChangeBeforeValidationCallback = this.state.formSettings.onChangeBeforeValidation || {};
|
||||
|
||||
const formState = previousState.formState.withMutations(mutState => {
|
||||
mutState.update('data', stateData => stateData.withMutations(mutStateData => {
|
||||
mutator(mutStateData);
|
||||
|
||||
if (typeof onChangeBeforeValidationCallback === 'object') {
|
||||
for (const key in onChangeBeforeValidationCallback) {
|
||||
const oldValue = previousState.formState.getIn(['data', key, 'value']);
|
||||
const newValue = mutStateData.getIn([key, 'value']);
|
||||
onChangeBeforeValidationCallback[key](mutStateData, key, oldValue, newValue);
|
||||
}
|
||||
} else {
|
||||
onChangeBeforeValidationCallback(mutStateData);
|
||||
}
|
||||
}));
|
||||
|
||||
validateFormState(this, mutState);
|
||||
});
|
||||
|
||||
let newState = {
|
||||
formState
|
||||
};
|
||||
|
||||
|
||||
const onChangeCallback = this.state.formSettings.onChange || {};
|
||||
|
||||
if (typeof onChangeCallback === 'object') {
|
||||
for (const key in onChangeCallback) {
|
||||
const oldValue = previousState.formState.getIn(['data', key, 'value']);
|
||||
const newValue = formState.getIn(['data', key, 'value']);
|
||||
onChangeCallback[key](newState, key, oldValue, newValue);
|
||||
}
|
||||
} else {
|
||||
onChangeCallback(newState);
|
||||
}
|
||||
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
|
||||
inst.updateFormValue = function(key, value) {
|
||||
this.setState(previousState => {
|
||||
const oldValue = previousState.formState.getIn(['data', key, 'value']);
|
||||
|
|
|
@ -7,9 +7,11 @@ import ReactDOM from 'react-dom';
|
|||
import {I18nextProvider,} from 'react-i18next';
|
||||
import i18n from './i18n';
|
||||
import {MosaicoSandbox} from './mosaico';
|
||||
import {UntrustedContentRoot} from './untrusted';
|
||||
import {UntrustedContentRoot, parentRPC} from './untrusted';
|
||||
|
||||
export default function() {
|
||||
parentRPC.init();
|
||||
|
||||
ReactDOM.render(
|
||||
<I18nextProvider i18n={ i18n }>
|
||||
<UntrustedContentRoot render={props => <MosaicoSandbox {...props} />} />
|
||||
|
|
|
@ -5,7 +5,7 @@ import {translate} from 'react-i18next';
|
|||
import PropTypes from "prop-types";
|
||||
import styles from "./mosaico.scss";
|
||||
|
||||
import {UntrustedContentHost} from './untrusted';
|
||||
import {UntrustedContentHost, parentRPC} from './untrusted';
|
||||
import {Icon} from "./bootstrap-components";
|
||||
import {
|
||||
getSandboxUrl,
|
||||
|
@ -101,7 +101,19 @@ export class MosaicoSandbox extends Component {
|
|||
initialMetadata: PropTypes.string
|
||||
}
|
||||
|
||||
async exportState(method, params) {
|
||||
const sandboxUrlBase = getSandboxUrl();
|
||||
const trustedUrlBase = getTrustedUrl();
|
||||
return {
|
||||
html: unbase(this.viewModel.exportHTML(), trustedUrlBase, sandboxUrlBase, true),
|
||||
model: unbase(this.viewModel.exportJSON(), trustedUrlBase, sandboxUrlBase),
|
||||
metadata: unbase(this.viewModel.exportMetadata(), trustedUrlBase, sandboxUrlBase)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
parentRPC.setMethodHandler('exportState', ::this.exportState);
|
||||
|
||||
if (!Mosaico.isCompatible()) {
|
||||
alert('Update your browser!');
|
||||
return;
|
||||
|
@ -151,23 +163,8 @@ export class MosaicoSandbox extends Component {
|
|||
Mosaico.start(config, template, metadata, model, allPlugins);
|
||||
}
|
||||
|
||||
async onMethodAsync(method, params) {
|
||||
if (method === 'exportState') {
|
||||
const sandboxUrlBase = getSandboxUrl();
|
||||
const trustedUrlBase = getTrustedUrl();
|
||||
return {
|
||||
html: unbase(this.viewModel.exportHTML(), trustedUrlBase, sandboxUrlBase, true),
|
||||
model: unbase(this.viewModel.exportJSON(), trustedUrlBase, sandboxUrlBase),
|
||||
metadata: unbase(this.viewModel.exportMetadata(), trustedUrlBase, sandboxUrlBase)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div/>;
|
||||
}
|
||||
}
|
||||
|
||||
MosaicoSandbox.prototype.onMethodAsync = async function(method, params) {
|
||||
return await this.getWrappedInstance().onMethodAsync(method, params);
|
||||
};
|
||||
|
|
|
@ -1,17 +1,31 @@
|
|||
:global .DayPicker {
|
||||
border-left: 1px solid lightgray;
|
||||
border-right: 1px solid lightgray;
|
||||
border-bottom: 1px solid lightgray;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
.form { // This is here to give the styles below higher priority than Bootstrap has
|
||||
:global .DayPicker {
|
||||
border-left: 1px solid lightgray;
|
||||
border-right: 1px solid lightgray;
|
||||
border-bottom: 1px solid lightgray;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
:global .form-horizontal .control-label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:global .form-control[disabled] {
|
||||
cursor: default;
|
||||
background-color: #eeeeee;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:global .ace_editor {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.dayPickerWrapper {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.buttonRow > * {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
@ -20,6 +34,11 @@
|
|||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.formDisabled {
|
||||
background-color: #eeeeee;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.formStatus {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
|
@ -33,25 +52,6 @@
|
|||
margin-right: 0px;
|
||||
}
|
||||
|
||||
:global .form-horizontal .control-label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.formDisabled {
|
||||
background-color: #eeeeee;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:global .form-control[disabled] {
|
||||
cursor: default;
|
||||
background-color: #eeeeee;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:global .ace_editor {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.tableSelectDropdown {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
@ -65,6 +65,7 @@
|
|||
background-color: white;
|
||||
}
|
||||
|
||||
|
||||
:global h3.legend {
|
||||
font-size: 21px;
|
||||
margin-bottom: 20px;
|
||||
|
|
|
@ -324,11 +324,12 @@ class Table extends Component {
|
|||
this.fetchAndNotifySelectionData();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.props.data) {
|
||||
this.table.clear();
|
||||
this.table.rows.add(this.props.data);
|
||||
} else {
|
||||
// XXX: Changing URL changing from data to dataUrl is not implemented
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
setRestrictedAccessToken
|
||||
} from "./urls";
|
||||
|
||||
@translate(null, { withRef: true })
|
||||
@withPageHelpers
|
||||
@withErrorHandling
|
||||
@requiresAuthenticatedUser
|
||||
|
@ -48,16 +47,16 @@ export class UntrustedContentHost extends Component {
|
|||
tokenMethod: PropTypes.string,
|
||||
tokenParams: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
singleToken: PropTypes.bool
|
||||
singleToken: PropTypes.bool,
|
||||
onMethodAsync: PropTypes.func
|
||||
}
|
||||
|
||||
isInitialized() {
|
||||
return !!this.accessToken && !!this.props.contentProps;
|
||||
}
|
||||
|
||||
receiveMessage(evt) {
|
||||
async receiveMessage(evt) {
|
||||
const msg = evt.data;
|
||||
console.log(msg);
|
||||
|
||||
if (msg.type === 'initNeeded') {
|
||||
if (this.isInitialized()) {
|
||||
|
@ -69,6 +68,11 @@ export class UntrustedContentHost extends Component {
|
|||
} else if (msg.type === 'rpcResponse') {
|
||||
const resolve = this.rpcResolves.get(msg.data.msgId);
|
||||
resolve(msg.data.ret);
|
||||
} else if (msg.type === 'rpcRequest') {
|
||||
const ret = await this.props.onMethodAsync(msg.data.method, msg.data.params);
|
||||
} else if (msg.type === 'clientHeight') {
|
||||
const newHeight = msg.data;
|
||||
this.contentNode.height = newHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,18 +161,12 @@ export class UntrustedContentHost extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
|
||||
return (
|
||||
<iframe className={styles.untrustedContent + ' ' + this.props.className} ref={node => this.contentNode = node} src={getSandboxUrl(this.props.contentSrc)} onLoad={::this.contentNodeLoaded}> </iframe>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UntrustedContentHost.prototype.ask = async function(method, params) {
|
||||
return await this.getWrappedInstance().ask(method, params);
|
||||
};
|
||||
|
||||
|
||||
@translate()
|
||||
export class UntrustedContentRoot extends Component {
|
||||
|
@ -180,6 +178,11 @@ export class UntrustedContentRoot extends Component {
|
|||
};
|
||||
|
||||
this.receiveMessageHandler = ::this.receiveMessage;
|
||||
|
||||
this.periodicTimeoutHandler = ::this.periodicTimeoutHandler;
|
||||
this.periodicTimeoutId = 0;
|
||||
|
||||
this.clientHeight = 0;
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
|
@ -187,9 +190,18 @@ export class UntrustedContentRoot extends Component {
|
|||
}
|
||||
|
||||
|
||||
async periodicTimeoutHandler() {
|
||||
const newHeight = document.body.clientHeight;
|
||||
if (this.clientHeight !== newHeight) {
|
||||
this.clientHeight = newHeight;
|
||||
this.sendMessage('clientHeight', newHeight);
|
||||
}
|
||||
this.periodicTimeoutId = setTimeout(this.periodicTimeoutHandler, 250);
|
||||
}
|
||||
|
||||
|
||||
async receiveMessage(evt) {
|
||||
const msg = evt.data;
|
||||
console.log(msg);
|
||||
|
||||
if (msg.type === 'initAvailable' && !this.state.initialized) {
|
||||
this.sendMessage('initNeeded');
|
||||
|
@ -203,9 +215,6 @@ export class UntrustedContentRoot extends Component {
|
|||
|
||||
} else if (msg.type === 'accessToken') {
|
||||
setRestrictedAccessToken(msg.data);
|
||||
} else if (msg.type === 'rpcRequest') {
|
||||
const ret = await this.contentNode.onMethodAsync(msg.data.method, msg.data.params);
|
||||
this.sendMessage('rpcResponse', {msgId: msg.data.msgId, ret});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,23 +224,20 @@ export class UntrustedContentRoot extends Component {
|
|||
|
||||
componentDidMount() {
|
||||
window.addEventListener('message', this.receiveMessageHandler, false);
|
||||
this.periodicTimeoutId = setTimeout(this.periodicTimeoutHandler, 0);
|
||||
this.sendMessage('initNeeded');
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('message', this.receiveMessageHandler, false);
|
||||
clearTimeout(this.periodicTimeoutId);
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
|
||||
const props = {
|
||||
...this.state.contentProps,
|
||||
ref: node => this.contentNode = node
|
||||
};
|
||||
|
||||
if (this.state.initialized) {
|
||||
return this.props.render(props);
|
||||
return this.props.render(this.state.contentProps);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
|
@ -240,4 +246,83 @@ export class UntrustedContentRoot extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ParentRPC {
|
||||
constructor(props) {
|
||||
this.receiveMessageHandler = ::this.receiveMessage;
|
||||
|
||||
this.rpcCounter = 0;
|
||||
this.rpcResolves = new Map();
|
||||
this.methodHandlers = new Map();
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
init() {
|
||||
window.addEventListener('message', this.receiveMessageHandler, false);
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
setMethodHandler(method, handler) {
|
||||
this.enforceInitialized();
|
||||
this.methodHandlers.set(method, handler);
|
||||
}
|
||||
|
||||
clearMethodHandler(method) {
|
||||
this.enforceInitialized();
|
||||
this.methodHandlers.delete(method);
|
||||
}
|
||||
|
||||
async ask(method, params) {
|
||||
this.enforceInitialized();
|
||||
this.rpcCounter += 1;
|
||||
const msgId = this.rpcCounter;
|
||||
|
||||
this.sendMessage('rpcRequest', {
|
||||
method,
|
||||
params,
|
||||
msgId
|
||||
});
|
||||
|
||||
return await (new Promise((resolve, reject) => {
|
||||
this.rpcResolves.set(msgId, resolve);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private methods
|
||||
|
||||
enforceInitialized() {
|
||||
if (!this.initialized) {
|
||||
throw new Error('ParentRPC not initialized');
|
||||
}
|
||||
}
|
||||
|
||||
async receiveMessage(evt) {
|
||||
const msg = evt.data;
|
||||
|
||||
if (msg.type === 'rpcResponse') {
|
||||
const resolve = this.rpcResolves.get(msg.data.msgId);
|
||||
resolve(msg.data.ret);
|
||||
|
||||
} else if (msg.type === 'rpcRequest') {
|
||||
let ret;
|
||||
|
||||
const method = msg.data.method;
|
||||
if (this.methodHandlers.has(method)) {
|
||||
const handler = this.methodHandlers.get(method);
|
||||
ret = await handler(method, msg.data.params);
|
||||
}
|
||||
|
||||
this.sendMessage('rpcResponse', {msgId: msg.data.msgId, ret});
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(type, data) {
|
||||
window.parent.postMessage({type, data}, getTrustedUrl());
|
||||
}
|
||||
}
|
||||
|
||||
export const parentRPC = new ParentRPC();
|
|
@ -134,7 +134,8 @@ module.exports.tryAuthByRestrictedAccessToken = (req, res, next) => {
|
|||
});
|
||||
};
|
||||
|
||||
module.exports.setup = app => {
|
||||
|
||||
module.exports.setupRegularAuth = app => {
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
};
|
||||
|
|
|
@ -424,11 +424,14 @@ function checkGlobalPermission(context, requiredOperations) {
|
|||
}
|
||||
|
||||
if (context.user.restrictedAccessHandler) {
|
||||
log.verbose('check global permissions with restrictedAccessHandler -- requiredOperations: ' + requiredOperations);
|
||||
const originalRequiredOperations = requiredOperations;
|
||||
const allowedPerms = context.user.restrictedAccessHandler.globalPermissions;
|
||||
if (allowedPerms) {
|
||||
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
||||
} else {
|
||||
requiredOperations = [];
|
||||
}
|
||||
log.verbose('check global permissions with restrictedAccessHandler -- requiredOperations: [' + originalRequiredOperations + '] -> [' + requiredOperations + ']');
|
||||
}
|
||||
|
||||
if (requiredOperations.length === 0) {
|
||||
|
@ -471,13 +474,28 @@ async function _checkPermissionTx(tx, context, entityTypeId, entityId, requiredO
|
|||
}
|
||||
|
||||
if (context.user.restrictedAccessHandler) {
|
||||
log.verbose('check permissions with restrictedAccessHandler -- entityTypeId: ' + entityTypeId + ' entityId: ' + entityId + ' requiredOperations: ' + requiredOperations);
|
||||
if (context.user.restrictedAccessHandler.permissions && context.user.restrictedAccessHandler.permissions[entityTypeId]) {
|
||||
const allowedPerms = context.user.restrictedAccessHandler.permissions[entityTypeId][entityId];
|
||||
if (allowedPerms) {
|
||||
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
||||
const originalRequiredOperations = requiredOperations;
|
||||
if (context.user.restrictedAccessHandler.permissions) {
|
||||
const entityPerms = context.user.restrictedAccessHandler.permissions[entityTypeId];
|
||||
|
||||
if (!entityPerms) {
|
||||
requiredOperations = [];
|
||||
} else if (entityPerms === true) {
|
||||
// no change to require operations
|
||||
} else if (entityPerms instanceof Set) {
|
||||
requiredOperations = requiredOperations.filter(perm => entityPerms.has(perm));
|
||||
} else {
|
||||
const allowedPerms = entityPerms[entityId];
|
||||
if (allowedPerms) {
|
||||
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
||||
} else {
|
||||
requiredOperations = [];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requiredOperations = [];
|
||||
}
|
||||
log.verbose('check permissions with restrictedAccessHandler -- entityTypeId: ' + entityTypeId + ' entityId: ' + entityId + ' requiredOperations: [' + originalRequiredOperations + '] -> [' + requiredOperations + ']');
|
||||
}
|
||||
|
||||
if (requiredOperations.length === 0) {
|
||||
|
|
4090
package-lock.json
generated
4090
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -88,7 +88,7 @@
|
|||
"mailparser": "^2.0.5",
|
||||
"marked": "^0.3.9",
|
||||
"memory-cache": "^0.2.0",
|
||||
"mjml": "3.3.5",
|
||||
"mjml": "^4.0.5",
|
||||
"mkdirp": "^0.5.1",
|
||||
"moment": "^2.18.1",
|
||||
"moment-timezone": "^0.5.13",
|
||||
|
|
5
shared/package-lock.json
generated
5
shared/package-lock.json
generated
|
@ -14,11 +14,12 @@
|
|||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz",
|
||||
"integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=",
|
||||
"requires": {
|
||||
"moment": "2.20.1"
|
||||
"moment": ">= 2.9.0"
|
||||
}
|
||||
},
|
||||
"owasp-password-strength-test": {
|
||||
"version": "github:bures/owasp-password-strength-test#50bfcf0035b1468b9d03a00eaf561d4fed4973eb"
|
||||
"version": "github:bures/owasp-password-strength-test#50bfcf0035b1468b9d03a00eaf561d4fed4973eb",
|
||||
"from": "github:bures/owasp-password-strength-test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue