From 49178856862c3d8915f85fb49910d61599666ee3 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sat, 7 Feb 2015 13:49:04 -0500 Subject: [PATCH] add rough client interface \n complete server interface for adding revisions \n remove scaffolding examples --- client/app/document/document.js | 31 + client/app/document/index/index.controller.js | 33 + client/app/document/index/index.jade | 32 + .../revision-new/revision-new.controller.js | 36 + .../document/revision-new/revision-new.jade | 26 + .../document/revision/revision.controller.js | 34 + client/app/document/revision/revision.jade | 28 + client/app/document/show/show.controller.js | 26 + client/app/document/show/show.jade | 27 + client/app/document/wdiff/wdiff.controller.js | 40 + client/app/document/wdiff/wdiff.jade | 19 + client/app/wdiff/wdiff.controller.js | 11 +- client/app/wdiff/wdiff.jade | 14 +- client/app/wdiff/wdiff.js | 2 +- client/components/elements/footer.jade | 8 + client/components/elements/header.jade | 3 + client/index.html | 6 + markdown-format-wdiff.sublime-project | 17 + markdown-format-wdiff.sublime-workspace | 1044 +++++++++++++++++ server/api/document/document.controller.js | 249 ++++ server/api/document/document.model.js | 18 + server/api/document/index.js | 30 + server/api/document/revision.model.js | 17 + server/api/thing/index.js | 15 - server/api/thing/thing.controller.js | 68 -- server/api/thing/thing.model.js | 12 - server/api/thing/thing.spec.js | 20 - server/api/wdiff/wdiff.controller.js | 59 +- server/components/wdiff/index.js | 76 ++ server/config/environment/development.js | 2 +- server/config/seed.js | 113 +- server/routes.js | 2 +- 32 files changed, 1889 insertions(+), 229 deletions(-) create mode 100644 client/app/document/document.js create mode 100644 client/app/document/index/index.controller.js create mode 100644 client/app/document/index/index.jade create mode 100644 client/app/document/revision-new/revision-new.controller.js create mode 100644 client/app/document/revision-new/revision-new.jade create mode 100644 client/app/document/revision/revision.controller.js create mode 100644 client/app/document/revision/revision.jade create mode 100644 client/app/document/show/show.controller.js create mode 100644 client/app/document/show/show.jade create mode 100644 client/app/document/wdiff/wdiff.controller.js create mode 100644 client/app/document/wdiff/wdiff.jade create mode 100644 client/components/elements/footer.jade create mode 100644 client/components/elements/header.jade create mode 100644 markdown-format-wdiff.sublime-project create mode 100644 markdown-format-wdiff.sublime-workspace create mode 100644 server/api/document/document.controller.js create mode 100644 server/api/document/document.model.js create mode 100644 server/api/document/index.js create mode 100644 server/api/document/revision.model.js delete mode 100644 server/api/thing/index.js delete mode 100644 server/api/thing/thing.controller.js delete mode 100644 server/api/thing/thing.model.js delete mode 100644 server/api/thing/thing.spec.js create mode 100644 server/components/wdiff/index.js diff --git a/client/app/document/document.js b/client/app/document/document.js new file mode 100644 index 0000000..2ac1417 --- /dev/null +++ b/client/app/document/document.js @@ -0,0 +1,31 @@ +'use strict'; + +angular.module('markdownFormatWdiffApp') + .config(function ($routeProvider) { + $routeProvider + .when('/', { + templateUrl: 'app/document/index/index.html', + controller: 'DocumentIndexCtrl' + }) + .when('/user/:userid', { + templateUrl: 'app/document/index/index.html', + controller: 'DocumentIndexCtrl' + }) + .when('/:id', { + templateUrl: 'app/document/show/show.html', + controller: 'DocumentShowCtrl' + }) + .when('/:id/revision/new', { + templateUrl: 'app/document/revision-new/revision-new.html', + controller: 'DocumentRevisionNewCtrl' + }) + .when('/:id/revision/:revisionid', { + templateUrl: 'app/document/revision/revision.html', + controller: 'DocumentRevisionCtrl' + }) + .when('/wdiff/:revisionida/:revisionidb', { + templateUrl: 'app/document/wdiff/wdiff.html', + controller: 'DocumentWdiffCtrl' + }); + + }); \ No newline at end of file diff --git a/client/app/document/index/index.controller.js b/client/app/document/index/index.controller.js new file mode 100644 index 0000000..a1a012a --- /dev/null +++ b/client/app/document/index/index.controller.js @@ -0,0 +1,33 @@ +'use strict'; + +angular.module('markdownFormatWdiffApp') + .controller('DocumentIndexCtrl', function ($scope, $routeParams, $http, Auth, $location) { + $scope.documents = []; + $scope.newDocumentTitle; + + $scope.getCurrentUser = Auth.getCurrentUser; + $scope.isLoggedIn = Auth.isLoggedIn; + + // if routeParams specifies a user, restrict the query to that user + var path = '/api/documents'+ ($routeParams.userid ? '/owner/'+$routeParams.userid : ''); + $http.get(path).success(function(documents) { + $scope.documents = documents; + }); + + + $scope.createDocument = function () { + if ($scope.newDocumentTitle == "") + return + + //save the document + $http.post('/api/documents', {title: $scope.newDocumentTitle}) + .success(function(newDocument) { + $scope.documents.push(newDocument); + //and document view page + $location.path('/'+newDocument._id); + }); + + }; + + $scope.json = function (object) { return JSON.stringify(object, null, " "); }; + }) diff --git a/client/app/document/index/index.jade b/client/app/document/index/index.jade new file mode 100644 index 0000000..d2d881f --- /dev/null +++ b/client/app/document/index/index.jade @@ -0,0 +1,32 @@ +nav(ng-include='"components/navbar/navbar.html"') + +nav(ng-include='"components/elements/header.html"', onload='title = "documents"') + +.container + .row(ng-show='isLoggedIn()') + .col-lg-9.col-md-12.center-block + form.form-inline + .form-group + label(for='title-input') + | New Document + input.form-control(type='text', id='title-input', placeholder='title', ng-model='newDocumentTitle') + button.btn.btn-default(type='submit', ng-click='createDocument()') + | Create + + .row + .col-lg-6.col-md-12(ng-repeat='document in documents | orderBy:"currentRevision.created":true' ) + h4 + a(href='/{{document._id}}') + {{document.title}} + div + p + | State: + {{document.currentRevision.state}} + p + | Updated: + {{document.currentRevision.created}} + pre + {{json(document)}} + + +footer(ng-include='"components/elements/footer.html"') \ No newline at end of file diff --git a/client/app/document/revision-new/revision-new.controller.js b/client/app/document/revision-new/revision-new.controller.js new file mode 100644 index 0000000..9beeaa3 --- /dev/null +++ b/client/app/document/revision-new/revision-new.controller.js @@ -0,0 +1,36 @@ +'use strict'; + +angular.module('markdownFormatWdiffApp') + .controller('DocumentRevisionNewCtrl', function ($scope, $routeParams, $http, Auth, $location) { + $scope.revision = {}; + + + $scope.getCurrentUser = Auth.getCurrentUser; + $scope.isLoggedIn = Auth.isLoggedIn; + + $scope.isOwner = function () { + var currentUser = Auth.getCurrentUser(); + if (!currentUser || !$scope.revision || !$scope.revision.owner) + return false; + + return $scope.revision.owner._id == currentUser._id; + }; + + var path = '/api/documents/' + $routeParams.id; + $http.get(path).success(function(revision) { + $scope.document = document; + $scope.revision = angular.copy(document.currentRevision); + }); + + + $scope.saveRevision = function() { + //save the revision to the document + $http.post('/api/documents/'+$routeParams.id+'/revisions', $scope.revision) + .success(function(newRevision) { + //and redirect to the revision view page + $location.path('/'+$routeParams.id+'/revision/'+newRevision._id); + }); + }; + + $scope.json = function (object) { return JSON.stringify(object, null, " "); }; + }) diff --git a/client/app/document/revision-new/revision-new.jade b/client/app/document/revision-new/revision-new.jade new file mode 100644 index 0000000..470b329 --- /dev/null +++ b/client/app/document/revision-new/revision-new.jade @@ -0,0 +1,26 @@ +nav(ng-include='"components/navbar/navbar.html"') + +nav(ng-include='"components/elements/header.html"', ng-onload='title = {{revision.created}}') + +.container + .row + form.col-lg-9.col-md-12.center-block + h4 + {{revision.document.title}} - {{revision.created}} + .form-group + label(for='status-input') + | Status + input.form-control(type='text', id='status-input', ng-model='revision.status') + .form-group + label(for='content-input') + | Content + textarea.form-control(id='content-input', ng-model='revision.content') + button.btn.btn-default(ng-click='saveRevision()') + | Save + + .row + pre.col-lg-9.col-md-12.center-block + {{json(revision)}} + + +footer(ng-include='"components/elements/footer.html"') \ No newline at end of file diff --git a/client/app/document/revision/revision.controller.js b/client/app/document/revision/revision.controller.js new file mode 100644 index 0000000..38f00e9 --- /dev/null +++ b/client/app/document/revision/revision.controller.js @@ -0,0 +1,34 @@ +'use strict'; + +angular.module('markdownFormatWdiffApp') + .controller('DocumentRevisionCtrl', function ($scope, $routeParams, $http, Auth) { + $scope.revision = {}; + + $scope.getCurrentUser = Auth.getCurrentUser; + $scope.isLoggedIn = Auth.isLoggedIn; + + //returns true if the current user is logged in and is the owner of this document / revision + $scope.isOwner = function () { + var currentUser = Auth.getCurrentUser(); + if (!currentUser || !$scope.revision || !$scope.revision.owner) + return false; + + return $scope.revision.owner._id == currentUser._id; + } + + //returns true if this revision is the current revision in its document + $scope.isCurrent = function () { + if (!$scope.revision) + return false; + + return $scope.revision._id == $scope.revision.document.currentRevision; + } + + var path = '/api/documents/'+$routeParams.id+'/revisions/' + $routeParams.revisionid; + $http.get(path).success(function(revision) { + $scope.revision = revision; + }); + + $scope.json = function (object) { return JSON.stringify(object, null, " "); }; + + }) diff --git a/client/app/document/revision/revision.jade b/client/app/document/revision/revision.jade new file mode 100644 index 0000000..fa8dbd2 --- /dev/null +++ b/client/app/document/revision/revision.jade @@ -0,0 +1,28 @@ +nav(ng-include='"components/navbar/navbar.html"') + +nav(ng-include='"components/elements/header.html"', ng-onload='title = {{revision.created}}') + +.container + .row + span(ng-show='isCurrent()') + | Current revision + a.btn.btn-primary(ng-hide='isCurrent()' href='/wdiff/{{revision._id}}/{{revision.document.currentRevision}}') + | wdiff current + + .row + .col-lg-6.col-md-9.center-block + h4 + {{revision.created}} + div + p + | State: + {{revision.state}} + p + | Updated: + {{revision.created}} + div(btf-markdown='revision.content') + pre + {{json(revision)}} + + +footer(ng-include='"components/elements/footer.html"') \ No newline at end of file diff --git a/client/app/document/show/show.controller.js b/client/app/document/show/show.controller.js new file mode 100644 index 0000000..f4d9439 --- /dev/null +++ b/client/app/document/show/show.controller.js @@ -0,0 +1,26 @@ +'use strict'; + +angular.module('markdownFormatWdiffApp') + .controller('DocumentShowCtrl', function ($scope, $routeParams, $http, Auth) { + $scope.document = {}; + + $scope.getCurrentUser = Auth.getCurrentUser; + $scope.isLoggedIn = Auth.isLoggedIn; + + $scope.isOwner = function () { + var currentUser = Auth.getCurrentUser(); + if (!currentUser || !$scope.document || !$scope.document.owner) + return false; + + return $scope.document.owner._id == currentUser._id; + } + + // if routeParams specifies a user, restrict the query to that user + var path = '/api/documents/' + $routeParams.id; + $http.get(path).success(function(document) { + $scope.document = document; + }); + + $scope.json = function (object) { return JSON.stringify(object, null, " "); }; + + }) diff --git a/client/app/document/show/show.jade b/client/app/document/show/show.jade new file mode 100644 index 0000000..f620479 --- /dev/null +++ b/client/app/document/show/show.jade @@ -0,0 +1,27 @@ +nav(ng-include='"components/navbar/navbar.html"') + +nav(ng-include='"components/elements/header.html"', ng-onload='title = {{document.title}}') + +.container + .row(ng-show='isOwner()') + form.col-lg-12 + a.btn.btn-primary(href='/{{document._id}}/revision/new') + | New Revision + + .row(ng-repeat='revision in document.revisions | orderBy:"created":true' ) + .col-lg-6.col-md-9.center-block + h4 + a(href='/{{document._id}}/revision/{{revision._id}}') + {{revision.created}} + div + p + | State: + {{revision.state}} + p + | Updated: + {{revision.created}} + pre + | {{json(revision)}} + + +footer(ng-include='"components/elements/footer.html"') \ No newline at end of file diff --git a/client/app/document/wdiff/wdiff.controller.js b/client/app/document/wdiff/wdiff.controller.js new file mode 100644 index 0000000..657586c --- /dev/null +++ b/client/app/document/wdiff/wdiff.controller.js @@ -0,0 +1,40 @@ +'use strict'; + +angular.module('markdownFormatWdiffApp') + .controller('DocumentWdiffCtrl', function ($scope, $routeParams, $http, Auth) { + $scope.wdiff = ""; + $scope.same = false; + $scope.revisionA = {}; + $scope.revisionB = {}; + + $scope.getCurrentUser = Auth.getCurrentUser; + $scope.isLoggedIn = Auth.isLoggedIn; +/* + //returns true if the current user is logged in and is the owner of this document / revision + $scope.isOwner = function () { + var currentUser = Auth.getCurrentUser(); + if (!currentUser || !$scope.revision || !$scope.revision.owner) + return false; + + return $scope.revision.owner._id == currentUser._id; + } + + //returns true if this revision is the current revision in its document + $scope.isCurrent = function () { + if (!$scope.revision) + return false; + + return $scope.revision._id == $scope.revision.document.currentRevision._id; + } +*/ + var path = '/api/documents/wdiff/'+$routeParams.revisionida+'/'+$routeParams.revisionidb; + $http.get(path).success(function(result) { + $scope.result = result; + $scope.wdiff = result.wdiff; + $scope.revisionA = result.a; + $scope.revisionB = result.b; + }); + + $scope.json = function (object) { return JSON.stringify(object, null, " "); }; + + }) diff --git a/client/app/document/wdiff/wdiff.jade b/client/app/document/wdiff/wdiff.jade new file mode 100644 index 0000000..176a559 --- /dev/null +++ b/client/app/document/wdiff/wdiff.jade @@ -0,0 +1,19 @@ +wdiff.jadenav(ng-include='"components/navbar/navbar.html"') + +nav(ng-include='"components/elements/header.html"', ng-onload='title = {{revision.created}}') + +.container + .row + //!!! add controls to view a and b + + .row + .col-lg-6.col-md-9.center-block + h4 + | wdiff {{a.created}} " -- " {{b.created}} + div + div(btf-markdown='wdiff') + pre + {{json(result)}} + + +footer(ng-include='"components/elements/footer.html"') \ No newline at end of file diff --git a/client/app/wdiff/wdiff.controller.js b/client/app/wdiff/wdiff.controller.js index 0de17b2..58cd11b 100644 --- a/client/app/wdiff/wdiff.controller.js +++ b/client/app/wdiff/wdiff.controller.js @@ -9,18 +9,13 @@ angular.module('markdownFormatWdiffApp') $scope.displayAsMarkdown = true; $scope.compare = function() { - $http.post('/api/wdiff', //+($scope.displayAsMarkdown ? '/markdown': ''), + $http.post('/api/wdiff'+($scope.displayAsMarkdown ? '/markdown': ''), { a: $scope.docA, b: $scope.docB }, {headers:{"Content-Type":"application/json"}}) .success(function (data) { if ($scope.displayAsMarkdown) { - var markdown = data.wdiff; - markdown = markdown.replace(/\[-/g, ''); - markdown = markdown.replace(/-\]/g, ''); - markdown = markdown.replace(/{\+/g, ''); - markdown = markdown.replace(/\+}/g, ''); - - $scope.wdiffMarkdown = markdown; //data.markdown; + + $scope.wdiffMarkdown = data.markdown; //data.markdown; $scope.wdiff = ''; } else { diff --git a/client/app/wdiff/wdiff.jade b/client/app/wdiff/wdiff.jade index ee5f052..f11c06d 100644 --- a/client/app/wdiff/wdiff.jade +++ b/client/app/wdiff/wdiff.jade @@ -1,8 +1,6 @@ -div(ng-include='"components/navbar/navbar.html"') +nav(ng-include='"components/navbar/navbar.html"') -header#banner.hero-unit - .container - h1 wdiff +nav(ng-include='"components/elements/header.html"', onload='title = "wdiff"') .container .row @@ -43,10 +41,4 @@ header#banner.hero-unit textarea.form-control(id='docB', ng-model='docB') -footer.footer - .container - p - | Wdiff online - = ' | ' - a(href='https://madanworb.com') Adam Brown - \ No newline at end of file +footer(ng-include='"components/elements/footer.html"') \ No newline at end of file diff --git a/client/app/wdiff/wdiff.js b/client/app/wdiff/wdiff.js index f9899a3..245f063 100644 --- a/client/app/wdiff/wdiff.js +++ b/client/app/wdiff/wdiff.js @@ -3,7 +3,7 @@ angular.module('markdownFormatWdiffApp') .config(function ($routeProvider) { $routeProvider - .when('/', { + .when('/test/wdiff', { templateUrl: 'app/wdiff/wdiff.html', controller: 'WdiffCtrl' }); diff --git a/client/components/elements/footer.jade b/client/components/elements/footer.jade new file mode 100644 index 0000000..debdbaa --- /dev/null +++ b/client/components/elements/footer.jade @@ -0,0 +1,8 @@ +div.footer + .container + p + a(href='https://madanworb.com') Adam Brown + = ' | ' + | This website is + a(href='https://github.com/adamarthurryan/wdiff-markdown-editor') open source + | . \ No newline at end of file diff --git a/client/components/elements/header.jade b/client/components/elements/header.jade new file mode 100644 index 0000000..9554f5b --- /dev/null +++ b/client/components/elements/header.jade @@ -0,0 +1,3 @@ +header#banner.hero-unit + .container + h1 {{title}} \ No newline at end of file diff --git a/client/index.html b/client/index.html index 308f9a6..36e4cee 100644 --- a/client/index.html +++ b/client/index.html @@ -68,6 +68,12 @@ + + + + + + diff --git a/markdown-format-wdiff.sublime-project b/markdown-format-wdiff.sublime-project new file mode 100644 index 0000000..35c9165 --- /dev/null +++ b/markdown-format-wdiff.sublime-project @@ -0,0 +1,17 @@ +{ + "folders": + [ + { + "follow_symlinks": true, + "path": "client" + }, + { + "follow_symlinks": true, + "path": "e2e" + }, + { + "follow_symlinks": true, + "path": "server" + } + ] +} diff --git a/markdown-format-wdiff.sublime-workspace b/markdown-format-wdiff.sublime-workspace new file mode 100644 index 0000000..bae075c --- /dev/null +++ b/markdown-format-wdiff.sublime-workspace @@ -0,0 +1,1044 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "wdif", + "wdiffMarkdown" + ], + [ + "selected", + "selectedProviders" + ], + [ + "btn", + "btn-primary" + ], + [ + "definition", + "definition" + ], + [ + "tran", + "translateLanguage" + ], + [ + "exp", + "expressionSearch" + ], + [ + "define", + "defineMeaning" + ], + [ + "colo", + "colorFactor" + ], + [ + "dup", + "duplicate_user" + ], + [ + "import_exter", + "import_external_images_nonce" + ], + [ + "page-break", + "page-break-inside" + ], + [ + "array_key", + "array_key_exists" + ], + [ + "guess", + "guess" + ], + [ + "category", + "category_list" + ], + [ + "posts_", + "posts_to_fix" + ], + [ + "in", + "inline-block" + ], + [ + "text-", + "text-decoration" + ], + [ + "get_ca", + "get_cat_id" + ], + [ + "flatten", + "flatten" + ], + [ + "con", + "contains" + ], + [ + "add", + "addClass" + ], + [ + "h", + "hidden" + ], + [ + "page", + "page-break-before" + ] + ] + }, + "buffers": + [ + { + "file": "client/app/wdiff/wdiff.controller.js", + "settings": + { + "buffer_size": 1167, + "line_ending": "Unix" + } + }, + { + "settings": + { + "buffer_size": 0, + "line_ending": "Unix" + } + }, + { + "file": "server/api/wdiff/index.js", + "settings": + { + "buffer_size": 248, + "line_ending": "Unix" + } + }, + { + "file": "server/api/wdiff/wdiff.controller.js", + "settings": + { + "buffer_size": 2687, + "line_ending": "Unix" + } + }, + { + "file": "client/index.html", + "settings": + { + "buffer_size": 3904, + "line_ending": "Unix" + } + }, + { + "file": "bower.json", + "settings": + { + "buffer_size": 617, + "line_ending": "Unix" + } + }, + { + "file": "client/app/app.js", + "settings": + { + "buffer_size": 1515, + "line_ending": "Unix" + } + }, + { + "file": "client/app/wdiff/wdiff.js", + "settings": + { + "buffer_size": 224, + "line_ending": "Unix" + } + }, + { + "file": "client/app/wdiff/wdiff.jade", + "settings": + { + "buffer_size": 1122, + "line_ending": "Unix" + } + }, + { + "file": "client/app/wdiff/wdiff.scss", + "settings": + { + "buffer_size": 553, + "line_ending": "Unix" + } + }, + { + "file": "bin/markdown-format-wdiff", + "settings": + { + "buffer_size": 711, + "line_ending": "Unix" + } + }, + { + "file": "bin/markdown-git-changes", + "settings": + { + "buffer_size": 2724, + "line_ending": "Unix" + } + }, + { + "contents": "'use strict';\n\nangular.module('markdownFormatWdiffApp')\n .config(function ($routeProvider) {\n $routeProvider\n .when('/oldmain', {\n templateUrl: 'app/main/main.html',\n controller: 'MainCtrl'\n });\n });", + "file": "client/app/main/main.js", + "file_size": -1, + "file_write_time": -1, + "settings": + { + "buffer_size": 228, + "line_ending": "Unix" + } + } + ], + "build_system": "", + "command_palette": + { + "height": 157.0, + "selected_items": + [ + [ + "Package Control: ins", + "Package Control: Install Package" + ], + [ + "install", + "Package Control: Install Package" + ], + [ + "isntall", + "Package Control: Install Package" + ], + [ + "Package Control: in", + "Package Control: Install Package" + ] + ], + "width": 571.0 + }, + "console": + { + "height": 256.0, + "history": + [ + "import urllib.request,os; pr='Preferences.sublime-settings'; ip='ignored_packages'; n='Package Control'; s=sublime.load_settings(pr); ig=s.get(ip); ig.append(n); s.set(ip,ig); sublime.save_settings('Preferences.sublime-settings'); pf=n+'.sublime-package'; urllib.request.install_opener(urllib.request.build_opener(urllib.request.ProxyHandler())); by=urllib.request.urlopen('https://packagecontrol.io/'+pf.replace(' ','%20')).read(); open(os.path.join(sublime.installed_packages_path(),pf),'wb').write(by); ig.remove(n); s.set(ip,ig); sublime.save_settings(pr); print('Package Control: 3.0.0 upgrade successful!')", + "import urllib.request,os,hashlib; h = '7183a2d3e96f11eeadd761d777e62404' + 'e330c659d4bb41d3bdf022e94cab3cd0'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)", + "Package Control: Install Package", + "import urllib.request,os,hashlib; h = '7183a2d3e96f11eeadd761d777e62404' + 'e330c659d4bb41d3bdf022e94cab3cd0'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)", + "alert(\"a\")", + "help" + ] + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": true, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "expanded_folders": + [ + "/Z/markdown-format-wdiff", + "/Z/markdown-format-wdiff/bin", + "/Z/markdown-format-wdiff/client", + "/Z/markdown-format-wdiff/client/app", + "/Z/markdown-format-wdiff/client/app/wdiff", + "/Z/markdown-format-wdiff/server", + "/Z/markdown-format-wdiff/server/api", + "/Z/markdown-format-wdiff/server/api/wdiff", + "/Z/markdown-format-wdiff/server/config", + "/Z/markdown-format-wdiff/server/config/environment" + ], + "file_history": + [ + "/Z/lookup-lists/Gruntfile.js", + "/Z/docker/data/lookup-lists/client/app/list/list/list.jade", + "/Z/docker/data/lookup-lists/client/app/list/list/list.controller.js", + "/Z/docker/data/lookup-lists/client/app/list/item/item.jade", + "/Z/docker/data/lookup-lists/server/api/lookup/index.js", + "/Z/docker/data/lookup-lists/server/components/lookup/index.js", + "/Z/docker/data/lookup-lists/server/components/lookup/omegawiki.js", + "/Z/docker/data/lookup-lists/server/components/lookup/openlibrary.js", + "/Z/docker/data/lookup-lists/server/components/lookup/dbpedia.js", + "/Z/docker/data/lookup-lists/client/app/list/list.js", + "/Z/docker/data/lookup-lists/client/app/list/item/item.scss", + "/Z/docker/data/lookup-lists/server/components/lookup/panlex.js", + "/Z/docker/data/lookup-lists/client/app/list/item/item.controller.js", + "/C/Users/adam/AppData/Roaming/Sublime Text 3/Packages/User/Preferences.sublime-settings", + "/C/Users/adam/AppData/Roaming/Sublime Text 3/Packages/Default/Preferences.sublime-settings", + "/Z/docker/data/lookup-lists/client/app/list/index/index.jade", + "/Z/docker/data/lookup-lists/client/index.html", + "/Z/docker/data/lookup-lists/server/api/list/list.controller.js", + "/Z/docker/data/lookup-lists/server/api/lookup/controller.js", + "/Z/docker/data/lookup-lists/client/app/list/view/list.scss", + "/Z/docker/data/lookup-lists/client/app/list/edit/item.scss", + "/Z/docker/data/lookup-lists/server/config/express.js", + "/Z/docker/data/lookup-lists/client/app/list/edit/item.jade", + "/Z/docker/data/lookup-lists/client/app/list/edit/item.controller.js", + "/Z/docker/data/lookup-lists/server/config/environment/production.js", + "/Z/docker/data/lookup-lists/server/config/environment/test.js", + "/Z/docker/data/lookup-lists/server/config/environment/development.js", + "/Z/docker/data/lookup-lists/client/app/list/view/list.jade", + "/Z/docker/data/lookup-lists/server/api/list/index.js", + "/Z/docker/data/lookup-lists/client/app/list/view/list.controller.js", + "/Z/docker/docker-mean-dev/startup", + "/C/Windows/System32/drivers/etc/hosts", + "/Z/docker/data/lookup-lists/client/app/list/index/index.controller.js", + "/Z/docker/data/lookup-lists/client/app/list/edit/edit.controller.js", + "/Z/docker/data/lookup-lists/client/app/main/main.js", + "/Z/docker/data/lookup-lists/client/components/navbar/navbar.jade", + "/Z/docker/data/lookup-lists/client/app/app.scss", + "/Z/docker/data/lookup-lists/bower.json", + "/Z/docker/data/lookup-lists/client/app/lookup/lookup.jade", + "/Z/docker/data/lookup-lists/client/app/list/edit/edit.jade", + "/Z/docker/data/lookup-lists/client/app/list/index/username.filter.js", + "/Z/docker/data/lookup-lists/server/api/list/list.model.js", + "/Z/docker/data/lookup-lists/server/api/list/list.socket.js", + "/Z/docker/data/lookup-lists/client/components/navbar/navbar.controller.js", + "/Z/docker/data/lookup-lists/client/app/lists/lists.controller.js", + "/Z/docker/data/lookup-lists/client/app/lists/lists.jade", + "/Z/docker/data/lookup-lists/client/app/lists/lists.scss", + "/Z/docker/data/lookup-lists/client/app/lookup/lookup.js", + "/Z/docker/data/lookup-lists/server/config/local.env.js", + "/Z/docker/data/lookup-lists/server/config/seed.js", + "/Z/docker/data/lookup-lists/client/app/lists/lists.js", + "/Z/docker/data/lookup-lists/Gruntfile.js", + "/Z/docker/docker-mean-dev/Dockerfile", + "/Z/docker/docker-mean-dev/bootstrap.sh", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/api/lookup/index.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/index.html", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/app/lookup/lookup.controller.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/app/lookup/lookup.scss", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/app/lookup/lookup.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/package.json", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/routes.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/api/lookup/controller.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/app/lookup/lookup.jade", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/bower_components/angular-sanitize/angular-sanitize.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/Gruntfile.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/api/list/list.controller.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/components/lookup/omegawiki.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/components/lookup/index.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/bower_components/jquery/src/css/addGetHookIf.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/client/bower_components/jquery/src/wrap.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/api/lookup/omegawiki/index.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-lists/server/api/lookup/provider.js", + "/C/Users/adam/Google Drive/projects/contracts/2014 lieutenants pump/product/wireless network manifest.txt", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/app/views/sparql-query.jade", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/app/views/index.jade", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/config/seed.js", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/app/controllers/home.js", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/app/views/omegawiki.jade", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/app/controllers/sparql.js", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/app/controllers/omegawiki.js", + "/C/Users/adam/AppData/Local/Temp/Temp1_BabelNet-API-3.0.zip/BabelNet-API-3.0/licenses/README", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/app/views/sparql-results.jade", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/config/express.js", + "/C/Users/adam/Downloads/karl-foaf.xrdf", + "/C/Users/adam/Downloads/foaf.rdf", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/SPARQL examples.md", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/package.json", + "/C/Users/adam/Desktop/vagrant/node/learning-sparql/README.md", + "/C/Users/adam/Downloads/wibi-ver1.0/README.txt", + "/C/cygwin/home/adam/.profile", + "/C/cygwin/home/adam/.bash_profile", + "/C/cygwin/home/adam/.bashrc", + "/C/Users/adam/AppData/Local/Temp/Temp1_wn-wikt.zip/data/cldr/wn-cldr-eng.tab", + "/C/Users/adam/AppData/Local/Temp/Temp1_wn-wikt.zip/data/cldr/wn-cldr-afr.tab", + "/C/Users/adam/AppData/Local/Temp/Temp1_wn-wikt.zip/data/wikt/wn-wikt-kur.tab", + "/C/Users/adam/AppData/Local/Temp/Temp1_wn-wikt.zip/data/wikt/wn-wikt-ibl.tab", + "/C/Users/adam/AppData/Local/Temp/Temp1_wn-wikt.zip/data/wikt/wn-wikt-hrx.tab", + "/C/Users/adam/AppData/Local/Temp/Temp1_wn-wikt.zip/data/wikt/wn-wikt-dng.tab", + "/C/Users/adam/AppData/Local/Temp/Temp1_wn-wikt.zip/data/README", + "/C/Users/adam/Desktop/vagrant/node/language-learning-panlex/server/api/dictionary/language.model.js", + "/C/Users/adam/Desktop/vagrant/node/language-learning-panlex/server/api/dictionary/index.js", + "/C/Users/adam/Desktop/vagrant/node/language-learning-panlex/server/api/dictionary/dictionary.controller.js", + "/C/Users/adam/Desktop/vagrant/node/language-learning-panlex/.gitignore", + "/C/Users/adam/Desktop/vagrant/node/README.md", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-notes/.gitignore", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/api/note/note.model.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/routes.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/api/user/user.model.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/api/note/note.controller.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/config/seed.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/api/note/index.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/Gruntfile.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/package.json", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/config/local.env.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-hello/server/views/404.jade", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-holmes/Gruntfile.js", + "/C/Users/adam/Desktop/vagrant/node/scripttest/Gruntfile.js", + "/C/Users/adam/Desktop/vagrant/node/scripttest/package.json", + "/C/Users/adam/Desktop/vagrant/node/angular-phonecat/app/index.html", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-holmes/package.json", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-holmes/app.js", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-holmes/bower.json", + "/C/Users/adam/Desktop/vagrant/node/learning-mean-holmes/bin/www", + "/C/Users/adam/Desktop/git/learning-nodejs-hello/README.md", + "/C/Users/adam/Desktop/git/learning-nodejs-passport/README.md", + "/C/Users/adam/Desktop/git/learning-nodejs-passport/test2/Gruntfile.js", + "/C/Users/adam/Desktop/git/learning-nodejs-passport/test.sh", + "/C/Users/adam/Desktop/git/learning-nodejs-passport/bootstrap-npm.sh" + ], + "find": + { + "height": 43.0 + }, + "find_in_files": + { + "height": 117.0, + "where_history": + [ + "C:\\Users\\adam\\Desktop\\vagrant\\node\\scripttest", + "C:\\Users\\adam\\Desktop\\git\\learning-nodejs-passport\\scripttest", + "", + "C:\\Users\\adam\\Google Drive\\projects\\library-private\\website\\theme\\newlibrary-catalog-basic", + "C:\\Users\\adam\\Google Drive\\projects\\library-private\\website\\theme\\twentyfourteen", + "C:\\Users\\adam\\Google Drive\\projects\\library-public\\website\\theme\\twentyfourteen" + ] + }, + "find_state": + { + "case_sensitive": false, + "find_history": + [ + "livereload", + "35729", + "!!!", + "open", + "runt.task.run", + "open", + "xdg", + "git", + " <", + "callback", + "ListViewCtrl", + " console.log(\"found one!\");\n console.log(resource);\n", + "isloggedin", + "user", + "!list.user == req.user", + "(!list.user == req.user)", + "open", + "xdg", + "fucks", + "main", + "open", + "wait", + "express", + "express:dev", + "aggregate", + "type", + "prop", + "promise", + "mongo", + "reload", + "signup", + "localhost", + "process.env.PORT", + "process.env", + "9000", + "serve:", + "local", + "127", + "local", + "TEMPLATE_DIRS", + "provision", + "[", + "python", + "flash", + "initializeCapture", + "elapsed", + "setupMouse", + "firebolt", + "h1", + "Owner", + "ruby", + "entry-footer", + "return_f", + " a ", + " a", + "h1", + "Alegreya Sans SC", + "entry-title", + "font-family", + "leto", + "script", + "font", + "newlibrary_catalog_basic_posted_on", + "date", + "newlibrary_catalog_basic_categorized_blog", + "thum", + "posts_to_fix", + "$html", + "external_image_options", + "add_media_page", + "menu", + "external_image_import_all_ajax", + "external_images_verify_permission", + "admin", + "json_encode", + "can", + "user_can", + "action", + "query", + "h3", + "h4", + "h5", + " ", + "", + "", + "", + "
    ", + "", + " style=\"color: #ffffff;\"", + "blockquote", + ":before", + "nth", + "home", + "2.0", + "10.1", + "get_sidebar", + "content-", + "content:", + "content", + "10.0", + "entry-title", + "10.0", + "2.0", + "add_query", + "wp_register", + "wp_register_file", + "wp_register_style", + "font", + "Lato", + "link", + "Lato", + "link", + "Lato", + "font", + "nth-child", + "nth-of", + "downlo", + "type", + ".post-thumbnail", + ".post-image", + "absolute", + "entry-header", + "FEATURED-CONTENT", + "FEATURED-CONTENT-INNER", + "archive-header", + "entry-header", + "#767676" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": false, + "replace_history": + [ + "list.user != req.user", + "", + ",", + "", + "\\n" + ], + "reverse": false, + "show_context": true, + "use_buffer2": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "selected": 1, + "sheets": + [ + { + "buffer": 0, + "file": "client/app/wdiff/wdiff.controller.js", + "semi_transient": false, + "settings": + { + "buffer_size": 1167, + "regions": + { + }, + "selection": + [ + [ + 491, + 491 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JavaScript.tmLanguage", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 1, + "type": "text" + }, + { + "buffer": 1, + "semi_transient": false, + "settings": + { + "buffer_size": 0, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "default_dir": "Z:\\markdown-format-wdiff\\server\\api", + "syntax": "Packages/Text/Plain text.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 0, + "type": "text" + }, + { + "buffer": 2, + "file": "server/api/wdiff/index.js", + "semi_transient": false, + "settings": + { + "buffer_size": 248, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JavaScript.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 11, + "type": "text" + }, + { + "buffer": 3, + "file": "server/api/wdiff/wdiff.controller.js", + "semi_transient": false, + "settings": + { + "buffer_size": 2687, + "regions": + { + }, + "selection": + [ + [ + 2524, + 2524 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JavaScript.tmLanguage", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 1142.0, + "zoom_level": 1.0 + }, + "stack_index": 2, + "type": "text" + }, + { + "buffer": 4, + "file": "client/index.html", + "semi_transient": false, + "settings": + { + "buffer_size": 3904, + "regions": + { + }, + "selection": + [ + [ + 2796, + 2796 + ] + ], + "settings": + { + "syntax": "Packages/HTML/HTML.tmLanguage", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 810.0, + "zoom_level": 1.0 + }, + "stack_index": 8, + "type": "text" + }, + { + "buffer": 5, + "file": "bower.json", + "semi_transient": false, + "settings": + { + "buffer_size": 617, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JSON.tmLanguage", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 9, + "type": "text" + }, + { + "buffer": 6, + "file": "client/app/app.js", + "semi_transient": false, + "settings": + { + "buffer_size": 1515, + "regions": + { + }, + "selection": + [ + [ + 302, + 302 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JavaScript.tmLanguage", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 10, + "type": "text" + }, + { + "buffer": 7, + "file": "client/app/wdiff/wdiff.js", + "semi_transient": false, + "settings": + { + "buffer_size": 224, + "regions": + { + }, + "selection": + [ + [ + 127, + 127 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JavaScript.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 5, + "type": "text" + }, + { + "buffer": 8, + "file": "client/app/wdiff/wdiff.jade", + "semi_transient": false, + "settings": + { + "buffer_size": 1122, + "regions": + { + }, + "selection": + [ + [ + 417, + 417 + ] + ], + "settings": + { + "syntax": "Packages/Jade/Syntaxes/Jade.tmLanguage", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 378.0, + "zoom_level": 1.0 + }, + "stack_index": 3, + "type": "text" + }, + { + "buffer": 9, + "file": "client/app/wdiff/wdiff.scss", + "semi_transient": false, + "settings": + { + "buffer_size": 553, + "regions": + { + }, + "selection": + [ + [ + 106, + 106 + ] + ], + "settings": + { + "syntax": "Packages/Sass/Syntaxes/Sass.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 4, + "type": "text" + }, + { + "buffer": 10, + "file": "bin/markdown-format-wdiff", + "semi_transient": false, + "settings": + { + "buffer_size": 711, + "regions": + { + }, + "selection": + [ + [ + 501, + 501 + ] + ], + "settings": + { + "syntax": "Packages/ShellScript/Shell-Unix-Generic.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 6, + "type": "text" + }, + { + "buffer": 11, + "file": "bin/markdown-git-changes", + "semi_transient": true, + "settings": + { + "buffer_size": 2724, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "syntax": "Packages/ShellScript/Shell-Unix-Generic.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 7, + "type": "text" + }, + { + "buffer": 12, + "file": "client/app/main/main.js", + "semi_transient": false, + "settings": + { + "buffer_size": 228, + "regions": + { + }, + "selection": + [ + [ + 134, + 134 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JavaScript.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 12, + "type": "text" + } + ] + } + ], + "incremental_find": + { + "height": 26.0 + }, + "input": + { + "height": 35.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ] + ], + "cols": + [ + 0.0, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": true, + "output.exec": + { + "height": 207.0 + }, + "output.find_results": + { + "height": 0.0 + }, + "project": "markdown-format-wdiff.sublime-project", + "replace": + { + "height": 50.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "selected_items": + [ + [ + "wait", + "client\\bower_components\\angular-sanitize\\angular-sanitize.js" + ] + ], + "width": 0.0 + }, + "select_project": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "select_symbol": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "selected_group": 0, + "settings": + { + }, + "show_minimap": false, + "show_open_files": true, + "show_tabs": false, + "side_bar_visible": true, + "side_bar_width": 321.0, + "status_bar_visible": true, + "template_settings": + { + } +} diff --git a/server/api/document/document.controller.js b/server/api/document/document.controller.js new file mode 100644 index 0000000..0f9df4a --- /dev/null +++ b/server/api/document/document.controller.js @@ -0,0 +1,249 @@ +/** + * 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 Document = require('./document.model'); +var Revision = require('./revision.model'); +var wdiff = require('../../components/wdiff'); +var mongoose = require('mongoose'); + + +// Get a list of all documents +exports.index = function(req, res) { + Document + .find() + .populate('owner', '_id name') + .populate('currentRevision', '_id state created') + .exec(function (err, documents) { + if(err) { return handleError(res, err); } + return res.json(200, documents); + }); +}; + +// Get a list of all documents +exports.indexForUser = function(req, res) { + Document + .find({owner: req.params.userid}) + .populate('owner', '_id name') + .populate('currentRevision', '_id state created') + .exec(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) + .populate('owner', '_id name') + .populate('currentRevision', '_id state created description content') + .populate('revisions', '_id state created description') + .exec(function (err, document) { + if(err) { return handleError(res, err); } + if(!document) { return res.send(404); } + return res.json(document); + }); +}; + +// Creates a new document with the current user as owner +exports.create = function(req, res) { + //we do not allow the api client to change the owner willy-nilly! + //can this be set in the schema somehow? + if (req.body.owner) { delete req.body.owner; } + + //nor document id + if (req.body._id) { delete req.body._id; } + + //the client cant add revisions either + if (req.body.revisions) { delete req.body.revisions; } + if (req.body.currentRevision) { delete req.body.currentRevision; } + + //add the current user to the document object + var document = _.merge(req.body, {owner: req.user._id}); + + + //and add to the db + Document.create(document, function(err, document) { + if(err) { return handleError(res, err); } + + //create an initial, empty revision + var revision = {document: document, status: 'empty', content:''}; + Revision.create(revision, function(err, revision) { + if(err) { return handleError(res, err); } + + //then add that revision back to the document + document.currentRevision = revision; + document.revisions = [revision]; + + //save the document and return + document.save(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) { + //we don't allow the id to be updated + //!!! do we even need to worry about this? Prolly not! + if(req.body._id) { delete req.body._id; } + + //??? should we allow the revisions to be updated here? Prolly not + if (req.body.revisions) { delete req.body.revisions; } + + Document.findById(req.params.id, function (err, document) { + if (err) { return handleError(res, err); } + if(!document) { return res.send(404); } + + // require user authentication + if (! mongoose.Types.ObjectId(document.owner).equals(req.user._id)) + {return res.send(401);} + + //do it + 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); } + + // require user authentication + if (! mongoose.Types.ObjectId(document.owner).equals(req.user._id)) + {return res.send(401);} + + //do it + document.remove(function(err) { + if(err) { return handleError(res, err); } + return res.send(204); + }); + }); +}; + +// Get a list of all revisions for a document +exports.indexRevisionsForDocument = function(req, res) { + Revision + .find({document: req.params.id}) + .populate('owner', '_id name') + .populate('document', '_id title currentRevision') + .exec(function (err, revisions) { + if(err) { return handleError(res, err); } + if(!revisions) { return res.send(404); } + + return res.json(200, revisions); + }); +}; + +// Show a revision by id +exports.showRevision = function(req, res) { + Revision + .findById(req.params.revisionid) + .populate('owner', '_id name') + .populate('document', '_id title currentRevision') + .exec(function (err, revision) { + if(err) { return handleError(res, err); } + if(!revision) { return res.send(404); } + + //ensure this revision actually belongs to the specified document + if (! mongoose.Types.ObjectId(revision.document._id).equals(req.params.id)) + {return res.status(403).send("Mismatch between revision id and document id.");} + + + // ??? require user authentication + //if (! mongoose.Types.ObjectId(document.owner).equals(req.user._id)) + // {return res.send(401);} + + return res.json(revision); + }); +}; + +// Create a new revision and add it to a document (with the current user as owner) +// This can only be performed by the user who owns the document +exports.createRevision = function(req, res) { + //we do not allow the api client to change the owner willy-nilly! + //can this be set in the schema somehow? + if (req.body.owner) { delete req.body.owner; } + + //similarly the document + if (req.body.document) { delete req.body.document; } + + //and the date + if (req.body.created) { delete req.body.created; } + + //get the record for the parent document + Document.findById(req.params.id).exec(function(err, document){ + if (err) { return handleError(res, err); } + if(!document) { return res.send(404); } + + // require user authentication + if (! mongoose.Types.ObjectId(document.owner).equals(req.user._id)) + {return res.send(401);} + + //set the owner and document fields for the revision + var revision = _.merge(req.body, {owner: req.user, document: document}); + + //create the record + Revision.create(revision, function (err, revision) { + if(err) { return handleError(res, err); } + + //and update the document + document.revisions.push(revision); + document.currentRevision = revision; + document.save(function (err) { + if (err) { return handleError(res, err); } + return res.json(200, revision); + }); + + }); + }); +}; + +//compares two revisions with wdiff +exports.wdiff = function(req, res) { + Revision.findById(req.params.revisionida).exec(function (err, revisiona) { + if(err) { return handleError(res, err); } + if(!revisiona) { return res.send(404); } + + + Revision.findById(req.params.revisionidb).exec(function (err, revisionb) { + if(err) { return handleError(res, err); } + if(!revisionb) { return res.send(404); } + + //??? do we care if both revisions have the same document? + //??? if they both have the same user? + //??? if the current user is the owner? + + wdiff(revisiona.content, revisionb.content, true, function(err, result){ + if(err) { return handleError(res, err); } + + result.a = revisiona; + result.b = revisionb; + + res.json(result); + }); + }); + }); +}; + +function handleError(res, err) { + return res.send(500, err); +} \ No newline at end of file diff --git a/server/api/document/document.model.js b/server/api/document/document.model.js new file mode 100644 index 0000000..6264c84 --- /dev/null +++ b/server/api/document/document.model.js @@ -0,0 +1,18 @@ +'use strict'; + +var mongoose = require('mongoose'), + Schema = mongoose.Schema; + +var DocumentSchema = new Schema({ + title: String, + owner: {type: Schema.Types.ObjectId, ref: 'User'}, + + //is it necessary to have the revisions list? + //yes - it maintains the order + revisions : [{ type: Schema.Types.ObjectId, ref: 'Revision' }], + + //do we need this? - current revision is just revisions[n-1] + currentRevision : {type: Schema.Types.ObjectId, ref: 'Revision'} +}); + +module.exports = mongoose.model('Document', DocumentSchema); diff --git a/server/api/document/index.js b/server/api/document/index.js new file mode 100644 index 0000000..09e4b72 --- /dev/null +++ b/server/api/document/index.js @@ -0,0 +1,30 @@ +'use strict'; + +var express = require('express'); +var controller = require('./document.controller'); +var auth = require('../../auth/auth.service'); + +var router = express.Router(); + +router.get('/', controller.index); +router.get('/owner/:userid', controller.indexForUser); + +router.get('/:id', controller.show); +router.post('/', auth.isAuthenticated(), controller.create); +//router.put('/:id', auth.isAuthenticated(), controller.update); +//router.patch('/:id', auth.isAuthenticated(), controller.update); +router.delete('/:id', auth.isAuthenticated(), controller.destroy); + +router.get('/:id/revisions', controller.indexRevisionsForDocument); +router.get('/:id/revisions/:revisionid', controller.showRevision); + + +router.post('/:id/revisions', auth.isAuthenticated(), controller.createRevision); +/* +router.put('/:id/revisions/:revisionid', auth.isAuthenticated(), controller.updateRevision); +router.patch('/:id/revisions/:revisionid', auth.isAuthenticated(), ontroller.updateRevision); +router.delete('/:id/revisions/:revisionid', auth.isAuthenticated(), controller.destroyRevision); +*/ +router.get('/wdiff/:revisionida/:revisionidb', controller.wdiff); + +module.exports = router; \ No newline at end of file diff --git a/server/api/document/revision.model.js b/server/api/document/revision.model.js new file mode 100644 index 0000000..658a413 --- /dev/null +++ b/server/api/document/revision.model.js @@ -0,0 +1,17 @@ +'use strict'; + +var mongoose = require('mongoose'), + Schema = mongoose.Schema; + +var RevisionSchema = new Schema({ + state: String, + //This property is denormalized for efficient querying + owner: {type: Schema.Types.ObjectId, ref: 'User'}, + created: {type: Date, default: Date.now}, + description: String, + content: String, + + document : {type: Schema.Types.ObjectId, ref: 'Document'} +}); + +module.exports = mongoose.model('Revision', RevisionSchema); \ No newline at end of file diff --git a/server/api/thing/index.js b/server/api/thing/index.js deleted file mode 100644 index 845c9f0..0000000 --- a/server/api/thing/index.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var express = require('express'); -var controller = require('./thing.controller'); - -var router = express.Router(); - -router.get('/', controller.index); -router.get('/:id', controller.show); -router.post('/', controller.create); -router.put('/:id', controller.update); -router.patch('/:id', controller.update); -router.delete('/:id', controller.destroy); - -module.exports = router; \ No newline at end of file diff --git a/server/api/thing/thing.controller.js b/server/api/thing/thing.controller.js deleted file mode 100644 index be6541e..0000000 --- a/server/api/thing/thing.controller.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Using Rails-like standard naming convention for endpoints. - * GET /things -> index - * POST /things -> create - * GET /things/:id -> show - * PUT /things/:id -> update - * DELETE /things/:id -> destroy - */ - -'use strict'; - -var _ = require('lodash'); -var Thing = require('./thing.model'); - -// Get list of things -exports.index = function(req, res) { - Thing.find(function (err, things) { - if(err) { return handleError(res, err); } - return res.json(200, things); - }); -}; - -// Get a single thing -exports.show = function(req, res) { - Thing.findById(req.params.id, function (err, thing) { - if(err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - return res.json(thing); - }); -}; - -// Creates a new thing in the DB. -exports.create = function(req, res) { - Thing.create(req.body, function(err, thing) { - if(err) { return handleError(res, err); } - return res.json(201, thing); - }); -}; - -// Updates an existing thing in the DB. -exports.update = function(req, res) { - if(req.body._id) { delete req.body._id; } - Thing.findById(req.params.id, function (err, thing) { - if (err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - var updated = _.merge(thing, req.body); - updated.save(function (err) { - if (err) { return handleError(res, err); } - return res.json(200, thing); - }); - }); -}; - -// Deletes a thing from the DB. -exports.destroy = function(req, res) { - Thing.findById(req.params.id, function (err, thing) { - if(err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - thing.remove(function(err) { - if(err) { return handleError(res, err); } - return res.send(204); - }); - }); -}; - -function handleError(res, err) { - return res.send(500, err); -} \ No newline at end of file diff --git a/server/api/thing/thing.model.js b/server/api/thing/thing.model.js deleted file mode 100644 index ed857cd..0000000 --- a/server/api/thing/thing.model.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose'), - Schema = mongoose.Schema; - -var ThingSchema = new Schema({ - name: String, - info: String, - active: Boolean -}); - -module.exports = mongoose.model('Thing', ThingSchema); \ No newline at end of file diff --git a/server/api/thing/thing.spec.js b/server/api/thing/thing.spec.js deleted file mode 100644 index 17c8c6c..0000000 --- a/server/api/thing/thing.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -var should = require('should'); -var app = require('../../app'); -var request = require('supertest'); - -describe('GET /api/things', function() { - - it('should respond with JSON array', function(done) { - request(app) - .get('/api/things') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) return done(err); - res.body.should.be.instanceof(Array); - done(); - }); - }); -}); diff --git a/server/api/wdiff/wdiff.controller.js b/server/api/wdiff/wdiff.controller.js index 02b7d9e..df8a51e 100644 --- a/server/api/wdiff/wdiff.controller.js +++ b/server/api/wdiff/wdiff.controller.js @@ -5,7 +5,8 @@ var _ = require('lodash'), temp = require('temp'), fs = require('fs'), - exec = require('child_process').exec; + exec = require('child_process').exec, + wdiff = require('../../components/wdiff'); // Automatically track and cleanup files at exit temp.track(); @@ -20,7 +21,7 @@ exports.compareMarkdown = function(req, res) { // Perform a comparison // The request should be a json object with two string fields: 'a' and 'b' - function doCompare(req, res, isMarkdown) { + function doCompare(req, res, asMarkdown) { //check for properly formatted request if (req.headers["content-type"].toLowerCase() != "application/json") @@ -32,59 +33,11 @@ exports.compareMarkdown = function(req, res) { var a = req.body.a; var b = req.body.b; - //!!! this nested file-open is not a good pattern - // better would be to use promises and write the two files asynchronously - - // open the first file - temp.open('wdiffa-', function(err, filea) { - //handle errors - if (err) + wdiff(a,b,asMarkdown, function(err, result){ + if (err) return handleError(res, err); - //write the string to the file - fs.write(filea.fd, a); - - //close the file - fs.close(filea.fd, function(err) { - if (err) - return handleError(res, err); - - //open the second file - temp.open('wdiffa-', function(err, fileb) { - if (err) - return handleError(res, err); - - //write the string to the file - fs.write(fileb.fd, b); - - //close the file - fs.close(fileb.fd, function(err) { - if (err) - return handleError(res, err); - - var cmd = "./bin/wdiff " + filea.path + " " +fileb.path; - exec(cmd, function(err, stdout) { - - if (err && err.code!=1 && err.code!=0) { - return handleError(res,err); - } - - //if no difference was found by wdiff, err.code will be 0 - var wdiffSame; - wdiffSame = (err && err.code == 0) ? true:false; - - //sub del and ins - var markdown = stdout; - markdown = markdown.replace(/\[-/g, ''); - markdown = markdown.replace(/-\]/g, ''); - markdown = markdown.replace(/{\+/g, ''); - markdown = markdown.replace(/\+}/g, ''); - - var resData = {wdiff:stdout, same: wdiffSame, markdown: markdown}; - }); - }); - }); - }); + res.json(result); }); } diff --git a/server/components/wdiff/index.js b/server/components/wdiff/index.js new file mode 100644 index 0000000..cc1792e --- /dev/null +++ b/server/components/wdiff/index.js @@ -0,0 +1,76 @@ +'use strict'; + +var _ = require('lodash'), + temp = require('temp'), + fs = require('fs'), + exec = require('child_process').exec; + +// Automatically track and cleanup files at exit +temp.track(); + +// Perform a comparison between a and b +// the callback should have parameters (err, result) +module.exports = function(a, b, asMarkdown, callback) { + + //!!! this nested file-open is not a good pattern + // better would be to use promises and write the two files asynchronously + + // open the first file + temp.open('wdiffa-', function(err, filea) { + //handle errors + if (err) + return callback(err); + + //write the string to the file + fs.write(filea.fd, a); + + //close the file + fs.close(filea.fd, function(err) { + if (err) + return callback(err); + + //open the second file + temp.open('wdiffa-', function(err, fileb) { + if (err) + return callback(err); + + //write the string to the file + fs.write(fileb.fd, b); + + //close the file + fs.close(fileb.fd, function(err) { + if (err) + return callback(err); + + var cmd = "./bin/wdiff " + filea.path + " " +fileb.path; + exec(cmd, function(err, stdout) { + + if (err && err.code!=1 && err.code!=0) { + return callback(err); + } + + //if no difference was found by wdiff, err.code will be 0 + var wdiffSame; + wdiffSame = (err && err.code == 0) ? true:false; + + + var resData = {wdiff:stdout, same: wdiffSame}; + if (asMarkdown) { + + //!!! this needs more sophisticated parsing + //sub del and ins for the wdiff tags + var markdown = stdout; + markdown = markdown.replace(/\[-/g, ''); + markdown = markdown.replace(/-\]/g, ''); + markdown = markdown.replace(/{\+/g, ''); + markdown = markdown.replace(/\+}/g, ''); + resData.wdiff=markdown; + } + + return callback(null, resData); + }); + }); + }); + }); + }); +} diff --git a/server/config/environment/development.js b/server/config/environment/development.js index f4b1194..39194d1 100644 --- a/server/config/environment/development.js +++ b/server/config/environment/development.js @@ -8,5 +8,5 @@ module.exports = { uri: 'mongodb://mongodb/markdownformatwdiff-dev' }, - seedDB: false + seedDB: true }; diff --git a/server/config/seed.js b/server/config/seed.js index 888dcc5..3da7816 100644 --- a/server/config/seed.js +++ b/server/config/seed.js @@ -5,45 +5,80 @@ 'use strict'; -var Thing = require('../api/thing/thing.model'); +var Document = require('../api/document/document.model'); +var Revision = require('../api/document/revision.model'); var User = require('../api/user/user.model'); -Thing.find({}).remove(function() { - Thing.create({ - name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' - }, { - name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' - }, { - name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' - }, { - name : 'Modular Structure', - info : 'Best practice client and server structures allow for more code reusability and maximum scalability' - }, { - name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' - },{ - name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' - }); -}); -User.find({}).remove(function() { - User.create({ - provider: 'local', - name: 'Test User', - email: 'test@test.com', - password: 'test' - }, { - provider: 'local', - role: 'admin', - name: 'Admin', - email: 'admin@admin.com', - password: 'admin' - }, function() { - console.log('finished populating users'); - } - ); -}); \ No newline at end of file +var oldUser; +var testUser; +var testDocument; +var testRevisionIds = []; + + +Document.find({owner:null}).remove(); +Revision.find({owner:null}).remove(); + + +User.findOne({email:'test@test.com'}).exec() + .then(function(user) { + oldUser = user; + + console.log('removing test revisions'); + Revision.find({owner: user}).remove().exec(); + + console.log('removing test documents'); + Document.find({owner: user}).remove().exec(); + + console.log('removing test user'); + User.find({email:'test@test.com'}).remove().exec(); + }, function (err) { console.log('err: '+err)}) + + .then(function() { + console.log('creating test user'); + return User.create({ + provider: 'local', + name: 'Test User', + email: 'test@test.com', + password: 'test' + }); + }, function (err) { console.log('err: '+err)}) + + .then(function(user) { + testUser = user; + console.log('creating test document'); + return Document.create({ + owner: testUser, + title: "test document", + }); + }, function (err) { console.log('err: '+err)}) + + .then(function(document) { + testDocument = document; + console.log('creating test revisions'); + return Revision.create({ + owner: testUser, + state: "rough draft", + description: "test revision a", + content: "blah, blah, blah", + document: testDocument + }, { + owner: testUser, + state: "final draft", + description: "test revision b", + content: "Blah, blah, blah!", + document: testDocument + }, function (err, revisionA, revisionB) { + console.log('updating document with revisions'); + testRevisionIds = [revisionA, revisionB]; + testDocument.currentRevision = revisionB; + testDocument.revisions.push(revisionA); + testDocument.revisions.push(revisionB); + return testDocument.save(); + }); + }, function (err) { console.log('err: '+err)}) + + .then(function() { + console.log('finished seeding db'); + }, function (err) { console.log('err: '+err)}) + ; \ No newline at end of file diff --git a/server/routes.js b/server/routes.js index 26f6ead..e64c3de 100644 --- a/server/routes.js +++ b/server/routes.js @@ -9,7 +9,7 @@ var errors = require('./components/errors'); module.exports = function(app) { // Insert routes below - app.use('/api/things', require('./api/thing')); + app.use('/api/documents', require('./api/document')); app.use('/api/wdiff', require('./api/wdiff')); app.use('/api/users', require('./api/user'));