From dcec227896207cb79def33550774a0731df6535a Mon Sep 17 00:00:00 2001 From: fazo96 Date: Tue, 27 May 2014 15:33:50 +0200 Subject: [PATCH] major refractor and bunch of new features --- LICENSE.md | 21 +++++ README.md | 32 ++++++- app.coffee | 111 ------------------------- client/client.coffee | 94 +++++++++++++++++++++ index.html => client/index.html | 29 ++++++- client/ribbon.css | 142 ++++++++++++++++++++++++++++++++ style.css => client/style.css | 10 ++- server/server.coffee | 18 ++++ server/util.coffee | 5 ++ 9 files changed, 342 insertions(+), 120 deletions(-) create mode 100644 LICENSE.md delete mode 100644 app.coffee create mode 100644 client/client.coffee rename index.html => client/index.html (73%) create mode 100644 client/ribbon.css rename style.css => client/style.css (83%) create mode 100644 server/server.coffee create mode 100644 server/util.coffee diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3fb4f69 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Enrico Fasoli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index e8f2f23..a36b40c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,32 @@ # Homework -New homework app rewritten using Meteor because I realized the MEAN stack was -a bad idea: meteor is just better (or at least faster to write). +Schoolwork management application for students. -**requires Meteorite** and `bootstrap-3` and `fontawesome` Meteorite packages. +### Try it +[the app is hosted online!](http://homework.meteor.com) + +### Development +Clone the repo, [install meteor](http://meteor.com) and **meteorite**. +Install dependent packages and then run `meteor`. That's it. ### License -GPLv3 +The MIT License (MIT) + +Copyright (c) 2014 Enrico Fasoli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app.coffee b/app.coffee deleted file mode 100644 index 6c69c1e..0000000 --- a/app.coffee +++ /dev/null @@ -1,111 +0,0 @@ -notes = new Meteor.Collection "notes" - -# Server -if Meteor.isServer - Accounts.config { - sendVerificationEmail: false - loginExpirationInDays: 1 - } - - Meteor.publish "my-notes", -> - notes.find( { userId: @userId } ) unless not @userId - - # Authentication - Accounts.validateNewUser (user) -> - if user.email and Meteor.check(user.email,String) is yes and user.email.contains '@' is yes and user.email.endsWith '.' is no and user.email.endsWith '@' is no - return yes - else throw new Meteor.Error 403, "Invalid Email" - if user.password and Meteor.check(user.password,String) is yes and user.password.length > 7 - return yes - else throw new Meteor.Error 403, "Password invalid" - -# Client -if Meteor.isClient - Deps.autorun -> Meteor.subscribe "my-notes" unless not Meteor.userId() - - # User Interface - Template.userInfo.events { - 'click #logout': (e,template) -> Meteor.logout() - 'keypress #newNote': (e,template) -> - if e.keyCode is 13 - notes.insert { - title: template.find('#newNote').value - content: "..." - userId: Meteor.userId() - } - template.find('#newNote').value = "" - } - Template.userInfo.in = -> Meteor.user().emails[0].address - - # Notes template - Template.notes.truncateNoteDesc = (s) -> - if s.length > 52 then s.slice(0,48)+"..." else s - Template.notes.notes = -> - d = notes.find().fetch(); - #d.splice d.indexOf(Session.get('note')), 1 ; d - Template.notes.events { - 'click .close-note': -> notes.remove @_id - 'click .edit-note': -> Session.set 'note', this - } - - # Note Editor - Template.editor.note = -> Session.get 'note' - saveCurrentNote = (t,e) -> - if e and e.keyCode isnt 13 then return; - notes.update Session.get('note')._id, - $set: - title: t.find('.title').value - content: t.find('.area').value - Template.editor.events - 'click .close-editor': -> Session.set 'note', undefined - 'click .save-editor': (e,t) -> saveCurrentNote t - #'keypress .edit-note': (e,t) -> saveCurrentNote t, e # Doesnt work?? - 'keypress .title': (e,t) -> saveCurrentNote t, e - - # Notifications - alerts = [] - alertDep = new Deps.Dependency - errCallback = (err) -> notify { msg: err.reason } - # Show a notification - notify = (data) -> - alerts.push { - title: data.title - msg: data.msg - id: data.id or alerts.length - type: data.type or "danger" - }; alertDep.changed() - # Clear all notifications - clearNotifications = -> alerts.clear(); alertDep.changed() - # Get all the notifications - Template.notifications.notification = -> alertDep.depend(); alerts - Template.notifications.events { - 'click .close-notification': (e,template) -> - alerts.splice alerts.indexOf(this), 1 - alertDep.changed() - } - - # Login and Register - pressLogin = (template) -> - mail = template.find('#mail').value; pass = template.find('#pass').value - Meteor.loginWithPassword mail, pass, (err) -> - errCallback err - Template.auth.working = -> Meteor.loggingIn() - Template.auth.events { - 'keypress .login': (e,template) -> - if e.keyCode is 13 then pressLogin template - # Login - 'click #login': (e,template) -> pressLogin template - # Register - 'click #register': (e,template) -> - mail = template.find('#mail').value; pass = template.find('#pass').value - if not mail or mail.contains '@' is no or mail.endsWith '.' is yes or mail.endsWith '@' is yes - notify { msg: "Invalid Email" } - else - try - Accounts.createUser { - email: mail, - password: pass - }, (e) -> errCallback e - catch err - notify { msg: err } - } diff --git a/client/client.coffee b/client/client.coffee new file mode 100644 index 0000000..1c8bd72 --- /dev/null +++ b/client/client.coffee @@ -0,0 +1,94 @@ +# Homework - Client Side +notes = new Meteor.Collection "notes" +Deps.autorun -> Meteor.subscribe "my-notes" unless not Meteor.userId() +#Meteor.subscribe "my-notes" + +# User Interface +Template.userInfo.events { + 'click #logout': (e,template) -> Meteor.logout() +} +Template.userInfo.in = -> Meteor.user().emails[0].address + +# Notes template +Template.notes.truncateNoteDesc = (s) -> + if s.length > 52 then s.slice(0,48)+"..." else s +Template.notes.notes = -> + d = notes.find().fetch() +Template.notes.events { + 'click .close-note': -> notes.remove @_id + 'click .edit-note': -> Session.set 'note', this + 'keypress #newNote': (e,template) -> + if e.keyCode is 13 + notes.insert { + title: template.find('#newNote').value + content: "..." + userId: Meteor.userId() + } + template.find('#newNote').value = "" +} + +# Note Editor +Template.editor.note = -> Session.get 'note' +saveCurrentNote = (t,e) -> + if e and e.keyCode isnt 13 then return; + notes.update Session.get('note')._id, + $set: + title: t.find('.title').value + content: t.find('.area').value +Template.editor.events + 'click .close-editor': -> Session.set 'note', undefined + 'click .save-editor': (e,t) -> saveCurrentNote t + #'keypress .edit-note': (e,t) -> saveCurrentNote t, e # Doesnt work?? + 'keypress .title': (e,t) -> saveCurrentNote t, e + +# Notifications +alerts = [] +alertDep = new Deps.Dependency +errCallback = (err) -> notify { msg: err.reason } +# Show a notification +notify = (data) -> + alerts.push { + title: data.title + msg: data.msg + id: data.id or alerts.length + type: data.type or "danger" + }; alertDep.changed() +# Clear all notifications +clearNotifications = -> alerts.clear(); alertDep.changed() +# Get all the notifications +Template.notifications.notification = -> alertDep.depend(); alerts +Template.notifications.events { + 'click .close-notification': (e,template) -> + alerts.splice alerts.indexOf(this), 1 + alertDep.changed() +} + +# Login and Register +pressLogin = (template) -> + mail = template.find('#mail').value; pass = template.find('#pass').value + Meteor.loginWithPassword mail, pass, (err) -> + errCallback err +Template.auth.working = -> Meteor.loggingIn() +Template.auth.events { + 'keypress .login': (e,template) -> + if e.keyCode is 13 then pressLogin template + # Login + 'click #login': (e,template) -> pressLogin template + # Register + 'click #register': (e,template) -> + mail = template.find('#mail').value; pass = template.find('#pass').value + if not mail + notify { msg: "Please enter an Email" } + else if not pass + notify { msg: "Please enter a password" } + else if pass.length < 8 + notify { msg: "Password too short" } + else # Sending actual registration request + try + Accounts.createUser { + email: mail, + password: pass + }, (e) -> errCallback e + catch err + notify { msg: err } +} diff --git a/index.html b/client/index.html similarity index 73% rename from index.html rename to client/index.html index 91fa4cd..fe73f51 100644 --- a/index.html +++ b/client/index.html @@ -3,19 +3,21 @@ + {{> ribbon}}
-
+
{{#if currentUser}} {{> editor}} {{> notes}} {{> userInfo}} {{else}} {{> auth}} {{/if}}
+
{{> footer}}
@@ -29,6 +31,9 @@ {{/each}} +
+ +
- + + + + diff --git a/client/ribbon.css b/client/ribbon.css new file mode 100644 index 0000000..f7c6a40 --- /dev/null +++ b/client/ribbon.css @@ -0,0 +1,142 @@ +/* +This cool github ribbon was developed by: +https://github.com/simonwhitaker +All credit for this file goes to him! +Repo for github-ribbon-css: https://github.com/simonwhitaker/github-fork-ribbon-css +*/ + +/* Left will inherit from right (so we don't need to duplicate code) */ +.github-fork-ribbon { + /* The right and left classes determine the side we attach our banner to */ + position: absolute; + + /* Add a bit of padding to give some substance outside the "stitching" */ + padding: 2px 0; + + /* Set the base colour */ + background-color: #a00; + + /* Set a gradient: transparent black at the top to almost-transparent black at the bottom */ + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgba(0, 0, 0, 0.15))); + background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); + background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); + background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); + background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); + + /* Add a drop shadow */ + -webkit-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5); + box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5); + + /* Set the font */ + font: 700 13px "Helvetica Neue", Helvetica, Arial, sans-serif; + + z-index: 9999; + pointer-events: auto; +} + +.github-fork-ribbon a, +.github-fork-ribbon a:hover { + /* Set the text properties */ + color: #fff; + text-decoration: none; + text-shadow: 0 -1px rgba(0, 0, 0, 0.5); + text-align: center; + + /* Set the geometry. If you fiddle with these you'll also need +to tweak the top and right values in .github-fork-ribbon. */ + width: 200px; + line-height: 20px; + + /* Set the layout properties */ + display: inline-block; + padding: 2px 0; + + /* Add "stitching" effect */ + border-width: 1px 0; + border-style: dotted; + border-color: #fff; + border-color: rgba(255, 255, 255, 0.7); +} + +.github-fork-ribbon-wrapper { + width: 150px; + height: 150px; + position: absolute; + overflow: hidden; + top: 0; + z-index: 9999; + pointer-events: none; +} + +.github-fork-ribbon-wrapper.fixed { + position: fixed; +} + +.github-fork-ribbon-wrapper.left { + left: 0; +} + +.github-fork-ribbon-wrapper.right { + right: 0; +} + +.github-fork-ribbon-wrapper.left-bottom { + position: fixed; + top: inherit; + bottom: 0; + left: 0; +} + +.github-fork-ribbon-wrapper.right-bottom { + position: fixed; + top: inherit; + bottom: 0; + right: 0; +} + +.github-fork-ribbon-wrapper.right .github-fork-ribbon { + top: 42px; + right: -43px; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} + +.github-fork-ribbon-wrapper.left .github-fork-ribbon { + top: 42px; + left: -43px; + + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); +} + + +.github-fork-ribbon-wrapper.left-bottom .github-fork-ribbon { + top: 80px; + left: -43px; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} + +.github-fork-ribbon-wrapper.right-bottom .github-fork-ribbon { + top: 80px; + right: -43px; + + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); +} diff --git a/style.css b/client/style.css similarity index 83% rename from style.css rename to client/style.css index 7292162..53b7e4a 100644 --- a/style.css +++ b/client/style.css @@ -15,6 +15,9 @@ input { clear:both; } +.custom-link { color: #999; } +.custom-link:hover { color: #101010;} + .subtitle { color: #999; } /* Custom Classes */ @@ -64,7 +67,12 @@ input { /* IDs */ -#quicknotes { +.footer-center-icon { + margin-left: 10px; + margin-right: 10px +} + +#ui-container { max-width: 500px; } diff --git a/server/server.coffee b/server/server.coffee new file mode 100644 index 0000000..ad0e649 --- /dev/null +++ b/server/server.coffee @@ -0,0 +1,18 @@ +# Homework - Server Side +notes = new Meteor.Collection "notes" + +Accounts.config { + sendVerificationEmail: true + loginExpirationInDays: 1 +} + +Meteor.publish "my-notes", -> + notes.find( { userId: @userId } ) unless not @userId + +# Authentication +Accounts.validateNewUser (user) -> + if Match.test(user.email, String) and validateEmail user.email is yes + if user.password and Match.test(user.password,String) is yes and user.password.length > 7 + return yes + else throw new Meteor.Error 403, "Invalid Password" + else throw new Meteor.Error 403, "Invalid Email" diff --git a/server/util.coffee b/server/util.coffee new file mode 100644 index 0000000..a24d234 --- /dev/null +++ b/server/util.coffee @@ -0,0 +1,5 @@ +# Utility Stuff for Homework + +validateEmail = (email) -> + re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + re.test email