WebFund 2015W: Assignment 4

From Soma-notes
Revision as of 19:02, 9 February 2015 by Soma (talk | contribs) (→‎Solutions)

Answer the following questions about notes-demo that you looked at in Tutorial 4. The code is also listed below. There are 20 points in 7 questions. There is also 1 point for a bonus question. This assignment is due by 10 AM on Monday, February 9, 2015. We will go through the solutions in class at that time.

Please submit your answers as a single text or PDF file called "<username>-comp2406-assign4.txt" or "<username>-comp2406-assign4.pdf", where username is your MyCarletonOne username. The first four lines of this file should be "COMP 2406 Assignment 4", your name, student number, and the date. You may wish to format your answers in Markdown to improve their appearance. If you do so, you may convert your text file to PDF using pandoc.

No other formats will be accepted. Submitting in another format will likely result in your assignment not being graded and you receiving no marks for this assignment. In particular do not submit an MS Word or OpenOffice file as your answers document!


Questions

  1. [4] In the listings below the following files were not included. What is the high-level purpose of each file, and when is each accessed? (Note that you do not need to refer to these files to answer the following questions.)
    • app.js
    • bin/www
    • views/error.jade
    • package.json
  2. [2] Can a user embed a hyperlink in the title of a note? What about in the content portion of the note? Why?
  3. [2] The /edit/[note number] page redirects to /notes when it cannot find the requested note. When it does so, it passes an error message of "Unknown Note to Edit" to the page; however, this error is not displayed anywhere except for the URL bar. How could you make /notes display errors in the body of the page?
  4. [2] You want to change the font used to render page headers (h1 tags) to in notes-demo to be the same font as the main text, but in 50 point bold italic letters. How would you change it?
  5. [2] You want to start playing with jQuery in your web application. Before you do this, however, you need to add code to include jQuery in your app. How would you add client-side jQuery support to notes-demo?
  6. [2] How would you add a Cancel button to the Note editor page? It should be a button (like the other buttons in the application) that takes the user back to the notes listing page.
  7. [6] How would you add a Date field to each note? The Date field should be shown beside the title in the main notes listing page. The Date should also be modifiable from the edit page.

Code

routes/index.js

var express = require('express');
var router = express.Router();

var userNotes = {"alice": 
		 [
		     {"title": "Grocery",
		      "content": "Eggs<br>Milk"},
		     {"title": "To Do",
		      "content": "Lots.<br>of.<br>things"}
		 ]
		};

router.get('/', function(req, res) {
    if (req.session.username) {
        res.redirect("/notes");
    } else {
	res.render('index', { title: 'COMP 2406 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",
				    userNotes: userNotes[username]});
    } else {
	res.redirect("/?error=Not Logged In");
    }
});

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

    if (!userNotes[username]) {
	userNotes[username] = [];
    }

    res.redirect("/notes")
});

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

router.get('/edit/:id', function(req, res) {
    var username = req.session.username;
    var id = req.params.id;
    var note;

    if (username) {
	note = userNotes[username][id];

	if (note) {
	    res.render("edit.jade", {title: "Edit Note",
				     note: note,
				     id: id});
	} else {
	    res.redirect("/notes?error=Unknown Note to Edit");
	}
    } else {
	res.redirect("/?error=Not Logged In");
    }
});

router.post('/update', function(req, res) {
    var username = req.session.username;
    var id = req.body.id;
    var title;
    
    if (username) {
	note = userNotes[username][id];

	if (note) {
	    note.title = req.body.title;
	    note.content = req.body.content;
	    title = "Update Succeded";
	} else {
	    title = "Update Failed";
	}
	
	res.render("updated.jade", {title: title,
				    updatedNote: note});
    } else {
	res.redirect("/?error=Not Logged In");
    }
});

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

    if (username) {
	nextNote = userNotes[username].length;
	note = {title: "Untitled",
		content: "No content"}
	userNotes[username][nextNote] = note;
	res.redirect("/edit/" + nextNote);
    } else {
	res.redirect("/?error=Not Logged In");
    }
});

