1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | |
9 |
|
10 |
|
11 | var MongooseAdminUser = require('./mongoose_admin_user.js').MongooseAdminUser,
|
12 | MongooseAdminAudit = require('./mongoose_admin_audit.js').MongooseAdminAudit,
|
13 | _ = require('underscore'),
|
14 | async = require('async'),
|
15 | permissions = require('./permissions'),
|
16 | mongoose = require('mongoose'),
|
17 | paths = require('./http/register_paths'),
|
18 | forms = require('j-forms').forms;
|
19 |
|
20 |
|
21 | exports = module.exports = MongooseAdmin;
|
22 | exports.version = '0.0.1';
|
23 |
|
24 | var app;
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | exports.createAdmin = function(app,options) {
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | options = options || {};
|
48 | var root = options.root || '';
|
49 | console.log('\x1b[36mMongooseAdmin is listening at path: \x1b[0m %s', root);
|
50 |
|
51 |
|
52 |
|
53 | paths.registerPaths(MongooseAdmin, app, '/' + root);
|
54 |
|
55 | app.use(require('express').static(__dirname + '/public'));
|
56 |
|
57 | MongooseAdmin.singleton = new MongooseAdmin(app, '/' + root);
|
58 |
|
59 | return MongooseAdmin.singleton;
|
60 |
|
61 | };
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | function MongooseAdmin(app, root) {
|
69 |
|
70 | this.app = app;
|
71 | this.root = root;
|
72 | this.models = {};
|
73 | this.title = "Backoffice";
|
74 | };
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | MongooseAdmin.prototype.buildPath = function(path) {
|
82 | return this.root + path;
|
83 | };
|
84 |
|
85 | MongooseAdmin.prototype.getAdminTitle = function(){
|
86 | return this.title;
|
87 | };
|
88 |
|
89 | MongooseAdmin.prototype.setAdminTitle = function(title)
|
90 | {
|
91 | this.title = title;
|
92 | };
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | MongooseAdmin.prototype.pushExpressConfig = function() {
|
99 | var currentViewsPath = MongooseAdmin.singleton.app.set('views');
|
100 | this.app.set('views', __dirname + '/views');
|
101 | return {'views': currentViewsPath};
|
102 | };
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | MongooseAdmin.prototype.popExpressConfig = function(config) {
|
108 | this.app.set('views', config.views);
|
109 | };
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | MongooseAdmin.prototype.close = function() {
|
117 | this.app.close();
|
118 | };
|
119 |
|
120 | function buildModelFilters(model,filters,dict) {
|
121 | if(!filters)
|
122 | return;
|
123 | setTimeout(function() {
|
124 | async.forEach(filters,function(filter,cbk) {
|
125 | model.collection.distinct(filter, function(err,results) {
|
126 | if(results) {
|
127 | if(results[0] && Array.isArray(results[0])) {
|
128 | results = _.flatten(results);
|
129 | }
|
130 | if(results.length > 30)
|
131 | results.splice(5);
|
132 | if(model.schema.paths[filter] && model.schema.paths[filter].options.ref) {
|
133 | mongoose.model(model.schema.paths[filter].options.ref).find()
|
134 | .where('_id').in(results).exec(function(err,refs) {
|
135 | if(refs)
|
136 | dict.push( {key:filter, values: _.map(refs,function(ref) { return { value:ref.id, text:ref.toString()}; }) });
|
137 | cbk(err);
|
138 | })
|
139 | }
|
140 | else {
|
141 | dict.push({key:filter, values: _.map(results, function(result) {
|
142 | return { value: result, text:result};
|
143 | })});
|
144 | cbk();
|
145 | }
|
146 | }
|
147 | else
|
148 | cbk(err);
|
149 | })
|
150 |
|
151 | },function(){
|
152 | })
|
153 | },1000);
|
154 | };
|
155 |
|
156 | MongooseAdmin.prototype.registerMongooseModel = function(modelName, model,fields, options) {
|
157 | options = options || {};
|
158 | options.actions = options.actions || [];
|
159 | options.actions.push({value:'delete', label:'Delete',func:function(user,ids,callback)
|
160 | {
|
161 | async.parallel(_.map(ids,function(id)
|
162 | {
|
163 | return function(cbk)
|
164 | {
|
165 | forms.checkDependecies(modelName,id,cbk);
|
166 | }
|
167 | }),function(err,results)
|
168 | {
|
169 | if(err)
|
170 | callback(err);
|
171 | else
|
172 | {
|
173 | var no_dependecies = _.filter(ids,function(result,index)
|
174 | {
|
175 | return results[index].length == 0;
|
176 | });
|
177 | model.remove({_id:{$in:no_dependecies}},callback);
|
178 | }
|
179 | });
|
180 | }});
|
181 | var filters = [];
|
182 | buildModelFilters(model,options.filters,filters);
|
183 |
|
184 | this.models[modelName] = {model: model,
|
185 | filters:filters,
|
186 | modelName:modelName,
|
187 | options: options,
|
188 | fields: fields};
|
189 |
|
190 | console.log('\x1b[36mMongooseAdmin registered model: \x1b[0m %s', modelName);
|
191 |
|
192 | permissions.registerModel(modelName);
|
193 | };
|
194 |
|
195 | MongooseAdmin.prototype.registerSingleRowModel = function(model,name,options)
|
196 | {
|
197 | model.is_single = true;
|
198 | this.models[name] = {model:model,options:options||{},fields:{},is_single:true,modelName:name}
|
199 | permissions.registerModel(name);
|
200 | };
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 | MongooseAdmin.prototype.registerModel = function(model, name, options) {
|
213 | this.models[name] = {model: model,
|
214 | modelName:name,
|
215 | options: options
|
216 | };
|
217 | console.log('\x1b[36mMongooseAdmin registered model: \x1b[0m %s', name);
|
218 |
|
219 | };
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 | MongooseAdmin.prototype.getRegisteredModels = function(user,onReady) {
|
229 | var models = [];
|
230 | for (var collectionName in this.models) {
|
231 | this.models[collectionName].model.is_single = this.models[collectionName].is_single;
|
232 | models.push(this.models[collectionName]);
|
233 | };
|
234 | models = _.filter(models,function(model)
|
235 | {
|
236 | return permissions.hasPermissions(user,model.modelName,'view');
|
237 | });
|
238 | onReady(null, models);
|
239 | };
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 | MongooseAdmin.prototype.getModel = function(collectionName, onReady) {
|
250 | onReady(null, this.models[collectionName].model, this.models[collectionName].fields, this.models[collectionName].options);
|
251 | };
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 | MongooseAdmin.prototype.modelCounts = function(collectionName,filters, onReady) {
|
261 | if(this.models[collectionName].is_single)
|
262 | {
|
263 | onReady(null,1);
|
264 | return;
|
265 | }
|
266 | this.models[collectionName].model.count(filters, function(err, count) {
|
267 | if (err) {
|
268 | console.log('Unable to get counts for model because: ' + err);
|
269 | } else {
|
270 | onReady(null, count);
|
271 | }
|
272 | });
|
273 | };
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 | MongooseAdmin.prototype.listModelDocuments = function(collectionName, start, count,filters,sort, onReady) {
|
286 | var listFields = this.models[collectionName].options.list;
|
287 | if(listFields)
|
288 | {
|
289 |
|
290 | var query = this.models[collectionName].model.find(filters);
|
291 | var sorts = this.models[collectionName].options.order_by;
|
292 | var populates = this.models[collectionName].options.list_populate;
|
293 | if(sort)
|
294 | sorts.unshift(sort);
|
295 | if(sorts)
|
296 | {
|
297 | for(var i=0; i<sorts.length; i++)
|
298 | {
|
299 | if(sorts[i].indexOf('-') == 0)
|
300 | query.sort(sorts[i].substring(1),'descending');
|
301 | else
|
302 | query.sort(sorts[i],'ascending');
|
303 | }
|
304 | }
|
305 | if(populates)
|
306 | {
|
307 | _.each(populates,function(populate)
|
308 | {
|
309 | query.populate(populate);
|
310 | });
|
311 | }
|
312 | query.skip(start).limit(count).execFind(function(err, documents) {
|
313 | if (err) {
|
314 | console.log('Unable to get documents for model because: ' + err);
|
315 | onReady('Unable to get documents for model', null);
|
316 | } else {
|
317 | var filteredDocuments = [];
|
318 | documents.forEach(function(document) {
|
319 | var d = {};
|
320 | d['_id'] = document['_id'];
|
321 | listFields.forEach(function(listField) {
|
322 | d[listField] = document.get(listField);
|
323 | });
|
324 | filteredDocuments.push(d);
|
325 | });
|
326 |
|
327 | onReady(null, filteredDocuments);
|
328 | }
|
329 | });
|
330 | }
|
331 | else
|
332 | {
|
333 | onReady(null,[]);
|
334 | }
|
335 | };
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 | MongooseAdmin.prototype.getDocument = function(collectionName, documentId, onReady) {
|
347 | this.models[collectionName].model.findById(documentId, function(err, document) {
|
348 | if (err) {
|
349 | console.log('Unable to get document because: ' + err);
|
350 | onReady('Unable to get document', null);
|
351 | } else {
|
352 | onReady(null, document);
|
353 | }
|
354 | });
|
355 | };
|
356 |
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 |
|
366 | MongooseAdmin.prototype.createDocument = function(req,user, collectionName, params, onReady) {
|
367 | var self = this;
|
368 | var model = this.models[collectionName].model;
|
369 | var form_type = this.models[collectionName].options.form || forms.AdminForm;
|
370 | if(permissions.hasPermissions(user,collectionName,'create'))
|
371 | {
|
372 |
|
373 | var form = new form_type(req,{data:params},model);
|
374 | form.is_valid(function(err,valid)
|
375 | {
|
376 | if(err)
|
377 | {
|
378 | onReady(err);
|
379 | return;
|
380 | }
|
381 | if(valid)
|
382 | {
|
383 | form.save(function(err,document)
|
384 | {
|
385 | if (err) {
|
386 |
|
387 | onReady(form);
|
388 | } else {
|
389 |
|
390 | if (self.models[collectionName].options && self.models[collectionName].options.post) {
|
391 | document = self.models[collectionName].options.post(document);
|
392 | }
|
393 | MongooseAdminAudit.logActivity(user, self.models[collectionName].modelName, collectionName, document._id, 'add', null, function(err, auditLog) {
|
394 | onReady(null, document);
|
395 | });
|
396 | }
|
397 | });
|
398 | }
|
399 | else
|
400 | {
|
401 | onReady(form,null);
|
402 | }
|
403 | });
|
404 | }
|
405 | else
|
406 | {
|
407 | onReady('unauthorizaed');
|
408 | }
|
409 | };
|
410 |
|
411 |
|
412 |
|
413 |
|
414 |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 |
|
420 |
|
421 | MongooseAdmin.prototype.updateDocument = function(req,user, collectionName, documentId, params, onReady) {
|
422 | var self = this;
|
423 | var fields = this.models[collectionName].fields;
|
424 | var model = this.models[collectionName].model;
|
425 | if(permissions.hasPermissions(user,collectionName,'update'))
|
426 | {
|
427 |
|
428 | var form_type = this.models[collectionName].options.form || forms.AdminForm;
|
429 | model.findById(documentId, function(err, document) {
|
430 | if (err) {
|
431 | console.log('Error retrieving document to update: ' + err);
|
432 | onReady('Unable to update', null);
|
433 | } else {
|
434 |
|
435 | var form = new form_type(req,{instance:document,data:params},model);
|
436 | form.is_valid(function(err,valid)
|
437 | {
|
438 | if(err)
|
439 | {
|
440 | onReady(err, null);
|
441 | return;
|
442 | }
|
443 | if(valid)
|
444 | {
|
445 | form.save(function(err,document)
|
446 | {
|
447 | if (err) {
|
448 |
|
449 | onReady(form, null);
|
450 | } else {
|
451 |
|
452 | if (self.models[collectionName].options && self.models[collectionName].options.post) {
|
453 | document = self.models[collectionName].options.post(document);
|
454 | }
|
455 | MongooseAdminAudit.logActivity(user, self.models[collectionName].modelName, collectionName, document._id, 'edit', null, function(err, auditLog) {
|
456 | onReady(null, document);
|
457 | });
|
458 | }
|
459 |
|
460 | });
|
461 | }
|
462 | else
|
463 | {
|
464 | onReady(form,null);
|
465 | }
|
466 | });
|
467 | }
|
468 | });
|
469 | }
|
470 | else
|
471 | {
|
472 | onReady('unauthorized');
|
473 | }
|
474 | };
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 | MongooseAdmin.prototype.deleteDocument = function(user, collectionName, documentId, onReady) {
|
486 | var self = this;
|
487 | var model = this.models[collectionName].model;
|
488 | if(permissions.hasPermissions(user,collectionName,'delete'))
|
489 | {
|
490 | model.findById(documentId, function(err, document) {
|
491 | if (err) {
|
492 | console.log('Error retrieving document to delete: ' + err);
|
493 | onReady('Unable to delete');
|
494 | } else {
|
495 | if (!document) {
|
496 | onReady('Document not found');
|
497 | } else {
|
498 | forms.unlinkDependencies(self.models[collectionName].modelName,documentId,function(err) {
|
499 | if(err)
|
500 | onReady('unlink dependencies failed');
|
501 | else {
|
502 | document.remove();
|
503 | MongooseAdminAudit.logActivity(user, self.models[collectionName].modelName, collectionName, documentId, 'del', null, function(err, auditLog) {
|
504 | onReady(null);
|
505 | });
|
506 | }
|
507 | });
|
508 | }
|
509 | }
|
510 | });
|
511 | }
|
512 | else
|
513 | {
|
514 | onReady('unauthorized')
|
515 | }
|
516 | };
|
517 |
|
518 | MongooseAdmin.prototype.orderDocuments =function(user,collectionName,data,onReady)
|
519 | {
|
520 |
|
521 | if(permissions.hasPermissions(user,collectionName,'order'))
|
522 | {
|
523 | var sorting_attr = this.models[collectionName].options.sortable;
|
524 | if(sorting_attr)
|
525 | {
|
526 | for(var id in data)
|
527 | {
|
528 | var set_dict = {};
|
529 | set_dict[sorting_attr] = data[id];
|
530 | this.models[collectionName].model.update({_id:id},{$set:set_dict},function(err,r)
|
531 | {
|
532 | });
|
533 | }
|
534 | }
|
535 |
|
536 | onReady(null);
|
537 | }
|
538 | else
|
539 | onReady('unauthorized');
|
540 | };
|
541 |
|
542 | MongooseAdmin.prototype.actionDocuments =function(user,collectionName,actionId,data,onReady)
|
543 | {
|
544 |
|
545 | if(permissions.hasPermissions(user,collectionName,'action'))
|
546 | {
|
547 | var action = _.find(this.models[collectionName].options.actions, function(action){ return action.value == actionId; });
|
548 | if(action)
|
549 | {
|
550 | action.func(user,data.ids,onReady);
|
551 | }
|
552 | else
|
553 | onReady('no action');
|
554 | }
|
555 | else
|
556 | onReady('unauthorized');
|
557 | };
|
558 |
|
559 |
|
560 |
|
561 |
|
562 |
|
563 |
|
564 |
|
565 |
|
566 |
|
567 |
|
568 | MongooseAdmin.userFromSessionStore = function(sessionStore) {
|
569 | return MongooseAdminUser.fromSessionStore(sessionStore);
|
570 | };
|
571 |
|
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 |
|
579 |
|
580 | MongooseAdmin.prototype.ensureUserExists = function(username, password) {
|
581 | MongooseAdminUser.ensureExists(username, password, function(err, adminUser) {
|
582 | if (!err) {
|
583 | console.log('Created admin user: ' + adminUser.fields.username);
|
584 | }
|
585 | });
|
586 | };
|
587 |
|
588 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 |
|
597 | MongooseAdmin.prototype.login = function(username, password, onReady) {
|
598 | MongooseAdminUser.getByUsernamePassword(username, password, function(err, adminUser) {
|
599 | onReady(err, adminUser);
|
600 | });
|
601 | };
|
602 |
|
603 |
|