WebFund 2015W Final Exam Review

From Soma-notes
Revision as of 19:55, 10 April 2015 by Soma (talk | contribs) (→‎Audio)

Audio

The audio from the exam review given on April 10, 2015 is now available. (Note: the file does not play in Firefox unfortunately.)

Notes

The final exam will be based on exam-notes, a simplified version of the solutions to tls-notes from Assignment 10.

Code

routes/index.js

var express = require('express');
var router = express.Router();
var mongodb = require('mongodb');
var mc = mongodb.MongoClient;
var ObjectID = mongodb.ObjectID;

var notesCollection, usersCollection;

mc.connect('mongodb://localhost/exam-notes', function(err, db) {
    if (err) {
        throw err;
    }
    
    notesCollection = db.collection('notes');
    usersCollection = db.collection('users');
});

router.post('/register', function(req, res) {
    var username = req.body.username;
    var password = req.body.password;

    var checkInsert = function(err, newUsers) {
        if (err) {
            res.redirect("/?error=Unable to add user");
        } else {
            res.redirect("/?error=User " + username +
                         " successfully registered");
        }
    }

    var checkUsername = function(err, user) {
        if (err) {
            res.redirect("/?error=unable to check username");
        } else if (user === null) {
            var newUser = {
                username: username,
                password: password
            };
            usersCollection.update({username: username},
                                   newUser,
                                   {upsert: true},
                                   checkInsert);    

        } else {
            res.redirect("/?error=user already exists");
        }
    }
    
    usersCollection.findOne({username: username}, checkUsername);
});

router.get('/', function(req, res) {
    if (req.session.username) {
        res.redirect("/notes");
    } else {
        res.render('index', { title: 'COMP 2406 Exam Notes Demo', 
                              error: req.query.error });
    }
});

router.get('/notes', function(req, res) {
    var username = req.session.username;

    if (username) {
        res.render("notes.jade", {username: username,
                                  title: username +"'s Notes"});
    } else {
        res.redirect("/?error=Not Logged In");
    }
});

router.post('/login', function(req, res) {
    var username = req.body.username;
    var password = req.body.password;
    
    var authenticateUser = function(err, user){
        if (err || user === null || password !== user.password) {
            res.redirect("/?error=invalid username or password");       
        } else {
            req.session.username = username;
            res.redirect("/notes");
        }
    }
    
    usersCollection.findOne({username: username}, authenticateUser);
});

router.post('/logout', function(req, res) {
    req.session.destroy(function(err){
        if (err) {
            console.log("Error: %s", err);
        }
    });
    res.redirect("/");
});

router.get('/getNotes', function(req, res) {
    var username = req.session.username;

    var renderNotes = function(err, notes) {
        if (err) {
            notes = [{"title": "Couldn't get notes",
                      "owner": username,
                      "content": "Error fetching notes!"}];
        }
        res.send(notes);
    }
    
    if (username) {
        notesCollection.find({owner: username}).toArray(renderNotes);
    } else {
        res.send([{"title": "Not Logged In",
                   "owner": "None",
                   "content": "Nobody seems to be logged in!"}]);
    }    
});

router.post('/updateNote', function(req, res) {
    var username = req.session.username;
    var id = req.body.id;
    var title = req.body.title;
    var content = req.body.content;
    
    var checkUpdate = function(err, result) {
        if (err) {
            res.send("ERROR: update failed");
        } else {
            res.send("update succeeded");
        }
    }
    
    if (username) {
        if (id && title && content) {
            notesCollection.update({_id: ObjectID(id)},
                                   {$set: {title: title,
                                           content: content}},
                                   checkUpdate);
        } else {
            res.send("ERROR: bad parameters");
        }
    } else {
        res.send("ERROR: not logged in");
    }
});

router.post('/newNote', function(req, res) {
    var username = req.session.username;
    var newNote;

    var reportInserted = function(err, notesInserted) {
        if (err) {
            res.send("ERROR: Could not create a new note");
        } else {
            res.send(notesInserted[0]._id);
        }
    }

    if (username) {
        newNote = {title: "Untitled",
                   owner: username,
                   content: "No content"};

        notesCollection.insert(newNote, reportInserted);
    } else {
        res.send("ERROR: Not Logged In");
    }
});

module.exports = router;

public/javascripts/register.js

$(function(){
    $("#register").on("click",function(){
        var $form = $("form");
        $form.attr("action", "/register");
        $form.submit();
    });
});

public/javascripts/notes.js

