mirror of
https://github.com/fazo96/homework.git
synced 2025-01-09 12:10:08 +01:00
major refractor and bunch of new features
This commit is contained in:
parent
8de8f3739e
commit
dcec227896
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -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.
|
32
README.md
32
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.
|
||||
|
111
app.coffee
111
app.coffee
@ -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 }
|
||||
}
|
94
client/client.coffee
Normal file
94
client/client.coffee
Normal file
@ -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 }
|
||||
}
|
@ -3,19 +3,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
</head>
|
||||
<body>
|
||||
{{> ribbon}}
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1 id="title">Homework<br>
|
||||
<small id="small">management for students</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="center-block" id="quicknotes">
|
||||
<div class="center-block" id="ui-container">
|
||||
{{#if currentUser}}
|
||||
{{> editor}} {{> notes}} {{> userInfo}}
|
||||
{{else}}
|
||||
{{> auth}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="center-block" align="center">{{> footer}}</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@ -29,6 +31,9 @@
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
<div align="center">
|
||||
<input type="text" id="newNote" class="form-control" placeholder="Add new note">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="auth">
|
||||
@ -52,10 +57,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="userInfo">
|
||||
<template name="userInfo"><hr>
|
||||
<div align="center">
|
||||
<input type="text" id="newNote" class="form-control" placeholder="Add new note">
|
||||
<p>Logged in as {{in}}</p>
|
||||
<p>{{in}}</p>
|
||||
<button type="button" id="logout" class="btn btn-danger">Logout</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -87,3 +91,20 @@
|
||||
</div>
|
||||
{{/each}}
|
||||
</template>
|
||||
|
||||
<template name="footer">
|
||||
<hr>
|
||||
<p>This app is <a href="https://en.wikipedia.org/wiki/Free_software">Free Software</a>, under the <a href="http://opensource.org/licenses/MIT">MIT License</a></p>
|
||||
<p>Built by Enrico Fasoli</p>
|
||||
<a class="custom-link" href="http://www.linkedin.com/profile/view?id=292450419"><i class="fa fa-linkedin fa-2x"></i></a>
|
||||
<a href="http://twitter.com/fazo96"><i class="fa fa-twitter fa-2x footer-center-icon"></i></a>
|
||||
<a class="custom-link" href="http://github.com/fazo96"><i class="fa fa-github fa-2x"></i></a>
|
||||
</template>
|
||||
|
||||
<template name="ribbon">
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a href="http://github.com/fazo96/homework">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
142
client/ribbon.css
Normal file
142
client/ribbon.css
Normal file
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
18
server/server.coffee
Normal file
18
server/server.coffee
Normal file
@ -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"
|
5
server/util.coffee
Normal file
5
server/util.coffee
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user