diff --git a/client/app/main/main.controller.js b/client/app/main/main.controller.js index 92b0e41..524bfcb 100644 --- a/client/app/main/main.controller.js +++ b/client/app/main/main.controller.js @@ -1,15 +1,24 @@ 'use strict'; angular.module('manticoreApp') .controller('MainCtrl', function ($scope, $http, Auth) { $scope.documents = []; $scope.isLoggedIn = Auth.isLoggedIn; $http.get('/api/documents').success(function(documents) { $scope.documents = documents; }); + $scope.openDocument = function (document) { + $http.get('/api/documents/snapshot/' + _.last(document.chunks)) + .success(function (data) { + + }).error(function (data) { + console.log(data); + }) + }; + $scope.deleteDocument = function(document) { $http.delete('/api/documents/' + document._id); }; }); diff --git a/client/app/main/main.jade b/client/app/main/main.jade index 380f66d..485cf88 100644 --- a/client/app/main/main.jade +++ b/client/app/main/main.jade @@ -1,23 +1,23 @@ div(ng-include='"components/navbar/navbar.html"') header#banner.hero-unit(ng-hide='isLoggedIn() ') .container h1 Manticore p.lead Realtime collaboration for rich office documents. img(src='assets/images/manticore.jpg', alt='Manticore') .container(ng-hide='!isLoggedIn()') .row .col-lg-12 h1.page-header Documents - ul.nav.nav-tabs.nav-stacked.col-md-4.col-lg-4.col-sm-6(ng-repeat='document in documents') + ul.col-md-4.col-lg-4.col-sm-6(ng-repeat='document in documents') li - a(href='#', tooltip='{{document.info}}') + a(ui-sref='editor({id: document._id})') | {{document.title}} //- footer.footer .container p | Manticore = ' | ' a(href='https://github.com/adityab/Manticure/issues?state=open') Issues diff --git a/server/api/document/document.controller.js b/server/api/document/document.controller.js index cc4c1ba..58c6eca 100644 --- a/server/api/document/document.controller.js +++ b/server/api/document/document.controller.js @@ -1,126 +1,141 @@ /** * Using Rails-like standard naming convention for endpoints. * GET /documents -> index * POST /documents -> create * GET /documents/:id -> show * PUT /documents/:id -> update * DELETE /documents/:id -> destroy */ 'use strict'; var _ = require('lodash'); var mongoose = require('mongoose'); var Grid = require('gridfs-stream'); var multer = require('multer'); var DocumentChunk = require('./document.model').DocumentChunk; var Document = require('./document.model').Document; var gfs = Grid(mongoose.connection.db, mongoose.mongo); // Get list of documents exports.index = function(req, res) { Document.find(function (err, documents) { if(err) { return handleError(res, err); } return res.json(200, documents); }); }; // Get a single document exports.show = function(req, res) { Document.findById(req.params.id, function (err, document) { if(err) { return handleError(res, err); } if(!document) { return res.send(404); } return res.json(document); }); }; exports.upload = function (req, res, next) { multer({ upload: null, limits: { fileSize: 1024 * 1024 * 20, // 20 Megabytes files: 5 }, onFileUploadStart: function (file) { - var firstChunk = new DocumentChunk(); + var chunkId = new mongoose.Types.ObjectId(); + + var firstChunk = new DocumentChunk({ + _id: chunkId + }); var newDocument = new Document({ title: file.originalname, creator: req.user._id, - chunks: [firstChunk._id] + chunks: [chunkId] }); this.upload = gfs.createWriteStream({ - _id: mongoose.Types.ObjectId(firstChunk._id), + _id: chunkId, filename: file.originalname, mode: 'w', chunkSize: 1024 * 4, content_type: file.mimetype, root: 'fs' }); this.upload.on('finish', function () { firstChunk.save(function (err) { if (!err) { newDocument.save(); } }); }); }, onFileUploadData: function (file, data) { this.upload.write(data); }, onFileUploadComplete: function (file) { this.upload.end(); } })(req, res, next); }; exports.acknowledgeUpload = function (req, res) { return res.send(200); }; -exports.showFile = function(req, res) { - Document.findById(req.params.id, function (err, document) { +exports.showSnapshot = function(req, res) { + var snapshotId = req.params.id; + + gfs.findOne({_id: snapshotId}, function (err, file) { if (err) { return handleError(res, err); } - if (!document) { return res.send(404); } - DocumentChunks.findById(document.chunks[document.chunks.length - 1]); + if (!file) { return res.send(404); } + + var download = gfs.createReadStream({ + _id: snapshotId + }); + download.on('error', function (err) { + return handleError(res, err); + }); + res.set('Content-Type', file.contentType); + res.attachment(file.filename) + download.pipe(res); }); }; // Creates a new document in the DB. exports.create = function(req, res) { Document.create(req.body, function(err, document) { if(err) { return handleError(res, err); } return res.json(201, document); }); }; // Updates an existing document in the DB. exports.update = function(req, res) { if(req.body._id) { delete req.body._id; } Document.findById(req.params.id, function (err, document) { if (err) { return handleError(res, err); } if(!document) { return res.send(404); } var updated = _.merge(document, req.body); updated.save(function (err) { if (err) { return handleError(res, err); } return res.json(200, document); }); }); }; // Deletes a document from the DB. exports.destroy = function(req, res) { Document.findById(req.params.id, function (err, document) { if(err) { return handleError(res, err); } if(!document) { return res.send(404); } document.remove(function(err) { if(err) { return handleError(res, err); } return res.send(204); }); }); }; function handleError(res, err) { return res.send(500, err); } diff --git a/server/api/document/index.js b/server/api/document/index.js index 75ad489..11385e8 100644 --- a/server/api/document/index.js +++ b/server/api/document/index.js @@ -1,19 +1,19 @@ 'use strict'; var express = require('express'); var controller = require('./document.controller'); var auth = require('../../auth/auth.service'); var router = express.Router(); router.get('/', auth.isAuthenticated(), controller.index); router.get('/:id', auth.isAuthenticated(), controller.show); +router.get('/snapshot/:id', controller.showSnapshot) router.post('/', auth.isAuthenticated(), controller.create); +router.post('/upload', auth.isAuthenticated(), controller.upload, + controller.acknowledgeUpload); router.put('/:id', auth.isAuthenticated(), controller.update); router.patch('/:id', auth.isAuthenticated(), controller.update); router.delete('/:id', auth.isAuthenticated(), controller.destroy); -router.post('/upload', auth.isAuthenticated(), controller.upload, - controller.acknowledgeUpload); - module.exports = router; diff --git a/server/config/express.js b/server/config/express.js index 4129eca..59c79b1 100644 --- a/server/config/express.js +++ b/server/config/express.js @@ -1,45 +1,45 @@ /** * Express configuration */ 'use strict'; var express = require('express'); var favicon = require('serve-favicon'); var morgan = require('morgan'); var compression = require('compression'); var bodyParser = require('body-parser'); var methodOverride = require('method-override'); var cookieParser = require('cookie-parser'); var errorHandler = require('errorhandler'); var path = require('path'); var config = require('./environment'); var passport = require('passport'); module.exports = function(app) { var env = app.get('env'); app.set('views', config.root + '/server/views'); app.set('view engine', 'jade'); app.use(compression()); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use(methodOverride()); app.use(cookieParser()); app.use(passport.initialize()); if ('production' === env) { app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); app.use(express.static(path.join(config.root, 'public'))); app.set('appPath', config.root + '/public'); app.use(morgan('dev')); } if ('development' === env || 'test' === env) { - app.use(require('connect-livereload')()); + app.use(require('connect-livereload')({ignore: ['/api']})); app.use(express.static(path.join(config.root, '.tmp'))); app.use(express.static(path.join(config.root, 'client'))); app.set('appPath', 'client'); app.use(morgan('dev')); app.use(errorHandler()); // Error handler - has to be last } -}; \ No newline at end of file +};