├── .bowerrc ├── .cfignore ├── .coveralls.yml ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── Dockerfile ├── Gruntfile.js ├── Procfile ├── README.md ├── Rakefile.rb ├── app ├── .DS_Store ├── .buildignore ├── .htaccess ├── 404.html ├── cms-content │ └── themes │ │ └── default │ │ └── bootstrap.css ├── favicon.ico ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── images │ ├── angular-cms-banner.png │ ├── angular-cms-bg.png │ ├── angular-cms-brand.png │ ├── angular-cms-icon.png │ ├── angular-cms-icons.png │ ├── angular-cms-logo.png │ ├── angular-cms.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ ├── avatar_2x.png │ ├── brand.png │ ├── favicon-160x160.png │ ├── favicon-16x16.png │ ├── favicon-196x196.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── feature-angular.png │ ├── feature-bootstrap.png │ ├── feature-coffeescript.png │ ├── feature-css3.png │ ├── feature-customize.png │ ├── feature-design.png │ ├── feature-grunt.png │ ├── feature-html5.png │ ├── feature-jasmine.png │ ├── feature-layout.png │ ├── feature-mongodb.png │ ├── feature-nodejs.png │ ├── feature-phantomjs.png │ ├── feature-webfonts.png │ ├── feature-yeoman.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ └── yeoman.png ├── index.html ├── libs │ ├── markdown.js │ ├── md5.js │ └── parse-1.2.17.min.js ├── robots.txt ├── scripts │ ├── app.js │ ├── config.js │ ├── controllers │ │ ├── admin.js │ │ ├── app.js │ │ ├── dashboard.js │ │ ├── docs.js │ │ ├── forgot-password.js │ │ ├── help.js │ │ ├── login.js │ │ ├── main.js │ │ ├── media.js │ │ ├── pages.js │ │ ├── plugins.js │ │ ├── profile.js │ │ ├── register.js │ │ ├── settings.js │ │ ├── sidebar.js │ │ ├── themes.js │ │ ├── users.js │ │ └── widgets.js │ ├── directives │ │ ├── cms-formgroup.js │ │ ├── cms-gravatar.js │ │ ├── cms-header.js │ │ ├── cms-panel.js │ │ ├── cms-uploader.js │ │ ├── cms-widget.js │ │ └── cms-widgets.js │ ├── filters │ │ ├── gravatar.js │ │ └── markdown.js │ └── services │ │ ├── cms-authservice.js │ │ ├── cms-dataservicefactory.js │ │ ├── cms-dataserviceprovider.js │ │ ├── cms-notify.js │ │ ├── cms-sessionservice.js │ │ ├── cms-socketservice.js │ │ ├── cms-usersfactory.js │ │ └── dataservice.js ├── styles │ ├── bootstrap.css │ ├── main.css │ └── main.sass └── views │ ├── _aside.html │ ├── _content.html │ ├── _footer.html │ ├── _header.html │ ├── _navbar.html │ ├── _sidebar.html │ ├── admin.html │ ├── dashboard.html │ ├── docs.html │ ├── forgot-password.html │ ├── help.html │ ├── index.html │ ├── login.html │ ├── main.html │ ├── media.html │ ├── pages.html │ ├── plugins.html │ ├── profile.html │ ├── register.html │ ├── reset-password.html │ ├── settings.html │ ├── themes.html │ ├── users.html │ └── widgets.html ├── bin ├── db.sh └── run.sh ├── bower.json ├── config ├── allow_ip_list ├── apache.crt ├── apache.key ├── black_list ├── config.json ├── entrust_root_certification_authority.cer ├── entrust_root_certification_authority.pem ├── entrust_ssl_ca.cer ├── hostfilters.js ├── mongodb-cert.key └── mongodb-cert.pem ├── content ├── index.ngdoc └── tutorial │ ├── .DS_Store │ ├── angularcms.ngdoc │ └── user-auth.ngdoc ├── fig.yml ├── gulpfile.js ├── karma-e2e.conf.js ├── karma.conf.js ├── licence.txt ├── manifest.yml ├── mongod ├── package.json ├── protractor.conf.js ├── routes ├── cms-auth.js ├── cms-db.js ├── cms-passport.js ├── cms-proxy.js ├── cms-rest.js ├── cms-router.js ├── cms-server.js ├── cms-sockets.js ├── cms-upload.js ├── generic.js ├── models │ ├── upload.js │ └── user.js ├── rest.js ├── socketserver.js └── views │ ├── account.ejs │ ├── index.ejs │ ├── layout.ejs │ ├── login.ejs │ ├── main.css │ └── register.ejs ├── server.js ├── stackato.yml └── test ├── .jshintrc ├── e2e └── app.js ├── intern.conf.js ├── protractor ├── j$.js ├── pages │ ├── app-page.js │ ├── login-page.js │ ├── register-page.js │ └── users-page.js └── spec │ ├── app-spec.js │ ├── login-spec.js │ ├── register-spec.js │ └── users-spec.js ├── routes ├── cms-auth-spec.js ├── cms-passport-spec.js ├── cms-proxy-spec.js ├── cms-rest-spec.js ├── cms-server-spec.js ├── cms-sockets-spec.js ├── cms-upload-spec.js ├── functional │ └── cms-passport.js └── rest-spec.js ├── runner.html └── spec ├── controllers ├── admin.js ├── app.js ├── dashboard.js ├── docs.js ├── forgot-password.js ├── help.js ├── login.js ├── main.js ├── media.js ├── pages.js ├── plugins.js ├── profile.js ├── register.js ├── settings.js ├── sidebar.js ├── themes.js ├── users.js └── widgets.js ├── directives ├── cms-formgroup.js ├── cms-gravatar.js ├── cms-header.js ├── cms-panel.js ├── cms-uploader.js ├── cms-widget.js └── cms-widgets.js ├── filters ├── gravatar.js └── markdown.js └── services ├── cmsauthservice.js ├── cmsdataservicefactory.js ├── cmsdataserviceprovider.js ├── cmsnotify.js ├── cmssessionservice.js ├── cmssocketservice.js ├── cmsusersfactory.js └── dataservice.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.cfignore: -------------------------------------------------------------------------------- 1 | .tmp 2 | .idea 3 | .grunt 4 | .sass-cache 5 | .project 6 | .DS_Store 7 | .c9 8 | 9 | node_modules 10 | atlassian-ide-plugin.xml 11 | app/bower_components 12 | coverage 13 | content 14 | db 15 | docs 16 | dist 17 | test -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: DAQFMSQC0X5NVfivbBPE7uAvwhSCeZKQY 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .tmp 3 | .idea 4 | .grunt 5 | .sass-cache 6 | .project 7 | .c9 8 | .DS_Store 9 | atlassian-ide-plugin.xml 10 | app/bower_components 11 | coverage 12 | db 13 | coverage 14 | docs 15 | dist 16 | node_modules -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = tab 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = false 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .tmp 3 | .idea 4 | .grunt 5 | .sass-cache 6 | .project 7 | .c9 8 | .DS_Store 9 | atlassian-ide-plugin.xml 10 | app/bower_components 11 | coverage 12 | db 13 | coverage 14 | docs 15 | dist 16 | node_modules 17 | angular-cms-db 18 | .log -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "angular": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | #node_js: 3 | # - 'v7.0.0' 4 | before_script: 5 | - 'npm install -g bower grunt-cli' 6 | - 'bower install' 7 | - 'grunt -f -v' 8 | #- sleep 2 9 | #- 'grunt serve' 10 | after_script: 11 | - cat coverage/**/lcov.info | codeclimate 12 | addons: 13 | code_climate: 14 | repo_token: 0c38fe0d4c6cc50053304b8cd4859d74624619e2707280ac9287be548e905404 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ### 2 | # docker-angular-cms 3 | # This is the docker container for angular-cms 4 | ### 5 | FROM ubuntu:14.04 6 | MAINTAINER Jonnie Spratley 7 | 8 | WORKDIR . 9 | COPY . /angular-cms 10 | 11 | RUN apt-get update && apt-get install -y \ 12 | git \ 13 | npm \ 14 | nodejs 15 | 16 | RUN ln -s /usr/bin/nodejs /usr/local/bin/node 17 | 18 | RUN npm install -g grunt-cli bower 19 | 20 | RUN cd /angular-cms \ 21 | npm install \ 22 | bower install \ 23 | grunt -v 24 | 25 | EXPOSE 1339 26 | CMD ["npm", "start"] -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node server.js -------------------------------------------------------------------------------- /Rakefile.rb: -------------------------------------------------------------------------------- 1 | # 2 | # @author Jonnie Spratley 3 | # @note This file contains tasks to help with deployment to live server, building the scripts, and cleaning up files. 4 | # 5 | 6 | #cleanup 7 | desc "remove all files from the temp folder" 8 | task :cleanup do 9 | FileUtils.rm_rf "./temp/*" 10 | end 11 | 12 | #build 13 | desc "Build the scripts and move to the public folder" 14 | task :build do 15 | 16 | end 17 | 18 | #deploy 19 | desc "Deploy distribution build to web server" 20 | task :deploy_d => :build do 21 | require 'net/ssh' 22 | require 'net/scp' 23 | 24 | server = 'jonniespratley.me' 25 | login = 'jonnie' 26 | 27 | Net::SSH.start(server, login, :password => "fred") do |ssh| 28 | ssh.scp.upload!("www", "/var", { :recursive => true, :verbose => true }) do |ch, name, sent, total| 29 | puts "#{name}: #{sent}/#{total}" 30 | end 31 | end 32 | end 33 | 34 | #deploy to development server 35 | 36 | #deploy to staging server 37 | desc "Deploy distribution build to test server" 38 | task :deploy_t => :build do 39 | require 'net/ssh' 40 | require 'net/scp' 41 | 42 | server = 'jonniespratley.me' 43 | login = 'jonnie' 44 | 45 | Net::SSH.start(server, login, :password => "fred") do |ssh| 46 | 47 | ssh.scp.upload!("www", "/var", { :recursive => true, :verbose => true }) do |ch, name, sent, total| 48 | puts "#{name}: #{sent}/#{total}" 49 | end 50 | end 51 | end 52 | 53 | #deploy to production server 54 | desc "Deploy distribution build to production server" 55 | task :deploy => :build do 56 | require 'net/ssh' 57 | require 'net/scp' 58 | 59 | server = 'jonniespratley.me' 60 | login = 'jonnie' 61 | 62 | Net::SSH.start(server, login, :password => "fred") do |ssh| 63 | ssh.scp.upload!("www", "/var", { :recursive => true, :verbose => true }) do |ch, name, sent, total| 64 | puts "#{name}: #{sent}/#{total}" 65 | end 66 | end 67 | end 68 | 69 | 70 | #deploy scripts to production server 71 | desc "Deploy distribution scripts to production server" 72 | task :deploy_scripts => :build do 73 | require 'net/ssh' 74 | require 'net/scp' 75 | 76 | server = 'jonniespratley.me' 77 | login = 'jonnie' 78 | 79 | Net::SSH.start(server, login, :password => "fred") do |ssh| 80 | ssh.scp.upload!("www/dist", "/var/www", { :recursive => true, :verbose => true }) do |ch, name, sent, total| 81 | puts "#{name}: #{sent}/#{total}" 82 | end 83 | end 84 | end 85 | 86 | #deploy to production server 87 | desc "Deploy .htaccess to production server" 88 | task :deploy_htacess => :build do 89 | require 'net/ssh' 90 | require 'net/scp' 91 | 92 | server = 'jonniespratley.me' 93 | login = 'jonnie' 94 | 95 | Net::SSH.start(server, login, :password => "fred") do |ssh| 96 | #Upload root .htaccess file 97 | ssh.scp.upload!(".htaccess", "/var/www", { :recursive => true, :verbose => true }) do |ch, name, sent, total| 98 | puts "#{name}: #{sent}/#{total}" 99 | end 100 | end 101 | end -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/.DS_Store -------------------------------------------------------------------------------- /app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/favicon.ico -------------------------------------------------------------------------------- /app/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /app/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /app/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/images/angular-cms-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/angular-cms-banner.png -------------------------------------------------------------------------------- /app/images/angular-cms-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/angular-cms-bg.png -------------------------------------------------------------------------------- /app/images/angular-cms-brand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/angular-cms-brand.png -------------------------------------------------------------------------------- /app/images/angular-cms-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/angular-cms-icon.png -------------------------------------------------------------------------------- /app/images/angular-cms-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/angular-cms-icons.png -------------------------------------------------------------------------------- /app/images/angular-cms-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/angular-cms-logo.png -------------------------------------------------------------------------------- /app/images/angular-cms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/angular-cms.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /app/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/apple-touch-icon.png -------------------------------------------------------------------------------- /app/images/avatar_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/avatar_2x.png -------------------------------------------------------------------------------- /app/images/brand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/brand.png -------------------------------------------------------------------------------- /app/images/favicon-160x160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/favicon-160x160.png -------------------------------------------------------------------------------- /app/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/favicon-16x16.png -------------------------------------------------------------------------------- /app/images/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/favicon-196x196.png -------------------------------------------------------------------------------- /app/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/favicon-32x32.png -------------------------------------------------------------------------------- /app/images/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/favicon-96x96.png -------------------------------------------------------------------------------- /app/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/favicon.ico -------------------------------------------------------------------------------- /app/images/feature-angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-angular.png -------------------------------------------------------------------------------- /app/images/feature-bootstrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-bootstrap.png -------------------------------------------------------------------------------- /app/images/feature-coffeescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-coffeescript.png -------------------------------------------------------------------------------- /app/images/feature-css3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-css3.png -------------------------------------------------------------------------------- /app/images/feature-customize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-customize.png -------------------------------------------------------------------------------- /app/images/feature-design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-design.png -------------------------------------------------------------------------------- /app/images/feature-grunt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-grunt.png -------------------------------------------------------------------------------- /app/images/feature-html5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-html5.png -------------------------------------------------------------------------------- /app/images/feature-jasmine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-jasmine.png -------------------------------------------------------------------------------- /app/images/feature-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-layout.png -------------------------------------------------------------------------------- /app/images/feature-mongodb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-mongodb.png -------------------------------------------------------------------------------- /app/images/feature-nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-nodejs.png -------------------------------------------------------------------------------- /app/images/feature-phantomjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-phantomjs.png -------------------------------------------------------------------------------- /app/images/feature-webfonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-webfonts.png -------------------------------------------------------------------------------- /app/images/feature-yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/feature-yeoman.png -------------------------------------------------------------------------------- /app/images/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /app/images/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/glyphicons-halflings.png -------------------------------------------------------------------------------- /app/images/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/mstile-144x144.png -------------------------------------------------------------------------------- /app/images/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/mstile-150x150.png -------------------------------------------------------------------------------- /app/images/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/mstile-310x150.png -------------------------------------------------------------------------------- /app/images/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/mstile-310x310.png -------------------------------------------------------------------------------- /app/images/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/mstile-70x70.png -------------------------------------------------------------------------------- /app/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/images/yeoman.png -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc directive 5 | * @name rfx.directive:rAutogrow 6 | * @element textarea 7 | * @function 8 | * 9 | * @description 10 | * Resize textarea automatically to the size of its text content. 11 | * 12 | * @example 13 | 14 | 15 | 16 |
{{text}}
17 |
18 |
19 | */ 20 | Parse.initialize('fYHs4Flnj7vgVHm9vaFiFTSKt5Mj2Bxf9e93mTOB', 'QPFGBNHs0QQHFS54atV71oKppd3gTgaFfQIHP2VW'); 21 | var app = angular.module('angularCmsApp', ['ngCookies', 'ngResource', 'ngSanitize', 'ngRoute', 'ngAnimate', 'mgcrea.ngStrap', 'fg']).config(function ($routeProvider) { 22 | var routeResolver; 23 | routeResolver = { 24 | delay: function ($q, $timeout) { 25 | var delay; 26 | delay = $q.defer(); 27 | $timeout(delay.resolve, 1000); 28 | delay.promise; 29 | } 30 | }; 31 | $routeProvider.when('/', { 32 | templateUrl: 'views/main.html', 33 | controller: 'MainCtrl' 34 | }).when('/docs', { 35 | templateUrl: 'views/docs.html', 36 | controller: 'DocsCtrl' 37 | }).when('/admin', { 38 | templateUrl: 'views/admin.html', 39 | controller: 'AdminCtrl' 40 | }).when('/plugins', { 41 | templateUrl: 'views/plugins.html', 42 | controller: 'PluginsCtrl' 43 | }).when('/themes', { 44 | templateUrl: 'views/themes.html', 45 | controller: 'ThemesCtrl' 46 | }).when('/widgets', { 47 | templateUrl: 'views/widgets.html', 48 | controller: 'WidgetsCtrl' 49 | }).when('/media', { 50 | templateUrl: 'views/media.html', 51 | controller: 'MediaCtrl' 52 | }).when('/settings', { 53 | templateUrl: 'views/settings.html', 54 | controller: 'SettingsCtrl' 55 | }).when('/users', { 56 | templateUrl: 'views/users.html', 57 | controller: 'UsersCtrl' 58 | }).when('/login', { 59 | templateUrl: 'views/login.html', 60 | controller: 'LoginCtrl' 61 | }).when('/register', { 62 | templateUrl: 'views/register.html', 63 | controller: 'RegisterCtrl' 64 | }).when('/dashboard', { 65 | templateUrl: 'views/dashboard.html', 66 | controller: 'DashboardCtrl' 67 | }).when('/profile', { 68 | templateUrl: 'views/profile.html', 69 | controller: 'ProfileCtrl' 70 | }).when('/pages', { 71 | templateUrl: 'views/pages.html', 72 | controller: 'PagesCtrl', 73 | resolve: { 74 | pages: function (DataService) { 75 | return DataService.fetch('pages').then(function (res) { 76 | return res.data; 77 | }); 78 | } 79 | } 80 | }).when('/help', { 81 | templateUrl: 'views/help.html', 82 | controller: 'HelpCtrl' 83 | }).when('/forgot-password', { 84 | templateUrl: 'views/forgot-password.html', 85 | controller: 'ForgotPasswordCtrl' 86 | }).when('/register', { 87 | templateUrl: 'views/register.html', 88 | controller: 'RegisterCtrl' 89 | }).otherwise({ 90 | redirectTo: '/' 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /app/scripts/controllers/admin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('AdminCtrl', function($scope) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | }); 5 | -------------------------------------------------------------------------------- /app/scripts/controllers/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('AppCtrl', function ($scope, $rootScope, $http, $log, $route, $location, $routeParams, $cookieStore, cmsSessionService, DataService, cmsSocketService) { 3 | var App; 4 | App = angular.copy(Config); 5 | App.ds = DataService; 6 | App.socket = cmsSocketService; 7 | 8 | 9 | App.route = $routeParams; 10 | App.session = cmsSessionService.getSession(); 11 | App.theme = $cookieStore.get('App.theme'); 12 | App.route = $route; 13 | App.location = $location; 14 | App.routeParams = $routeParams; 15 | App.roles = ['guest', 'user', 'admin']; 16 | 17 | 18 | $scope.name = 'AppCtrl'; 19 | 20 | window.App = $scope.App = $rootScope.App = App; 21 | 22 | 23 | angular.element(document).ready(function () { 24 | angular.element('.nav').bind('click', 'a', function (e) { 25 | $log.info(e); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /app/scripts/controllers/dashboard.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('DashboardCtrl', function($scope) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.fullscreen = function() { 5 | var i; 6 | i = document.getElementById("dashboard"); 7 | if (i.requestFullscreen) { 8 | return i.requestFullscreen(); 9 | } else if (i.webkitRequestFullscreen) { 10 | return i.webkitRequestFullscreen(); 11 | } else if (i.mozRequestFullScreen) { 12 | return i.mozRequestFullScreen(); 13 | } else { 14 | if (i.msRequestFullscreen) { 15 | return i.msRequestFullscreen(); 16 | } 17 | } 18 | }; 19 | }); 20 | -------------------------------------------------------------------------------- /app/scripts/controllers/docs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('DocsCtrl', function($scope) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | }); 5 | -------------------------------------------------------------------------------- /app/scripts/controllers/forgot-password.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('ForgotPasswordCtrl', function($scope) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | }); 5 | -------------------------------------------------------------------------------- /app/scripts/controllers/help.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('HelpCtrl', function($scope) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.readmeEl = angular.element('#readme'); 5 | $scope.loadReadme = function() { 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /app/scripts/controllers/login.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Login Controller - Handles the login.html view for authenticating a user. 5 | */ 6 | angular.module('angularCmsApp').controller('LoginCtrl', function($scope, $rootScope, $cookieStore, cmsAuthService, cmsNotify, cmsSessionService) { 7 | $scope.user = { 8 | email: null, 9 | password: null, 10 | remember: false 11 | }; 12 | /** 13 | login - This functionality should be moved into the session service that handles 14 | setting the session and changing the location of the page. 15 | */ 16 | $scope.login = function(u) { 17 | return cmsAuthService.login(u); 18 | }; 19 | $scope.logout = function(user) { 20 | return cmsAuthService.logout(user); 21 | }; 22 | $scope.name = 'login'; 23 | }); 24 | -------------------------------------------------------------------------------- /app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('MainCtrl', function($scope, $rootScope) { 3 | return $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | }); 5 | -------------------------------------------------------------------------------- /app/scripts/controllers/media.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('MediaCtrl', function($scope, $http, DataService) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.uploads = []; 5 | $scope.getUploads = function() { 6 | DataService.fetch('uploads').then(function(res) { 7 | return $scope.uploads = res.data; 8 | }); 9 | return $scope.uploader = { 10 | files: [] 11 | }; 12 | }; 13 | return $scope.getUploads(); 14 | }); 15 | -------------------------------------------------------------------------------- /app/scripts/controllers/pages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('PagesCtrl', function($scope, $log, pages, DataService, cmsNotify) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.pages = pages; 5 | $scope.page = {}; 6 | $scope.selectPage = function(p) { 7 | $scope.page = p; 8 | return $log.info(p); 9 | }; 10 | $scope.getPages = function() { 11 | return DataService.fetch('pages').then(function(res) { 12 | return $scope.pages = res.data; 13 | }); 14 | }; 15 | $scope.save = function(p) { 16 | if (p._id != null) { 17 | p.modified = new Date(); 18 | } 19 | if (p._id == null) { 20 | p.created = new Date(); 21 | } 22 | return DataService.save('pages', p).then(function(res) { 23 | $scope.getPages(); 24 | $scope.page = {}; 25 | cmsNotify('.alerts', 'success', 'Success!', 'Page Update.', 5000); 26 | return $log.info(res); 27 | }); 28 | }; 29 | $scope.remove = function(p) { 30 | return DataService.destroy('pages', p).then(function(res) { 31 | $scope.getPages(); 32 | return $log.info('Page deleted', res); 33 | }); 34 | }; 35 | return $scope.pageSchema = { 36 | 'fields': [ 37 | { 38 | 'type': 'text', 39 | 'name': 'title', 40 | 'displayName': 'Title:', 41 | 'validation': { 42 | 'messages': {}, 43 | 'required': true, 44 | 'minlength': 2, 45 | 'maxlength': 18 46 | }, 47 | 'placeholder': 'Enter title here', 48 | 'tooltip': 'Enter the page title here' 49 | }, { 50 | 'type': 'selectlist', 51 | 'name': 'parent', 52 | 'displayName': 'Parent:', 53 | 'options': [ 54 | { 55 | 'value': '1', 56 | 'text': 'Option 1' 57 | }, { 58 | 'value': '2', 59 | 'text': 'Option 2' 60 | }, { 61 | 'value': '3', 62 | 'text': 'Option 3' 63 | } 64 | ], 65 | 'value': '1' 66 | }, { 67 | 'type': 'selectlist', 68 | 'name': 'template', 69 | 'displayName': 'Template:', 70 | 'options': [ 71 | { 72 | 'value': '1', 73 | 'text': 'Option 1' 74 | }, { 75 | 'value': '2', 76 | 'text': 'Option 2' 77 | }, { 78 | 'value': '3', 79 | 'text': 'Option 3' 80 | } 81 | ], 82 | 'value': '1' 83 | }, { 84 | 'type': 'textarea', 85 | 'name': 'body', 86 | 'displayName': 'Body:', 87 | 'validation': { 88 | 'messages': {} 89 | }, 90 | 'placeholder': 'Enter body here', 91 | 'tooltip': 'Enter page body here' 92 | }, { 93 | 'type': 'checkboxlist', 94 | 'name': 'status', 95 | 'displayName': 'Status:', 96 | 'options': [ 97 | { 98 | 'value': 'published', 99 | 'text': 'Published' 100 | }, { 101 | 'value': 'draft', 102 | 'text': 'Draft' 103 | }, { 104 | 'value': 'private', 105 | 'text': 'Private' 106 | } 107 | ], 108 | 'value': 'draft', 109 | 'tooltip': 'Select the page status' 110 | } 111 | ] 112 | }; 113 | }); 114 | -------------------------------------------------------------------------------- /app/scripts/controllers/plugins.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('PluginsCtrl', function($scope) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.plugins = [ 5 | { 6 | id: 1, 7 | title: 'Google Chart Widgets', 8 | description: 'This plugin brings Google Charts to the dashboard, configurable settings to visualize any content type.', 9 | enabled: true 10 | }, { 11 | id: 2, 12 | title: 'Twitter Widget', 13 | description: 'This plugin enables your Twitter feed on your dashboard and public stream widgets for your pages.', 14 | enabled: true 15 | } 16 | ]; 17 | }); 18 | -------------------------------------------------------------------------------- /app/scripts/controllers/profile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('ProfileCtrl', function($scope, $rootScope, DataService, cmsNotify) { 3 | $scope.user = $rootScope.App.session.user; 4 | return $scope.update = function(u) { 5 | return DataService.save('users', u).then(function(data) { 6 | return cmsNotify('.profile-message', 'success', 'Success!', "Your account was successfully updated."); 7 | }); 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /app/scripts/controllers/register.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('RegisterCtrl', function($scope, $log, cmsAuthService) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.user = { 5 | username: null, 6 | email: null, 7 | password: null, 8 | role: 'member', 9 | created: new Date(), 10 | modified: new Date(), 11 | metadata: { 12 | avatar: '', 13 | name: null, 14 | about: null 15 | } 16 | }; 17 | return $scope.register = function(user) { 18 | $log.info('register', user); 19 | return cmsAuthService.register(user); 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /app/scripts/controllers/settings.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('SettingsCtrl', function($scope, mySchema) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.myForm = { 5 | schema: mySchema 6 | }; 7 | $scope.config = Config; 8 | return $scope.tabs = [ 9 | { 10 | title: 'General', 11 | content: '' 12 | }, { 13 | title: 'Client', 14 | content: '' 15 | } 16 | ]; 17 | }); 18 | 19 | angular.module('angularCmsApp').value('mySchema', { 20 | fields: [ 21 | { 22 | type: 'text', 23 | name: 'firstName', 24 | displayName: 'First name', 25 | validation: { 26 | messages: {}, 27 | required: true 28 | }, 29 | placeholder: 'Enter your first name here', 30 | tooltip: 'Enter your first name here' 31 | }, { 32 | type: 'text', 33 | name: 'lastName', 34 | displayName: 'Last name', 35 | validation: { 36 | messages: {}, 37 | required: true 38 | }, 39 | placeholder: 'Enter your last name here', 40 | tooltip: 'Enter your last name here' 41 | }, { 42 | type: 'radiobuttonlist', 43 | name: 'sex', 44 | displayName: 'Sex', 45 | options: [ 46 | { 47 | value: 'male', 48 | text: 'Male' 49 | }, { 50 | value: 'female', 51 | text: 'Female' 52 | } 53 | ], 54 | value: 'male' 55 | }, { 56 | type: 'email', 57 | name: 'email', 58 | displayName: 'Email', 59 | validation: { 60 | messages: {} 61 | }, 62 | placeholder: 'Enter your email address here', 63 | tooltip: 'Enter your email address here' 64 | }, { 65 | type: 'checkboxlist', 66 | name: 'color', 67 | displayName: 'Colors', 68 | options: [ 69 | { 70 | value: 'red', 71 | text: 'Red' 72 | }, { 73 | value: 'blue', 74 | text: 'Blue' 75 | }, { 76 | value: 'green', 77 | text: 'Green' 78 | } 79 | ], 80 | value: {} 81 | } 82 | ] 83 | }); 84 | -------------------------------------------------------------------------------- /app/scripts/controllers/sidebar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('SidebarCtrl', function($scope, $rootScope) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.items = $rootScope.App.menu.user; 5 | $scope.selected = null; 6 | $scope.select = function(item) { 7 | angular.forEach($rootScope.App.menu.admin, function(item) { 8 | return item.selected = false; 9 | }); 10 | angular.forEach($rootScope.App.menu.user, function(item) { 11 | return item.selected = false; 12 | }); 13 | return item.selected = true; 14 | }; 15 | $scope.sidebar = { 16 | closed: false 17 | }; 18 | return $scope.toggleSidebar = function() { 19 | return $scope.sidebar.closed = !$scope.sidebar.closed; 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /app/scripts/controllers/themes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('ThemesCtrl', function($scope, $rootScope, $cookieStore) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | return $scope.selectTheme = function(theme) { 5 | $cookieStore.put('App.theme', theme); 6 | return $rootScope.App.theme = theme; 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/controllers/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('UsersCtrl', function($scope, DataService) { 3 | $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | $scope.user = { 5 | username: null, 6 | email: null, 7 | password: null, 8 | role: 'member', 9 | created_at: new Date(), 10 | updated_at: new Date(), 11 | metadata: { 12 | avatar: '', 13 | name: null, 14 | about: null 15 | } 16 | }; 17 | $scope.users = []; 18 | $scope.groups = ['Admin', 'Member', 'Public']; 19 | $scope.getGroups = function() { 20 | return DataService.fetch('groups').then(function(res) { 21 | $scope.groups = res.data; 22 | return console.log(data); 23 | }); 24 | }; 25 | $scope.selectUser = function(user) { 26 | return $scope.user = user; 27 | }; 28 | $scope.getUsers = function() { 29 | return DataService.fetch('users').then(function(res) { 30 | $scope.users = res.data; 31 | if (!$scope.groups) { 32 | return $scope.getGroups(); 33 | } 34 | }); 35 | }; 36 | $scope.deleteUser = function(index, user) { 37 | var ask; 38 | ask = confirm("Delete " + user.email + "?"); 39 | if (ask) { 40 | return DataService.destroy('users', user).then(function(res) { 41 | $scope.users.pop(index); 42 | return $scope.getUsers(); 43 | }); 44 | } 45 | }; 46 | return $scope.addUser = function(user) { 47 | if (user.password) { 48 | delete user.password; 49 | } 50 | user.updated_at = new Date(); 51 | return DataService.save('users', user).then(function(data) { 52 | $('#user-modal').modal('hide'); 53 | $scope.getUsers(); 54 | if (!user._id) { 55 | $scope.users.push(user); 56 | } 57 | return $scope.user = {}; 58 | }); 59 | }; 60 | }); 61 | -------------------------------------------------------------------------------- /app/scripts/controllers/widgets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').controller('WidgetsCtrl', function($scope) { 3 | return $scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma']; 4 | }); 5 | -------------------------------------------------------------------------------- /app/scripts/directives/cms-formgroup.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc directive 4 | @name angularCmsApp.directive:cmsFormGroup 5 | @element div 6 | @function 7 | 8 | @description 9 | Resize textarea automatically to the size of its text content. 10 | **Note:** ie<9 needs pollyfill for window.getComputedStyle 11 | 12 | @example 13 | 14 | 15 | 16 |
{{text}}
17 |
18 |
19 | */ 20 | 'use strict'; 21 | angular.module('angularCmsApp').directive('cmsFormGroup', function() { 22 | return { 23 | template: '
', 24 | restrict: 'E', 25 | replace: true, 26 | transclude: true, 27 | scope: { 28 | label: '@', 29 | hint: '@' 30 | }, 31 | link: function(scope, element, attrs) { 32 | console.log(attrs); 33 | return element.find('input').addClass('form-control'); 34 | } 35 | }; 36 | }); 37 | -------------------------------------------------------------------------------- /app/scripts/directives/cms-gravatar.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc directive 4 | @name angularCmsApp.directive:cmsGravatar 5 | @element div 6 | @function 7 | 8 | @description 9 | This is a Gravatar directive for displaying an image by a users email address. 10 | 11 | @example 12 | 13 | 14 | 15 | HTML example come here ==> `
` 16 |
17 | 18 |
19 | */ 20 | 'use strict'; 21 | angular.module('angularCmsApp').directive('cmsGravatar', function() { 22 | return { 23 | template: '
', 24 | restrict: 'E', 25 | scope: { 26 | email: '@' 27 | }, 28 | link: function(scope, element) { 29 | return element.text('this is the cmsGravatar directive'); 30 | } 31 | }; 32 | }); 33 | -------------------------------------------------------------------------------- /app/scripts/directives/cms-header.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc directive 4 | @name angularCmsApp.directive:cmsHeader 5 | @element div 6 | @function 7 | @description 8 | Provides a directive for adding a standardized header across pages. 9 | 10 | @example 11 | 12 | 13 | 14 | 15 | 16 | */ 17 | 'use strict'; 18 | angular.module('angularCmsApp').directive('cmsHeader', function() { 19 | return { 20 | template: '', 21 | restrict: 'E', 22 | transclude: true, 23 | replace: true, 24 | scope: { 25 | icon: '@', 26 | title: '@' 27 | }, 28 | link: function(scope, element, attrs) {} 29 | }; 30 | }); 31 | -------------------------------------------------------------------------------- /app/scripts/directives/cms-panel.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc directive 4 | @name angularCmsApp.directive:cmsPanel 5 | @description This is a panel for angular-cms. 6 | 7 | @example 8 | 9 | 10 | 11 | HTML example come here ==> `
` 12 |
13 | 14 |
15 | */ 16 | 'use strict'; 17 | angular.module('angularCmsApp').directive('cmsPanel', function() { 18 | return { 19 | transclude: true, 20 | replace: true, 21 | scope: { 22 | id: '@', 23 | title: '@' 24 | }, 25 | template: '
\n
{{title}}
\n
\n
\'', 26 | restrict: 'E', 27 | link: function(scope, element, attrs) {} 28 | }; 29 | }); 30 | -------------------------------------------------------------------------------- /app/scripts/directives/cms-uploader.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc directive 4 | @name angularCmsApp.directive:cmsUploader 5 | @element div 6 | @function 7 | @description 8 | This is an example uploader. 9 | */ 10 | 'use strict'; 11 | angular.module('angularCmsApp').directive('cmsUploader', function() { 12 | var postLink; 13 | return { 14 | scope: { 15 | ngModel: '=' 16 | }, 17 | template: '
\n
\n

Drop files here

\n

\n or\n

\n \n \n
\n
', 18 | restrict: 'E', 19 | replace: true, 20 | transclude: false, 21 | require: '^?ngModel', 22 | link: postLink = function($scope, $element, $attrs, ngModel) { 23 | var Uploader; 24 | Uploader = function() { 25 | this.files = []; 26 | this.inputEl = document.getElementById('uploader-input'); 27 | this.input = $element.find('#uploader-input'); 28 | this.input.hide(); 29 | $element.find('button').bind('click', (function(_this) { 30 | return function(e) { 31 | return _this.input.trigger('click'); 32 | }; 33 | })(this)); 34 | this.inputEl.addEventListener('change', (function(_this) { 35 | return function(e) { 36 | var file, _i, _len, _ref, _results; 37 | _this.files = _this.inputEl.files; 38 | _ref = _this.files; 39 | _results = []; 40 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 41 | file = _ref[_i]; 42 | _results.push(_this.uploadFile(file)); 43 | } 44 | return _results; 45 | }; 46 | })(this)); 47 | return this.uploadFile = function(file) { 48 | var form, transferCanceled, transferComplete, transferFailed, updateProgress, xhr; 49 | form = new FormData(); 50 | form.append('files[]', file); 51 | xhr = new XMLHttpRequest(); 52 | updateProgress = function(oEvent) { 53 | var percentComplete; 54 | console.log(oEvent.loaded, oEvent.total); 55 | if (oEvent.lengthComputable) { 56 | percentComplete = oEvent.loaded / oEvent.total; 57 | return console.log(percentComplete); 58 | } else { 59 | return console.log(event); 60 | } 61 | }; 62 | transferComplete = function(evt) { 63 | return console.warn("The transfer is complete."); 64 | }; 65 | transferFailed = function(evt) { 66 | return console.error("An error occurred while transferring the file."); 67 | }; 68 | transferCanceled = function(evt) { 69 | return console.warn("The transfer has been canceled by the user."); 70 | }; 71 | xhr.addEventListener("progress", updateProgress, false); 72 | xhr.addEventListener("load", transferComplete, false); 73 | xhr.addEventListener("error", transferFailed, false); 74 | xhr.addEventListener("abort", transferCanceled, false); 75 | xhr.onload = function() { 76 | return console.log('Upload complete'); 77 | }; 78 | xhr.open('POST', $attrs.action, true); 79 | xhr.send(form); 80 | return console.log('Upload ', file); 81 | }; 82 | }; 83 | return new Uploader(); 84 | } 85 | }; 86 | }); 87 | -------------------------------------------------------------------------------- /app/scripts/directives/cms-widget.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc directive 4 | @name angularCmsApp.directive:cmsWidget 5 | @element div 6 | @function 7 | 8 | @description 9 | Provides a widget component that acts as a container for other content. 10 | 11 | @example 12 | 13 | 14 | 15 | This is my content. 16 | 17 | 18 | 19 | */ 20 | angular.module('angularCmsApp').directive('cmsWidget', function() { 21 | 'use strict'; 22 | return { 23 | restrict: 'ECA', 24 | replace: true, 25 | transclude: true, 26 | scope: { 27 | id: "@", 28 | title: "@", 29 | size: "@", 30 | icon: "@" 31 | }, 32 | require: '?^cmsWidgets', 33 | template: '
\n
\n \n {{title}}\n
\n
\n
\n
', 34 | link: function(scope, element, attrs, ctrl) { 35 | var opened, toggle, widgetTitle; 36 | toggle = function() { 37 | var opened; 38 | opened = !opened; 39 | element.find('section').slideToggle(function() { 40 | element.toggleClass((opened ? 'closed' : 'opened')); 41 | }); 42 | }; 43 | widgetTitle = element.find('header'); 44 | widgetTitle.css({ 45 | cursor: 'move' 46 | }); 47 | opened = true; 48 | widgetTitle.bind('click', toggle); 49 | } 50 | }; 51 | }); 52 | -------------------------------------------------------------------------------- /app/scripts/directives/cms-widgets.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc directive 4 | @name angularCmsApp.directive:cmsWidgets 5 | @element div 6 | @function 7 | 8 | @description 9 | This is the container for widgets. 10 | 11 | @example 12 | 13 | 14 | 15 | HTML example come here ==> `
` 16 |
17 | 18 |
19 | */ 20 | 21 | angular.module('angularCmsApp').directive('cmsWidgets', function() { 22 | 'use strict'; 23 | return { 24 | restrict: 'EMA', 25 | replace: true, 26 | transclude: true, 27 | scope: {}, 28 | template: '
\n \n
\n \n
\n
', 29 | controller: function($scope) { 30 | var widgets; 31 | widgets = $scope.widgets = []; 32 | $scope.add = function(widget) { 33 | if (widgets.length === 0) { 34 | $scope.select(widget); 35 | } 36 | return widgets.push(widget); 37 | }; 38 | $scope.select = function(widget) { 39 | angular.forEach(widgets, function(widget) { 40 | widget.selected = false; 41 | }); 42 | widget.selected = true; 43 | }; 44 | } 45 | }; 46 | }); 47 | -------------------------------------------------------------------------------- /app/scripts/filters/gravatar.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc filter 4 | @name angularCmsApp.filter:gravatar 5 | @function 6 | 7 | @description 8 | This is the Gravatar filter that takes a users email and create the proper MD5 hash. 9 | */ 10 | 'use strict'; 11 | angular.module('angularCmsApp').filter('gravatar', function() { 12 | return function(input) { 13 | if (!input) { 14 | input = 'test@gmail.com'; 15 | } 16 | return 'http://www.gravatar.com/avatar/' + MD5(input); 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /app/scripts/filters/markdown.js: -------------------------------------------------------------------------------- 1 | /** 2 | @ngdoc filter 3 | @name angularCmsApp.filter:markdown 4 | @function 5 | 6 | @description 7 | This is a Markdown to HTML filter. 8 | */ 9 | 'use strict'; 10 | angular.module('angularCmsApp').filter('markdown', function () { 11 | return function (input) { 12 | if(input){ 13 | return markdown.toHTML(input); 14 | } 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /app/scripts/services/cms-authservice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | @module AuthService 5 | @description 6 | This service will take care of authentication of a user, common methods include: 7 | * authorize 8 | * logout 9 | * register 10 | * forgot 11 | * currentUser 12 | */ 13 | angular.module('angularCmsApp').service('cmsAuthService', function($q, $http, $log, $rootScope, $cookieStore, $location, cmsSessionService, cmsNotify) { 14 | var cmsAuthService; 15 | return cmsAuthService = { 16 | endpoint: '/api/v2', 17 | 18 | /** 19 | authorize - I handle authorizing a user. 20 | */ 21 | authorize: function(user) { 22 | return $http.post(this.endpoint + "/login", user); 23 | }, 24 | 25 | /** 26 | session - I handle getting a session. 27 | */ 28 | session: function() { 29 | return $http.get(this.endpoint + "/session"); 30 | }, 31 | 32 | /** 33 | register - I handle register a user. 34 | */ 35 | register: function(user) { 36 | $log.info('trying to register', user); 37 | return $http.post(this.endpoint + "/register", user).then((function(_this) { 38 | return function(res) { 39 | $log.info(res); 40 | return _this.authorize(res.data); 41 | }; 42 | })(this), function(err) { 43 | $log.error(err); 44 | return cmsNotify('.message', 'danger', 'Error!', err.data.message, 4000); 45 | }); 46 | }, 47 | 48 | /** 49 | Logout method to clear the session. 50 | @param {Object} user - A user model containing remember 51 | */ 52 | logout: function(user) { 53 | cmsSessionService.setUserAuthenticated(null); 54 | return $rootScope.apply(function() { 55 | return $location.reload(); 56 | }); 57 | }, 58 | 59 | /** 60 | Login 61 | */ 62 | login: function(user) { 63 | return this.authorize(user).then(function(res) { 64 | var session; 65 | cmsNotify('.login-message', 'success', 'Success!', "Welcome back.", 5000); 66 | session = { 67 | user: res.data, 68 | authorized: true 69 | }; 70 | cmsSessionService.setSession(session); 71 | $rootScope.App.session = session; 72 | $log.info('login-result', res); 73 | return $rootScope.App.location.path('/dashboard'); 74 | }, function(err) { 75 | $log.error(err); 76 | return cmsNotify('.login-message', 'danger', 'Error!', err.data.message); 77 | }); 78 | } 79 | }; 80 | }); 81 | -------------------------------------------------------------------------------- /app/scripts/services/cms-dataservicefactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').factory('cmsDataServiceFactory', function() { 3 | var meaningOfLife; 4 | meaningOfLife = 42; 5 | return { 6 | someMethod: function() { 7 | return meaningOfLife; 8 | } 9 | }; 10 | }); 11 | -------------------------------------------------------------------------------- /app/scripts/services/cms-dataserviceprovider.js: -------------------------------------------------------------------------------- 1 | var DataServiceProvider; 2 | 3 | angular.module("angularCmsApp").provider("cmsDataServiceProvider", DataServiceProvider = function() { 4 | var DataServiceFactory, options; 5 | DataServiceFactory = void 0; 6 | options = void 0; 7 | this.options = function(value) { 8 | return options = !!value; 9 | }; 10 | this.$get = [ 11 | "options", DataServiceFactory = function(options) { 12 | return new cmsDataService(options); 13 | } 14 | ]; 15 | }); 16 | -------------------------------------------------------------------------------- /app/scripts/services/cms-notify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').factory('cmsNotify', [ 3 | '$timeout', '$q', function($timeout, $q) { 4 | var notices, notify; 5 | notices = []; 6 | notify = function(el, type, title, msg, timeout) { 7 | var alert; 8 | notices.push({ 9 | type: type, 10 | title: title, 11 | msg: msg 12 | }); 13 | alert = "
\n \n " + title + " " + msg + "\n
"; 14 | if (el) { 15 | angular.element(el).prepend(alert); 16 | } else { 17 | angular.element('.container').prepend(alert); 18 | } 19 | if (timeout) { 20 | return $timeout(function() { 21 | return angular.element('.alert').fadeOut().remove(); 22 | }, timeout); 23 | } 24 | }; 25 | return notify; 26 | } 27 | ]); 28 | -------------------------------------------------------------------------------- /app/scripts/services/cms-sessionservice.js: -------------------------------------------------------------------------------- 1 | angular.module('angularCmsApp').service('cmsSessionService', [ 2 | '$q', '$rootScope', '$cookieStore', '$location', '$log', function($q, $rootScope, $cookieStore, $location, $log) { 3 | var SessionService, getUserAuthenticated, setUserAuthenticated, userIsAuthenticated; 4 | userIsAuthenticated = $cookieStore.get('App.session'); 5 | setUserAuthenticated = function(value) { 6 | window.sessionStorage.setItem('userIsAuthenticated', value); 7 | $cookieStore.put('App.session', value); 8 | userIsAuthenticated = value; 9 | return $log.info("user is authorized: " + userIsAuthenticated.authorized); 10 | }; 11 | getUserAuthenticated = function() { 12 | window.sessionStorage.getItem('userIsAuthenticated'); 13 | $log.info("user is authorized: " + userIsAuthenticated.authorized); 14 | return userIsAuthenticated.authorized; 15 | }; 16 | $rootScope.$on("$locationChangeStart", function(event, next, current) { 17 | var i, msg, _results; 18 | $rootScope.$emit('session:route:start', { 19 | event: event, 20 | next: next, 21 | current: current 22 | }); 23 | angular.element('.active').removeClass('active'); 24 | _results = []; 25 | for (i in window.routes) { 26 | if (next.indexOf(i) !== -1) { 27 | if (window.routes[i].requireLogin && !getUserAuthenticated()) { 28 | msg = "You need to be authenticated to see this page!"; 29 | $log.warn(msg); 30 | event.preventDefault(); 31 | $rootScope.$emit('session:unauthorized', event); 32 | _results.push($location.path('/')); 33 | } else { 34 | angular.element('a[href="#' + $location.path() + '"]').addClass('active'); 35 | _results.push($rootScope.$emit('session:authorized', event)); 36 | } 37 | } else { 38 | _results.push(void 0); 39 | } 40 | } 41 | return _results; 42 | }); 43 | SessionService = { 44 | adapter: null, 45 | session: $cookieStore.get('App.session'), 46 | isAuthenticated: false, 47 | getUserAuthenticated: getUserAuthenticated, 48 | setUserAuthenticated: setUserAuthenticated, 49 | getSession: function() { 50 | if ($cookieStore.get('App.session')) { 51 | return $cookieStore.get('App.session'); 52 | } else { 53 | return {}; 54 | } 55 | }, 56 | setSession: function(value) { 57 | return $cookieStore.put('App.session', value); 58 | }, 59 | login: function(user) { 60 | var _ref; 61 | $rootScope.$emit('session:login', user); 62 | return (_ref = SessionService.adapter) != null ? typeof _ref.login === "function" ? _ref.login(user) : void 0 : void 0; 63 | }, 64 | logout: function(user) { 65 | var _ref; 66 | $rootScope.$emit('session:logout', user); 67 | SessionService.setUserAuthenticated(user); 68 | return (_ref = SessionService.adapter) != null ? typeof _ref.logout === "function" ? _ref.logout(user) : void 0 : void 0; 69 | }, 70 | register: function(user) { 71 | var _ref; 72 | $rootScope.$emit('session:register', user); 73 | return (_ref = SessionService.adapter) != null ? typeof _ref.register === "function" ? _ref.register(user) : void 0 : void 0; 74 | }, 75 | routeResolver: function() {} 76 | }; 77 | return SessionService; 78 | } 79 | ]); 80 | -------------------------------------------------------------------------------- /app/scripts/services/cms-socketservice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc service 5 | * @name angularCmsApp.cmsSocketService 6 | * @description 7 | * # cmsSocketService 8 | * Service in the angularCmsApp. 9 | */ 10 | angular.module('angularCmsApp').factory('cmsSocketService', function ($rootScope) { 11 | /** 12 | * I am a WebSocketClient 13 | * @param options 14 | * @returns {{instance: WebSocket, close: Function, send: Function}} 15 | * @constructor 16 | */ 17 | var WebSocketClient = function (options) { 18 | var _ws = new WebSocket(options.endpoint, options.protocol); 19 | _ws.onmessage = function (e) { 20 | $rootScope.$emit(options.protocol, e); 21 | return console.log(e.data); 22 | }; 23 | _ws.onerror = function (e) { 24 | return console.log(e); 25 | }; 26 | _ws.onclose = function (e) { 27 | return console.log(e); 28 | }; 29 | _ws.onopen = function (e) { 30 | _ws.send('update'); 31 | }; 32 | return { 33 | instance: _ws, 34 | close: function () { 35 | return _ws.close(); 36 | }, 37 | send: function (obj) { 38 | try { 39 | _ws.send(JSON.stringify(obj)); 40 | } catch (err) { 41 | 42 | throw err; 43 | } 44 | } 45 | }; 46 | }; 47 | return WebSocketClient; 48 | }); 49 | -------------------------------------------------------------------------------- /app/scripts/services/cms-usersfactory.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | @ngdoc service 4 | @name angularCmsApp.service:cmsUsersFactory 5 | @function 6 | 7 | @description 8 | This is the UsersFactory. 9 | */ 10 | 'use strict'; 11 | angular.module('angularCmsApp').factory('cmsUsersFactory', function() { 12 | var meaningOfLife; 13 | meaningOfLife = 42; 14 | return { 15 | someMethod: function() { 16 | return meaningOfLife; 17 | } 18 | }; 19 | }); 20 | -------------------------------------------------------------------------------- /app/scripts/services/dataservice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module('angularCmsApp').service('DataService', ['$http', function ($http) { 3 | var DataService = { 4 | endpoint: '/api/v2/angular-cms', 5 | request: function (path, method, params, data) { 6 | var defaults; 7 | defaults = { 8 | method: method, 9 | url: this.endpoint + path, 10 | cache: false, 11 | data: data, 12 | params: params 13 | }; 14 | return $http(defaults); 15 | }, 16 | fetch: function (collection, params) { 17 | return this.request('/' + collection, 'GET', params); 18 | }, 19 | get: function (collection, id, params) { 20 | return this.request('/' + collection + '/' + id, 'GET', params); 21 | }, 22 | save: function (collection, data) { 23 | if (data && data._id) { 24 | return this._update(collection, data); 25 | } else { 26 | return this._create(collection, data); 27 | } 28 | }, 29 | destroy: function (collection, data) { 30 | return this.request('/' + collection + '/' + data._id, 'DELETE'); 31 | }, 32 | _create: function (collection, data) { 33 | return this.request('/' + collection, 'POST', null, data); 34 | }, 35 | _update: function (collection, data) { 36 | return this.request('/' + collection + '/' + data._id, 'PUT', null, data); 37 | } 38 | }; 39 | return DataService; 40 | } 41 | ]); 42 | -------------------------------------------------------------------------------- /app/styles/main.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/app/styles/main.sass -------------------------------------------------------------------------------- /app/views/_aside.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |

Menu

7 |
8 |
9 | 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /app/views/_content.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 10 |
11 |
12 | {{alert.code}}! {{alert.message}} 13 | 14 |
15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /app/views/_footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 11 |
12 | -------------------------------------------------------------------------------- /app/views/_header.html: -------------------------------------------------------------------------------- 1 | 56 | 57 | 58 | 59 | 65 | -------------------------------------------------------------------------------- /app/views/_navbar.html: -------------------------------------------------------------------------------- 1 | 69 | 75 | -------------------------------------------------------------------------------- /app/views/_sidebar.html: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /app/views/admin.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

WebSockets

4 |

Use this document as a way to quickly start a new WebSocket project.

5 |
6 |
7 | 8 | 9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 | Log 25 | 26 | 27 |
28 | 46 | 47 |
48 | 49 | Send 50 |
51 |
52 | 53 | 54 |
55 |
56 | Output 57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
ClientMessageTime
{{item.id}}{{item.message}}{{item.datetime | date:'mediumTime'}}
77 |
78 |
79 | 82 |
83 | 84 |
85 | -------------------------------------------------------------------------------- /app/views/dashboard.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | Content 15 | 16 |
    17 |
  • 18 | 42 Pages 19 |
  • 20 |
  • 21 | 2 Categories 22 |
  • 23 |
  • 24 | 5 Tags 25 |
  • 26 |
  • 27 | 23 Files 28 |
  • 29 |
  • 30 | 5 Plugins 31 |
  • 32 |
33 |
34 |
35 | 36 | System 37 | 38 |
39 |
40 |

41 | You are using Angular-CMS 0.1.0 42 |

43 | 44 | 52 | 53 |
54 | 55 |
56 |
57 |
58 | 59 |
60 |
61 |
62 |
63 | 64 |
65 |
66 |
67 |
68 | 69 |
70 |
71 |
72 | 80 |
81 |
82 | 83 | 84 |
-------------------------------------------------------------------------------- /app/views/docs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/forgot-password.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 | 7 |
8 | Error {{error.code}}! {{error.message}} 9 |
10 |
11 | 12 |
13 | 16 |
17 |
18 |
19 | Back to Login 20 | Create an account 21 |
22 |
-------------------------------------------------------------------------------- /app/views/help.html: -------------------------------------------------------------------------------- 1 |

Help

2 |
3 | 4 |
5 |
6 |
7 | 10 |

Welcome to Angular-CMS!

11 |

Here are some links to help get you started:

12 |
13 |
14 | 18 |
19 |

Next Steps

20 |

Add some plugins

21 |

Upload media

22 |
23 |
24 |

More Actions

25 |

Manage Users

26 |

Site Settings

27 |
28 |
29 |
30 | 31 | 32 | 33 |
34 | 35 |
36 | 37 |
-------------------------------------------------------------------------------- /app/views/index.html: -------------------------------------------------------------------------------- 1 |
2 | 13 | 14 | 15 | 29 |
30 |
31 |
32 |
33 | 34 |
35 |
36 | Current Theme

Twenty Twelve 1.0

37 |

38 | The 2012 theme for Angular-CMS takes us back to the Bootstrap 2.3, featuring a full range of post formats, each displayed beautifully in their own unique way. Design details abound, starting with a vibrant color scheme and matching header images, beautiful typography and icons, and a flexible layout that looks great on any device, big or small. 39 |

40 |

41 | OPTIONS:Widgets | Menus | Header 42 |

43 |
44 |
45 |
46 |
47 |
48 | 49 |

Twenty Thirteen

50 |

51 | By jonniespratley 52 |

53 | 56 |
57 |
58 | 59 |

Twenty Fourteen

60 |

61 | By jonniespratley 62 |

63 | 66 |
67 |
68 |
69 |
70 | 71 | -------------------------------------------------------------------------------- /app/views/login.html: -------------------------------------------------------------------------------- 1 |
2 | 32 | 33 | 34 |
35 | -------------------------------------------------------------------------------- /app/views/main.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
6 | 7 |

8 | {{App.feature.title}} 9 |

10 |

11 | {{App.feature.body}} 12 |

13 |
    14 |
  • 15 | 16 |
  • 17 |
  • 18 | 19 |
  • 20 |
  • 21 | 22 |
  • 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | 35 |

{{item.title}}

36 |

{{item.body}}

37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 | 45 | 46 |
47 | -------------------------------------------------------------------------------- /app/views/media.html: -------------------------------------------------------------------------------- 1 | 2 |

Media

3 |
4 |
5 | 15 | 16 | 17 | 38 |
39 |
40 |
41 | 44 | 47 |
48 |
49 | 50 |
51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 67 | 68 | 69 | 70 | 71 | 72 |
FileTypeDate
65 | 66 | {{file.filename}}{{file.type}}{{file.created}}
73 | 74 |
    75 | 76 |
    77 |
    78 | 79 | ... 82 |
    83 |
    84 |
85 | 86 |
87 |

88 | -------------------------------------------------------------------------------- /app/views/pages.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
7 |
8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 37 | 38 | 39 |
13 | 14 | TitleDateActions
24 | 25 | {{page.title}}{{page.publish}}{{page.created | date:'medium'}} 29 |
30 | 33 | 36 |
40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 51 | 54 |
55 |
56 |
57 | -------------------------------------------------------------------------------- /app/views/plugins.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |
7 |
8 | 9 |
10 | 13 | 28 |
29 | 32 |
33 |
34 | 35 | 36 | 37 |
38 |
39 |
40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 59 |
44 | 45 | PluginDescription
53 | 54 | {{plugin.title}}{{plugin.description}}
60 |
61 |
62 |

63 | 1 - 10 of 146 items 10 Per Page 64 |

65 |
66 |
67 |
    68 |
  • 69 | Prev 70 |
  • 71 |
  • 72 | 1 73 |
  • 74 |
  • 75 | 2 76 |
  • 77 |
  • 78 | 3 79 |
  • 80 |
  • 81 | 4 82 |
  • 83 |
  • 84 | Next 85 |
  • 86 |
87 |
88 |
89 | 90 |
91 | 92 |
93 | 94 | -------------------------------------------------------------------------------- /app/views/profile.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 20 | 23 |
24 |
25 |
26 |
27 | 36 |
37 | -------------------------------------------------------------------------------- /app/views/register.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 | 9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 | 22 |
23 |
24 | 28 |
29 |
30 | 33 |
34 |
35 |
36 | Back to Login 37 |
38 |
39 | -------------------------------------------------------------------------------- /app/views/reset-password.html: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /app/views/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 |
7 | 8 |
9 |
10 |
12 |
13 |
14 | 15 |
16 |
17 |
18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 | 27 |
28 | 29 | 30 |
31 |
32 | Example Form 33 |
34 |
35 |
36 | 37 | 38 | 39 |
42 | 43 | 44 | 45 |
46 | 47 |
48 | 49 |
50 | 51 | 71 | 72 |
73 | 74 | 75 | General 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Network 86 | 87 | 88 | 89 | 90 | 91 | 92 |
93 | 96 | 99 |
100 |
101 |
102 | 103 |
104 | 105 | -------------------------------------------------------------------------------- /app/views/themes.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Themes

4 |
5 |
6 |
7 |
8 |
9 | 12 |
13 |
14 | Current Theme 15 |

{{App.theme}} 1.0

16 |

17 | This is going to be meta data for the theme. 18 |

19 |

20 | OPTIONS: 21 | Widgets | Menus | Header 22 |

23 |
24 |
25 |
26 | 27 |
28 | 39 |
40 | -------------------------------------------------------------------------------- /app/views/widgets.html: -------------------------------------------------------------------------------- 1 |

Widgets

2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | NAVBAR 11 |
12 |
13 |
14 |
15 |
16 |
17 | CONTENT 18 |
19 |
20 |
21 |
22 | SIDEBAR 23 |
24 |
25 |
26 | 27 |
28 |
29 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 53 |
54 |
55 |
56 |
57 |
App.widgets: {{App.widgets | json}}
58 | 59 | -------------------------------------------------------------------------------- /bin/db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mongod --dbpath db --rest --jsonp; -------------------------------------------------------------------------------- /bin/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #start the mongo db and server 4 | mongod --dbpath db --rest --jsonp; 5 | 6 | #start the api server 7 | node server; 8 | 9 | #start the web server 10 | grunt serve; 11 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-cms", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "json3": "~3.2.4", 6 | "es5-shim": "~2.1.0", 7 | "jquery": "~1.10.2", 8 | "bootstrap": "~3.2.0", 9 | "font-awesome": "~4.0.3", 10 | "angular-ui-utils": "bower", 11 | "angular-strap": "~2.0.0", 12 | "jquery-file-upload": "~9.5.7", 13 | "jquery-ui": "~1.10.4", 14 | "angular-motion": "~0.3.2", 15 | "bootstrap-additions": "~0.2.2", 16 | "socket.io": "~0.9.15", 17 | "angular": "~1.2.21", 18 | "angular-route": "~1.2.21", 19 | "angular-resource": "~1.2.21", 20 | "angular-cookies": "~1.2.21", 21 | "angular-animate": "~1.2.21", 22 | "angular-sanitize": "~1.2.21", 23 | "angular-form-gen": "~0.0.2", 24 | "pouchdb": "~3.6.0" 25 | }, 26 | "devDependencies": { 27 | "angular-mocks": "~1.2.21" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /config/allow_ip_list: -------------------------------------------------------------------------------- 1 | 1.2.3.4 2 | 127.0.0.1:3501 3 | 127.0.0.1 -------------------------------------------------------------------------------- /config/apache.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIECTCCAvGgAwIBAgIJAPxs7sqZrZc4MA0GCSqGSIb3DQEBBQUAMIGaMQswCQYD 3 | VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUm9zZXZpbGxl 4 | MRcwFQYDVQQKDA5BcHBNYXRyaXggSW5jLjEcMBoGA1UEAwwTd3d3Lm15YXBwbWF0 5 | cml4LmNvbTErMCkGCSqGSIb3DQEJARYcZGV2ZWxvcG1lbnRAYXBwbWF0cml4aW5j 6 | LmNvbTAeFw0xMjExMjAxODAxMDFaFw0xMzExMjAxODAxMDFaMIGaMQswCQYDVQQG 7 | EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUm9zZXZpbGxlMRcw 8 | FQYDVQQKDA5BcHBNYXRyaXggSW5jLjEcMBoGA1UEAwwTd3d3Lm15YXBwbWF0cml4 9 | LmNvbTErMCkGCSqGSIb3DQEJARYcZGV2ZWxvcG1lbnRAYXBwbWF0cml4aW5jLmNv 10 | bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM1HKzoOEFucSQEitFS5 11 | y5DhMDcKwtcix+pS9zzb5yCwbAGW1+vn0dOz3/bDlMSFDcOaSpiP2tmeSFMzpVQG 12 | y7K8N0p+AAsdxbyLNJBHFwHzAzo5TEzWsKWkR6+6opiiJJzkOYhDKA12efXGEyJx 13 | zGeaIfn9hGah8shKDqlS0mEphlpau+fDku4OgErHcjMgzHlVPg9WNplFiz9jfh6A 14 | YALoBUiOyW4+gQDwi7uQmIopMaRqHkSjiNs5xv5mL3WDyp7d7sT+74PupyalE+ul 15 | vMv8Gs0f2EtGKtu2uFHcrIXItI/abhYk4JqX1jHfL9GHndg6AEXLcHGQ1brAXtFj 16 | raECAwEAAaNQME4wHQYDVR0OBBYEFNtn4ZH4l6UFrQiYv4qfa2ukuCZyMB8GA1Ud 17 | IwQYMBaAFNtn4ZH4l6UFrQiYv4qfa2ukuCZyMAwGA1UdEwQFMAMBAf8wDQYJKoZI 18 | hvcNAQEFBQADggEBAGnTUKr4nYPvf1x4u+YgIMf1kkN0L8JhFYKyfiC7TpWC4DyE 19 | ovEJKrXkU13HNYg04NLWx/CxlXS1Z+Iqx0pyDenjlhMBFNoD8a78w99czkZWDNT2 20 | AfGbu4/URUi/oXiPkSbKR9VG4hRoaBbprqMNR67uGfHUksiX21nclrckO3sEd9sL 21 | q5Hbsoc/9DYmpqYgVaaziTW3d7tMpu64sLAgAroioPeP8FSgXDI4BKK9LEsqxUQJ 22 | Ab/SA6TUMrtRlnDxtSpxrcK41uL4l8wzZgDaQYjuHr3xRRbDyBD/POcZSWXZkKux 23 | mi1n+AmKVqLPcikkKWkClnd40dO+wrWBidsqI1M= 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /config/apache.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDNRys6DhBbnEkB 3 | IrRUucuQ4TA3CsLXIsfqUvc82+cgsGwBltfr59HTs9/2w5TEhQ3DmkqYj9rZnkhT 4 | M6VUBsuyvDdKfgALHcW8izSQRxcB8wM6OUxM1rClpEevuqKYoiSc5DmIQygNdnn1 5 | xhMiccxnmiH5/YRmofLISg6pUtJhKYZaWrvnw5LuDoBKx3IzIMx5VT4PVjaZRYs/ 6 | Y34egGAC6AVIjsluPoEA8Iu7kJiKKTGkah5Eo4jbOcb+Zi91g8qe3e7E/u+D7qcm 7 | pRPrpbzL/BrNH9hLRirbtrhR3KyFyLSP2m4WJOCal9Yx3y/Rh53YOgBFy3BxkNW6 8 | wF7RY62hAgMBAAECggEAPoKemjYwKwWDgbIEwgw1/i9k1E4VmRfBUwzitwir8Fto 9 | +G7r362fSt1qpSVXxyRRVbBFaQHdfgliMtv2S4kqPIpuN+P4tWnijhDB9/F+Axzy 10 | baeHIZr6/eXx4oRuARbXGWOyVVlF+BuM9hfUOdf3i9D26b6Ws/47BVzv7AYJhIZp 11 | z3U1CQDDwkNmZQ0bih1BATbxN6Z9eoXjYh+fPbeuy1agYzOe/ybFIxJoVmYQ6Ng8 12 | 1IGl36BDf3IvjnBaUtMJU2NgH3kkvUdsNvMQxCJCS1ClKnN4+hKELA0gh3syrIEO 13 | NckTMxUdoScBbJKXwBNSX2lQLn1tzYpfTnWpX/XsJQKBgQDx1BEkp2x81EbZsN4e 14 | yQo+qu4qDoipVKckbzUv2n0ij9kKhyYyCrrLIIat/yqdE/qvxt+ROdQ/QTI9uTMB 15 | DCr19IeStv2UBep9bXpliRLtj/uwbZ357xqS8K+9naMySo71ZsbbqI9RABXujL6k 16 | x7c1L40yMibIFzQUJeCTcz9jiwKBgQDZTsT8XlIgB2VbgLYsuTymewLgWgkLY31y 17 | 4kE/VHv3dj9HY8EZX54u0SraEdm8ATfJKhPGiCwA7+oFGYk4CvG4gN6e0v65mk/T 18 | mBtA1cCCORp2UHcWgx8OyLJjBdzcQC40n+hRTOiIjsmnqp362QemxgC9thV1qKWU 19 | jxOYI6TpAwKBgFuS038Oyz0l4UJ0AFimBnQepBtW75c3kCdy2TAqy3H4CNc8wFD4 20 | 0S00PuvKkWdz9lzzsRs8S8w8Mvs+kzrVUbtbgq8jDjksWFRwzYV/d6Rv/n4btFO9 21 | oFF+RP5HoFHd++6fobPyS3r/zk5MC7E4U38XZ59XcGoap0Tt5vHPFXTrAoGBAMxj 22 | qyGXcXUQn8O9vxs3qxUG9ECiTol4rUCGEEqJowOxI3l5GOFNrxFnyP9M9NdA6gB6 23 | ZBlCcOj8vsbHKuocWRhplXobCFz4CAcDtPZ5VdTl8r6/lhMFhwu3txkuJXq/nvX+ 24 | RVtalAgFDSR3dJnA0dKJv56wCWUeF4cwP4TEWdKTAoGBAOr3/BtIWKLA3XDft7VP 25 | U/NqbURWW5YDIGHuQwsjcKyTe4wzvbSnD33nYiUwMyEq4QnMp7Ob9Fj1iSpn28B3 26 | VnrM7L5mjzBAEE5ZfdZqY3XwjbrS8/UTJtvECqIhPXW1g9IF6wxYgbfbQvbBEZo0 27 | PEJLdg4dzWGl0HGoJVLzELyC 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /config/black_list: -------------------------------------------------------------------------------- 1 | facebook.com 2 | -------------------------------------------------------------------------------- /config/entrust_root_certification_authority.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/config/entrust_root_certification_authority.cer -------------------------------------------------------------------------------- /config/entrust_root_certification_authority.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC 3 | VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 4 | Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW 5 | KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl 6 | cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw 7 | NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw 8 | NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy 9 | ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV 10 | BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ 11 | KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo 12 | Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 13 | 4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 14 | KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI 15 | rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi 16 | 94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB 17 | sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi 18 | gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo 19 | kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE 20 | vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA 21 | A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t 22 | O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua 23 | AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP 24 | 9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ 25 | eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m 26 | 0vdXcDazv/wor3ElhVsT/h5/WrQ8 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /config/entrust_ssl_ca.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC 3 | VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u 4 | ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc 5 | KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u 6 | ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 7 | MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE 8 | ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j 9 | b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF 10 | bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg 11 | U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA 12 | A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ 13 | I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 14 | wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC 15 | AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb 16 | oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 17 | BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p 18 | dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk 19 | MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp 20 | b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu 21 | dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 22 | MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi 23 | E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa 24 | MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI 25 | hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN 26 | 95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd 27 | 2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= 28 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /config/hostfilters.js: -------------------------------------------------------------------------------- 1 | { 2 | "hostname.domain.tld": { 3 | "redirect": "www.google.com" 4 | }, 5 | "*:80": { 6 | "proxyto": "localhost:9000", 7 | "validuser": { 8 | "admin": "admin", 9 | "user": "fred" 10 | }, 11 | "description": "Very secret project here ;-)" 12 | } 13 | } -------------------------------------------------------------------------------- /config/mongodb-cert.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOQr5PKtb1p+QaIB 3 | a4Qlj/kCAo3jY6Gj7IaNvjc3ijcgVPNApuaooZgDH4a1URrvZnAaGyKwOfHFXia8 4 | sEVoPSAQhQ36Dqvo38cPvcU41tV7sF/orjMUBWqemm/wF5DCzZFFibMGydhZyVhc 5 | Fr/+RQSpxYzOIqUsVbFS1/nEmusdAgMBAAECgYEA2KIwQpSWR9Xd3MH6YcIEGshk 6 | wnBldN+dPvcu5WAPE13YKgh1LkRnAFvev9hAxBEOMlsm7l1g9oKMbLzzo0Jf3NkK 7 | M5ZK+CjmRqCJP0EWG+C+sgDRfF8HrhkT5Kn9X4FtOZ9OzUhUxqnPHnQh9S6/gCnd 8 | SS28Gxzl7cqF0XssrEUCQQD/n7W9R2TD5SuKFFsUkL8KuorMKspsL463pJHQtPlF 9 | XsmfcyE/HvRxgeod+C4c3jR4genNyRZU3yZ+3zksEjxvAkEA5IHX7LziLGdBCM/O 10 | Lppmm5K61fTQEq+atHVOoHcHe/97Pbukd1yPM3nM1PyX3BMyJ6wNPCQNkG0wFBf4 11 | m2evMwJBAMHrMd7ea0Xk84uzd4j5acRE0YijKZ660UTldqzirXt/terdVypgXEZD 12 | rQyBOtIxi5Kj02+Gjgdipfx3cRLK3scCQEcX9rwbECtMfLlnHfj15NKmVibXIh2O 13 | MQ3QKYRXRCbaF9ffF45jZ9JWJNpnNEAuYL5jE0MQ3H+A2SKHSrvfCG0CQHOrS0Su 14 | csh+3+hHxGfDOPCiyPa0MWHsrzcD3GvTFP6E1QNnYFRlfOxBQHb68CfYlSPSscJX 15 | Gv4pcBytBHGcst4= 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /config/mongodb-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDDjCCAnegAwIBAgIJAKihhdozF2jpMA0GCSqGSIb3DQEBBQUAMIGfMQswCQYD 3 | VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUm9zZXZpbGxl 4 | MRgwFgYDVQQKDA9BcHBNYXRyaXgsIEluYy4xFDASBgNVBAsMC0RldmVsb3BtZW50 5 | MQ8wDQYDVQQDDAZKb25uaWUxJjAkBgkqhkiG9w0BCQEWF2pvbm5pZUBhcHBtYXRy 6 | aXhpbmMuY29tMB4XDTEyMTIyNzIwMTg0NFoXDTEzMTIyNzIwMTg0NFowgZ8xCzAJ 7 | BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlSb3Nldmls 8 | bGUxGDAWBgNVBAoMD0FwcE1hdHJpeCwgSW5jLjEUMBIGA1UECwwLRGV2ZWxvcG1l 9 | bnQxDzANBgNVBAMMBkpvbm5pZTEmMCQGCSqGSIb3DQEJARYXam9ubmllQGFwcG1h 10 | dHJpeGluYy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOQr5PKtb1p+ 11 | QaIBa4Qlj/kCAo3jY6Gj7IaNvjc3ijcgVPNApuaooZgDH4a1URrvZnAaGyKwOfHF 12 | Xia8sEVoPSAQhQ36Dqvo38cPvcU41tV7sF/orjMUBWqemm/wF5DCzZFFibMGydhZ 13 | yVhcFr/+RQSpxYzOIqUsVbFS1/nEmusdAgMBAAGjUDBOMB0GA1UdDgQWBBSvczML 14 | X/vti9ZthTDFgMC0Gm4RDDAfBgNVHSMEGDAWgBSvczMLX/vti9ZthTDFgMC0Gm4R 15 | DDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBALmhMLVIKFpAVKXT4BNX 16 | 7x6n66VXs76gtzES/jG6JjztaAAiqsde6ajxRkD+JSqrY9BKFdPOKkACCNBdatw2 17 | kwvHpvEsKI/NYDq0c3W7G5jJLAEOAYID2rW9U56G+uVnPLykMOJZnLM5PRJzp958 18 | PUrdFTnDSUEOs8BSYbuSI4B7 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /content/tutorial/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/content/tutorial/.DS_Store -------------------------------------------------------------------------------- /content/tutorial/user-auth.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name 2 - User Authentication 3 | @description 4 | 5 | ## User Authentication 6 | As a admin I want to have user authentication for accessing the data. 7 | 8 | **Scenario:** 9 | Tom goes to the local farmers market to sell his products. Tom uses angular-cms to manage his sales and employees, he wants to user authentication to administor his content. 10 | 11 | 1. Tom sells a burger for $10, Tom needs to add the sale to his records. 12 | * Tom opens his tablets web browser and enters the url of his cms, clicks the sign in link and is presented with a login form. 13 | * Tom enters his email and password then clicks the login button. 14 | * Tom is then directed to the Dashboard to add the sale. 15 | 16 | **GUI Mock-up**: 17 | The following is a mock-up created from visualizing the scenario above. 18 | 19 | ![image](https://dl.dropboxusercontent.com/u/26906414/angular-cms/docs/cms-login.png) 20 | 21 | 22 | **Storyboard:** 23 | 24 | 1. Enter valid user email address. 25 | 2. Enter user password. 26 | 3. Select "Stay signed in" checkbox. 27 | 4. Click "Sign in" button. 28 | 5. **Success** - Dashboard should appear after successful authentication 29 | 6. **Fail** - Alert dialog should appear stating the issue. 30 | 31 | -------------------------------------------------------------------------------- /fig.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonniespratley/angular-cms/539eae39251a8705cde0c297ebe12a684b74226d/fig.yml -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var del = require('del'); 4 | var gulp = require('gulp'); 5 | var coffee = require('gulp-coffee'); 6 | var uglify = require('gulp-uglify'); 7 | var protractor = require('gulp-protractor').protractor; 8 | 9 | var server = require('gulp-server-livereload'); 10 | 11 | gulp.task('webserver', function() { 12 | gulp.src('app') 13 | .pipe(server({ 14 | livereload: true, 15 | defaultFile: 'index.html', 16 | directoryListing: true, 17 | open: true 18 | })); 19 | }); 20 | 21 | 22 | gulp.task('clean', function(cb) { 23 | del(['build'], cb); 24 | }); 25 | 26 | /** 27 | * CoffeeScript Source Files 28 | */ 29 | gulp.task('js', function () { 30 | gulp 31 | .src('./app/scripts/**/*.coffee') 32 | .pipe(coffee({ 33 | bare: true 34 | })) 35 | .pipe(uglify()) 36 | .pipe(gulp.dest('./.tmp/scripts')); 37 | }); 38 | 39 | /** 40 | * CoffeeScript Test Files 41 | */ 42 | gulp.task('js:test', function () { 43 | gulp.src('./test/**/*.coffee') 44 | .pipe(coffee({ 45 | bare: true 46 | })).pipe(gulp.dest('./.tmp')); 47 | }); 48 | 49 | gulp.task('watch', function () { 50 | gulp.watch('./app/scripts/**/*.coffee', ['js']); 51 | gulp.watch('./test/**/*.coffee', ['js:test']) 52 | }); 53 | 54 | 55 | gulp.task('test:e2e', ['js:test'], function(){ 56 | gulp.src(['./.tmp/protractor/**/*-spec.js']) 57 | .pipe(protractor({ 58 | configFile: 'protractor.conf.js' 59 | })) 60 | .on('error', function (e) { 61 | throw e; 62 | }); 63 | }); 64 | 65 | gulp.task('default', ['js:test', 'test']); -------------------------------------------------------------------------------- /karma-e2e.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | 9 | // testing framework to use (jasmine/mocha/qunit/...) 10 | frameworks: ['ng-scenario'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | '.tmp/e2e/*.js' 15 | ], 16 | 17 | preprocessors: { 18 | 'test/e2e/**/*.coffee': ['coffee'] 19 | }, 20 | 21 | // list of files / patterns to exclude 22 | exclude: [], 23 | 24 | // web server port 25 | port: 9090, 26 | 27 | // level of logging 28 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 29 | logLevel: config.LOG_INFO, 30 | coffeePreprocessor: { 31 | options: { 32 | bare: true 33 | } 34 | }, 35 | 36 | 37 | // enable / disable watching file and executing tests whenever any file changes 38 | autoWatch: false, 39 | 40 | 41 | // Start these browsers, currently available: 42 | // - Chrome 43 | // - ChromeCanary 44 | // - Firefox 45 | // - Opera 46 | // - Safari (only Mac) 47 | // - PhantomJS 48 | // - IE (only Windows) 49 | browsers: ['PhantomJS'], 50 | 51 | 52 | // Continuous Integration mode 53 | // if true, it capture browsers, run tests and exit 54 | singleRun: false, 55 | 56 | // Uncomment the following lines if you are using grunt's server to run the tests 57 | proxies: { 58 | '/': 'http://localhost:9000/' 59 | }, 60 | // URL root prevent conflicts with the site root 61 | urlRoot: '_karma_' 62 | }); 63 | }; 64 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | 'use strict'; 6 | config.set({ 7 | // base path, that will be used to resolve files and exclude 8 | basePath: '.', 9 | 10 | // testing framework to use (jasmine/mocha/qunit/...) 11 | frameworks: ['jasmine'], 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | 16 | //Components 17 | 'app/bower_components/jquery/jquery.js', 18 | 'app/bower_components/angular/angular.js', 19 | 'app/bower_components/angular-animate/angular-animate.js', 20 | 'app/bower_components/angular-mocks/angular-mocks.js', 21 | 'app/bower_components/angular-resource/angular-resource.js', 22 | 'app/bower_components/angular-cookies/angular-cookies.js', 23 | 'app/bower_components/angular-sanitize/angular-sanitize.js', 24 | 'app/bower_components/angular-route/angular-route.js', 25 | 'app/bower_components/angular-ui-utils/ui-utils.js', 26 | 'app/bower_components/angular-strap/dist/angular-strap.min.js', 27 | 'app/bower_components/angular-*/dist/angular-*.js', 28 | 29 | //Libs 30 | 'app/libs/parse-1.2.17.min.js', 31 | 'app/libs/md5.js', 32 | 'app/libs/markdown.js', 33 | 34 | //Coffee source 35 | //'app/scripts/**/*.coffee', 36 | //'test/spec/**/*.coffee' 37 | 38 | //JavaScript Source 39 | 'app/scripts/**/*.js', 40 | 'test/spec/**/*.js' 41 | ], 42 | 43 | // list of files / patterns to exclude 44 | exclude: [], 45 | 46 | // web server port 47 | port: 9090, 48 | 49 | // level of logging 50 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 51 | logLevel: config.LOG_INFO, 52 | 53 | // enable / disable watching file and executing tests whenever any file changes 54 | autoWatch: false, 55 | // coverage reporter generates the coverage 56 | reporters: ['progress', 'coverage'], 57 | // Start these browsers, currently available: 58 | // - Chrome 59 | // - ChromeCanary 60 | // - Firefox 61 | // - Opera 62 | // - Safari (only Mac) 63 | // - PhantomJS 64 | // - IE (only Windows) 65 | browsers: ['PhantomJS'], 66 | preprocessors: { 67 | 'app/scripts/**/*.js': ['coverage'] 68 | }, 69 | 70 | 71 | // optionally, configure the reporter 72 | coverageReporter: { 73 | reporters: [ 74 | {type: 'html', dir: 'coverage/'}, 75 | {type: 'lcov', dir: 'coverage/', file: 'lcov.info'} 76 | ] 77 | }, 78 | // Continuous Integration mode 79 | // if true, it capture browsers, run tests and exit 80 | singleRun: true 81 | }); 82 | }; 83 | -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | License: GNU General Public License v2 2 | The full license can be found at http://opensource.org/licenses/gpl-license.php -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: angular-cms 4 | buildpack: https://github.com/cloudfoundry/heroku-buildpack-nodejs.git 5 | #memory: 256MB 6 | stack: cflinuxfs2 7 | path: . 8 | command: node server.js 9 | services: 10 | - my_uaa_instance 11 | env: 12 | NODE_ENV: 'production' 13 | DEBUG: '*' 14 | -------------------------------------------------------------------------------- /mongod: -------------------------------------------------------------------------------- 1 | mongod --bind_ip=$IP --dbpath=data --nojournal --rest "$@" 2 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | 2 | // An example configuration file. 3 | exports.config = { 4 | baseUrl: 'http://127.0.0.1:9000', 5 | // The address of a running selenium server. 6 | seleniumAddress: 'http://localhost:4444/wd/hub', 7 | // ---- 3. To use remote browsers via Sauce Labs ----------------------------- 8 | // If sauceUser and sauceKey are specified, seleniumServerJar will be ignored. 9 | // The tests will be run remotely using Sauce Labs. 10 | //sauceUser: SAUCE_USERNAME, 11 | //sauceKey: SAUCE_ACCESS_KEY, 12 | // Use sauceSeleniumAddress if you need to customize the URL Protractor 13 | // uses to connect to sauce labs (for example, if you are tunneling selenium 14 | // traffic through a sauce connect tunnel). Default is 15 | //sauceSeleniumAddress: 'http://localhost:4445/wd/hub', 16 | 17 | // Capabilities to be passed to the webdriver instance. 18 | capabilities: { 19 | 'name': 'AngularCMS', 20 | 'browserName': 'chrome', 21 | 'chromeOptions': { 22 | 'args': ['show-fps-counter=true'] 23 | } 24 | }, 25 | 26 | params: { 27 | baseUrl: 'http://127.0.0.1:9000' 28 | }, 29 | 30 | // Spec patterns are relative to the current working directly when 31 | // protractor is called. 32 | specs: [ 33 | '.tmp/protractor/**/*-spec.js', 34 | 'test/protractor/**/*-spec.js' 35 | ], 36 | 37 | // Options to be passed to Jasmine-node. 38 | jasmineNodeOpts: { 39 | showColors: true, 40 | defaultTimeoutInterval: 30000 41 | }, 42 | onPrepare: function(){ 43 | browser.driver.get(browser.params.baseUrl); 44 | var SpecReporter = require('jasmine-spec-reporter'); 45 | jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true})); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /routes/cms-auth.js: -------------------------------------------------------------------------------- 1 | var bodyParser = require('body-parser'), 2 | 3 | util = require('util'), 4 | session = require('express-session'), 5 | crypto = require('crypto'), 6 | bcrypt = require('bcrypt-nodejs'); 7 | 8 | var cmsAuth = function(config, app) { 9 | console.warn('cms-auth initialized'); 10 | 11 | //### hashPassword 12 | //Hash password using basic sha1 hash. 13 | var hashPassword = function(pass, salt) { 14 | return bcrypt.hashSync(pass); 15 | }; 16 | 17 | var UserModel = require('./models/user'); 18 | var User = new UserModel(config, app); 19 | var cmsAuth = { 20 | /** 21 | * //### login 22 | //I handle trying to authorized a user with the v1 api server. 23 | * @param req 24 | * @param res 25 | * @param next 26 | */ 27 | login: function(req, res, next) { 28 | var query = {}; 29 | if (req.body.username) { 30 | query.username = req.body.username; 31 | } 32 | if (req.body.email) { 33 | query.username = req.body.email; 34 | } 35 | query.password = hashPassword(req.body.password, query.username); 36 | console.warn('trying to login', query); 37 | 38 | query._id = 'user-'+ query.username; 39 | 40 | app.locals.db.get(query._id).then(function(resp){ 41 | console.log('found user', resp); 42 | res.status(200).json(resp); 43 | }).catch(function(err){ 44 | console.log('No user so creating'); 45 | res.status(404).json(err); 46 | }); 47 | 48 | 49 | /* 50 | 51 | User.findOne({ 52 | username: query.username 53 | }, function(err, data) { 54 | if (err) { 55 | return res.json(400, err); 56 | } 57 | try { 58 | if (data && bcrypt.compareSync(req.body.password, data.password)) { 59 | req.session.user = data; 60 | return res.json(200, data); 61 | } else { 62 | return res.json(404, { 63 | message: 'Wrong username/password!' 64 | }); 65 | } 66 | } catch (error) { 67 | return res.json(404, { 68 | message: error 69 | }); 70 | } 71 | }); 72 | */ 73 | }, 74 | /** 75 | * Handle registering a new user 76 | * @param req 77 | * @param res 78 | * @param next 79 | */ 80 | register: function(req, res, next) { 81 | var data = req.body; 82 | 83 | //TODO: Need to make this externalized. 84 | if (req.body.username) { 85 | data.username = req.body.username; 86 | } 87 | if (req.body.email) { 88 | data.email = req.body.email; 89 | } 90 | data._id = 'user-'+ data.username; 91 | data.password = hashPassword(req.body.password, data.username); 92 | data.created_at = new Date(); 93 | data.updated_at = new Date(); 94 | data.active = false; 95 | data.groups = ['public']; 96 | 97 | console.warn('trying to register', data); 98 | 99 | app.locals.db.get(data._id).then(function(resp){ 100 | console.log('found user', resp); 101 | res.status(404).json({ 102 | message: 'User already exists' 103 | }); 104 | }).catch(function(err){ 105 | console.log('No user so creating'); 106 | app.locals.db.put(data).then(function(resp){ 107 | res.status(201).json(resp); 108 | }); 109 | }) 110 | 111 | 112 | 113 | 114 | /* 115 | //Try and find user 116 | User.find({ 117 | username: data.username 118 | }, function(err, u) { 119 | console.log('found user', err, util.inspect(u, { 120 | colors: true 121 | })); 122 | var user = new User(data); 123 | if (err) { 124 | res.json(400, { 125 | message: 'Problem registering!' 126 | }); 127 | } 128 | 129 | if (u.length) { 130 | res.jsonp(400, { 131 | message: 'Username already exists!' 132 | }); 133 | } else { 134 | user.save(function(er, ok) { 135 | if (er) { 136 | return res.json(400, { 137 | message: 'Problem registering!' 138 | }); 139 | } else { 140 | return res.jsonp(201, ok); 141 | } 142 | }); 143 | } 144 | 145 | }); 146 | */ 147 | }, 148 | 149 | 150 | 151 | session: function(req, res, next) { 152 | var user = req.session; 153 | if (req.session && req.session.user) { 154 | user = req.session.user; 155 | } 156 | console.warn(util.inspect(user, { 157 | colors: true 158 | })); 159 | return res.send({ 160 | message: 'Your session', 161 | data: user 162 | }); 163 | } 164 | }; 165 | 166 | 167 | app.get(config.apiBase + '/login', bodyParser.json(), cmsAuth.login); 168 | app.post(config.apiBase + '/login', bodyParser.json(), cmsAuth.login); 169 | app.post(config.apiBase + '/register', bodyParser.json(), cmsAuth.register); 170 | app.get(config.apiBase + '/session', bodyParser.json(), cmsAuth.session); 171 | }; 172 | 173 | module.exports = cmsAuth; 174 | -------------------------------------------------------------------------------- /routes/cms-db.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(config) { 3 | console.warn('cms-db initialized'); 4 | 5 | 6 | }; 7 | -------------------------------------------------------------------------------- /routes/cms-proxy.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | util = require('util'), 3 | httpProxy = require('http-proxy'); 4 | 5 | 6 | module.exports = function(config, app) { 7 | console.warn('cms-proxy', 'initialized', config.proxy); 8 | 9 | /** 10 | * @TODO - HTTPS Key and Cert 11 | * 12 | * This is the location of your https cert and key. 13 | */ 14 | var httpsKey = fs.readFileSync(config.https.key).toString(); 15 | var httpsCert = fs.readFileSync(config.https.cert).toString(); 16 | 17 | 18 | /** 19 | * @TODO - Proxy Options 20 | * This object holds options used for creating a proxy server. 21 | */ 22 | var options = { 23 | port: 8080, 24 | host: { 25 | hostname: 'localhost', 26 | port: 8181 27 | }, 28 | proxy: { 29 | hostname: 'localhost', 30 | port: 5001 31 | }, 32 | api: { 33 | hostname: 'localhost', 34 | port: 5151 35 | }, 36 | key: httpsKey, 37 | cert: httpsCert, 38 | hostncmsOnly: true, 39 | router: {} 40 | }; 41 | 42 | //Create proxy server and proxy requests 43 | var proxyServer = httpProxy.createServer(options, function(req, res, proxy) { 44 | 45 | console.log('Proxy server started on port: ' + options.host.port); 46 | 47 | // console.log('proxyServer', options); 48 | if (req.url.match(/^\/api\//)) { 49 | proxy.proxyRequest(req, res, { 50 | host: '127.0.0.1', 51 | port: options.api.port 52 | }); 53 | util.log('Routing request: API server'.warn); 54 | } else { 55 | 56 | /* Default express server */ 57 | proxy.proxyRequest(req, res, { 58 | host: '127.0.0.1', 59 | port: options.api.port 60 | }); 61 | util.log('Routing request: App Server'.warn); 62 | } 63 | }); 64 | 65 | 66 | //Start the proxy server 67 | proxyServer.listen(config.proxy.port, function() { 68 | console.log('cms-proxy server listening on ', config.proxy.port); 69 | }); 70 | }; 71 | -------------------------------------------------------------------------------- /routes/cms-rest.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | bodyParser = require('body-parser'), 3 | session = require('express-session'), 4 | RestResource = require('./rest'); 5 | 6 | module.exports = function(config, app) { 7 | 'use strict'; 8 | var router = express.Router(); 9 | 10 | router.use(function(req, res, next) { 11 | console.log('cms-rest Time:', Date.now(), req.params, req.method, req.body); 12 | next(); 13 | }); 14 | 15 | router.get(config.apiBase, RestResource.index); 16 | router.get(config.apiBase + '/plugins', RestResource.plugins); 17 | router.get(config.apiBase + '/readme', RestResource.readme); 18 | 19 | //Dynamic REST 20 | router.get(config.apiBase + '/:db/:id?', RestResource.get); 21 | router.post(config.apiBase + '/:db/:id?', bodyParser.json(), RestResource.add); 22 | router.put(config.apiBase + '/:db/:id?', bodyParser.json(), RestResource.edit); 23 | router.delete(config.apiBase + '/:db/:id?', RestResource.destroy); 24 | 25 | console.warn('cms-rest', 'initialized'); 26 | 27 | app.use('/', router); 28 | }; 29 | -------------------------------------------------------------------------------- /routes/cms-router.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var express = require('express'); 3 | 4 | module.exports = function(config, app) { 5 | 'use strict'; 6 | var router = express.Router(); 7 | router.use(function(req, res, next) { 8 | console.log('cms-router Time:', Date.now()); 9 | next(); 10 | }); 11 | 12 | //Fix for cloud foundry 13 | if (process.env.VCAP_APP_PORT) { 14 | config.port = process.env.VCAP_APP_PORT; 15 | } 16 | 17 | // TODO: Using pouchdb 18 | var PouchDB = require('pouchdb'); 19 | PouchDB.debug('*'); 20 | var db = new PouchDB(config.db.local); 21 | var db2 = new PouchDB(config.db.remote); 22 | PouchDB.sync(db, db2); 23 | app.locals.db = db; 24 | 25 | console.log('Connected to', config.db.local); 26 | 27 | // TODO: 28 | 29 | /* 30 | router.route('/users/:user_id') 31 | .all(function(req, res, next) { 32 | // runs for all HTTP verbs first 33 | // think of it as route specific middleware! 34 | next(); 35 | }) 36 | .get(function(req, res, next) { 37 | res.json(req.user); 38 | }) 39 | .put(function(req, res, next) { 40 | // just an example of maybe updating the user 41 | req.user.name = req.params.name; 42 | // save user ... etc 43 | res.json(req.user); 44 | }) 45 | .post(function(req, res, next) { 46 | next(new Error('not implemented')); 47 | }) 48 | .delete(function(req, res, next) { 49 | next(new Error('not implemented')); 50 | }) 51 | 52 | 53 | var replication = PouchDB.replicate('mydb', 'http://localhost:5984/mydb', {live: true}) 54 | .on('change', function (info) { 55 | // handle change 56 | }).on('complete', function (info) { 57 | // handle complete 58 | }).on('uptodate', function (info) { 59 | // handle up-to-date 60 | }).on('error', function (err) { 61 | // handle error 62 | }); 63 | 64 | replication.cancel(); // whenever you want to cancel 65 | */ 66 | 67 | var serverPort = process.env.PORT || config.port; 68 | var serverHost = process.env.IP || config.host; 69 | 70 | 71 | require('./cms-db')(config); 72 | require('./cms-auth')(config, app); 73 | // require('./cms-passport')(config, app); 74 | //require('./cms-upload')(config, app); 75 | require('./cms-server')(config, app); 76 | require('./cms-rest')(config, app); 77 | //require('./cms-proxy')(config, app); 78 | //require('./cms-sockets')(config, server); 79 | 80 | this.mount = function() { 81 | console.warn('cms-router', 'mounted'); 82 | 83 | app.use('/', router); 84 | }; 85 | 86 | }; 87 | -------------------------------------------------------------------------------- /routes/cms-server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | path = require('path'), 3 | serveStatic = require('serve-static'), 4 | finalhandler = require('finalhandler'), 5 | bodyParser = require('body-parser'); 6 | 7 | 8 | module.exports = function(config, app) { 9 | console.warn('cms-server initialized'); 10 | 11 | var router = express.Router(); 12 | 13 | var options = { 14 | dotfiles: 'ignore', 15 | etag: false, 16 | extensions: [ 17 | 'js', 18 | 'png', 19 | 'html', 'jpeg', 'jpg', 'gif', 20 | 'css' 21 | ], 22 | index: true, 23 | maxAge: '1d', 24 | redirect: false, 25 | setHeaders: function(res, path) { 26 | res.set('x-timestamp', Date.now()); 27 | } 28 | }; 29 | 30 | 31 | 32 | router.all('/', function(req, res, next) { 33 | res.header('Access-Control-Allow-Origin', '*'); 34 | res.header('Access-Control-Allow-Headers', 'X-Requested-With'); 35 | next(); 36 | console.log('cms-server', req.method); 37 | }); 38 | 39 | 40 | app.use('/', router); 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /routes/cms-sockets.js: -------------------------------------------------------------------------------- 1 | var events = require('events'), 2 | util = require('util'), 3 | q = require('q'), 4 | WebSocketServer = require('websocket').server, 5 | WebSocketRouter = require('websocket').router; 6 | 7 | //////////////////////////// 8 | //## Socket Server 9 | //This is a socket server implementation for 'real' time analytics and other data. 10 | //This is for use with geo analytics and other backend data from the app. listen for connected clients 11 | // 12 | // ### Server Channels 13 | //These are the events that this socket server dispatches. 14 | // 15 | //1. cms:authorization 16 | //2. cms:client:message 17 | //3. cms:client:connect 18 | //4. cms:client:disconnect 19 | //5. cms:server:message 20 | //6. cms:server:disconnect 21 | //7. cms:server:connect 22 | //8. cms: 23 | module.exports = function (config, app) { 24 | 'use strict'; 25 | events.EventEmitter.call(this); 26 | console.warn('cms-socket initialized'); 27 | 28 | //Start the websocket server 29 | //SocketServer.init(proxyServer); 30 | var cmsSockets = {}, 31 | connections, wsserver, wsclient, router, self = cmsSockets; 32 | 33 | 34 | var delay = function (fn, time) { 35 | var defer = q.defer(); 36 | setTimeout(function () { 37 | fn(); 38 | defer.resolve(); 39 | }, time); 40 | return defer.promise; 41 | }; 42 | 43 | var delayedSocketPush = function (socket, time) { 44 | return delay(function (msg) { 45 | socket.emit('msg', { 46 | datetime: new Date(), 47 | message: msg, 48 | id: 'Server' 49 | }); 50 | }, time); 51 | }; 52 | 53 | //Hold the names of events that this socket server listens for and emits 54 | self.events = { 55 | session: { 56 | pageView: 'cms:session:pageView', 57 | hashChange: 'cms:session:hashChange', 58 | login: 'cms:session:login', 59 | logout: 'cms:session:logout' 60 | }, 61 | server: { 62 | message: 'cms:server:message', 63 | connected: 'cms:server:connect', 64 | disconnected: 'cms:server:disconnect' 65 | }, 66 | client: { 67 | message: 'cms:client:message', 68 | connected: 'cms:client:connect', 69 | disconnected: 'cms:client:disconnect' 70 | } 71 | }; 72 | 73 | 74 | //Store a list of the connected clients 75 | connections = []; 76 | 77 | var serverConfig = { 78 | httpServer: app, 79 | }; 80 | 81 | wsserver = new WebSocketServer(); 82 | wsserver.mount(serverConfig); 83 | 84 | router = new WebSocketRouter(); 85 | router.attachServer(wsserver); 86 | 87 | /** 88 | * Echo Protocol 89 | */ 90 | router.mount('*', 'echo-protocol', function (request) { 91 | console.log('mounted to echo protocol'); 92 | 93 | var conn = request.accept(request.origin); 94 | 95 | conn.on('message', function (message) { 96 | console.log('routed message', util.inspect(message, {colors: true})); 97 | }); 98 | conn.send('hey'); 99 | }); 100 | 101 | /** 102 | * Update Protocol 103 | */ 104 | router.mount('*', 'update-protocol', function (request) { 105 | console.log('mounted to update protocol'); 106 | var conn = request.accept(request.origin); 107 | conn.on('message', function (message) { 108 | console.log('update all the things', message); 109 | }); 110 | }); 111 | 112 | util.inherits(cmsSockets, events.EventEmitter); 113 | 114 | }; 115 | -------------------------------------------------------------------------------- /routes/models/upload.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(){}; 3 | -------------------------------------------------------------------------------- /routes/models/user.js: -------------------------------------------------------------------------------- 1 | // app/models/user.js 2 | // load the things we need 3 | //var mongoose = require( 'mongoose' ); 4 | var bcrypt = require( 'bcrypt-nodejs' ); 5 | var hash = bcrypt.hashSync("bacon"); 6 | 7 | 8 | 9 | 10 | // create the model for users and expose it to our app 11 | module.exports = function(config, app){ 12 | // define the schema for our user model 13 | this.Schema = { 14 | id: String, 15 | provider: String, 16 | displayName: String, 17 | name: Object, 18 | emails: Array, 19 | photos: Array, 20 | username: String, 21 | email: String, 22 | password: String, 23 | active: Boolean, 24 | meta: Object, 25 | token: String, 26 | created_at: Date, 27 | updated_at: Date, 28 | local: { 29 | email: String, 30 | password: String, 31 | }, 32 | facebook: { 33 | id: String, 34 | token: String, 35 | email: String, 36 | name: String 37 | }, 38 | twitter: { 39 | id: String, 40 | token: String, 41 | displayName: String, 42 | username: String 43 | }, 44 | google: { 45 | id: String, 46 | token: String, 47 | email: String, 48 | name: String 49 | } 50 | 51 | } ; 52 | // methods ====================== 53 | this.method = function(name, cb){ 54 | this[name] = cb; 55 | console.log('adding method', name); 56 | } 57 | // generating a hash 58 | this.method( 'generateHash', function (password) { 59 | return bcrypt.hashSync( password, bcrypt.genSaltSync( 8 ), null ); 60 | }); 61 | 62 | // checking if password is valid 63 | this.method( 'validPassword', function (password) { 64 | return bcrypt.compareSync( password, this.password ); 65 | }); 66 | 67 | this.method( 'findOrCreate', function (profile, fn) { 68 | console.warn( 'findOrCreate', profile ); 69 | this.findByEmail( profile.emails[0], function (err, user) { 70 | fn( user ); 71 | } ); 72 | }); 73 | 74 | this.method( 'findByUsername', function (username, fn) { 75 | console.warn( 'findByUsername', username ); 76 | this.find( {username: username}, function (err, data) { 77 | if (err) { 78 | fn( false, err ); 79 | } 80 | fn( data ); 81 | } ); 82 | } ); 83 | 84 | this.method( 'findByEmail', function (email, fn) { 85 | console.warn( 'findByEmail', email ); 86 | for (var i = 0, len = users.length; i < len; i++) { 87 | var user = users[i]; 88 | if (user.email === email) { 89 | return fn( null, user ); 90 | } 91 | } 92 | return fn( null, null ); 93 | } ); 94 | 95 | this.method('find', function(user){ 96 | console.log('find this uer', user); 97 | }) 98 | 99 | 100 | }; 101 | -------------------------------------------------------------------------------- /routes/views/account.ejs: -------------------------------------------------------------------------------- 1 | 2 | <% layout('layout') -%> 3 | 4 |

Username: <%= user.username %>

5 |

Email: <%= user.email %>

6 | 7 |
8 |
9 | 12 |
13 |
14 | 17 |
18 |
19 | 20 | <%= user %> 21 | -------------------------------------------------------------------------------- /routes/views/index.ejs: -------------------------------------------------------------------------------- 1 | <% layout('layout') -%> 2 | <% if (!user) { %> 3 |

Welcome! Please log in.

4 | 5 | Login with Facebook 6 | Login with Google 7 | Login with Twitter 8 | Login 9 | <% } else { %> 10 |

Hello, <%= user.username %>.

11 | <% } %> 12 | -------------------------------------------------------------------------------- /routes/views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Passport-Local Example 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | <%- body %> 14 |
15 |
16 | 17 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /routes/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | <% layout('layout') -%> 3 |
4 |
5 | 6 | 32 | 33 |
34 | 35 | 36 |
37 | -------------------------------------------------------------------------------- /routes/views/register.ejs: -------------------------------------------------------------------------------- 1 | 2 | <% layout('layout') -%> 3 |
4 |
5 |
6 | 10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 | 25 |
26 |
27 | 31 |
32 |
33 | 36 |
37 |
38 |
39 | Back to Login 40 |
41 |
42 | 43 |
-------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Server - This is the Node.js Server. 3 | * @object 4 | */ 5 | 6 | var fs = require('fs'), 7 | cmsRouter = require('./routes/cms-router.js'), 8 | path = require('path'), 9 | serveStatic = require('serve-static'), 10 | config = JSON.parse(fs.readFileSync('./config/config.json')); 11 | 12 | //Test if services 13 | if (process.env.VCAP_SERVICES) { 14 | var cloudServices = JSON.parse(process.env.VCAP_SERVICES); 15 | 16 | console.warn('cloud services', cloudServices); 17 | //var dbcreds = services['mongodb'][0].credentials; 18 | } 19 | 20 | var port = process.env.PORT || config.port; 21 | var host = process.env.VCAP_APP_HOST || "127.0.0.1"; 22 | config.host = host; 23 | config.port = port; 24 | 25 | var express = require('express'); 26 | var app = express(); 27 | var router = new cmsRouter(config, app); 28 | router.mount(); 29 | 30 | var options = { 31 | dotfiles: 'ignore', 32 | etag: false, 33 | extensions: [ 34 | 'js', 35 | 'png', 36 | 'html', 'jpeg', 'jpg', 'gif', 37 | 'css' 38 | ], 39 | index: true, 40 | maxAge: '1d', 41 | redirect: false, 42 | setHeaders: function(res, path) { 43 | res.set('x-timestamp', Date.now()); 44 | } 45 | }; 46 | var staticDir = config.publicDir; 47 | 48 | console.log('staticDir', staticDir); 49 | 50 | 51 | //app.use(express.static(config.publicDir)); 52 | //app.use(express.static(config.staticDir)); 53 | //app.use(express.static('www')); 54 | app.use(express.static('app')); 55 | 56 | app.use('/www', express.static('./www')); 57 | app.use(express.static(path.resolve(__dirname, config.staticDir), options)); 58 | app.all('/', function(req, res, next) { 59 | res.header('Access-Control-Allow-Origin', '*'); 60 | res.header('Access-Control-Allow-Headers', 'X-Requested-With'); 61 | next(); 62 | console.log('cms-server', req.method); 63 | }); 64 | 65 | app.get('/', function(res, req, next) { 66 | var indexFile = config.publicDir + path.sep + 'index.html'; 67 | console.log('Loading index', indexFile); 68 | req.sendFile(indexFile); 69 | next(); 70 | }); 71 | 72 | app.listen(port, function() { 73 | console.log('express server listening on port: ' + port); 74 | }); 75 | -------------------------------------------------------------------------------- /stackato.yml: -------------------------------------------------------------------------------- 1 | app-dir: /WWW/2015Projects/angular-cms 2 | applications: 3 | /WWW/2015Projects/angular-cms: 4 | name: angular-cms 5 | url: angular-cms.jsapps.io 6 | autoscale: 7 | cpu: 8 | max: 80 9 | min: 20 10 | enabled: no 11 | instances: 12 | max: 2 13 | min: 1 14 | buildpack: https://github.com/cloudfoundry/heroku-buildpack-nodejs.git 15 | command: node server.js 16 | disk: 2048 17 | instances: 1 18 | memory: 256 19 | name: angular-cms 20 | placement-zone: default 21 | sso-enabled: no 22 | stack: lucid64 23 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "after": false, 23 | "afterEach": false, 24 | "angular": false, 25 | "before": false, 26 | "beforeEach": false, 27 | "browser": false, 28 | "describe": false, 29 | "expect": false, 30 | "inject": false, 31 | "it": false, 32 | "spyOn": false 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /test/intern.conf.js: -------------------------------------------------------------------------------- 1 | // Learn more about configuring this file at . 2 | // These default settings work OK for most people. The options that *must* be changed below are the 3 | // packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites. 4 | /* 5 | SAUCE_USERNAME=jonniespratleyge SAUCE_ACCESS_KEY=66f8830c-ec8f-441c-8f06-e8947d830a90 ./node_modules/.bin/intern-runner config=tests/intern 6 | */ 7 | 8 | define({ 9 | //sauce connect 10 | tunnelOptions: { 11 | username: 'jonniespratleyge', 12 | accessKey: '66f8830c-ec8f-441c-8f06-e8947d830a90' 13 | }, 14 | // The port on which the instrumenting proxy will listen 15 | proxyPort: 9900, 16 | 17 | // A fully qualified URL to the Intern proxy 18 | proxyUrl: 'http://localhost:9900/', 19 | 20 | // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the 21 | // specified browser environments in the `environments` array below as well. See 22 | // https://code.google.com/p/selenium/wiki/DesiredCapabilities for standard Selenium capabilities and 23 | // https://saucelabs.com/docs/additional-config#desired-capabilities for Sauce Labs capabilities. 24 | // Note that the `build` capability will be filled in with the current commit ID from the Travis CI environment 25 | // automatically 26 | capabilities: { 27 | 'selenium-version': '2.41.0' 28 | }, 29 | 30 | // Browsers to run integration testing against. Note that version numbers must be strings if used with Sauce 31 | // OnDemand. Options that will be permutated are browserName, version, platform, and platformVersion; any other 32 | // capabilities options specified for an environment will be copied as-is 33 | environments: [ 34 | {browserName: 'chrome'} 35 | ], 36 | 37 | // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service 38 | maxConcurrency: 3, 39 | 40 | // Name of the tunnel class to use for WebDriver tests 41 | tunnel: 'SauceLabsTunnel', 42 | 43 | // The desired AMD loader to use when running unit tests (client.html/client.js). Omit to use the default Dojo 44 | // loader 45 | useLoader: { 46 | 'host-node': 'dojo/dojo', 47 | 'host-browser': 'node_modules/dojo/dojo.js' 48 | }, 49 | 50 | // Configuration options for the module loader; any AMD configuration options supported by the specified AMD loader 51 | // can be used here 52 | loader: { 53 | // Packages that should be registered with the loader in each testing environment 54 | packages: [ 55 | { name: 'routes', location: './routes' } 56 | ] 57 | }, 58 | 59 | // Non-functional test suite(s) to run in each browser 60 | suites: [ 61 | 'test/routes/cms-auth-spec', 62 | 'test/routes/cms-passport-spec', 63 | 'test/routes/cms-proxy-spec', 64 | 'test/routes/cms-rest-spec', 65 | 'test/routes/cms-server-spec', 66 | 'test/routes/cms-sockets-spec', 67 | 'test/routes/cms-upload-spec' 68 | /* 'myPackage/tests/foo', 69 | 'myPackage/tests/bar' */ 70 | ], 71 | 72 | // Functional test suite(s) to run in each browser once non-functional tests are completed 73 | functionalSuites: [ 74 | //'tests/functional/index' 75 | /* 'myPackage/tests/functional' */ ], 76 | 77 | // A regular expression matching URLs to files that should not be included in code coverage analysis 78 | excludeInstrumentation: /^(?:tests|node_modules|app\/bower_components|www|config|bin)\// 79 | }); 80 | -------------------------------------------------------------------------------- /test/protractor/j$.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | J$ Helpers - I am test helpers 4 | */ 5 | module.exports = { 6 | element: function(selector, label) { 7 | if (label) { 8 | console.warn('finding', label); 9 | } 10 | return $(selector); 11 | }, 12 | input: function(name) { 13 | return element(protractor.By.css("[name=" + name + "]")).getWebElement(); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /test/protractor/pages/app-page.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | App Page - I handle general actions in the app. 4 | */ 5 | var AppPage; 6 | 7 | AppPage = { 8 | title: $('.navbar-brand'), 9 | get: function() { 10 | return browser.get('/#'); 11 | }, 12 | refresh: function() { 13 | return browser.refresh(); 14 | } 15 | }; 16 | 17 | module.exports = AppPage; 18 | -------------------------------------------------------------------------------- /test/protractor/pages/login-page.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | Login Page - I handle actions on the login page. 4 | */ 5 | var LoginPage; 6 | 7 | LoginPage = { 8 | username: $('#username'), 9 | password: $('#password'), 10 | get: function() { 11 | return browser.get('#/login'); 12 | }, 13 | logout: function() { 14 | return $('.dropdown-toggle').getWebElement().then(function(el) { 15 | return el.click().then(function() { 16 | return $('[href="#/login"]').click(); 17 | }); 18 | }); 19 | }, 20 | login: function(u, p) { 21 | this.username.sendKeys(u); 22 | this.password.sendKeys(p); 23 | return element(protractor.By.css('button[type="submit"]')).click(); 24 | } 25 | }; 26 | 27 | module.exports = LoginPage; 28 | -------------------------------------------------------------------------------- /test/protractor/pages/register-page.js: -------------------------------------------------------------------------------- 1 | var RegisterPage, j$; 2 | 3 | j$ = require('../j$'); 4 | 5 | 6 | /** 7 | Register Page - I handle actions on the register page 8 | */ 9 | 10 | RegisterPage = { 11 | email: $('#email'), 12 | username: $('#username'), 13 | password: $('#password'), 14 | password2: $('#password2'), 15 | agree: $('#agree'), 16 | submit: element(protractor.By.buttonText('Sign up')), 17 | get: function() { 18 | return browser.get('#/register'); 19 | }, 20 | register: function(username, password) { 21 | this.email.sendKeys(username); 22 | this.username.sendKeys(username); 23 | this.password.sendKeys(password); 24 | this.password2.sendKeys(password); 25 | return this.agree.click().then((function(_this) { 26 | return function() { 27 | return _this.submit.click(); 28 | }; 29 | })(this)); 30 | } 31 | }; 32 | 33 | module.exports = RegisterPage; 34 | -------------------------------------------------------------------------------- /test/protractor/pages/users-page.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | UsersPage - I handle actions on the users page 4 | */ 5 | var UsersPage; 6 | 7 | module.exports = UsersPage = function() { 8 | this.newUserBtn = element(protractor.By.buttonText("New User")); 9 | this.submitBtn = element(protractor.By.buttonText("Submit")); 10 | this.inputs = { 11 | email: element(protractor.By.model("user.email")), 12 | username: element(protractor.By.model("user.username")), 13 | password: element(protractor.By.model("user.password")), 14 | name: element(protractor.By.model("user.meta.name")), 15 | summary: element(protractor.By.model("user.meta.summary")) 16 | }; 17 | this.get = function() { 18 | return browser.get('/#/users'); 19 | }; 20 | return this.setForm = function(email, username, password, name, summary) { 21 | this.newUserBtn.click(); 22 | browser.sleep(500); 23 | this.inputs.username.sendKeys(username); 24 | this.inputs.email.sendKeys(email); 25 | this.inputs.password.sendKeys(password); 26 | this.inputs.name.sendKeys(name); 27 | this.inputs.summary.sendKeys(summary); 28 | this.submitBtn.click(); 29 | return browser.sleep(1000); 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /test/protractor/spec/app-spec.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | Protractor e2e Tests 4 | */ 5 | describe('Angular-CMS App', function() { 6 | var appPage; 7 | appPage = require('../pages/app-page'); 8 | return it('should have the correct title', function() { 9 | return appPage.title.getText().then(function(val) { 10 | return expect(val).toContain('angular-cms'); 11 | }); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/protractor/spec/login-spec.js: -------------------------------------------------------------------------------- 1 | var loginPage; 2 | 3 | loginPage = require('../pages/login-page'); 4 | 5 | 6 | /** 7 | Login - The user login implementation 8 | */ 9 | 10 | describe('Login:', function() { 11 | beforeEach(function() { 12 | return loginPage.get(); 13 | }); 14 | afterEach(function() { 15 | return loginPage.logout(); 16 | }); 17 | return it('should allow a user to login', function() { 18 | return loginPage.login('test@gmail.com', 'test').then(function() { 19 | return expect(browser.getLocationAbsUrl()).toContain('/dashboard'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/protractor/spec/register-spec.js: -------------------------------------------------------------------------------- 1 | var registerPage, testEmail; 2 | registerPage = require('../pages/register-page'); 3 | /** 4 | Register - The user registration implementation 5 | */ 6 | testEmail = Date.now() + '-test@email.com'; 7 | describe('Register: ', function () { 8 | return it('should allow a user to register', function () { 9 | registerPage.get(); 10 | expect(browser.getCurrentUrl()).toContain('register'); 11 | return registerPage.register(testEmail, 'test').then(function () { 12 | return expect(browser.getCurrentUrl()).toContain('login'); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/protractor/spec/users-spec.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/routes/cms-auth-spec.js: -------------------------------------------------------------------------------- 1 | /* global define */ 2 | define([ 3 | 'intern!object', 4 | 'intern/chai!assert', 5 | 'intern/dojo/node!request' 6 | ], function (registerSuite, assert, request) { 7 | 'use strict'; 8 | registerSuite({ 9 | name: 'cms-auth', 10 | 'should have /auth/login route': function () { 11 | this.skip(); 12 | }, 13 | 'should have /auth/register route': function () { 14 | this.skip(); 15 | }, 16 | 'should have /auth/me route': function () { 17 | this.skip(); 18 | } 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/routes/cms-passport-spec.js: -------------------------------------------------------------------------------- 1 | /* global define */ 2 | define([ 3 | 'intern!object', 4 | 'intern/chai!expect', 5 | 'intern/dojo/node!path', 6 | 'intern/dojo/node!fs', 7 | 'intern/dojo/node!supertest', 8 | 'intern/dojo/node!express', 9 | 'intern/dojo/node!../../routes/cms-passport', 10 | ], function (registerSuite, expect, path, fs, request, express, cmsPassport) { 11 | 'use strict'; 12 | 13 | 14 | var app = express(); 15 | var config = JSON.parse(fs.readFileSync(process.cwd() + '/config/config.json')); 16 | config.port = 9191; 17 | 18 | cmsPassport(config, app); 19 | 20 | 21 | registerSuite({ 22 | name: 'cms-passport', 23 | 'should have /proxy route': function () { 24 | this.skip(); 25 | }, 26 | 'should have a /auth/google/callback route': function () { 27 | this.skip(); 28 | }, 29 | 'should have a /auth/google route': function () { 30 | this.skip(); 31 | }, 32 | 'should have /auth/me route': function () { 33 | this.skip(); 34 | }, 35 | 'should have /auth/register route': function () { 36 | this.skip(); 37 | }, 38 | 'POST - /register - should return user on successful registration': function () { 39 | 40 | var dfd = this.async(); 41 | request(app) 42 | .post('/auth/register') 43 | .send({ 44 | "username": Date.now() + "test@email.com", 45 | "email": Date.now() + "test@email.com", 46 | "password": "test", 47 | "metadata": { 48 | "avatar": "", 49 | "name": "Jonnie Dollas" 50 | } 51 | }) 52 | .set('Accept', 'application/json') 53 | .expect('Content-Type', /json/) 54 | .expect(201, dfd.resolve()); 55 | }, 56 | 'POST - /login - should return user on successful login': function () { 57 | var dfd = this.async(); 58 | var validUser = { 59 | username: 'test@gmail.com', 60 | password: 'test' 61 | }; 62 | request(app) 63 | .post('/auth/login') 64 | .send(validUser) 65 | .set('Accept', 'application/json') 66 | .expect('Content-Type', /json/) 67 | .expect(200) 68 | .end(function(err, res){ 69 | if (err) {throw err;} 70 | console.log(res.body); 71 | dfd.callback(res); 72 | }); 73 | } 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/routes/cms-proxy-spec.js: -------------------------------------------------------------------------------- 1 | /* global define */ 2 | define([ 3 | 'intern!object', 4 | 'intern/chai!assert', 5 | 'intern/dojo/node!request' 6 | ], function (registerSuite, assert, request) { 7 | 'use strict'; 8 | registerSuite({ 9 | name: 'cms-proxy', 10 | 'should have /proxy route': function () { 11 | this.skip(); 12 | } 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/routes/cms-rest-spec.js: -------------------------------------------------------------------------------- 1 | /* global define */ 2 | define([ 3 | 'intern!object', 4 | 'intern/chai!assert', 5 | 'intern/dojo/node!request' 6 | ], function (registerSuite, assert, request) { 7 | 'use strict'; 8 | registerSuite({ 9 | name: 'cms-rest', 10 | 'GET - /collection - should return array of items': function () { 11 | this.skip(); 12 | }, 13 | 'GET - /collection/:id - should return object item': function () { 14 | this.skip(); 15 | }, 16 | 'POST - /collection - should return object on success': function () { 17 | this.skip(); 18 | }, 19 | 'PUT - /collection/:id - should return object on success': function () { 20 | this.skip(); 21 | } 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/routes/cms-server-spec.js: -------------------------------------------------------------------------------- 1 | /* global define */ 2 | define([ 3 | 'intern!object', 4 | 'intern/chai!assert', 5 | 'intern/dojo/node!request' 6 | ], function (registerSuite, assert, request) { 7 | 'use strict'; 8 | registerSuite({ 9 | name: 'cms-server', 10 | 'should have /proxy route': function () { 11 | this.skip(); 12 | } 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/routes/cms-sockets-spec.js: -------------------------------------------------------------------------------- 1 | /* global define */ 2 | define([ 3 | 'intern!object', 4 | 'intern/chai!assert', 5 | 'intern/dojo/node!request' 6 | ], function (registerSuite, assert, request) { 7 | 'use strict'; 8 | registerSuite({ 9 | name: 'cms-sockets', 10 | 'should have /proxy route': function () { 11 | this.skip(); 12 | } 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/routes/cms-upload-spec.js: -------------------------------------------------------------------------------- 1 | /* global define */ 2 | define([ 3 | 'intern!object', 4 | 'intern/chai!assert', 5 | 'intern/dojo/node!request' 6 | ], function (registerSuite, assert, request) { 7 | 'use strict'; 8 | registerSuite({ 9 | name: 'cms-upload', 10 | 'should have /proxy route': function () { 11 | this.skip(); 12 | } 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/routes/functional/cms-passport.js: -------------------------------------------------------------------------------- 1 | 'submit form': function () { 2 | return this.remote 3 | .get('/login') 4 | .findById('username') 5 | .click() 6 | .type('test@gmail.com') 7 | .end() 8 | .findById('password') 9 | .click() 10 | .type('test') 11 | .end() 12 | .findById('submit') 13 | .click() 14 | .end() 15 | .setFindTimeout(Infinity) 16 | .findById('#account') 17 | .setFindTimeout(0) 18 | .text() 19 | .then(function (resultText) { 20 | assert.ok(resultText.indexOf( 21 | 'email') > -1, 22 | 'test@gmail.com'); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /test/routes/rest-spec.js: -------------------------------------------------------------------------------- 1 | var request = require('supertest'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var expect = require('chai').expect; 5 | var express = require('express'); 6 | var app = express(); 7 | var config = JSON.parse(fs.readFileSync(process.cwd() + '/config/config.json')); 8 | config.port = 9191 9 | 10 | 11 | var cmsRoutes = require(process.cwd() + '/routes/cms-routes'); 12 | cmsRoutes.mount(config, app); 13 | 14 | app.listen(9292); 15 | 16 | var endpoint = 'http://localhost:8181/api/v2'; 17 | var username = "nodetest" + Date.now(); 18 | var postData = { 19 | "username": username, 20 | "email": username + "@email.com", 21 | "password": "test", 22 | "active": true, 23 | "groups": ["member"], 24 | "metadata": { 25 | "avatar": "", 26 | "name": "Node Test User" 27 | } 28 | }; 29 | 30 | 31 | 32 | describe('Testing: API Server', function () { 33 | 34 | it('POST - /api/v2/users/register - should return user on successful registation', function (done) { 35 | request(app) 36 | .post('/api/v2/register') 37 | .send({ 38 | "username": Date.now() + "test@email.com", 39 | "email": Date.now() + "test@email.com", 40 | "password": "test", 41 | "metadata": { 42 | "avatar": "", 43 | "name": "Jonnie Dollas" 44 | } 45 | }) 46 | .expect("Content-Type", /json/) 47 | .expect(201, done); 48 | }); 49 | 50 | it('POST - /api/v2/users/login - should return user on successful login', function (done) { 51 | var validUser = { 52 | username: 'test@gmail.com', 53 | password: 'test' 54 | }; 55 | request(app) 56 | .post('/api/v2/login') 57 | .send(validUser) 58 | .expect("Content-Type", /json/) 59 | .expect(200, done); 60 | }); 61 | it('POST - /api/v2/users/login - should return false on unsuccessful login', function (done) { 62 | var invalidUser = { 63 | username: 'test1', 64 | password: 'wrongpassword' 65 | }; 66 | request(app) 67 | .post('/api/v2/login') 68 | .send(invalidUser) 69 | .expect("Content-Type", /json/) 70 | .expect(404, done); 71 | }); 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /test/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | End2end Test Runner 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/spec/controllers/admin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: AdminCtrl', function() { 3 | var AdminCtrl, scope; 4 | AdminCtrl = null; 5 | scope = null; 6 | beforeEach(module('angularCmsApp')); 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return AdminCtrl = $controller('AdminCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: AppCtrl', function() { 3 | var AppCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | AppCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return AppCtrl = $controller('AppCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('it should have name on scope', function() { 14 | return expect(scope.name).toBe('AppCtrl'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/dashboard.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: DashboardCtrl', function() { 3 | var DashboardCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | DashboardCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return DashboardCtrl = $controller('DashboardCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | return xit('should toggle full screen when fullscreen button is clicked', function() { 17 | spyOn(window, 'webkitRequestFullscreen'); 18 | $scope.fullscreen(); 19 | return expect(window.webkitRequestFullscreen).toHaveBeenCalled(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/spec/controllers/docs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: DocsCtrl', function() { 3 | var DocsCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | DocsCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return DocsCtrl = $controller('DocsCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/forgot-password.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: ForgotPasswordCtrl', function() { 3 | var ForgotPasswordCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | ForgotPasswordCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return ForgotPasswordCtrl = $controller('ForgotPasswordCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/help.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: HelpCtrl', function() { 3 | var HelpCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | HelpCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return HelpCtrl = $controller('HelpCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/login.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: LoginCtrl', function() { 3 | var LoginCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | LoginCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return LoginCtrl = $controller('LoginCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | it('should have a null user object', function() { 14 | return expect(scope.user).toBeNull; 15 | }); 16 | it('should have a login method defined', function() { 17 | return expect(scope.login).toBeDefined; 18 | }); 19 | return it('should have a logout method defined', function() { 20 | return expect(scope.logout).toBeDefined; 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: MainCtrl', function() { 3 | var MainCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | MainCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return MainCtrl = $controller('MainCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/media.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: MediaCtrl', function() { 3 | var MediaCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | MediaCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return MediaCtrl = $controller('MediaCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return xit('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/pages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: PagesCtrl', function() { 3 | var PagesCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | PagesCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope, $injector) { 8 | scope = $rootScope.$new(); 9 | return PagesCtrl = $controller('PagesCtrl', { 10 | $scope: scope, 11 | $log: $injector.get('$log'), 12 | pages: [], 13 | DataService: $injector.get('DataService') 14 | }); 15 | })); 16 | return it('should attach a list of awesomeThings to the scope', function() { 17 | return expect(scope.awesomeThings.length).toBe(3); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/spec/controllers/plugins.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: PluginsCtrl', function() { 3 | var PluginsCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | PluginsCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return PluginsCtrl = $controller('PluginsCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/profile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: ProfileCtrl', function() { 3 | var ProfileCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | ProfileCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return ProfileCtrl = $controller('ProfileCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return xit('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/register.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: RegisterCtrl', function() { 3 | var RegisterCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | RegisterCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return RegisterCtrl = $controller('RegisterCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/settings.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: SettingsCtrl', function() { 3 | var SettingsCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | SettingsCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return SettingsCtrl = $controller('SettingsCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/sidebar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: SidebarCtrl', function() { 3 | var $rootScope, SidebarCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | SidebarCtrl = {}; 6 | scope = {}; 7 | $rootScope = {}; 8 | beforeEach(inject(function($controller, _$rootScope_) { 9 | $rootScope = _$rootScope_; 10 | scope = $rootScope.$new(); 11 | $rootScope.App = { 12 | menu: { 13 | admin: [ 14 | { 15 | id: 1, 16 | title: 'Item 1', 17 | href: 'item1' 18 | }, { 19 | id: 2, 20 | title: 'Item 2', 21 | href: 'item2' 22 | } 23 | ], 24 | user: [ 25 | { 26 | id: 1, 27 | title: 'Item 1', 28 | href: 'item1' 29 | }, { 30 | id: 2, 31 | title: 'Item 2', 32 | href: 'item2' 33 | } 34 | ] 35 | } 36 | }; 37 | return SidebarCtrl = $controller('SidebarCtrl', { 38 | $scope: scope, 39 | $rootScope: $rootScope 40 | }); 41 | })); 42 | it('should attach a list of awesomeThings to the scope', function() { 43 | return expect(scope.awesomeThings.length).toBe(3); 44 | }); 45 | it('should have defaults setup', function() { 46 | expect(scope.selected).toBe(null); 47 | expect(scope.items).toBe($rootScope.App.menu.user); 48 | return expect(scope.sidebar.closed).toBe(false); 49 | }); 50 | it('should toggle the sidebar', function() { 51 | scope.toggleSidebar(); 52 | expect(scope.sidebar.closed).toBe(true); 53 | scope.toggleSidebar(); 54 | return expect(scope.sidebar.closed).toBe(false); 55 | }); 56 | return it('should set selected item.selected to boolean', function() { 57 | scope.select(scope.items[0]); 58 | expect($rootScope.App.menu.user[0].selected).toBe(true); 59 | expect($rootScope.App.menu.user[1].selected).toBe(false); 60 | scope.select($rootScope.App.menu.admin[0]); 61 | expect($rootScope.App.menu.admin[0].selected).toBe(true); 62 | return expect($rootScope.App.menu.admin[1].selected).toBe(false); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/spec/controllers/themes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: ThemesCtrl', function() { 3 | var ThemesCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | ThemesCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return ThemesCtrl = $controller('ThemesCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/controllers/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: UsersCtrl', function() { 3 | var UsersCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | UsersCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return UsersCtrl = $controller('UsersCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | return it('should set selected user on scope', function() { 17 | scope.selectUser({ 18 | id: 1 19 | }); 20 | return expect(scope.user.id).toBe(1); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/widgets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Controller: WidgetsCtrl', function() { 3 | var WidgetsCtrl, scope; 4 | beforeEach(module('angularCmsApp')); 5 | WidgetsCtrl = {}; 6 | scope = {}; 7 | beforeEach(inject(function($controller, $rootScope) { 8 | scope = $rootScope.$new(); 9 | return WidgetsCtrl = $controller('WidgetsCtrl', { 10 | $scope: scope 11 | }); 12 | })); 13 | return it('should attach a list of awesomeThings to the scope', function() { 14 | return expect(scope.awesomeThings.length).toBe(3); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/spec/directives/cms-formgroup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Directive: cmsFormGroup', function() { 3 | var scope; 4 | beforeEach(module('angularCmsApp')); 5 | scope = {}; 6 | beforeEach(inject(function($controller, $rootScope) { 7 | return scope = $rootScope.$new(); 8 | })); 9 | return xit('should make hidden element visible', inject(function($compile) { 10 | var element; 11 | element = angular.element(''); 12 | element = $compile(element)(scope); 13 | return expect(element.text()).toBe('this is the cmsFormGroup directive'); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /test/spec/directives/cms-gravatar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Directive: cmsGravatar', function() { 3 | var scope; 4 | beforeEach(module('angularCmsApp')); 5 | scope = {}; 6 | beforeEach(inject(function($controller, $rootScope) { 7 | return scope = $rootScope.$new(); 8 | })); 9 | return it('should make hidden element visible', inject(function($compile) { 10 | var element; 11 | element = angular.element(''); 12 | element = $compile(element)(scope); 13 | return expect(element.text()).toBe('this is the cmsGravatar directive'); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /test/spec/directives/cms-header.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Directive: cmsHeader', function() { 3 | var scope; 4 | beforeEach(module('angularCmsApp')); 5 | scope = {}; 6 | beforeEach(inject(function($controller, $rootScope) { 7 | return scope = $rootScope.$new(); 8 | })); 9 | return it('should make hidden element visible', inject(function($compile) { 10 | var element; 11 | element = angular.element(''); 12 | return element = $compile(element)(scope); 13 | })); 14 | }); 15 | -------------------------------------------------------------------------------- /test/spec/directives/cms-panel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Directive: cmsPanel', function() { 3 | var scope; 4 | beforeEach(module('angularCmsApp')); 5 | scope = { 6 | title: 'Test' 7 | }; 8 | beforeEach(inject(function($controller, $rootScope) { 9 | return scope = $rootScope.$new(); 10 | })); 11 | return xit('should make hidden element visible', inject(function($compile) { 12 | var element; 13 | element = angular.element('this is the cmsPanel directive'); 14 | element = $compile(element)(scope); 15 | return expect(element.text()).toBe('Test'); 16 | })); 17 | }); 18 | -------------------------------------------------------------------------------- /test/spec/directives/cms-uploader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Directive: cmsUploader', function() { 3 | var scope; 4 | beforeEach(module('angularCmsApp')); 5 | scope = {}; 6 | beforeEach(inject(function($controller, $rootScope) { 7 | return scope = $rootScope.$new(); 8 | })); 9 | return xit('should make hidden element visible', inject(function($compile) { 10 | var element; 11 | element = angular.element(''); 12 | element = $compile(element)(scope); 13 | return expect(element.text()).toBe('this is the cmsUploader directive'); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /test/spec/directives/cms-widget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Directive: cmsWidget', function() { 3 | var scope; 4 | beforeEach(module('angularCmsApp')); 5 | scope = {}; 6 | beforeEach(inject(function($controller, $rootScope) { 7 | return scope = $rootScope.$new(); 8 | })); 9 | return xit('should make hidden element visible', inject(function($compile) { 10 | var element; 11 | element = angular.element(''); 12 | element = $compile(element)(scope); 13 | return expect(element.text()).toBe('this is the cmsWidget directive'); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /test/spec/directives/cms-widgets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Directive: cmsWidgets', function() { 3 | var scope; 4 | beforeEach(module('angularCmsApp')); 5 | scope = {}; 6 | beforeEach(inject(function($controller, $rootScope) { 7 | return scope = $rootScope.$new(); 8 | })); 9 | return xit('should make hidden element visible', inject(function($compile) { 10 | var element; 11 | element = angular.element(''); 12 | element = $compile(element)(scope); 13 | return expect(element.text()).toBe('this is the cmsWidgets directive'); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /test/spec/filters/gravatar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Filter: gravatar', function() { 3 | var gravatar; 4 | beforeEach(module('angularCmsApp')); 5 | gravatar = {}; 6 | beforeEach(inject(function($filter) { 7 | return gravatar = $filter('gravatar'); 8 | })); 9 | return it('should return the hashed url', function() { 10 | var text; 11 | text = 'jonniespratley@gmail.com'; 12 | return expect(gravatar(text)).toBe('http://www.gravatar.com/avatar/f6112e781842d6a2b4636b35451401ff'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/spec/filters/markdown.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Filter: markdown', function() { 3 | var markdown; 4 | beforeEach(module('angularCmsApp')); 5 | markdown = {}; 6 | beforeEach(inject(function($filter) { 7 | return markdown = $filter('markdown'); 8 | })); 9 | return xit('should return the input prefixed with "markdown filter:"', function() { 10 | var text; 11 | return text = '#H1'; 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/spec/services/cmsauthservice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: cmsAuthService', function() { 3 | var cmsAuthService, httpBackend, successfulResponse, unsuccessfulResponse; 4 | beforeEach(module('angularCmsApp')); 5 | cmsAuthService = null; 6 | httpBackend = null; 7 | successfulResponse = { 8 | "success": true, 9 | "result": { 10 | "_id": "537e75385b11c600e0968da7", 11 | "email": "test1@email.com", 12 | "password": "9bfc2a094175a37a07c41986aa9ab4350c2c31ab", 13 | "metadata": { 14 | "avatar": "", 15 | "name": "Jonnie Dollas" 16 | } 17 | } 18 | }; 19 | unsuccessfulResponse = { 20 | status: false, 21 | error: true, 22 | message: 'No user found!' 23 | }; 24 | beforeEach(inject(function(_cmsAuthService_, _$httpBackend_) { 25 | cmsAuthService = _cmsAuthService_; 26 | return httpBackend = _$httpBackend_; 27 | })); 28 | it('should define the Auth Service', function() { 29 | return expect(!!cmsAuthService).toBe(true); 30 | }); 31 | describe('Successful login', function() { 32 | beforeEach(function() { 33 | return httpBackend.whenPOST('/api/v2/users/login').respond(successfulResponse); 34 | }); 35 | return it('should resolve a promise on successful auth', function() { 36 | var user; 37 | user = null; 38 | expect(user).toBeNull(); 39 | return cmsAuthService.authorize({ 40 | email: 'test@email.com', 41 | password: 'fred' 42 | }).then(function(data) { 43 | user = data.results; 44 | expect(user).not.toBeNull(); 45 | return expect(user.email).toBe('test@email.com'); 46 | }); 47 | }); 48 | }); 49 | describe('Unsuccessful login', function() { 50 | beforeEach(function() { 51 | return httpBackend.whenPOST('/api/v2/users/login').respond(unsuccessfulResponse); 52 | }); 53 | return it('should reject a promise on unsuccessful auth', function() {}); 54 | }); 55 | describe('Successful registration', function() { 56 | beforeEach(function() { 57 | return httpBackend.whenPOST('/api/v2/users/register').respond(successfulResponse); 58 | }); 59 | return it('should resolve a promise on successful registration', function() { 60 | return cmsAuthService.register({ 61 | username: 'admin', 62 | password: 'admin' 63 | }); 64 | }); 65 | }); 66 | return describe('Unsuccessful registration', function() { 67 | beforeEach(function() { 68 | return httpBackend.whenPOST('/api/v2/users/register').respond(unsuccessfulResponse); 69 | }); 70 | return it('should reject a promise on unsuccessful registration', function() {}); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/spec/services/cmsdataservicefactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: cmsDataServiceFactory', function() { 3 | var cmsDataServiceFactory; 4 | beforeEach(module('angularCmsApp')); 5 | cmsDataServiceFactory = {}; 6 | beforeEach(inject(function(_cmsDataServiceFactory_) { 7 | return cmsDataServiceFactory = _cmsDataServiceFactory_; 8 | })); 9 | return it('should do something', function() { 10 | return expect(!!cmsDataServiceFactory).toBe(true); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/spec/services/cmsdataserviceprovider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: cmsDataServiceProvider', function() { 3 | var cmsDataServiceProvider; 4 | beforeEach(module('angularCmsApp')); 5 | cmsDataServiceProvider = {}; 6 | beforeEach(inject(function(_cmsDataServiceProvider_) { 7 | return cmsDataServiceProvider = _cmsDataServiceProvider_; 8 | })); 9 | return xit('should do something', function() { 10 | return expect(!!cmsDataServiceProvider).toBe(true); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/spec/services/cmsnotify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: cmsNotify', function() { 3 | var cmsNotify; 4 | beforeEach(module('angularCmsApp')); 5 | cmsNotify = {}; 6 | beforeEach(inject(function(_cmsNotify_) { 7 | return cmsNotify = _cmsNotify_; 8 | })); 9 | return it('should do something', function() { 10 | return expect(!!cmsNotify).toBe(true); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/spec/services/cmssessionservice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: cmsSessionService', function() { 3 | var cmsSessionService; 4 | beforeEach(module('angularCmsApp')); 5 | cmsSessionService = {}; 6 | beforeEach(inject(function(_cmsSessionService_) { 7 | var Cmssessionservice; 8 | return Cmssessionservice = _cmsSessionService_; 9 | })); 10 | return it('should do something', function() { 11 | return expect(!!cmsSessionService).toBe(true); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/spec/services/cmssocketservice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: cmsSocketService', function () { 3 | var cmsSocketService; 4 | beforeEach(module('angularCmsApp')); 5 | cmsSocketService = {}; 6 | beforeEach(inject(function (_cmsSocketService_) { 7 | cmsSocketService = _cmsSocketService_; 8 | })); 9 | it('should do something', function () { 10 | expect(!!cmsSocketService).toBe(true); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/spec/services/cmsusersfactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: cmsUsersFactory', function() { 3 | var cmsUsersFactory; 4 | beforeEach(module('angularCmsApp')); 5 | cmsUsersFactory = {}; 6 | beforeEach(inject(function(_cmsUsersFactory_) { 7 | return cmsUsersFactory = _cmsUsersFactory_; 8 | })); 9 | return it('should do something', function() { 10 | return expect(!!cmsUsersFactory).toBe(true); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/spec/services/dataservice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('Service: DataService', function () { 3 | var $httpBackend, $q, $rootScope, baseUrl, ds, errorCallback, successCallback, testObject; 4 | beforeEach(module('angularCmsApp')); 5 | baseUrl = '/api/v2/angular-cms'; 6 | ds = null; 7 | $httpBackend = null; 8 | $q = null; 9 | $rootScope = null; 10 | successCallback = null; 11 | errorCallback = null; 12 | testObject = { 13 | _id: 1, 14 | title: 'Test Object', 15 | body: 'This is a sample object for testing.' 16 | }; 17 | beforeEach(inject(function (_$rootScope_, _DataService_, _$httpBackend_, _$q_) { 18 | ds = _DataService_; 19 | $httpBackend = _$httpBackend_; 20 | $q = _$q_; 21 | $rootScope = _$rootScope_; 22 | successCallback = jasmine.createSpy('success'); 23 | errorCallback = jasmine.createSpy('error'); 24 | })); 25 | it('should have DataService defined', function () { 26 | expect(!!ds).toBe(true); 27 | }); 28 | it('should have a fetch method', function () { 29 | expect(ds.fetch).toBeDefined(); 30 | }); 31 | it('should have a save method', function () { 32 | expect(ds.save).toBeDefined(); 33 | }); 34 | it('should have a destroy method', function () { 35 | expect(ds.destroy).toBeDefined(); 36 | }); 37 | it('should invoke create method if _id is not on object', function () { 38 | $httpBackend.expectPOST('' + baseUrl + '/posts').respond(200, [{}, {}]); 39 | //ds = jasmine.createSpy('ds', ['_create', '_update', 'save']); 40 | spyOn(ds, '_create').and.callThrough(); 41 | spyOn(ds, '_update').and.callThrough(); 42 | 43 | ds.save('posts', {title: 'New Post'}).then(successCallback, errorCallback); 44 | $httpBackend.flush(); 45 | 46 | expect(successCallback).toHaveBeenCalled(); 47 | expect(errorCallback).not.toHaveBeenCalled(); 48 | expect(ds._create).toHaveBeenCalled(); 49 | expect(ds._update).not.toHaveBeenCalled(); 50 | }); 51 | it('should invoke update method if _id is on object', function () { 52 | $httpBackend.expectPUT('' + baseUrl + '/posts/1').respond(200, { 53 | message: 'Success updating object' 54 | }); 55 | spyOn(ds, '_create').and.callThrough(); 56 | spyOn(ds, '_update').and.callThrough(); 57 | ds.save('posts', testObject).then(successCallback, errorCallback); 58 | $httpBackend.flush(); 59 | expect(successCallback).toHaveBeenCalled(); 60 | expect(errorCallback).not.toHaveBeenCalled(); 61 | expect(ds._create).not.toHaveBeenCalled(); 62 | expect(ds._update).toHaveBeenCalled(); 63 | }); 64 | it('GET - /collection - should reject promise on error', function () { 65 | $httpBackend.expectGET('' + baseUrl + '/posts').respond(404, { 66 | message: 'Data not found' 67 | }); 68 | ds.fetch('posts').then(successCallback, errorCallback); 69 | $httpBackend.flush(); 70 | expect(successCallback).not.toHaveBeenCalled(); 71 | expect(errorCallback).toHaveBeenCalled(); 72 | }); 73 | it('GET - /collection - should resolve promise on success', function () { 74 | $httpBackend.expectGET('' + baseUrl + '/posts').respond(200, [{}, {}]); 75 | ds.fetch('posts').then(successCallback, errorCallback); 76 | $httpBackend.flush(); 77 | expect(successCallback).toHaveBeenCalled(); 78 | expect(errorCallback).not.toHaveBeenCalled(); 79 | }); 80 | it('GET - /collection/id - should reject promise on error', function () { 81 | $httpBackend.expectGET('' + baseUrl + '/posts/1').respond(404, { 82 | message: 'Data not found' 83 | }); 84 | ds.get('posts', 1).then(successCallback, errorCallback); 85 | $httpBackend.flush(); 86 | expect(successCallback).not.toHaveBeenCalled(); 87 | expect(errorCallback).toHaveBeenCalled(); 88 | }); 89 | it('GET - /collection/id - should resolve promise on success', function () { 90 | $httpBackend.expectGET('' + baseUrl + '/posts/1').respond(200, testObject); 91 | ds.get('posts', 1).then(successCallback, errorCallback); 92 | $httpBackend.flush(); 93 | expect(successCallback).toHaveBeenCalled(); 94 | expect(errorCallback).not.toHaveBeenCalled(); 95 | }); 96 | it('DELETE - /collection/id - should resolve promise on success', function () { 97 | $httpBackend.expectDELETE('' + baseUrl + '/posts/1').respond(200, { 98 | message: 'Success deleting object' 99 | }); 100 | ds.destroy('posts', testObject).then(successCallback, errorCallback); 101 | $httpBackend.flush(); 102 | expect(successCallback).toHaveBeenCalled(); 103 | expect(errorCallback).not.toHaveBeenCalled(); 104 | }); 105 | it('DELETE - /collection/id - should reject promise on error', function () { 106 | $httpBackend.expectDELETE('' + baseUrl + '/posts/1').respond(404, { 107 | message: 'Error deleting object' 108 | }); 109 | ds.destroy('posts', testObject).then(successCallback, errorCallback); 110 | $httpBackend.flush(); 111 | expect(successCallback).not.toHaveBeenCalled(); 112 | expect(errorCallback).toHaveBeenCalled(); 113 | }); 114 | }); 115 | --------------------------------------------------------------------------------