module.exports = router;


views/layout.jade

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
    block header
  body
    block content


views/index.jade

extends layout

block content
  h1= title
  p Welcome to #{title}
  - if(error)
    div #{error}
  p Please log in
  div
    form(action="/login", method="post")
        div
            input(type="text", name="username")
            label(for="username") Username
        button(type="submit") Login


views/notes.jade

extends layout

block content
  h1 #{username} Account
  p Welcome #{username}
  form(action="/logout", method="post")
        button(type="submit") Logout
  
  h2 Notes
  ul
    - for (i=0; i < userNotes.length; i++)
      li
        div
          b= userNotes[i].title + ": "
          -var note_url = "/edit/" + i
          a(href=note_url) [edit]
          p!= userNotes[i].content
  a(href="/new") New Note


views/edit.jade

extends layout

block content
  h1= title
  div
    form(action="/update", method="post")
        input(type="hidden", name="id", value=id)
        div
            input#title(type="text", name="title", value=note.title)
        div
            textarea(cols="40", rows="5",
                     name="content", wrap="virtual")=note.content
        button(type="submit") Update


views/updated.jade

extends layout

block content
  h1 Note updated

  p The following note was updated:
    ul
     li
      b= updatedNote.title
  a(href="/notes") Return


public/stylesheets/styles.css

body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

Solutions

    • app.js contains the base parts of the express framework that regular developers are likely to want to inspect and change. For example, this is where 404 handlers are defined (for developer and production modes).
    • bin/www is the file that is used to start the application. It is part of the express framework but it is part of the framework that regular developers shouldn't need to touch. It starts the web server and handles using the PORT environment variable to change which port it runs on.
    • views/error.jade is the jade template for error pages, particularly 404 pages.
    • package.json defines the node modules that the application depends upon. It is used by npm to install these dependencies in the node_modules directory. (Note that so far in this class you have been given apps with node_modules. If this directory was omitted but you had the correct package.json, you would just have to type "npm install" in the top-level directory to regenerate the node_modules directory.)
  1. A user cannot embed a hyperlink in the title of a note but can embed one in the content of a note because of the different ways user-supplied data is incorporated into the notes.jade template. The title in included using an "=", which causes all HTML special characters to be quoted/escaped. Thus the inclusion of a "<" gets transformed into a "<". The contents of the note are included using the "!=" operator which causes the referenced variable to be included in the output with no modifications. Normally "!=" is only safe for input that has not been provided by a user or for user input that has been carefully sanitized.
  2. You would change the handling of /notes to be similar to that of /. Specifically, you would pass the error to the notes.jade template as part of the second object argument to res.render() as on line 18 of routes/index.js. This error would be rendered using a conditional div in notes.jade similar to the one on lines 6-7 of index.jade.
  3. Follow the pattern of the body section of styles.css to create an "h1" section at the end of the file. You don't need to include the padding line; however, do include the font specification, changing the font size to 50px and prepending with "italic bold".
  4. Modify layout.jade to include a script tag in the header section (around line 5) that specifies a source (src) of either a local copy of jQuery or the URL of the jQuery library on another server (e.g., on a content distribution network (CDN)).
  5. Copy lines 6-7 from notes.jade to edit.jade, changing the action to be "/notes", the method to be "get", and the main text to be "Cancel" (rather than Logout). Note this will only cancel the editing of an existing note; it won't undo the creation of a new note. To undo note creation you first would need to implement note deletion!
  6. To implement a Date field, you need to:
    • Modify notes.jade so the date field will be rendered. For example you could edit line 14, changing it to
      b= userNotes[i].title + " (" + userNotes[i].date + "): "
    • Modify edit.jade so there is the correct field. For example you insert two lines at line 10, the first saying "div" and the next indented under it
      input#date(type="text", name="date", value=note.date)
      You could also use the "date" type in newer browsers.