$(function() {
    var insertNotesIntoDOM = function(userNotes) {
        var i, theNote;

        $(".notes").remove();

        if (userNotes.length < 1) {
            $("#notesList").append('<div class="notes">No notes!</div>');
            return;
        }

        for (i=0; i < userNotes.length; i++) {
            console.log("Adding note " + i);
            theNote = userNotes[i];
            $("#notesList").append('<div class="notes list-group-item ' +
				   'list-group-item-default"> ' +
                                   '<a href="#" ' + 
                                   'class="notes list-group-item-heading" ' +
                                   'id="edit' + i + '">' +
                                   '<h4>' + theNote.title + '</h4></a> ' +
                                   '<p class="list-group-item-text">' + 
                                   theNote.content + '</p>' +
                                   '</div>');
            $("#edit" + i).click(userNotes[i], editNote);
        }
    }

    var getAndListNotes = function() {
        $.getJSON("/getNotes", insertNotesIntoDOM);
    }

    var updateNoteOnServer = function(event) {
        var theNote = event.data;
        theNote.title = $("#noteTitle").val();
        theNote.content = $("#noteContent").val();
        noteListing();
        $.post("/updateNote",
               {"id": theNote._id,
                "title": theNote.title,
                "content": theNote.content},
               getAndListNotes);
    }

    var editNote = function(event) {
        var theNote = event.data;

        $(".notesArea").replaceWith('<div class="notesArea" ' +
                                    'id="editNoteDiv"></div>');
        $("#editNoteDiv").append('<h2>Edit Note</h2>');

        $("#editNoteDiv").append('<div><input type="text" ' +
                                 'id="noteTitle"></input></div>');
        $("#noteTitle").val(theNote.title);

        $("#editNoteDiv").append('<div>' +
                                 '<textarea cols="40" rows="5" ' +
                                 'id="noteContent" wrap="virtual">' +
                                 '</textarea></div>');
        $("#noteContent").val(theNote.content);


        $("#editNoteDiv").append('<button type="button" ' + 
                                 'class="btn btn-success" ' +
                                 'id="updateNote">' +
                                 'Update</button>')
        $("#updateNote").click(theNote, updateNoteOnServer);
    }
    
    var editNewNote = function(result) {
        var theNote = {"title": "",
                       "content": ""};

        if (result.indexOf("ERROR") === -1) {
            theNote._id = result;
            editNote({data: theNote});
        } else {
            noteListing();
        }
    }

    var newNote = function() {
        $.post("/newNote", editNewNote);
    }

    var noteListing = function() {
        $(".notesArea").replaceWith('<div class="notesArea" ' +
                                    'id="listNotesDiv"></div>');

        $("#listNotesDiv").append('<h2>Notes</h2>');
        $("#listNotesDiv").append('<div id="notesList" class="list-group well">' +
                                  '<a class="notes list-group-item">Loading Notes...</a>' +
                                  '</div>');

        $("#listNotesDiv").append('<button id="newNote"' +
                                  ' class="btn btn-primary"' +
                                  ' type="button">' +
                                  'New Note</button>');
        $("#newNote").click(newNote);

        getAndListNotes();
    }

    noteListing();
});

views/layout.jade

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/libs/bootstrap/css/bootstrap.css')
    link(rel='stylesheet', href='/libs/bootstrap/css/bootstrap-theme.css')
    link(rel='stylesheet', href='/stylesheets/style.css')
    script(src='/libs/jquery.js')
    script(src='/libs/bootstrap/js/bootstrap.js')
    block header
  body(role="document")
    block content

views/index.jade

extends layout

block header
  script(src="javascripts/register.js")

block content
  h1= title
  p Welcome to #{title}
  - if(error)
    div.error #{error}
  div
    form(action="/login", method="post")
        div.form-group
            label(for="username") Username
            input.form-control.limitwidth(type="text", name="username")
        div.form-group
            label(for="password") Password
            input.form-control.limitwidth(type="password", name="password")
        button.btn.btn-default(type="submit") Login
        button#register.btn.btn-warning(type="button") Register

views/notes.jade

extends layout

block header
  script(src="javascripts/notes.js")

block content
  div.page-header
    h1#pageTitle #{username}&#39;s Account
    p#pageWelcome Welcome #{username}

  form(action="/logout", method="post")
        button.btn.btn-info(type="submit") Logout
  
  div.notesArea

views/error.jade

extends layout

block content
  h1= message
  h2= error.status
  pre #{error.stack}