cmsmon
Version:
573 lines (502 loc) • 19.7 kB
JavaScript
;
import 'ng-file-upload';
import TypeClass from './Type';
import QueryBuilder from "./QueryBuilder";
import 'angular-websocket';
import Uuid from 'uuid';
import 'jquery-ui/ui/widgets/draggable';
import 'jquery-ui/ui/widgets/resizable';
import traverse from 'traverse';
import 'angular-translate';
import 'angular-ui-notification';
import 'angular-ui-notification/dist/angular-ui-notification.min.css'
import _io from 'socket.io-client';
window.io = _io;
window.Enum = {
Load: {NOT: 'NOT', LOADING: 'LOADING', LOADED: 'LOADED', PART_LOADED: 'PART_LOADED'},
EditMode: {ALL: 'ALL', VIEWELEMENT: 'VIEWELEMENT', DATAELEMENT: 'DATAELEMENT', CONTAINER: 'CONTAINER'}
}
const modelModule = angular
.module('common.data', ['ngFileUpload', 'ngWebSocket', 'pascalprecht.translate', 'ui-notification'])
.config(config)
.factory('cms', cms)
.run(run);
config.$inject = ['$translateProvider'];
function config($translateProvider) {
$translateProvider.translations('en', {
Add: 'Add',
Setting: 'Setting',
DeleteAll: 'Delete all',
Show: 'Show',
Submit: 'Submit',
Cancel: 'Cancel',
Apply: 'Apply',
'Save and add': 'Save and add'
});
$translateProvider.translations('de', {
Add: 'Hinzufügen',
Setting: 'Einstellung',
DeleteAll: 'alles Löschen',
Show: 'Anzeigen',
Submit: 'Absenden',
Cancel: 'Abbrechen',
Apply: 'Übernehmen',
'Save and add': 'Speichern und hinzufügen'
});
$translateProvider.preferredLanguage('de');
}
cms.$inject = ['$http', 'Upload'];
function cms($http, Upload) {
const data = {
socketQueue: {}
};
const editState = {
/**
* 0: edit by drag and drop element
* 1: edit by container
*/
editMode: Enum.EditMode.DATAELEMENT,
dragType: null,
showContainerEdit: false
}
function changeEditMode(mode) {
if (mode === Enum.EditMode.DATAELEMENT) {
$('body').addClass('cms-data-element');
} else {
$('body').removeClass('cms-data-element')
}
}
changeEditMode(Enum.EditMode.DATAELEMENT);
function getType(type, ref, cb, content, onfly = true) {
let Type = data.types[type];
if (!Type || !Type.template || !ref || !_.find(Type.list, {_id: ref})) {
let query = ref ? (Type && _.find(Type.list, {_id: ref}) ? 'element=false' : `element=${ref}`) : 'element=false';
if (!Type) Type = data.types[type] = {list: []};
if (!Type.template) query += '&template=true';
if ((content && content._id) || !onfly) query = '';
$http.post(`/cms-types/${type}?${query}`, JsonFn.stringify(content)).then(res => {
const result = JsonFn.clone(res.data, true);
if (_.find(Type.list, {_id: ref})) {
_.remove(Type.list, {_id: ref});
}
if (!ref || !_.find(Type.list, {_id: ref})) {
ref = result.data ? result.data._id : null;
Type.list.push(result.data);
}
if (!Type.template) {
Type.template = result.template;
Type.form = result.form;
Type.fn = result.fn;
Type.serverFn = result.serverFn;
Type.info = result.info;
}
cb(_.find(Type.list, {_id: ref}));
})
} else {
cb(_.find(Type.list, {_id: ref}));
}
}
function createElement(type, content, cb, onfly = true) {
if (!onfly) {
updateElement(type, content, cb);
} else {
getType(type, null, cb, content, onfly);
}
}
function removeElement(type, _id, cb, onerror) {
$http.delete(`api/v1/${type}/${_id}`).then(() => {
_.remove(Types[type].list, {_id: _id});
if (cb) cb();
}, () => {
if (onerror) onerror();
});
}
function updateContainerPage() {
const url = location.pathname;
$http.post(`/cms-container-page${url}`, {containers: data.containers}).then(()=> {
console.log('Update container page successful');
});
}
const loadElementsPending = [];
function countElements(type, cb, paramsBuilder) {
sendWs({path: `get/api/v1/${type}/count`, params: paramsBuilder.buildJson()}, ({result:count}) => {
if (cb) cb(count);
});
/*$http.get(`/api/v1/${type}/count?${params}`, _transform).then(res => {
if (cb) cb(res.data.count);
});*/
}
function sendWs(msg, cb) {
const _uuid = Uuid.v1();
data.socketQueue[_uuid] = cb;
socket.send(JsonFn.stringify(_.assign(msg, {uuid: _uuid})));
}
function loadElements(type, cb, paramsBuilder) {
if (!paramsBuilder && data.types[type] && data.types[type]._load === Enum.Load.LOADED) {
if (cb) cb(data.types[type].list);
return;
}
console.time("loadElements");
sendWs({
path: `get/api/v1/${type}`,
params: paramsBuilder ? paramsBuilder.buildJson() : {}
}, ({result:_list}) => {
console.timeEnd("loadElements");
if (!paramsBuilder) {
data.types[type].list = _list;
data.types[type]._load = Enum.Load.LOADED;
} else {
data.types[type].list = _.unionWith(_list, _.filter(data.types[type].list, e => e !== null), (e1, e2) => e1._id === e2._id);
data.types[type].queryList = _list.map(e => _.find(data.types[type].list, e2 => e2._id === e._id));
data.types[type]._load = Enum.Load.PART_LOADED;
}
if (cb) cb(data.types[type].queryList);
}
);
/*$http.get(`/api/v1/${type}?${JsonFn.stringify(paramsBuilder)}`, _transform).then(res => {
var list = JsonFn.clone(res.data, true);
if (!paramsBuilder) {
data.types[type].list = list;
data.types[type]._load = Enum.Load.LOADED;
} else {
data.types[type].list = _.unionWith(data.types[type].list, list, (e1, e2) => e1._id === e2._id);
data.types[type].queryList = list.map(e => _.find(data.types[type].list, e2 => e2._id === e._id));
data.types[type]._load = Enum.Load.PART_LOADED;
}
loadElementsPending.forEach(cb => cb(data.types[type].queryList));
loadElementsPending.length = 0;
});*/
}
function findByRef(type, ref) {
return _.find(data.types[type].list, {_id: ref});
}
function findByID(type, ID) {
return _.find(data.types[type].list, {ID});
}
function findFnByID(type, ID) {
const e = _.find(data.types[type].list, {ID});
const fn = JsonFn.clone(data.types[type].fn);
_.each(fn, (f, k) => fn[k] = f.bind(e));
return fn;
}
function findFnByRef(type, ref) {
const e = findByRef(type, ref);
const fn = JsonFn.clone(data.types[type].fn);
_.each(fn, (f, k) => fn[k] = f.bind(e));
return fn;
}
function walkInContainers(containers, cb) {
function walk(containers) {
_.each(containers, container => {
_.each(container.elements, element => {
// save when no data in client exists
loadElements(element.type, () => {
const Type = Types[element.type];
const e = _.find(Type.list, e => e._id === element.ref);
cb(element, e, container);
if (element.containers && element.containers.length > 0) {
walk(element.containers);
}
})
});
});
}
walk(containers);
}
function updateElement(type, model, resolve, fail) {
sendWs({
path: `post/api/v1/${type}`,
model
}, ({result:model}) => {
var oldModel = _.find(Types[type].list, {_id: model._id});
if (oldModel && !angular.equals(oldModel, model)) {
for (var member in oldModel) delete oldModel[member];
_.assign(oldModel, model);
} else {
Types[type].list.push(model);
}
if (resolve) resolve(model);
});
}
function listColumns(form) {
if (form[0].isTab) {
const _fields = [];
form.forEach(({fields}) => {
_fields.push(...fields.map(field => ({
value: field.key,
label: field.templateOptions.label || field.key
})));
})
return _fields;
}
return form.map(field => ({value: field.key, label: field.templateOptions.label || field.key}));
}
function findField(form, property, deep = false) {
if (!deep) {
if (form[0].isTab) {
let result;
form.forEach(({fields}) => {
const f = fields.find(f => f.key === property);
if (f) result = f;
})
return result;
}
return form.find(f => f.key === property);
}
let result;
const last = property.split('.').pop();
traverse(form).forEach(function (node) {
if (node && node.key === last) {
let path = _.reduce(this.parents.filter(({node:{key}}) => !_.isEmpty(key)), (path, parent) => {
path += `.${parent.node.key}`;
return path;
}, '');
if (_.isEmpty(path)) {
path = last
} else {
path = path.substring(1) + '.' + last;
}
if (path === property) {
result = node;
this.stop();
}
}
});
return result;
}
function getContainer(path) {
if (!data.containers) {
data.containers = {};
updateContainerPage();
}
let container = _.get(data.containers, path);
// create if not exists
if (!container) {
const _path = _.dropRight(path.split('\.')).join('.');
(_.isEmpty(_path) ? data.containers : _.get(data.containers, _path))[path.split('\.').pop()] = {elements: []};
container = _.get(data.containers, path);
updateContainerPage();
}
return container;
}
function parseAndSaveData(_data) {
Object.assign(data, _data);
for (let k in data.types) {
data.types[k] = new TypeClass(data.types[k]);
}
}
function exportAll(filename, types) {
$http.post(`/cms-export`, {filename, types}).then(function (res) {
confirm('Export successful');
});
}
function importAll(types, url) {
$http.post(`/cms-import`, {types, url}).then(function (res) {
confirm('Import successful');
});
}
const uploadFile = function (file, path, cb) {
Upload.upload({
url: `/cms-files/${path}`,
data: {file}
}).then(function () {
if (cb) cb();
});
}
function deleteElements(type, cb) {
$http.delete(`/cms-types/${type}`).then(function (res) {
if (cb) cb();
confirm('delete successful');
});
}
function getAdminList() {
const _types = _.pick(data.types, function (Type) {
if (editState.editMode === Enum.EditMode.ALL) return true;
if (editState.editMode === Enum.EditMode.VIEWELEMENT) return Type.info.isViewElement;
if (editState.editMode === Enum.EditMode.DATAELEMENT) return !Type.info.isViewElement;
return true;
})
let i = -1;
return _.map(_types, (Type, k) => {
i++;
let _children = [];
const createChildren = (_properties, query, path) => {
const properties = JsonFn.clone(_properties);
const children = [];
if (!properties || properties.length === 0) return;
const property = properties.shift();
const field = findField(Type.form, property, true);
if (field.type === 'refSelect') {
var _type = field.templateOptions.Type;
data.types[_type].list.forEach((_element) => {
const _path = `${path}.children[${children.length}]`;
let _query = [{[property]: _element._id}];
_query = query ? query.concat(_query) : _query;
children.push({
children: createChildren(properties, _query, _path),
text: _element[data.types[_type].info.title],
type: k,
path: _path,
columns: _.remove(listColumns(Type.form), _property => _property !== property),
query: {$and: _query}
});
})
} else if (field.type === 'select-ref-static') {
var _type = field.templateOptions.Type;
data.types[_type].list.forEach((_element) => {
const _path = `${path}.children[${children.length}]`;
let _query = [{[property]: _element[data.types[_type].info.title]}];
_query = query ? query.concat(_query) : _query;
children.push({
children: createChildren(properties, _query, _path),
text: _element[data.types[_type].info.title],
type: k,
path: _path,
columns: _.remove(listColumns(Type.form), _property => _property !== property),
query: {$and: _query}
});
})
} else if (field.type === 'array' && field.templateOptions.field.type === 'refSelect') {
var _type = field.templateOptions.field.templateOptions.Type;
data.types[_type].list.forEach((_element) => {
const _path = `${path}.children[${children.length}]`;
let _query = [{[property]: _element._id}];
_query = query ? query.concat(_query) : _query;
children.push({
children: createChildren(properties, _query, _path),
text: _element[data.types[_type].info.title],
type: k,
path: _path,
columns: _.remove(listColumns(Type.form), _property => _property !== property),
query: {$and: _query}
});
})
} else if (field.type === 'select') {
const {options} = field.templateOptions;
_.each(options, ({name, value}) => {
const _path = `${path}.children[${children.length}]`;
let _query = [{[property]: value}];
_query = query ? query.concat(_query) : _query;
children.push({
children: createChildren(properties, _query, _path),
text: name,
type: k,
path: _path,
columns: _.remove(listColumns(Type.form), _property => _property !== property),
query: {$and: _query}
});
})
}
return children;
}
var config = data.types.Config.list.find(config => config.type === k);
const _path = `[${i}]`;
let columns = listColumns(Type.form);
if (config) {
try {
config.dynamicQuery.forEach(dynamicQuery => {
if (dynamicQuery.field.length === 0) return;
_children.push(...createChildren(dynamicQuery.field, null, _path));
});
columns = _.filter(columns, col => {
if (_.isEmpty(config.showFields)) return true;
return config.showFields.indexOf(col.value) !== -1;
})
} catch (e) {
}
}
return {
children: _children,
columns,
text: Type.label || k,
type: k,
path: _path
}
});
}
function execServerFn(type, model, fnName, ...args) {
return $http.post(`/cms-types/${type}/${model._id}/${fnName}`, args);
}
function execServerFnForWrapper(name, fnName, ...args) {
return $http.post(`/cms-wrappers/${name}/${fnName}`, args);
}
function getTitle(type, ref) {
const Type = data.types[type];
const e = _.find(Type.list, {_id: ref})
return e[Type.info.title];
}
return window.cms = {
sendWs,
findByID,
findFnByID,
findByRef,
findFnByRef,
getType,
createElement,
updateElement,
removeElement,
findField,
data,
get types() {
return data.types;
},
editState,
loadElements,
countElements,
loadElementsPending,
updateContainerPage,
walkInContainers,
getContainer,
parseAndSaveData,
exportAll,
importAll,
changeEditMode,
uploadFile,
getAdminList,
listColumns,
QueryBuilder,
deleteElements,
execServerFn,
execServerFnForWrapper,
getTitle
}
}
run.$inject = ['cms', '$http', '$websocket'];
function run(cms, $http, $websocket) {
const data = cms.data;
try {
cms.parseAndSaveData(JsonFn.parse($('#cms-data').text(), true));
data.serverFn = data.setupServerFn(data.serverFn, $http.post);
delete data.setupServerFn;
window.Types = data.types;
window.Local = data.Local = {};
} catch (e) {
}
//menu
$('body').append(`<div cms-nav></div>`);
//panel
$('body').prepend(`<div cms-container-edit></div>`);
$('body').addClass('cms-admin-mode');
let new_uri = '';
const {wsAddress} = cms.data.online;
if (wsAddress) {
new_uri = wsAddress;
} else {
let loc = window.location;
/*if (loc.protocol === "https:") {
new_uri = "wss:";
} else {
new_uri = "ws:";
}*/
new_uri += "ws://" + loc.host;
//new_uri += loc.pathname;
}
var socket = io.connect(new_uri);
window.socket = cms.socket = socket;
socket.on('message', (event) => {
const _data = JsonFn.parse(event, true);
if (!_data.uuid) return;
cms.data.socketQueue[_data.uuid](_data)
});
}
export default modelModule.name;