@zenginehq/frontend-config
Advanced tools
Comparing version 3.5.0 to 3.6.0
{ | ||
"name": "@zenginehq/frontend-config", | ||
"version": "3.5.0", | ||
"version": "3.6.0", | ||
"description": "Helper module for implementing Zengine plugin configuration forms.", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -11,2 +11,3 @@ # zn-frontend-config | ||
- Toggling configurations on and off | ||
- Automated webhook management | ||
- Highly extensible via _events_ and _custom field types_ | ||
@@ -19,7 +20,7 @@ | ||
```shell | ||
# Run this from your frontend plugin's src diretory. | ||
npm i @zenginehq/frontend-config --save | ||
``` | ||
It's crucial that this gets run inside the `plugins/myplugin/src` directory alongside your plugin's other code to get built properly. | ||
Note: when using maya, it's crucial that this gets run inside the `plugins/myplugin/src` directory alongside your plugin's other code to get built properly. | ||
If you are using the newer [mayan](https://github.com/ZengineHQ/mayan) then you can just run the install from your frontend plugin's root. | ||
@@ -26,0 +27,0 @@ ## Updating |
@@ -5,2 +5,12 @@ plugin.service('wgnConfigInputs', [function () { | ||
var workspaceInput = { | ||
type: 'workspace', | ||
template: 'wgn-config-input-workspace', | ||
options: { | ||
exclusive: { | ||
required: false | ||
} | ||
} | ||
}; | ||
var formInput = { | ||
@@ -141,2 +151,3 @@ type: 'form', | ||
return [ | ||
workspaceInput, | ||
formInput, | ||
@@ -143,0 +154,0 @@ fieldInput, |
@@ -6,8 +6,11 @@ plugin.controller('wgnConfigCtrl', ['$scope', '$q', '$routeParams', 'znData', 'znModal', 'znMessage', 'wgnConfigSrv', | ||
var _workspaceId = $routeParams.workspace_id; | ||
var _forms = []; | ||
var _workspaces = []; | ||
var _forms = {}; | ||
var _fields = {}; | ||
var _folders = {}; | ||
var _formsLoading = {}; | ||
var _fieldsLoading = {}; | ||
var _foldersLoading = {}; | ||
var _originalConfig; | ||
var _webhook = false; | ||
@@ -58,7 +61,3 @@ /** | ||
doRunHook('add', $scope.editing.config).then(function (data) { | ||
if (data && angular.isObject(data)) { | ||
angular.extend($scope.editing.config, data); | ||
} | ||
doRunHook('add', $scope.editing.config).finally(function () { | ||
doResetTab(); | ||
@@ -83,7 +82,3 @@ $scope.wgnConfigForm.$setPristine(); | ||
doRunHook('edit', $scope.editing.config).then(function (data) { | ||
if (data && angular.isObject(data)) { | ||
angular.extend($scope.editing.config, data); | ||
} | ||
doRunHook('edit', $scope.editing.config).finally(function () { | ||
doResetTab(); | ||
@@ -113,3 +108,5 @@ $scope.wgnConfigForm.$setPristine(); | ||
return configService.deleteConfig(_workspaceId, $scope.editing.config, $scope.configs).then(function () { | ||
doRunHook('delete', shallowCopy($scope.editing.config)).finally(function () { | ||
return doRunHook('delete', shallowCopy($scope.editing.config)).finally(function () { | ||
return _webhook ? _webhook.service.delete($scope.editing.config.webhookId) : $q.when(); | ||
}).then(function () { | ||
doDiscardChanges(); | ||
@@ -139,11 +136,3 @@ znMessage('The configuration has been deleted!', 'info'); | ||
return doRunHook('save', $scope.editing.config).then(function (data) { | ||
if (data && angular.isObject(data)) { | ||
angular.extend($scope.editing.config, data); | ||
} | ||
return doSaveConfig($scope.editing.config); | ||
}).then(function () { | ||
znMessage('Configuration saved!', 'saved'); | ||
return doSaveConfig($scope.editing.config).then(function () { | ||
if ($scope.settings.toggle && !$scope.editing.config.enabled && !('$id' in $scope.editing.config)) { | ||
@@ -163,4 +152,2 @@ znModal({ | ||
var savedConfig = $scope.editing.config; | ||
if ($scope.settings.multi) { | ||
@@ -173,5 +160,6 @@ doDiscardChanges(); | ||
$scope.saving = false; | ||
return doRunHook('postSave', savedConfig); | ||
return doRunHook('save', $scope.editing.config).finally(function () { | ||
znMessage('Configuration saved!', 'saved'); | ||
$scope.saving = false; | ||
}); | ||
}); | ||
@@ -187,4 +175,6 @@ }; | ||
return doRunHook('disable', $scope.editing.config).then(function () { | ||
return doSaveConfig($scope.editing.config); | ||
return doRunHook('disable', $scope.editing.config).finally(function () { | ||
return doSaveConfig($scope.editing.config).then(function () { | ||
return _webhook ? _webhook.service.disable($scope.editing.config.webhookId) : $q.when(); | ||
}); | ||
}).catch(function () { | ||
@@ -207,4 +197,6 @@ $scope.editing.config.enabled = true; | ||
doRunHook('enable', $scope.editing.config).then(function () { | ||
return doSaveConfig($scope.editing.config); | ||
return doRunHook('enable', $scope.editing.config).finally(function () { | ||
return doSaveConfig($scope.editing.config).then(function () { | ||
return _webhook ? _webhook.service.enable($scope.editing.config.webhookId) : $q.when(); | ||
}); | ||
}).catch(function () { | ||
@@ -273,10 +265,10 @@ $scope.editing.config.enabled = false; | ||
* | ||
* @param {string} fieldDefId The field definition id. | ||
* @param {string} defId The field definition id. | ||
*/ | ||
$scope.onSelectForm = function (fieldDefId) { | ||
$scope.onSelectForm = function (defId) { | ||
/*jshint maxcomplexity:6 */ | ||
var formId = $scope.editing.config[fieldDefId]; | ||
var formId = $scope.editing.config[defId]; | ||
if (formId && (!(formId in _fields) || !_fields[formId].length)) { | ||
loadFields(formId, fieldDefId); | ||
loadFields(formId, defId); | ||
} | ||
@@ -290,13 +282,43 @@ | ||
/** | ||
* Initializes a form field. | ||
* Initializes a form input. | ||
* | ||
* @param {string} fieldDefId The field definition id. | ||
* @param {string} defId The field definition id. | ||
*/ | ||
$scope.initFormField = function (fieldDefId) { | ||
$scope.initFormInput = function (def) { | ||
if (!def.belongsTo || $scope.editing.config[def.belongsTo]) { | ||
if ($scope.loading) { | ||
$scope.options.on('init', function () { | ||
$scope.onSelectForm(def.id); | ||
}); | ||
} else { | ||
$scope.onSelectForm(def.id); | ||
} | ||
} | ||
}; | ||
/** | ||
* Loads forms for the selected workspace. | ||
* | ||
* @param {string} defId The field definition id. | ||
*/ | ||
$scope.onSelectWorkspace = function (defId) { | ||
var wsid = $scope.editing.config[defId]; | ||
if (wsid && (!(wsid in _forms) || !_forms[wsid].length)) { | ||
loadForms(wsid); | ||
} | ||
}; | ||
/** | ||
* Initializes a workspace input. | ||
* | ||
* @param {string} defId The field definition id. | ||
*/ | ||
$scope.initWorkspaceInput = function (defId) { | ||
if ($scope.loading) { | ||
$scope.options.on('init', function () { | ||
$scope.onSelectForm(fieldDefId); | ||
$scope.onSelectWorkspace(defId); | ||
}); | ||
} else { | ||
$scope.onSelectForm(fieldDefId); | ||
$scope.onSelectWorkspace(defId); | ||
} | ||
@@ -306,12 +328,50 @@ }; | ||
/** | ||
* Loads all workspaces for a given input. | ||
* | ||
* @param {Object} fieldDef The workspace input definition. | ||
* | ||
* @return {Array<Object>} | ||
*/ | ||
$scope.getWorkspaces = function (def) { | ||
if (!$scope.editing.config) { | ||
return _workspaces; | ||
} | ||
var filterWorkspaces = []; | ||
angular.forEach($scope.settings.pages, function (page) { | ||
angular.forEach(page.fields, function (f) { | ||
// Split into two if statements for legibility. | ||
if (f.type === 'workspace' && f.id !== def.id && f.exclusive) { | ||
if (f.id in $scope.editing.config && $scope.editing.config[f.id]) { | ||
filterWorkspaces.push($scope.editing.config[f.id]); | ||
} | ||
} | ||
}); | ||
}); | ||
// Filter values used in other inputs. | ||
return _workspaces.filter(function (f) { | ||
return filterWorkspaces.indexOf(f.id) === -1; | ||
}); | ||
}; | ||
/** | ||
* Loads all forms for a given input. | ||
* If a type is passed, it hides forms set for other form inputs in the list. | ||
* | ||
* @param {Object} fieldDef The field input definition. | ||
* @param {Object} def The form input definition. | ||
* | ||
* @return {Array<Object>} | ||
*/ | ||
$scope.getForms = function (fieldDef) { | ||
$scope.getForms = function (def, workspaceId) { | ||
// Allow overidding workspaceId but default to current one. | ||
workspaceId = workspaceId || _workspaceId; | ||
// Sanity when dealing with forms belonging to a workspace. | ||
if (!(workspaceId in _forms)) { | ||
return []; | ||
} | ||
if (!$scope.editing.config) { | ||
return _forms; | ||
return _forms[workspaceId]; | ||
} | ||
@@ -323,3 +383,3 @@ | ||
// Split into two if statements for legibility. | ||
if (f.type === 'form' && f.id !== fieldDef.id && f.exclusive) { | ||
if (f.type === 'form' && f.id !== def.id && f.exclusive) { | ||
if (f.id in $scope.editing.config && $scope.editing.config[f.id]) { | ||
@@ -333,5 +393,5 @@ filterForms.push($scope.editing.config[f.id]); | ||
// Filter values used in other inputs. | ||
return _forms.filter(function (f) { | ||
return _forms[workspaceId] ? _forms[workspaceId].filter(function (f) { | ||
return filterForms.indexOf(f.id) === -1; | ||
}); | ||
}) : []; | ||
}; | ||
@@ -430,19 +490,13 @@ | ||
if (config.enabled) { | ||
doRunHook('enable', config).then(function (data) { | ||
if (data && angular.isObject(data)) { | ||
angular.extend(config, data); | ||
} | ||
doRunHook('enable', config).finally(function () { | ||
return doSaveConfig(config).then(function () { | ||
znMessage('Configuration ' + config.name + ' enabled!', 'saved'); | ||
return _webhook ? _webhook.service.enable(config.webhookId) : $q.when(); | ||
}); | ||
}); | ||
} else { | ||
doRunHook('disable', config).then(function (data) { | ||
if (data && angular.isObject(data)) { | ||
angular.extend(config, data); | ||
} | ||
doRunHook('disable', config).finally(function () { | ||
return doSaveConfig(config).then(function () { | ||
znMessage('Configuration ' + config.name + ' disabled!', 'saved'); | ||
return _webhook ? _webhook.service.disable(config.webhookId) : $q.when(); | ||
}); | ||
@@ -522,8 +576,36 @@ }); | ||
/** | ||
* Loads data on all available workspaces. | ||
*/ | ||
function loadWorkspaces () { | ||
return znData('Workspaces').get({ limit: 200 }).then(function (workspaces) { | ||
_workspaces = workspaces.slice(); | ||
}).catch(function (err) { | ||
znMessage(err, 'error'); | ||
}); | ||
} | ||
/** | ||
* Loads form data for the given workspace. | ||
* | ||
* @param {number} workspaceId | ||
*/ | ||
function loadForms (workspaceId) { | ||
_formsLoading[workspaceId] = true; | ||
return znData('Forms').get({ 'workspace.id': workspaceId, 'limit': 200 }).then(function (forms) { | ||
_forms[workspaceId] = forms; | ||
}).catch(function (err) { | ||
znMessage(err, 'error'); | ||
}).finally(function () { | ||
_formsLoading[workspaceId] = false; | ||
}); | ||
} | ||
/** | ||
* Loads field data for the given form. | ||
* | ||
* @param {number} formId The actual form id. | ||
* @param {string} fieldDefId The field definition id. | ||
* @param {string} defId The field definition id. | ||
*/ | ||
function loadFields (formId, fieldDefId) { | ||
function loadFields (formId, defId) { | ||
_fieldsLoading[formId] = true; | ||
@@ -538,3 +620,3 @@ | ||
angular.forEach($scope.options.getDependentFields(fieldDefId), function (f) { | ||
angular.forEach($scope.options.getDependentFields(defId), function (f) { | ||
if (f.restrict) { | ||
@@ -614,6 +696,18 @@ var res = f.restrict.split('|'); | ||
angular.forEach(highlighted, function (input) { | ||
/*jshint maxcomplexity:9 */ | ||
/*jshint maxcomplexity:11 */ | ||
var inputTypeFormatted = input.type.charAt(0).toUpperCase() + input.type.substr(1); | ||
switch (input.type) { | ||
case 'workspace': | ||
var workspace = $scope.getWorkspaces(input).filter(function (w) { | ||
return w.id === $scope.editing.config[input.id]; | ||
})[0]; | ||
if (workspace) { | ||
formatedHighligts.push({ | ||
type: inputTypeFormatted, | ||
value: workspace.name | ||
}); | ||
} | ||
break; | ||
case 'form': | ||
@@ -689,3 +783,41 @@ var form = $scope.getForms(input).filter(function (f) { | ||
function doSaveConfig (config) { | ||
return configService.save(_workspaceId, $scope.settings.multi, $scope.configs, config); | ||
var promise = $q.when(config); | ||
if (_webhook && !('webhookId' in config)) { | ||
var options = Object.assign({}, _webhook.options); | ||
if (!(options['form.id'] in config)) { | ||
throw new Error('Config: Invalid form.id for webhook'); | ||
} | ||
options['form.id'] = config[options['form.id']]; | ||
promise = _webhook.service.create(options).then(function (webhook) { | ||
config.webhookId = webhook.id; | ||
config.webhookKey = webhook.secretKey; | ||
return config; | ||
}).catch(function (err) { | ||
znMessage('There was an error creating the webhook.', 'error'); | ||
return config; | ||
}); | ||
} | ||
return promise.then(function (cfg) { | ||
return configService.save(_workspaceId, $scope.settings.multi, $scope.configs, cfg).then(function () { | ||
return cfg; | ||
}); | ||
}).then(function (cfg) { | ||
// Now that we know the config id, update the webhook URL to add it when using multi configs. | ||
if ($scope.settings.multi) { | ||
return _webhook.service.load(cfg.webhookId).then(function (wh) { | ||
if (wh.url.indexOf('config=') === -1) { | ||
var separator = wh.url.indexOf('?') === -1 ? '?' : '&'; | ||
return _webhook.service.update({ | ||
id: wh.id, | ||
url: wh.url + separator + 'config=' + encodeURI(cfg.$id) | ||
}); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
@@ -735,2 +867,3 @@ | ||
$scope.settings = $scope.options.getConfig(); | ||
_webhook = $scope.options.getWebhook(); | ||
doResetTab(); | ||
@@ -755,8 +888,9 @@ | ||
}).then(function () { | ||
// Load available forms. | ||
return znData('Forms').get({ 'workspace.id': _workspaceId, 'limit': 200 }); | ||
}).then(function (forms) { | ||
_forms = forms; | ||
if ($scope.options.hasWorkspaceField()) { | ||
return loadWorkspaces(); | ||
} else if ($scope.options.hasFormField()) { | ||
return loadForms(_workspaceId); | ||
} | ||
}); | ||
} | ||
}]); |
@@ -95,3 +95,6 @@ plugin.service('wgnConfigSrv', ['$q', '$firebase', 'znData', function ($q, $firebase, znData) { | ||
return $ref.$add(config); | ||
return $ref.$add(config).then(function (ref) { | ||
var id = 'key' in ref && angular.isFunction(ref.key) ? ref.key() : ref.path.n.pop(); | ||
config.$id = id; | ||
}); | ||
} | ||
@@ -98,0 +101,0 @@ |
plugin.service('wgnConfigSettings', ['$q', 'wgnConfigInputs', function ($q, configInputs) { | ||
return function (args) { | ||
return function (title) { | ||
var srv = this; | ||
var _defaults = { | ||
title: 'My Plugin', | ||
title: title || 'My Plugin', | ||
icon: 'icon-puzzle', | ||
@@ -14,17 +14,13 @@ help: 'This is some instructional text decribing what this plugin is and how to use it. Please customize it.', | ||
var _currentForm = false; | ||
var _currentWorkspace = false; | ||
var _fieldIds = []; | ||
var _formInputs = []; | ||
var _workspaceInputs = []; | ||
var _fieldTypes = {}; | ||
var _highlightedFields = []; | ||
var _hooks = {}; | ||
var _webhook = false; | ||
// Accept either a configuration object or a string for the title. | ||
if (!angular.isObject(args)) { | ||
args = { | ||
title: args ? args.toString() : '' | ||
}; | ||
} | ||
var _settings = angular.extend({}, _defaults); | ||
var _settings = angular.extend({}, _defaults, args); | ||
// Ensure pages is always an empty array. | ||
@@ -79,3 +75,3 @@ _settings.pages = []; | ||
srv.field = function (def) { | ||
/*jshint maxcomplexity:14 */ | ||
/*jshint maxcomplexity:22 */ | ||
// Make sure we have a page, this will only be false if a field is added before a page. | ||
@@ -127,6 +123,11 @@ if (_currentPage === false) { | ||
// For convenience, if we have defined a form on the current page, set a default "belongsTo" value for | ||
// field types that require it. | ||
if ('belongsTo' in opts && !('belongsTo' in def) && _currentForm !== false) { | ||
def.belongsTo = _formInputs[_currentForm]; | ||
// Try to auto-fill the belongsTo option if it's required but not set. | ||
if ('belongsTo' in opts && !('belongsTo' in def)) { | ||
if (def.type !== 'form' && _currentForm !== false) { | ||
// For non-form fields, belongsTo will require a forn. | ||
def.belongsTo = _formInputs[_currentForm]; | ||
} else if (def.type === 'form' && _currentWorkspace !== false) { | ||
// For form fields, belongsTo is always a workspace. | ||
def.belongsTo = _workspaceInputs[_currentWorkspace]; | ||
} | ||
} | ||
@@ -159,2 +160,5 @@ | ||
_currentForm = _formInputs.length - 1; | ||
} else if (def.type === 'workspace') { | ||
_workspaceInputs.push(def.id); | ||
_currentWorkspace = _workspaceInputs.length - 1; | ||
} | ||
@@ -164,4 +168,12 @@ | ||
if ('belongsTo' in def) { | ||
if (_formInputs.indexOf(def.belongsTo) === -1) { | ||
throw new Error('Config: Invalid "belongsTo" for field "' + def.id + '", no form field exists with id "' + def.belongsTo + '"'); | ||
if (def.type === 'field' || def.type === 'folder' || def.type === 'choice') { | ||
if (_formInputs.indexOf(def.belongsTo) === -1) { | ||
throw new Error('Config: Invalid "belongsTo" for field "' + def.id + '", no form field exists with id "' + def.belongsTo + '"'); | ||
} | ||
} else if (def.type === 'form') { | ||
if (_workspaceInputs.indexOf(def.belongsTo) === -1) { | ||
throw new Error('Config: Invalid "belongsTo" for field "' + def.id + '", no workspace field exists with id "' + def.belongsTo + '"'); | ||
} | ||
} else { | ||
throw new Error('Config: Invalid "belongsTo" for field "' + def.id + '", the "' + def.type + '" field type doesn\'t support it.'); | ||
} | ||
@@ -226,2 +238,40 @@ } | ||
/** | ||
* Enable Webhook support form configurations. | ||
* | ||
* @param {wgnWebhook} A webhook service instance | ||
* @param {Object} options Options to pass to the webhook service | ||
*/ | ||
srv.webhook = function (webhook, options) { | ||
// Validate required options. | ||
if (!('url' in options)) { | ||
throw new Error('Config: Missing required param "url" in webhook options.'); | ||
} | ||
if (!('form.id' in options)) { | ||
throw new Error('Config: Missing required param "form.id" in webhook options.'); | ||
} | ||
// Finally make sure the form.id acually exists. | ||
if (_formInputs.indexOf(options['form.id']) === -1) { | ||
throw new Error('Config: Inexistent form id specified in param "form.id" in webhook options.'); | ||
} | ||
_webhook = { | ||
service: webhook, | ||
options: options | ||
}; | ||
return srv; | ||
}; | ||
/** | ||
* Returns webhook configs if they exist. | ||
* | ||
* @returns {{webhook: wgnWebhook, options: Object} | false} | ||
*/ | ||
srv.getWebhook = function () { | ||
return _webhook; | ||
}; | ||
/** | ||
* Registers a callback to run when a certain hook is fired. | ||
@@ -241,4 +291,3 @@ * | ||
'init', | ||
'save', | ||
'postSave' | ||
'save' | ||
]; | ||
@@ -362,9 +411,9 @@ | ||
/** | ||
* Returns field definitions that belong to a certain form. | ||
* Returns field definitions that belong to a certain form or workspace. | ||
* | ||
* @param {string} formDefId | ||
* @param {string} defId | ||
* | ||
* @return {Array<Object>} | ||
*/ | ||
srv.getDependentFields = function (formDefId) { | ||
srv.getDependentFields = function (defId) { | ||
var fields = []; | ||
@@ -374,3 +423,3 @@ | ||
angular.forEach(p.fields, function (f) { | ||
if ('belongsTo' in f && f.belongsTo === formDefId) { | ||
if ('belongsTo' in f && f.belongsTo === defId) { | ||
fields.push(f); | ||
@@ -385,2 +434,20 @@ } | ||
/** | ||
* Returns whether there are any form inputs in the defined config fields. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
srv.hasFormField = function () { | ||
return _formInputs.length > 0; | ||
}; | ||
/** | ||
* Returns whether there are any workspace inputs in the defined config fields. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
srv.hasWorkspaceField = function () { | ||
return _workspaceInputs.length > 0; | ||
}; | ||
/** | ||
* Returns a config settings object. | ||
@@ -387,0 +454,0 @@ * This is the final product of this service. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
218367
37
1608
36