UNPKG

17.8 kBJavaScriptView Raw
1/*!
2 * Mongoose Admin
3 * Copyright (c) 2011 Marc Campbell (marc.e.campbell@gmail.com)
4 * MIT Licensed
5 */
6
7
8 /**
9 * Module dependencies
10 */
11var 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
21exports = module.exports = MongooseAdmin;
22exports.version = '0.0.1';
23
24var app;
25
26/**
27 * Create the admin singleton object
28 *
29 * @param {String} dbUri
30 * @param {Number} port
31 *
32 * @api public
33 */
34exports.createAdmin = function(app,options) {
35// if (options.port) {
36// var app = app || (app = require('http'));
37//
38// app.listen(options.port);
39// require('http/paths').registerPaths(app, '/');
40//
41// console.log('\x1b[36mMongooseAdmin is listening on port: \x1b[0m %d', options.port);
42// console.log('\x1b[36mMongooseAdmin is connected using db: \x1b[0m %s', dbUri);
43//
44// MongooseAdmin.singleton = new MongooseAdmin(app, '');
45// return MongooseAdmin.singleton;
46// } else if (options.app && options.root) {
47 options = options || {};
48 var root = options.root || '';
49 console.log('\x1b[36mMongooseAdmin is listening at path: \x1b[0m %s', root);
50// console.log('\x1b[36mMongooseAdmin is connected using db: \x1b[0m %s', dbUri);
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 * MongooseAdmin Constructor
65 *
66 * @api private
67 */
68function MongooseAdmin(app, root) {
69 //mongoose.connect(dbUri);
70 this.app = app;
71 this.root = root;
72 this.models = {};
73 this.title = "Backoffice";
74};
75
76/**
77 * Build a full path that can be used in a URL
78 *
79 * @param {String} path
80 */
81MongooseAdmin.prototype.buildPath = function(path) {
82 return this.root + path;
83};
84
85MongooseAdmin.prototype.getAdminTitle = function(){
86 return this.title;
87};
88
89MongooseAdmin.prototype.setAdminTitle = function(title)
90{
91 this.title = title;
92};
93
94/**
95 * Push the mongoose-admin express config to the current config
96 *
97 */
98MongooseAdmin.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 * Replace the mongoose-admin express config with the original
106 */
107MongooseAdmin.prototype.popExpressConfig = function(config) {
108 this.app.set('views', config.views);
109};
110
111/**
112 * Stop listening and end the admin process
113 *
114 * @api public
115 */
116MongooseAdmin.prototype.close = function() {
117 this.app.close();
118};
119
120function 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
156MongooseAdmin.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
195MongooseAdmin.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* Register a new mongoose model/schema with admin
205*
206* @param {String} modelName
207* @param {Object} fields
208* @param {Object} options
209*
210* @api public
211*/
212MongooseAdmin.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 * Retrieve a list of all registered models
223 *
224 * @param {Function} onReady
225 *
226 * @api public
227 */
228MongooseAdmin.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 * Get a single model from the registered list with admin
243 *
244 * @param {String} collectionName
245 * @param {Function} onReady
246 *
247 * @api public
248 */
249MongooseAdmin.prototype.getModel = function(collectionName, onReady) {
250 onReady(null, this.models[collectionName].model, this.models[collectionName].fields, this.models[collectionName].options);
251};
252
253/**
254 * Get the counts of a model
255 *
256 * @param {String} collectionName
257 *
258 * @api public
259 */
260MongooseAdmin.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 * List a page of documents from a model
277 *
278 * @param {String} collectionName
279 * @param {Number} start
280 * @param {Number} count
281 * @param {Function} onReady
282 *
283 * @api public
284 */
285MongooseAdmin.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 * Retrieve a single document
339 *
340 * @param {String} collectionName
341 * @param {String} documentId
342 * @param {Function} onReady
343 *
344 * @api public
345 */
346MongooseAdmin.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 * Create a new document
359 *
360 * @param {String} collectionName
361 * @param {Object} params
362 * @param {Function} onReady
363 *
364 * @api public
365 */
366MongooseAdmin.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 //console.log('Error saving document: ' + err);
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 * Update a document
413 *
414 * @param {String} collectionName
415 * @param {String} documentId
416 * @param {Object} params
417 * @param {Function} onReady
418 *
419 * @api public
420 */
421MongooseAdmin.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// console.log('Unable to update document: ' + err);
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 * Delete, remove a document
478 *
479 * @param {String} collectionName
480 * @param {String} documentId
481 * @param {Function} onReady
482 *
483 * @api public
484 */
485MongooseAdmin.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
518MongooseAdmin.prototype.orderDocuments =function(user,collectionName,data,onReady)
519{
520 //console.log(data);
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
542MongooseAdmin.prototype.actionDocuments =function(user,collectionName,actionId,data,onReady)
543{
544 //console.log(data);
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 * Deserialize a user from a session store object
563 *
564 * @param {Object} sessionStore
565 *
566 * @api private
567 */
568MongooseAdmin.userFromSessionStore = function(sessionStore) {
569 return MongooseAdminUser.fromSessionStore(sessionStore);
570};
571
572/**
573 * Create an admin user account
574 *
575 * @param {String} username
576 * @param {Stirng} password
577 *
578 * @api public
579 */
580MongooseAdmin.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 * Log in as a user
590 *
591 * @param {String} username
592 * @param {String} password
593 * @param {Function} onReady
594 *
595 * @api public
596 */
597MongooseAdmin.prototype.login = function(username, password, onReady) {
598 MongooseAdminUser.getByUsernamePassword(username, password, function(err, adminUser) {
599 onReady(err, adminUser);
600 });
601};
602
603