changeset 108:86b843748c81

merged
author Benoît Allard <benoit.allard@greenbone.net>
date Wed, 15 Oct 2014 15:22:59 +0200
parents 0576cbcd28fe (current diff) 4ea7966dcc99 (diff)
children e5c047d392ca
files farol/templates/cache/load.j2
diffstat 47 files changed, 1206 insertions(+), 446 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Wed Oct 15 15:22:50 2014 +0200
+++ b/CHANGES	Wed Oct 15 15:22:59 2014 +0200
@@ -1,8 +1,10 @@
-Farol 0.1.1 (2014-??-??)
-========================
+Farol 0.2 (2014-??-??)
+======================
 
-This is the first patch release of Farol 0.1, it fixes various issues, and
-improve usability.
+This is the second release of Farol. Farol is a Security Advisory Management
+(Web) Platform.
+
+This release, fixes various issues, and is a major interface improvement.
 
 Thanks to all the contributors:
 Michael Wiegand and Benoît Allard.
@@ -15,6 +17,8 @@
 * Implement three different kind of 'caching': disableds, global or
   session-based.
 * Add possibility to download the rendered document.
+* Add Description for each field on each edit/add page.
+* Add possibility to delete elements.
 * Various styling improvements.
 
 
--- a/farol/cache.py	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/cache.py	Wed Oct 15 15:22:59 2014 +0200
@@ -102,15 +102,8 @@
     del_current()
     return redirect(url_for('new'))
 
-@mod.route('/load/<element>', methods=['GET', 'POST'])
+@mod.route('/load/<element>', methods=['POST'])
 def load(element):
-    if request.method != 'POST':
-        if has_current():
-            # Suggest to save first
-            return render_template('cache/load.j2', element=element)
-
-        # Ouch, GET request changing state of the server ...
-
     dirname = _caching_dir()
     element = secure_filename(element)
     if dirname is None:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farol/controller.py	Wed Oct 15 15:22:59 2014 +0200
@@ -0,0 +1,93 @@
+# -*- encoding: utf-8 -*-
+# Description:
+# Common controller Web stuffs
+#
+# Authors:
+# Benoît Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""\
+Some common routines for handling of Notes, Acknowledgments and Reference that
+is shared for the Document and the Vulnerabilities.
+"""
+
+import re
+from datetime import datetime, timedelta
+
+try:
+    from datetime import timezone
+except ImportError:
+    from farolluz.py2 import FixedTimeZone as timezone
+
+from flask import request
+
+from farolluz.cvrf import CVRFNote, CVRFReference, CVRFAcknowledgment
+from farolluz.parsers.cvrf import parseDate as parseXMLDate
+
+def split_fields(field, separator=','):
+    if not field:
+        return []
+    return [f.strip() for f in field.split(separator)]
+
+def update_note_from_request(note):
+    note._type = request.form['type']
+    note._ordinal = int(request.form['ordinal'])
+    note._note = request.form['note']
+    note._title = request.form['title'] or None
+    note._audience = request.form['audience'] or None
+
+def create_note_from_request():
+    title = request.form['title'] or None
+    audience = request.form['audience'] or None
+
+    return CVRFNote(request.form['type'], int(request.form['ordinal']),
+                    request.form['note'], title, audience)
+
+def update_reference_from_request(ref):
+    ref._type = request.form['type'] or None
+    ref._url = request.form['url']
+    ref._description = request.form['description']
+
+def create_reference_from_request():
+    return CVRFReference(request.form['url'], request.form['description'],
+                         request.form['type'] or None)
+
+def update_acknowledgment_from_request(ack):
+    ack._names = split_fields(request.form['names'])
+    ack._organizations = split_fields(request.form['organizations'])
+    ack._description = request.form['description'] or None
+    ack._url = request.form['url'] or None
+
+def create_acknowledgment_from_request():
+    ack = CVRFAcknowledgment()
+    ack._names = split_fields(request.form['names'])
+    ack._organizations = split_fields(request.form['organizations'])
+    ack._description = request.form['description'] or None
+    ack._url = request.form['url'] or None
+    return ack
+
+def parseDate(string):
+    """ An extended version of the XML parser's one, that also unsderstand
+    date without time. """
+    try: return parseXMLDate(string)
+    except AttributeError: pass
+    # Absorb AttributeError, and try to parse it a second time ...
+    m = re.match('(\d{4})-(\d{2})-(\d{2})', string)
+    return datetime(int(m.group(1)), int(m.group(2)), int(m.group(3)),
+                    tzinfo=timezone(timedelta(hours=0, minutes=0)))
--- a/farol/document.py	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/document.py	Wed Oct 15 15:22:59 2014 +0200
@@ -23,14 +23,18 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
 from flask import (Blueprint, render_template, abort, redirect, request,
-    url_for)
+    url_for, flash)
 
-from farolluz.parsers.cvrf import parseDate, parseVersion
-from farolluz.cvrf import (CVRFNote, CVRFReference, CVRFAcknowledgment,
-    CVRFPublisher, CVRFTracking, CVRFTrackingID, CVRFGenerator, CVRFRevision,
+from farolluz.parsers.cvrf import parseVersion
+from farolluz.cvrf import (CVRFNote, CVRFReference, CVRFPublisher,
+    CVRFTracking, CVRFTrackingID, CVRFGenerator, CVRFRevision,
     CVRFAggregateSeverity)
 from farolluz.renderer import utcnow
 
+from .controller import (update_note_from_request, create_note_from_request,
+    update_reference_from_request, create_reference_from_request,
+    update_acknowledgment_from_request, create_acknowledgment_from_request,
+    split_fields, parseDate)
 from .session import document_required, get_current
 
 
@@ -72,7 +76,7 @@
     tracking = get_current()._tracking
     if tracking is None:
         wasNone = True
-        tracking = CVRFTracking(CVRFTrackingID(''), 'Draft', (0,), utcnow(), utcnow())
+        tracking = CVRFTracking(CVRFTrackingID(''), 'Draft', (0,0), utcnow(), utcnow())
     generator = tracking._generator
     if not tracking._generator:
         generator = CVRFGenerator()
@@ -80,9 +84,7 @@
         return render_template('document/edit_tracking.j2', tracking=tracking, version='.'.join('%s'%v for v in tracking._version), generator=generator, now=utcnow(), statuses=tracking.STATUSES)
 
     tracking._identification._id = request.form['id']
-    aliases = []
-    if request.form['id_aliases']:
-        aliases = [a.strip() for a in request.form['id_aliases'].split(',')]
+    aliases = split_fields(request.form['id_aliases'])
     tracking._identification._aliases = aliases
     tracking._status = request.form['status']
     tracking._version = parseVersion(request.form['version'])
@@ -124,6 +126,9 @@
 def add_revision():
     tracking = get_current()._tracking
     if request.method != 'POST':
+        if tracking is None:
+            flash('The tracking information should be set first to be able to add a revision.', 'danger')
+            return redirect(url_for('.edit_tracking'))
         version = tracking._version
         version = version[:-1] + (version[-1] + 1,)
         return render_template('document/edit_revision.j2', number='.'.join("%d"%v for v in version), date=utcnow(), action='Add')
@@ -137,6 +142,16 @@
         tracking._currentDate = date
     return redirect(url_for('.view'))
 
+@document.route('/revision/<int:index>/del', methods=['POST'])
+@document_required
+def del_revision(index):
+    history = get_current()._tracking._history
+    if not (0 <= index < len(history)):
+        flash('Revision not found', 'danger')
+        abort(404)
+    del history[index]
+    return redirect(url_for('.view'))
+
 @document.route('/distribution/edit', methods=['GET', 'POST'])
 @document_required
 def edit_distribution():
@@ -179,11 +194,7 @@
     if request.method != 'POST':
         return render_template('document/edit_note.j2', note=note, types = note.TYPES)
 
-    note._type = request.form['type']
-    note._ordinal = int(request.form['ordinal'])
-    note._note = request.form['note']
-    note._title = request.form['title'] or None
-    note._audience = request.form['audience'] or None
+    update_note_from_request(note)
     return redirect(url_for('.view_note', ordinal=note._ordinal ))
 
 
@@ -197,13 +208,19 @@
             next_ordinal = notes[-1]._ordinal + 1
         return render_template('document/edit_note.j2', ordinal=next_ordinal, types=CVRFNote.TYPES, action='Add')
 
-    title = None
-    audience = None
-    title = request.form['title'] or None
-    audience = request.form['audience'] or None
+    note = create_note_from_request()
+    get_current().addNote(note)
+    return redirect(url_for('.view'))
 
-    note = CVRFNote(request.form['type'], int(request.form['ordinal']), request.form['note'], title, audience)
-    get_current().addNote(note)
+@document.route('/note/<int:ordinal>/del', methods=['POST'])
+@document_required
+def del_note(ordinal):
+    cvrf = get_current()
+    note = cvrf.getNote(ordinal)
+    if note is None:
+        flash('Note not found', 'danger')
+        abort(404)
+    cvrf._notes.remove(note)
     return redirect(url_for('.view'))
 
 @document.route('/reference/<int:index>/edit', methods=['GET', 'POST'])
@@ -216,9 +233,7 @@
     if request.method != 'POST':
         return render_template('document/edit_reference.j2', _type=ref._type, url=ref._url, description=ref._description, types=('',) + ref.TYPES)
 
-    ref._type = request.form['type'] or None
-    ref._url = request.form['url']
-    ref._description = request.form['description']
+    update_reference_from_request(ref)
     return redirect(url_for('.view'))
 
 
@@ -228,10 +243,20 @@
     if request.method != 'POST':
         return render_template('document/edit_reference.j2', action='Add', types=('',) + CVRFReference.TYPES)
 
-    ref = CVRFReference(request.form['url'], request.form['description'], request.form['type'] or None)
+    ref = create_reference_from_request()
     get_current().addReference(ref)
     return redirect(url_for('.view'))
 
+@document.route('/reference/<int:index>/del', methods=['POST'])
+@document_required
+def del_reference(index):
+    refs = get_current()._references
+    if not (0 <= index < len(refs)):
+        flash('Reference not found', 'danger')
+        abort(404)
+    del refs[index]
+    return redirect(url_for('.view'))
+
 @document.route('/acknowledgment/<int:index>')
 @document_required
 def view_acknowledgment(index):
@@ -249,24 +274,31 @@
     except IndexError:
         abort(404)
     if request.method != 'POST':
-        return render_template('document/edit_acknowledgment.j2', name=ack._name, organization=ack._organization, description=ack._description, url=ack._url, action='Update')
+        return render_template('document/edit_acknowledgment.j2',
+            names=ack._names, organizations=ack._organizations,
+            description=ack._description, url=ack._url,
+            action='Update')
 
-    ack._name = request.form['name'] or None
-    ack._organization = request.form['organization'] or None
-    ack._description = request.form['description'] or None
-    ack._url = request.form['url'] or None
+    update_acknowledgment_from_request(ack)
     return redirect(url_for('.view'))
 
 @document.route('/acknowledgment/add', methods=['GET', 'POST'])
 @document_required
 def add_acknowledgment():
     if request.method != 'POST':
-        return render_template('document/edit_acknowledgment.j2', action='Add')
+        return render_template('document/edit_acknowledgment.j2',
+            action='Add')
 
-    ack = CVRFAcknowledgment()
-    ack._name = request.form['name'] or None
-    ack._organization = request.form['organization'] or None
-    ack._description = request.form['description'] or None
-    ack._url = request.form['url'] or None
+    ack = create_acknowledgment_from_request()
     get_current().addAcknowledgment(ack)
     return redirect(url_for('.view'))
+
+@document.route('/acknowledgment/<int:index>/del', methods=['POST'])
+@document_required
+def del_acknowledgment(index):
+    acks = get_current()._acknowledgments
+    if not( 0 <= index < len(acks)):
+        flash('Acknowledgment not found', 'danger')
+        abort(404)
+    del acks[index]
+    return redirect(url_for('.view'))
--- a/farol/main.py	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/main.py	Wed Oct 15 15:22:59 2014 +0200
@@ -80,7 +80,8 @@
         error = None
     except ValidationError as ve:
         error = ve
-    return dict(has_current=True, vulnerabilities=vulns, products=prods, error=error)
+    return dict(has_current=True, vulnerabilities=vulns, products=prods,
+                error=error, current_id=cvrf.getDocId())
 
 @app.template_filter('secure_filename')
 def makeId(string):
@@ -140,11 +141,7 @@
 @app.route('/new', methods=['GET', 'POST'])
 def new():
     if request.method != 'POST':
-        current_id = None
-        cvrf = get_current()
-        if has_current():
-            current_id = get_current().getDocId()
-        return render_template('new.j2', has_document=has_current(), now=utcnow(), current_id=current_id)
+        return render_template('new.j2', has_document=has_current(), now=utcnow())
 
     if 'rhsa' in request.form:
         set_RHSA(request.form['id'])
--- a/farol/producttree.py	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/producttree.py	Wed Oct 15 15:22:59 2014 +0200
@@ -49,9 +49,23 @@
 @document_required
 def create():
     get_current().createProductTree()
+    return redirect(url_for('.view'))
+
+@producttree.route('/')
+@document_required
+@producttree_required
+def view():
+    cvrf = get_current()
+    return render_template('producttree/view.j2',
+                           producttree=cvrf._producttree, cvrf=cvrf)
+
+@producttree.route('/delete', methods=['POST'])
+@producttree_required
+def delete():
+    # XXX: We should first check if no PID and GID is used ...
+    get_current()._producttree = None
     return redirect(url_for('document.view'))
 
-
 @producttree.route('/branch/<path:path>/edit', methods=['GET', 'POST'])
 @document_required
 @producttree_required
@@ -59,30 +73,29 @@
     path = [int(p) for p in path.split('/')]
     cvrf = get_current()
     ptree = cvrf._producttree
-    try:
-        branch = cvrf._producttree.getBranch(path)
-    except ValueError:
-        abort(404)
+    try: branch = cvrf._producttree.getBranch(path)
+    except (ValueError, IndexError): abort(404)
     if request.method != 'POST':
         branches = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getNotTerminalBranches(branch)]
-        return render_template('producttree/edit_branch.j2', branch=branch, branches=branches, types=branch.TYPES)
+        return render_template('producttree/edit_branch.j2',
+                               _type=branch._type, name=branch._name, parentpath=branch.getParentPath(),
+                               branches=branches, types=branch.TYPES)
 
     pbranch = ptree
     if request.form['parent']:
-        pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')])
+        try: pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')])
+        except (ValueError, IndexError): abort(404)
 
     if pbranch is not branch.getParent():
         # We have to 're-link' the element ...
         # 1. unlink
         branch.unlink()
         # 2. set the new parent
-        branch._parentbranch = pbranch
-        # 3. add to the Tree (again)
-        ptree.addBranch(branch)
+        branch.link(pbranch)
 
     branch._type = request.form['type']
     branch._name = request.form['name']
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
 
 @producttree.route('/branch/add', methods=['GET', 'POST'])
 @document_required
@@ -92,14 +105,35 @@
     ptree = cvrf._producttree
     if request.method != 'POST':
         branches = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getNotTerminalBranches()]
-        return render_template('producttree/edit_branch.j2', branch=CVRFProductBranch('', '', ptree), action='Add', branches=branches, types=CVRFProductBranch.TYPES)
+        return render_template('producttree/edit_branch.j2',
+                               action='Add',
+                               branches=branches, types=CVRFProductBranch.TYPES)
 
     pbranch = ptree
     if request.form['parent']:
-        pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')])
+        try: pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')])
+        except (ValueError, IndexError): abort(404)
     branch = CVRFProductBranch(request.form['type'], request.form['name'], pbranch)
-    ptree.addBranch(branch)
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
+
+@producttree.route('/branch/<path:path>/del', methods=['POST'])
+@document_required
+@producttree_required
+def del_branch(path):
+    path = [int(p) for p in path.split('/')]
+    cvrf = get_current()
+    ptree = cvrf._producttree
+    try: branch = cvrf._producttree.getBranch(path)
+    except (ValueError, IndexError):
+        flash('Cannot find Branch', 'danger')
+        abort(404)
+    if branch._childs:
+        flash('Cannot delete a branch with childs', 'danger')
+        abort(403)
+    branch.unlink()
+    del branch
+    return redirect(url_for('.view'))
+
 
 @producttree.route('/product/<productid>')
 @document_required
@@ -110,7 +144,9 @@
         product = cvrf.getProductForID(productid)
     except IndexError:
         abort(404)
-    return render_template('producttree/view_product.j2', product=product, cvrf=cvrf)
+    return render_template('producttree/view_product.j2',
+        product=product, groups=[g for g in cvrf._producttree._groups if productid in g._productids],
+        cvrf=cvrf)
 
 @producttree.route('/product/<productid>/edit', methods=['GET', 'POST'])
 @document_required
@@ -137,28 +173,31 @@
 
     oldp = product._parent
     if request.form['parent_branch']:
-        pbranch = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')])
+        try: pbranch = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')])
+        except (ValueError, IndexError): abort(404)
         if pbranch is not oldp:
             # Gonna be funny, needs re-link
             product.unlink()
-            product._parent = pbranch
-            ptree.addProduct(product)
+            # And Link again
+            product.link(pbranch)
     elif request.form['parent_relationship']:
         prel = ptree._relationships[int(request.form['parent_relationship'])]
         if prel is not oldp:
+            # Unlink
             product.unlink()
-            product._parent = prel
-            ptree.addProduct(product)
+            # Link again
+            product.link(prel)
     else:
         if ptree is not oldp:
+            # Unlink
             product.unlink()
-            product._parent = ptree
-            ptree.addProduct(product)
+            # Link again
+            product.link(ptree)
 
     product._productid = request.form['productid']
     product._name = request.form['name']
     product._cpe = request.form['cpe'] or None
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
 
 @producttree.route('/product/add', methods=['GET', 'POST'])
 @document_required
@@ -175,17 +214,34 @@
 
     if request.form['parent_branch'] and request.form['parent_relationship']:
         flash('Cannot set a parent branch and parent relationship', 'danger')
-        return redirect(url_for('.edit_product', productid=productid))
+        return redirect(url_for('.add_product'))
 
     parent = ptree
     if request.form['parent_branch']:
-        parent = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')])
+        try: parent = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')])
+        except (ValueError, IndexError): abort(404)
     elif request.form['parent_relationship']:
         parent = ptree._relationships[int(request.form['parent_relationship'])]
 
     product = CVRFFullProductName(request.form['productid'], request.form['name'], parent, request.form['cpe'] or None)
     ptree.addProduct(product)
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
+
+@producttree.route('/product/<productid>/del', methods=['POST'])
+@document_required
+@producttree_required
+def del_product(productid):
+    cvrf = get_current()
+    ptree = cvrf._producttree
+    try:
+        product = cvrf.getProductForID(productid)
+    except KeyError:
+        abort(404)
+    product.unlink()
+    ptree._products.remove(product)
+    del product
+    return redirect(url_for('.view'))
+
 
 @producttree.route('/relationship/<int:index>/edit', methods=['GET', 'POST'])
 @document_required
@@ -204,7 +260,7 @@
     rel._productreference = request.form['productreference']
     rel._relationtype = request.form['relationtype']
     rel._relatestoproductreference = request.form['relatestoproductreference']
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
 
 @producttree.route('/relationship/add', methods=['GET', 'POST'])
 @document_required
@@ -228,7 +284,25 @@
 
     rel = CVRFRelationship(prodid1, request.form['relationtype'], prodid2)
     ptree.addRelationship(rel)
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
+
+@producttree.route('/relationship/<int:index>/del', methods=['POST'])
+@document_required
+@producttree_required
+def del_relationship(index):
+    rels = get_current()._producttree._relationships
+    if not (0 <= index < len(rels)):
+        flash('Relationship not found', 'danger')
+        abort(404)
+    rel = rels[index]
+
+    if not rel.isOrphaned():
+        flash('Not able to delete Relationship with Product', 'danger')
+        abort(403)
+
+    del rels[index]
+    return redirect(url_for('.view'))
+
 
 @producttree.route('/group/<groupid>/edit', methods=['GET', 'POST'])
 @document_required
@@ -246,7 +320,7 @@
     group._productids = []
     for productid in request.form.getlist('products'):
         group.addProductID(productid)
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
 
 @producttree.route('/group/add', methods=['GET', 'POST'])
 @document_required
@@ -260,4 +334,19 @@
     for productid in request.form.getlist('products'):
         group.addProductID(productid)
     get_current()._producttree.addGroup(group)
-    return redirect(url_for('document.view'))
+    return redirect(url_for('.view'))
+
+@producttree.route('/group/<groupid>/del', methods=['POST'])
+@document_required
+@producttree_required
+def del_group(groupid):
+    cvrf = get_current()
+    try:
+        group = get_current().getGroupForID(groupid)
+    except KeyError:
+        flash('Group not found', 'danger')
+        abort(404)
+
+    cvrf._producttree._groups.remove(group)
+    return redirect(url_for('.view'))
+
--- a/farol/session.py	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/session.py	Wed Oct 15 15:22:59 2014 +0200
@@ -29,7 +29,7 @@
 from functools import wraps
 import uuid
 
-from flask import session, flash, redirect, url_for
+from flask import session, redirect, url_for
 
 CVRFs = {}
 
--- a/farol/static/style.css	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/static/style.css	Wed Oct 15 15:22:59 2014 +0200
@@ -13,6 +13,16 @@
   margin-bottom: -50px;
 }
 */
+
+form.inlined-form {
+  display: inline-block;
+}
+
+/* To behave as a link, it should'nt have padding ... */
+.btn-link {
+  padding: 0;
+}
+
 .footer {
   height: 130px;
   margin-top: 20px;
--- a/farol/templates/base.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/base.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -23,6 +23,8 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 -#}
 
+{% from "macros.j2" import modal, POST_button -%}
+
 <!doctype html>
 
 <html lang="en">
@@ -75,14 +77,14 @@
             </ul>
           </li>
           {% endif %}
-          {% if has_current %}
-            {% if error %}
-              <p class="navbar-text">Document is <a id="error-popover" href="#" tabindex="0" class="navbar-link" data-toggle="popover" data-trigger="focus" data-placement="bottom" title="First Error:" data-content="{{ error }}"><strong>invalid</strong></a></p>
-            {% else %}
-              <p class="navbar-text">Document looks valid</p>
-            {% endif %}
+        </ul>
+        {% if has_current %}
+          {% if error %}
+            <p class="navbar-text">Document is <a id="error-popover" href="#" tabindex="0" class="navbar-link" data-toggle="popover" data-trigger="focus" data-placement="bottom" title="First Error:" data-content="{{ error }}"><strong>invalid</strong></a></p>
+          {% else %}
+            <p class="navbar-text">Document looks valid</p>
           {% endif %}
-        </ul>
+        {% endif %}
         <ul class="nav navbar-nav navbar-right">
           {% if has_current %}
           <li class="dropdown{{ ' active' if active == 'render' }}">
@@ -96,10 +98,20 @@
           <li class="dropdown">
             <a href="#" class="dropdown-toggle" data-toggle="dropdown">Cache <span class="caret"></span></a>
             <ul class="dropdown-menu" role="menu">
-              <li role="presentation"{{ ' class="disabled"' if not has_current }}><a role="menuitem" tabindex="-1" href="{{ url_for('cache.save') }}">Save current</a>
+              <li role="presentation"{{ ' class="disabled"' if not has_current }}><a role="menuitem" tabindex="-1" href="{{ url_for('cache.save') }}">Save {{ current_id }}</a>
               </li>
               <li role="presentation" class="divider"></li>
-              {% for element in cache | sort %}<li role="presentation"><a href="{{ url_for('cache.load', element=element)}}">{{ element }}</a></li>{% endfor %}
+              {% for element in cache | sort %}
+                <li role="presentation">
+                  {% if has_current %}
+                    <a href="#{{element}}_modal" data-toggle="modal">Load {{ element }}</a>
+                  {% else %}
+                    {% call(selector) POST_button(url_for('cache.load', element=element), out=True) %}
+                      <a role="menuitem" href="#" onclick="{{ selector }}.submit();return false;">Load {{ element }}</a>
+                    {% endcall %}
+                  {% endif %}
+                </li>
+              {% endfor %}
             </ul>
           </li>
           {% endif %}
@@ -107,6 +119,19 @@
       </div>{# /.navbar-collapse #}
     </div>{# /.container-fluid #}
   </nav>
+  {% if has_current %}
+    {% for element in cache %}
+      {# Put the modals for the load action here #}
+      {% call modal(element + '_modal', 'Load %s from cache' % element) %}
+        <p>You asked to load <strong>{{ element }}</strong>, your changes to <strong>{{ current_id }}</strong> will be lost.</p>
+        <p>Do you want to save <strong>{{ current_id }}</strong> first ?</p>
+      </div>
+      <div class="modal-footer">
+        <a href="{{ url_for('cache.save') }}" class="btn btn-success">Save {{ current_id }}</a>
+        {{ POST_button(url_for('cache.load', element=element), text="Load " + element, style="btn-danger") }}
+      {% endcall %}
+    {% endfor %}
+  {% endif %}
   <div class="main container">
     {% with messages = get_flashed_messages(with_categories=True) %}
       {% if messages %}
--- a/farol/templates/cache/load.j2	Wed Oct 15 15:22:50 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-{#
-# Description:
-# Web Template used in Farol Design
-#
-# Authors:
-# Benoît Allard <benoit.allard@greenbone.net>
-#
-# Copyright:
-# Copyright (C) 2014 Greenbone Networks GmbH
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
--#}
-
-{% extends "base.j2" %}
-{% from "macros.j2" import textinput %}
-{% block title %}Edit the title{% endblock %}
-
-{% block content %}
-<p>You asked to load {{ element }}, but you still have an unsaved document loaded. Do you want to <a href="{{ url_for('.save') }}">save</a> it first ?</p>
-<form role="form" method="POST">
-<button class="btn btn-primary" type="submit">Load "{{ element }}"</button>
-<a class="btn btn-danger" href="{{ url_for('welcome') }}">Cancel</a>
-</form>
-{% endblock %}
--- a/farol/templates/common_edits.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/common_edits.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -23,7 +23,7 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 -#}
 
-{% from "macros.j2" import textinput, examples %}
+{% from "macros.j2" import textinput, textarea, selectinput, examples %}
 
 {% macro edit_title_type(doctitle, doctype) %}
 {% call textinput("title", "Document Title", "", doctitle, required=True) %}
@@ -36,3 +36,81 @@
 {{ examples(['Vulnerability Report', 'Security Bulletin', 'Security Notice']) }}
 {% endcall %}
 {% endmacro %}
+
+{% macro edit_note(types, note, ordinal) %}
+<p><strong>Note</strong> is a place to put all manner of text blobs related to the document as a whole. It can be a concise summary of the overall document or a more compartmentalized and area-specific textual discussion. Depending on the need, there can be zero, one, or several <strong>Note</strong> elements in a given CVRF document.
+
+<p>The note should contain a compartmentalized textual discussion constrained by its <em>Type</em> attribute.</p>
+{% call selectinput("type", "Type", types, note and note._type or '') %}
+<p><em>Type</em> can be one of the following:</p>
+<dl class="dl-horizontal">
+  <dt>General:</dt>
+  <dd>A general, high-level note (<em>Title</em> may have more information).</dd>
+  <dt>Details:</dt>
+  <dd>A low-level detailed discussion (<em>Title</em> may have more information).</dd>
+  <dt>Description:</dt>
+  <dd>A description of something (<em>Title</em> may have more information).</dd>
+  <dt>Summary:</dt>
+  <dd>A summary of something (<em>Title</em> may have more information).</dd>
+  <dt>FAQ:</dt>
+  <dd>A list of frequently asked questions.</dd>
+  <dt>Legal Disclaimer:</dt>
+  <dd>Any possible legal discussion, including constraints, surrounding the document.</dd>
+  <dt>Other:</dt>
+  <dd>Something that doesn’t fit (<em>Title</em> should have more information).</dd>
+</dl>
+{% endcall %}
+{% call textinput("ordinal", "Ordinal", "", ordinal or note._ordinal, type="number", required=True, extras={'min': '1'}) %}
+<p><em>Ordinal</em> is a mandatory, locally significant value used to track notes inside a CVRF document at the root (document) level. It is provided to uniquely identify a <strong>Note</strong>.</p>
+<p>There should be one of these values for every <strong>Note</strong> inside <strong>Document Notes</strong>, and it is recommended that <em>Ordinal</em> should be instantiated as a monotonically increasing counter, indexed from 1. Each <em>Ordinal</em> that tracks a <strong>Note</strong> inside <strong>Document Notes</strong> is completely independent from an <em>Ordinal</em> tracking a <strong>Note</strong> inside <strong>Vulnerability/Notes</strong>.</p>
+{% endcall %}
+{% call textinput("title", "Title", "", note and note._title or '') %}
+<p><em>Title</em> should be a concise description of what is contained in the text.</p>
+{% endcall %}
+{% call textinput("audience", "Audience", "", note and note._audience or '') %}
+<p><em>Audience</em> will indicate who is intended to read it.</p>
+{% endcall %}
+{% call textarea("note", "Note", "", note and note._note or '', 10, required=True) %}
+<p><strong>Note</strong> is a place to put all manner of text blobs related to the document as a whole. It can be a concise summary of the overall document or a more compartmentalized and area-specific textual discussion.</p>
+<p>The note should contain a compartmentalized textual discussion constrained by its <em>Type</em> attribute.</p>
+{% endcall %}
+{% endmacro %}
+
+{% macro edit_reference(types, _type, url, description) %}
+<p>The <strong>Reference</strong> container should include references to any conferences, papers, advisories, and other resources that are related and considered to be of value to the document consumer.</p>
+<p>The <strong>Reference</strong> element contains a description of a related document. This may include a plaintext or HTML version of the advisory or other related documentation, such as white papers or mitigation documentation.</p>
+{% call selectinput("type", "Type", types, _type) %}
+<p>The <em>Type</em> attribute denotes the type of the document reference relative to the given document. The following types are available:</p>
+<dl class="dl-horizontal">
+  <dt>External:</dt>
+  <dd>The default value indicates the reference is external to the document.</dd>
+  <dt>Self:</dt>
+  <dd>This indicates the related document is actually a direct reference to itself.</dd>
+</dl>
+{% endcall %}
+{% call textinput("url", "URL", "http://...", url, type="url", required=True) %}
+<p><strong>URL</strong> is the fixed URL or location of the reference.</p>
+{% endcall %}
+{% call  textinput("description", "Description", "", description, required=True) %}
+<p><strong>Description</strong> is a descriptive title or the name of the reference.</p>
+{% endcall %}
+{% endmacro %}
+
+{% macro edit_acknowledgment(names, organizations, description, url) %}
+<p><strong>Acknowledgment</strong> contains recognition of external parties that reported noncritical/low-severity security issues or provided information, observations, or suggestions that contributed to improved security or improved documentation in future releases of the document producer's products. This may also contain recognition to external parties that contributed toward producing this document.</p>
+<p>This element indicates collaboration with the security community in a positive fashion and is an important part of a notice or advisory. Care should be taken to ensure that individuals would like to be acknowledged before they are included.</p>
+
+{% call textinput("names", "Names", "", names | join(', '), help="Multiple names should be comma-separated.") %}
+<p>The <strong>Name</strong> should contain the name of the party being acknowledged.</p>
+{% endcall %}
+{% call textinput("organizations", "Organizations", "", organizations | join(', '), help="Multiple organizations should be comma-separated.") %}
+<p>The <strong>Organization</strong> should contain the organization of the party or if the <strong>Name</strong> is omitted, the organization itself that is being acknowledged.</p>
+{% endcall %}
+{% call textarea("description", "Description", "", description, 5) %}
+<p>The <strong>Description</strong> can contain any contextual details the document producers wish to make known about the acknowledgment or acknowledged parties.</p>
+{{ examples (['Vendor X would like to thank [Name 3] from [OrgName] for reporting this issue.', 'Vendor  X would like to thank the following researchers for their contributions to making this project more secure:  [Name 1], [Name 2], [Name 3]']) }}
+{% endcall %}
+{% call textinput("url", "URL", "http://...", url, type="url") %}
+<p><strong>URL</strong> is the optional URL to the person, place, or thing being acknowledged.</p>
+{% endcall %}
+{% endmacro %}
--- a/farol/templates/document/edit_acknowledgment.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/document/edit_acknowledgment.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,25 +24,13 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, selectinput %}
-{% block title %}Edit the title{% endblock %}
+{% from "common_edits.j2" import edit_acknowledgment %}
+{% block title %}Edit an Acknowledgment{% endblock %}
 
 {% block content %}
-<p><strong>Acknowledgment</strong> contains recognition of external parties that reported noncritical/low-severity security issues or provided information, observations, or suggestions that contributed to improved security or improved documentation in future releases of the document producer's products. This may also contain recognition to external parties that contributed toward producing this document.</p>
 <form role="form" method="POST">
 
-{% call textinput("name", "Name", "", name) %}
-<p>The <strong>Name</strong> should contain the name of the party being acknowledged.</p>
-{% endcall %}
-{% call textinput("organization", "Organization", "", organization) %}
-<p>The <strong>Organization</strong> should contain the organization of the party or if the <strong>Name</strong> is omitted, the organization itself that is being acknowledged.</p>
-{% endcall %}
-{% call textinput("description", "Description", "", description) %}
-<p>The <strong>Description</strong> can contain any contextual details the document producers wish to make known about the acknowledgment or acknowledged parties.</p>
-{% endcall %}
-{% call textinput("url", "URL", "http://...", url, type="url") %}
-<p><strong>URL</strong> is the optional URL to the person, place, or thing being acknowledged.</p>
-{% endcall %}
+  {{ edit_acknowledgment(names, organizations, description, url) }}
 
 <button class="btn btn-primary" type="submit">{{ action }}</button>
 <a class="btn btn-danger" href="{{ url_for('.view') }}">Cancel</a>
--- a/farol/templates/document/edit_note.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/document/edit_note.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,45 +24,14 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, textarea, selectinput %}
+{% from "common_edits.j2" import edit_note %}
+
 {% block title %}Edit the type{% endblock %}
 
 {% block content %}
 <form role="form" method="POST">
 
-{% call selectinput("type", "Type", types, note and note._type or '') %}
-<p><em>Type</em> can be one of the following:</p>
-<dl class="dl-horizontal">
-  <dt>General:</dt>
-  <dd>A general, high-level note (<em>Title</em> may have more information).</dd>
-  <dt>Details:</dt>
-  <dd>A low-level detailed discussion (<em>Title</em> may have more information).</dd>
-  <dt>Description:</dt>
-  <dd>A description of something (<em>Title</em> may have more information).</dd>
-  <dt>Summary:</dt>
-  <dd>A summary of something (<em>Title</em> may have more information).</dd>
-  <dt>FAQ:</dt>
-  <dd>A list of frequently asked questions.</dd>
-  <dt>Legal Disclaimer:</dt>
-  <dd>Any possible legal discussion, including constraints, surrounding the document.</dd>
-  <dt>Other:</dt>
-  <dd>Something that doesn’t fit (<em>Title</em> should have more information).</dd>
-</dl>
-{% endcall %}
-{% call textinput("ordinal", "Ordinal", "", ordinal or note._ordinal, type="number", required=True, extras={'min': '1'}) %}
-<p><em>Ordinal</em> is a mandatory, locally significant value used to track notes inside a CVRF document at the root (document) level. It is provided to uniquely identify a <strong>Note</strong>.</p>
-<p>There should be one of these values for every <strong>Note</strong> inside <strong>Document Notes</strong>, and it is recommended that <em>Ordinal</em> should be instantiated as a monotonically increasing counter, indexed from 1. Each <em>Ordinal</em> that tracks a <strong>Note</strong> inside <strong>Document Notes</strong> is completely independent from an <em>Ordinal</em> tracking a <strong>Note</strong> inside <strong>Vulnerability/Notes</strong>.</p>
-{% endcall %}
-{% call textinput("title", "Title", "", note and note._title or '') %}
-<p><em>Title</em> should be a concise description of what is contained in the text.</p>
-{% endcall %}
-{% call textinput("audience", "Audience", "", note and note._audience or '') %}
-<p><em>Audience</em> will indicate who is intended to read it.</p>
-{% endcall %}
-{% call textarea("note", "Note", "", note and note._note or '', 10, required=True) %}
-<p><strong>Note</strong> is a place to put all manner of text blobs related to the document as a whole. It can be a concise summary of the overall document or a more compartmentalized and area-specific textual discussion.</p>
-<p>The note should contain a compartmentalized textual discussion constrained by its <em>Type</em> attribute.</p>
-{% endcall %}
+ {{ edit_note(types, note, ordinal) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
 <a class="btn btn-danger" href="{% if action=='Add' %}{{ url_for('.view') }}{% else %}{{ url_for('.view_note', ordinal=note._ordinal) }}{% endif %}">Cancel</a>
--- a/farol/templates/document/edit_publisher.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/document/edit_publisher.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -44,7 +44,7 @@
   <dd>Everyone using a vendor’s product.</dd>
   <dt>Other:</dt>
   <dd>Catchall for everyone else. Currently this includes forwarders, republishers, language translators, and miscellaneous contributors.</dd>
-</ul>
+</dl>
 {% endcall %}
 {% call textinput("vendorid", "Vendor ID", "", publisher._vendorid or '') %}
 <p>The optional <em>Vendor ID</em> attribute is a unique identifier (OID) that a vendor uses as issued by FIRST under the auspices of IETF. At the time of this writing, OID is a work in progress.</p>
--- a/farol/templates/document/edit_reference.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/document/edit_reference.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -25,26 +25,13 @@
 
 {% extends "base.j2" %}
 {% from "macros.j2" import textinput, selectinput %}
-{% block title %}Edit the title{% endblock %}
+{% from "common_edits.j2" import edit_reference %}
+{% block title %}Edit a Reference{% endblock %}
 
 {% block content %}
 <form role="form" method="POST">
 
-{% call selectinput("type", "Type", types, _type) %}
-<p>The <em>Type</em> attribute denotes the type of the document reference relative to the given document. The following types are available:</p>
-<dl class="dl-horizontal">
-  <dt>External:</dt>
-  <dd>The default value indicates the reference is external to the document.</dd>
-  <dt>Self:</dt>
-  <dd>This indicates the related document is actually a direct reference to itself.</dd>
-</dl>
-{% endcall %}
-{% call textinput("url", "URL", "http://...", url, type="url", required=True) %}
-<p><strong>URL</strong> is the fixed URL or location of the reference.</p>
-{% endcall %}
-{% call  textinput("description", "Description", "", description, required=True) %}
-<p><strong>Description</strong> is a descriptive title or the name of the reference.</p>
-{% endcall %}
+  {{ edit_reference(types, _type, url, description) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
 <a class="btn btn-danger" href="{{ url_for('.view') }}">Cancel</a>
--- a/farol/templates/document/view.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/document/view.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,7 +24,7 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import panel, label_value %}
+{% from "macros.j2" import panel, label_value, add_button, edit_button, delete_button %}
 {% block title %}Document{% endblock %}
 
 {% set active='document' %}
@@ -32,10 +32,10 @@
 {% block content %}
 <div class='clearfix page-header'>
   <h1>{{ cvrf._title }} <small>({{ cvrf._type }})</small></h1>
-  <a class="pull-right" href="{{ url_for('.edit_title') }}">edit title</a>
+  <div class="pull-right">{{ edit_button(url_for('.edit_title'), "edit title") }}</div>
 </div>
 {% call panel(heading="Publisher", title=3, collapsible=False) %}
-  <a class="pull-right" href="{{ url_for('.edit_publisher') }}">edit</a>
+  <div class="pull-right">{{ edit_button(url_for('.edit_publisher')) }}</div>
   <div class="form-horizontal">
     {% for label, value in [("Type", cvrf._publisher._type),
                             ("VendorID", cvrf._publisher._vendorid or ''),
@@ -46,7 +46,7 @@
   </div>
 {% endcall %}
 {% call panel(heading="Document Tracking", title=3, collapsible=False) %}
-  <a class="pull-right" href="{{ url_for('.edit_tracking') }}">edit</a>
+  <div class="pull-right">{{ edit_button(url_for('.edit_tracking')) }}</div>
   <div class="form-horizontal">
     {% for label, value in [("Identification", cvrf._tracking._identification),
                             ("Status", cvrf._tracking._status),
@@ -60,7 +60,10 @@
     {% for revision in cvrf._tracking._history %}
       <li class="list-group-item">
         <div class="form-horizontal">
-          <a class="pull-right" href="{{ url_for('.edit_revision', index=loop.index0) }}">edit revision</a>
+          <div class="pull-right">
+            {{ edit_button(url_for('.edit_revision', index=loop.index0)) }}
+            {{ delete_button(url_for('.del_revision', index=loop.index0)) }}
+          </div>
           {{ label_value('Number', revision._number | join('.'), right=8) }}
           {{ label_value('Date', revision._date) }}
           {{ label_value('Description', revision._description) }}
@@ -68,7 +71,7 @@
       </li>
     {% endfor %}
     <li class="list-group-item clearfix">
-      <a class="pull-right" href="{{ url_for('.add_revision') }}">add revision</a>
+      <div class="pull-right">{{ add_button(url_for('.add_revision'), "add revision") }}</div>
     </li>
     </div>
   </ul>
@@ -88,12 +91,12 @@
   {% endif %}
 {% endcall %}
 {% call panel(heading="Distribution", title=3, collapsible=False) %}
-  <a class="pull-right" href="{{ url_for('.edit_distribution') }}">edit</a>
+  <div class="pull-right">{{ edit_button(url_for('.edit_distribution')) }}</div>
   <p>{{ (cvrf._distribution or '') | replace('\n', '<br>') }}</p>
 {% endcall %}
 {% call panel(heading="Aggregate Severity", title=3, collapsible=False) %}
   <div class="form-horizontal">
-  <a class="pull-right" href="{{ url_for('.edit_severity') }}">edit</a>
+  <div class="pull-right">{{ edit_button(url_for('.edit_severity')) }}</div>
   {% if cvrf._aggregateseverity %}
     {{ label_value('Namespace', (cvrf._aggregateseverity._namespace or '') | urlize, right=8) }}
     {{ label_value('Severity', cvrf._aggregateseverity._severity) }}
@@ -108,17 +111,23 @@
       <li><a href="{{ url_for('.view_note', ordinal=note._ordinal) }}">{{ note.getTitle() }}</a></li>
     {% endfor %}
     </ul>
-    <a class="pull-right" href="{{ url_for('.add_note') }}">add</a>
+    <div class="pull-right">{{ add_button(url_for('.add_note')) }}</div>
     {% endcall %}
   </div>
   <div class="col-sm-4">
     {% call panel(type="warning", heading="References", badge=cvrf._references | length, title=3) %}
     <ul>
     {% for reference in cvrf._references %}
-      <li><a href="{{ reference._url }}" target="_blank">{{ reference._description }}{% if reference._type %} ({{ reference._type }}){% endif %}</a> (<a href="{{ url_for('.edit_reference', index=loop.index0) }}">edit</a>)</li>
+      <li>
+        <a href="{{ reference._url }}" target="_blank">
+          {{ reference._description }}{% if reference._type %} ({{ reference._type }}){% endif %}
+        </a>
+        {{ edit_button(url_for('.edit_reference', index=loop.index0)) }}
+        {{ delete_button(url_for('.del_reference', index=loop.index0)) }}
+      </li>
     {% endfor %}
     </ul>
-    <a class="pull-right" href="{{ url_for('.add_reference') }}">add</a>
+    <div class="pull-right">{{ add_button(url_for('.add_reference')) }}</div>
     {% endcall %}
   </div>
   <div class="col-sm-4">
@@ -128,7 +137,7 @@
       <li><a href="{{ url_for('.view_acknowledgment', index=loop.index0) }}">{{ ack.getTitle() }}</a></li>
     {% endfor %}
     </ul>
-    <a class="pull-right" href="{{ url_for('.add_acknowledgment') }}">add</a>
+    <div class="pull-right">{{ add_button(url_for('.add_acknowledgment')) }}</div>
     {% endcall %}
   </div>
 </div>
@@ -136,58 +145,10 @@
 
 <div class="clearfix">
   <div class="col-md-6">
-    {% call panel(type="danger", heading="Product Tree", title=3) %}
+    {% call panel(type="danger", heading="Product Tree", title=3, collapsible=False) %}
       {% if cvrf._producttree %}
-        {% call panel(heading="Branches", badge=cvrf._producttree._branches | length, title=4, extended=True) %}
-          {% for branch in cvrf._producttree._branches recursive %}
-            {% call panel() %}
-              <p>{{ branch._type}}: <em>{{ branch._name }}</em> (<a href="{{ url_for('producttree.edit_branch', path=branch.getPath() | join('/')) }}">edit</a>)</p>
-              {% if branch._product %}
-                <strong><a href="{{ url_for('producttree.view_product', productid=branch._product._productid) }}">{{ branch._product._name }}</a></strong> (<a href="{{ url_for('producttree.edit_product', productid=branch._product._productid) }}">edit</a>)
-              {% else %}
-                {{ loop(branch._childs) }}
-              {% endif %}
-              {% if branch.isOrphaned() %}
-                <p class="text-danger">This branch is <em>orphaned</em>. A <a href="{{ url_for('producttree.add_product') }}">product</a> or a new <a href="{{ url_for('producttree.add_branch') }}">branch</a> should be created as child of this one.</p>
-              {% endif %}
-            {% endcall %}
-          {% endfor %}
-          <a href="{{ url_for('producttree.add_branch') }}" class="pull-right">add branch</a>
-        {% endcall %}
-        {% call panel(heading="Products", badge=cvrf._producttree.nbProducts(), title=4) %}
-          {% for product in cvrf._producttree._products if product._parent is sameas cvrf._producttree %}
-            <p><strong><a href="{{ url_for('producttree.view_product', productid=product._productid) }}">{{ product._name }}</a></strong> (<a href="{{ url_for('producttree.edit_product', productid=product._productid) }}">edit</a>)</p>
-          {% endfor %}
-          <a href="{{ url_for('producttree.add_product') }}" class="pull-right">add product</a>
-        {% endcall %}
-        {% call panel(heading="Relationships", badge=cvrf._producttree._relationships | length, title=4) %}
-          {% for relationship in cvrf._producttree._relationships %}
-            {% call panel() %}
-              <p><em>{{ cvrf.getProductForID(relationship._productreference)._name }}</em> as {{ relationship._relationtype | lower }} <em>{{ cvrf.getProductForID(relationship._relatestoproductreference)._name }}</em> (<a href="{{ url_for('producttree.edit_relationship', index=loop.index0) }}">edit</a>)</p>
-              {% if relationship._product %}<p><strong><a href="{{ url_for('producttree.view_product', productid=relationship._product._productid) }}">{{ relationship._product._name }}</a></strong> (<a href="{{ url_for('producttree.edit_product', productid=relationship._product._productid) }}">edit</a>)</p>{% endif %}
-              {% if relationship.isOrphaned() %}
-                <p class="text-danger">This relationship is <em>orphaned</em>. A product should be <a href="{{ url_for('producttree.add_product') }}">created</a> as child of this one.</p>
-              {% endif %}
-            {% endcall %}
-          {% endfor %}
-          <a href="{{ url_for('producttree.add_relationship') }}" class="pull-right">add</a>
-        {% endcall %}
-        {% call panel(heading="Groups", badge=cvrf._producttree._groups | length, title=4) %}
-          {% for group in cvrf._producttree._groups %}
-            {% call panel() %}
-              <a href="{{ url_for('producttree.edit_group', groupid=group._groupid) }}" class="pull-right">edit</a>
-              <ul>
-                {% for productid in group._productids %}
-                  {% with product = cvrf.getProductForID(productid) %}
-                    <li><a href="{{ url_for('producttree.view_product', productid=product._productid) }}">{{ product._name }}</a></li>
-                  {% endwith %}
-                {% endfor %}
-              </ul>
-              {% if group._description %}<p class="small">{{ group._description }}</p>{% endif %}
-            {% endcall %}
-          {% endfor %}
-          <a href="{{ url_for('producttree.add_group') }}" class="pull-right">add</a>
-        {% endcall %}
+        <p>{{ cvrf._producttree._products | length }} Products defined</p>
+        <a href="{{ url_for('producttree.view') }}">View</a>
       {% else %}
         <em>No Product tree present</em>
         <form action="{{ url_for('producttree.create') }}" method="POST" name="createProdTree">
@@ -204,7 +165,7 @@
         <li><a href="{{ url_for('vulnerability.view', ordinal=vulnerability._ordinal) }}">{{ vulnerability.getTitle() }}</a></li>
       {% endfor %}
     </ul>
-    <a href="{{ url_for('vulnerability.add') }}" class="pull-right">add</a>
+    <div class="pull-right">{{ add_button(url_for('vulnerability.add')) }}</div>
     {% endcall %}
   </div>
 </div>
--- a/farol/templates/document/view_acknowledgment.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/document/view_acknowledgment.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,16 +24,17 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import label_value %}
+{% from "macros.j2" import label_value, edit_button, delete_button %}
 {% block title %}{{ acknowledgment.getTitle() }}{% endblock %}
 
 {% block content %}
-<a class="pull-right" href="{{ url_for('.edit_acknowledgment', index=index) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_acknowledgment', index=index)) }}</div>
 <div class='page-header'>
-  <h1>{{ acknowledgment._name }} <small>{{ acknowledgment._organization }}</small></h1>
+  <h1>{{ acknowledgment._names | join(', ') }} <small>{{ acknowledgment._organizations | join(', ') }}</small></h1>
 </div>
 <div class="form-horizontal">
   {{ label_value('Description', acknowledgment._description or '') }}
   {{ label_value('URL', (acknowledgment._url or '') | urlize) }}
 </div>
+<div class="pull-right">{{ delete_button(url_for('.del_acknowledgment', index=index)) }}</div>
 {% endblock %}
--- a/farol/templates/document/view_note.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/document/view_note.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,14 +24,15 @@
 -#}
 
 {% extends "base.j2" %}
-
+{% from "macros.j2" import edit_button, delete_button %}
 {% block title %}{{ note._title }}{% endblock %}
 
 {% block content %}
-<a class="pull-right" href="{{ url_for('.edit_note', ordinal=note._ordinal) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_note', ordinal=note._ordinal)) }}</div>
 <div class='page-header'>
   <h1>{{ note._title or '' }} <small>{{ note._type }} (#{{ note._ordinal }})</small></h1>
 </div>
 {% if note._audience %}<p>Audience: <em>{{ note._audience }}</em></p>{% endif %}
 <p>{{ note._note | replace('\n', '<br>') }}</p>
+<div class="pull-right">{{ delete_button(url_for('.del_note', ordinal=note._ordinal)) }}</div>
 {% endblock %}
--- a/farol/templates/macros.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/macros.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -56,26 +56,6 @@
 </div>
 {% endmacro %}
 
-{% macro modal(id, title, size=None) %}
-<div class="modal fade" id="{{ id }}" tabindex="-1" role="dialog" aria-labelledby="{{ id }}_title" aria-hidden="true">
-  <div class="modal-dialog{{ ' modal-%s' % size if size}}">
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
-        <h4 class="modal-title" id="{{ id }}_title">{{ title }}</h4>
-      </div>
-      <div class="modal-body">
-        {{ caller() }}
-      </div>{# We don't need a footer yet ...
-      <div class="modal-footer">
-        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-        <button type="button" class="btn btn-primary">Save changes</button>
-      </div>#}
-    </div>{# /.modal-content #}
-  </div>{# /.modal-dialog #}
-</div>{# /.modal #}
-{% endmacro %}
-
 {% macro textarea(name, label, placeholder="", value=None, rows=10, required=False) %}
 <div class="form-group">
   {% if caller %}
@@ -132,7 +112,8 @@
   <div class="panel-heading">
     {%- if title %}<h{{ title }} class="panel-title">{% endif -%}
     {%- if collapsible %}<a data-toggle="collapse" href="#{{ heading | secure_filename }}">{% endif -%}
-    {{ heading }}{% if badge is not none %}<span class="pull-right badge">{{ badge }}</span>{% endif %}
+    {{ heading }}
+    {%- if badge is not none %}<span class="pull-right badge">{{ badge }}</span>{% endif %}
     {%- if collapsible %}</a>{% endif -%}
     {%- if title %}</h{{ title }}>{% endif -%}
   </div>
@@ -145,6 +126,26 @@
 </div>
 {% endmacro %}
 
+{% macro modal(id, title, size=None) %}
+<div class="modal fade" id="{{ id }}" tabindex="-1" role="dialog" aria-labelledby="{{ id }}_title" aria-hidden="true">
+  <div class="modal-dialog{{ ' modal-%s' % size if size}}">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
+        <h4 class="modal-title" id="{{ id }}_title">{{ title }}</h4>
+      </div>
+      <div class="modal-body">
+        {{ caller() }}
+      </div>{# We don't need a footer yet ...
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+        <button type="button" class="btn btn-primary">Save changes</button>
+      </div>#}
+    </div>{# /.modal-content #}
+  </div>{# /.modal-dialog #}
+</div>{# /.modal #}
+{% endmacro %}
+
 {# This should be placed inside a .form-horizontal #}
 {% macro label_value(label, value, left=2, right=10) %}
 <div class="form-group">
@@ -155,16 +156,8 @@
 </div>
 {% endmacro %}
 
-{% macro examples(list) %}
-{% if False %}
-<dl>
-  <dt>Example{{ 's' if list | length > 1 }}:</dt>
-  {% for elem in list %}
-  <dd><samp>{{ elem }}</samp></dd>
-  {% endfor %}
-</dl>
-{% else %}
-<p><strong>Example{{ 's' if list | length > 1 }}:</strong></p>
+{% macro examples(list, title='') %}
+<p><strong>{{ title + ' ' if title }}Example{{ 's' if list | length > 1 }}:</strong></p>
 {% if list | length == 1 %}
 <samp>{{ list[0] }}</samp>
 {% else %}
@@ -174,5 +167,32 @@
   {%- endfor %}
 </ul>
 {% endif %}
+{% endmacro %}
+
+{% macro POST_button(url, text, fields, out=False, style='btn-default') %}
+{% if caller and out %}
+  {{ caller('$(this).next()') }}
 {% endif %}
+<form class="inlined-form" action="{{ url }}" method="POST"{{ ' style="display:none"' if out}}>
+  {% for elem in fields %}
+  <input type=hidden name="{{ elem }}", value="{{ fields[elem] }}">
+  {% endfor %}
+  {% if caller %}
+    {{ caller('parentNode') if not out }}
+  {% else %}
+  <button type="submit" class="btn {{ style }}">{{ text }}</button>
+  {% endif %}
+</form>
 {% endmacro %}
+
+{% macro edit_button(url, text="edit") -%}
+  <a href="{{ url }}" class="btn btn-default btn-xs" role="btn">{{ text }}</a>
+{%- endmacro %}
+
+{% macro add_button(url, text="add") -%}
+  <a href="{{ url }}" class="btn btn-success btn-xs" role="btn">{{ text }}</a>
+{%- endmacro %}
+
+{% macro delete_button(url, hiddens={}, text="delete") -%}
+  {{ POST_button(url, text, hiddens, style="btn-danger btn-xs") }}
+{%- endmacro %}
--- a/farol/templates/new.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/new.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -171,7 +171,15 @@
 {% if caching %}
   {% call panel(heading="Load a document from the cache", title=4, collapsible=False) %}
   <ul>
-    {% for element in cache | sort %}<li><a href="{{ url_for('cache.load', element=element)}}">{{ element }}</a></li>{% endfor %}
+    {% for element in cache | sort %}
+      <li>
+        {% if has_current %}
+          <a href="#{{element}}_modal" data-toggle="modal">{{ element }}</a>
+        {% else %}
+          {{ POST_button(url_for('cache.load', element=element), text=element, style="btn-link") }}
+        {% endif %}
+      </li>
+    {% endfor %}
   </ul>
   {% endcall %}
 {% endif %}
--- a/farol/templates/producttree/edit_branch.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/producttree/edit_branch.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -30,11 +30,27 @@
 {% block content %}
 <form role="form" method="POST">
 
-{{ selectinput('type', "Type", types, branch._type) }}
-{{ textinput('name', "Name", value=branch._name, required=True) }}
-{{ selectinput2('parent', "Path to Parent Branch", branches, branch.getParentPath()) }}
+{% call selectinput('type', "Type", types, _type) %}
+<p>Branch <em>Types</em>:</p>
+<dl class="dl-horizontal">
+  <dt>Vendor:</dt><dd>The name of the vendor or manufacturer that makes the product</dd>
+  <dt>Product Family:</dt><dd>The product family that the product falls into</dd>
+  <dt>Product Name:</dt><dd>The name of the product</dd>
+  <dt>Product Version:</dt><dd>The product version, can be numeric or some other descriptor</dd>
+  <dt>Patch Level:</dt><dd>The patch level of the product</dd>
+  <dt>Service Pack:</dt><dd>The service pack of the product</dd>
+  <dt>Architecture:</dt><dd>The architecture for which the product is intended</dd>
+  <dt>Language:</dt><dd>The language of the product</dd>
+  <dt>Legacy:</dt><dd>A nonspecific legacy entry</dd>
+  <dt>Specification:</dt><dd>A specification such as a standard, best common practice, etc.</dd>
+</dl>
+{% endcall %}
+{% call textinput('name', "Name", value=name, required=True) %}
+<p><em>Name</em> will contain the canonical descriptor or “friendly name” of the branch.</p>
+{% endcall %}
+{{ selectinput2('parent', "Path to Parent Branch", branches, parentpath) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
-<a class="btn btn-danger" href="{{ url_for('document.view') }}">Cancel</a>
+<a class="btn btn-danger" href="{{ url_for('.view') }}">Cancel</a>
 </form>
 {% endblock %}
--- a/farol/templates/producttree/edit_group.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/producttree/edit_group.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,17 +24,28 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, textarea, selectinput2 %}
+{% from "macros.j2" import textinput, selectinput2, examples %}
 {% block title %}Edit the product{% endblock %}
 
 {% block content %}
+<p>If groups are defined, products can be referred to using the Group ID attribute in many other parts of the document, rather than repeatedly having to list all members individually.</p>
+<p>Whether groups are defined or not, the ability to reference each product individually in other parts of the document is not affected. In fact, the creator of a document can choose to use either direct product references or group references.</p>
+<p><strong>Note:</strong></p>
+<p>Given that a single product can be a member of more than one group, some areas of the CVRF document may not allow product references by group to avoid ambiguity.</p>
+<p>Each <strong>Group</strong> container defines a new logical group of products that can then be referred to in other parts of the document to address a group of products with a single identifier. <strong>Group</strong> members are defined by adding one <strong>Product ID</strong> element for each member of the group.</p>
 <form role="form" method="POST">
 
-{{ textinput('groupid', "Group ID", value=groupid, required=True) }}
-{{ textarea('description', "Description", '', description, 3) }}
+{% call textinput('groupid', "Group ID", value=groupid, required=True) %}
+<p>The <em>Group ID</em> attribute is required to identify a <strong>Group</strong> so that it can be referred to from other parts in the document. There is no predefined or required format for the <em>Group ID</em> as long as it uniquely identifies a group in the context of the current document. Examples include incremental integers or GUIDs.</p>
+{{ examples(['CVRFGID-0001', 'GRP-0001']) }}
+{% endcall %}
+{% call textinput('description', "Description", '', description) %}
+<p><strong>Description</strong> is a short, optional description of the group.</p>
+{{ examples(['The x64 versions of the operating system.']) }}
+{% endcall %}
 {{ selectinput2('products', "Products", products, productids, multiple=True) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
-<a class="btn btn-danger" href="{{ url_for('document.view') }}">Cancel</a>
+<a class="btn btn-danger" href="{{ url_for('.view') }}">Cancel</a>
 </form>
 {% endblock %}
--- a/farol/templates/producttree/edit_product.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/producttree/edit_product.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,22 +24,30 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, selectinput2 %}
+{% from "macros.j2" import textinput, selectinput2, examples %}
 {% block title %}Edit the product{% endblock %}
 
 {% set active = 'product' %}
 
 {% block content %}
-<form role="form" method="POST">
+<p>The <strong>Full Product Name</strong> elements define the endpoints of the <strong>Product Tree</strong> and occur directly at the root level, at the branch level, or as the result of a relationship between two products.</p><form role="form" method="POST">
 
-{{ textinput('name', "Product Name", value=product._name, required=True) }}
-{{ textinput('productid', "ProductID", placeholder="1234", value=product._productid, required=True) }}
-{{ textinput('cpe', "CPE", placeholder="cpe:/a:...", value=product._cpe) }}
+{% call textinput('name', "Product Name", value=product._name, required=True) %}
+<p>The value of a <strong>Full Product Name</strong> element should be the product’s full canonical name, including version number and other attributes, as it would be used in a human-friendly document.</p>
+{{ examples(['Microsoft Host Integration Server 2006 Service Pack 1', 'Microsoft Office 2008 for Mac 12.3.1 Update']) }}
+{% endcall %}
+{% call textinput('productid', "ProductID", placeholder="1234", value=product._productid, required=True) %}
+<p>The <em>Product ID</em> attribute is required to identify a <strong>Full Product Name</strong> so that it can be referred to from other parts in the document. There is no predefined or required format for the <em>Product ID</em> as long as it uniquely identifies a product in the context of the current document. Examples include incremental integers or Globally Unique Identifiers (GUIDs).</p>
+{{ examples(['CVRFPID-0004']) }}
+{% endcall %}
+{% call textinput('cpe', "CPE", placeholder="cpe:/a:...", value=product._cpe) %}
+<p>The Common Platform Enumeration (<em>CPE</em>) attribute refers to a method for naming platforms. The structure for CPE is described at http://cpe.mitre.org. The <em>CPE</em> can be either an integer (if MITRE has an entry for the platform in question) or a candidate string from the vendor if no MITRE entry yet exists.</p>
+{% endcall %}
 <hr>
 {{ selectinput2('parent_branch', "Parent Branch", orphaned_leaves , product.getParentPath()) }}
 {{ selectinput2('parent_relationship', "Parent relationship", orphaned_relationships, current_rel) }}
 
 <button class="btn btn-primary" type="submit">{{ action }}</button>
-<a class="btn btn-danger" href="{{ url_for('document.view') }}">Cancel</a>
+<a class="btn btn-danger" href="{{ url_for('.view') }}">Cancel</a>
 </form>
 {% endblock %}
--- a/farol/templates/producttree/edit_relationship.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/producttree/edit_relationship.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -28,13 +28,25 @@
 {% block title %}Edit the product{% endblock %}
 
 {% block content %}
+<p>The <strong>Relationship</strong element establishes a link between two existing <strong>Full Product Name</strong> elements, allowing the document producer to define a combination of two products that form a new <strong>Full Product Name</strong> entry.</p>
+<p>This situation arises when a product is vulnerable only when installed together with another, or to describe operating system components. As a <strong>Relationship</strong> connects two existing products with each other, there need to be at least two <strong>Full Product Name</strong> entries present in the <strong>Product Tree</strong> before a Relationship element can be created.</p>
+<p><strong>Relationship</strong> elements live at the root of a <strong>Product Tree</strong>, and they have three mandatory attributes: Product Reference and Relates To Product Reference each contain the Product ID token for the two products that will form the relationship, and the Type attribute defines how the products are related.</p>
 <form role="form" method="POST">
 
 {{ selectinput2('productreference', "ProductReference", products, productreference) }}
-{{ selectinput('relationtype', "Relation Type", types, relationtype) }}
+{% call selectinput('relationtype', "Relation Type", types, relationtype) %}
+<p>Consider two previously constructed products with <em>Product IDs</em> CVRFPID-0001 and CVRFPID-0002. CVRF v1.1 supports the following <strong>Relationship</strong> <em>Type</em> values:</p>
+<dl class="dl-horizontal">
+  <dt>default component of:</dt><dd>CVRFPID-0001 is a default component of CVRFPID-0002</dd>
+  <dt>optional component of:</dt><dd>CVRFPID-0001 is an optional component of CVRFPID-0002</dd>
+  <dt>external component of:</dt><dd>CVRFPID-0001 is an external component of CVRFPID-0002</dd>
+  <dt>installed on:</dt><dd>CVRFPID-0001 is installed on CVRFPID-0002</dd>
+  <dt>installed with:</dt><dd>CVRFPID-0001 is installed with CVRFPID-0002</dd>
+</dl>
+{% endcall %}
 {{ selectinput2('relatestoproductreference', "Relates to Product Reference", products, relatestoproductreference) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
-<a class="btn btn-danger" href="{{ url_for('document.view') }}">Cancel</a>
+<a class="btn btn-danger" href="{{ url_for('.view') }}">Cancel</a>
 </form>
 {% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farol/templates/producttree/view.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -0,0 +1,118 @@
+{#
+# Description:
+# Web Template used in Farol Design
+#
+# Authors:
+# Benoît Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+-#}
+
+{% extends "base.j2" %}
+{% from "macros.j2" import panel, modal, add_button, edit_button, delete_button %}
+{% block title %}Product Tree{% endblock %}
+
+{% set active = 'product' %}
+
+{% block content %}
+<div class='page-header'>
+  <h1>Document's Product Tree<a href="#prodtree_modal" data-toggle="modal">?</a> <small>{{ producttree._products | length }} Products defined</small></h1>
+</div>
+{% call modal('prodtree_modal', "Product Tree") %}
+<p>The <strong>Product Tree</strong> container contains all the fully qualified product names that can be referenced elsewhere in the document (specifically when describing the products that are affected by a vulnerability using the <strong>Product Statuses</strong>, <strong>Threats</strong>, <strong>CVSS Score Sets</strong>, and <strong>Remediation</strong> containers). The <strong>Product Tree</strong> can have as many branches as needed, but each endpoint of the tree must be terminated with a <strong>Full Product Name</strong> element, which represents a product that can be referenced elsewhere.</p>
+<p>The <strong>Product Tree</strong> can be kept simple (flat) or made more detailed (branched out). It also supports concatenating products to describe relationships, such as components contained in a product or products installed on other products.</p>
+<dl>
+  <dt>Flat:</dt>
+  <dd><p>In the simplest case, a flat <strong>Product TreeM</strong> would contain one or more <strong>Full Product Name</strong> elements at the root level, one for each product that needs to be described.</p></dd>
+  <dt>Branched:</dt>
+  <dd><p>In a more detailed <strong>Product Tree</strong>, the root element would contain one or more <strong>Branch</strong> elements at the root level, one for each class/type/category of product, each of which again contains one or more <strong>Branch</strong> elements until all desired categories and subcategories are described to the satisfaction of the document issuer. Then each open <strong>Branch</strong> element is terminated with the actual product item in the form of a <strong>Full Product Name</strong> element.</p></dd>
+  <dt>Concatenated:</dt>
+  <dd><p>No matter whether a flat or branched structure is chosen, you may need to be able to describe the combination of two <strong>Full Product Name</strong> elements, such as when a product is only vulnerable when installed together with another, or to describe operating system components. To do that, a <strong>Relationship</strong> element is inserted at the root of the <strong>Product Tree</strong>, with attributes establishing a link between two existing <strong>Full Product Name</strong> elements, allowing the document producer to define a combination of two products that form a new <strong>Full Product Name</strong> entry.</p></dd>
+  <dt>Grouped:</dt>
+  <dd><p>Once <strong>Full Product Name</strong> elements are defined, they may be freely added to logical groups, which may then be used to refer to a group of products. Given that it is possible for a product to be a member of more than one logical group, some areas of the CVRF document may not allow references to product groups to avoid ambiguity.</p></dd>
+</dl>
+{% endcall %}
+{% call panel(heading="Branches", badge=producttree._branches | length, title=4, extended=True) %}
+  {% for branch in producttree._branches recursive %}
+    {% call panel() %}
+      <div>
+        {{ branch._type}}: <em>{{ branch._name }}</em>
+        {{ edit_button(url_for('.edit_branch', path=branch.getPath() | join('/'))) }}
+        {% if branch.isOrphaned() %}
+          {{ delete_button(url_for('.del_branch', path=branch.getPath() | join('/'))) }}
+        {% endif %}
+      </div>
+      {% if branch._product %}
+        <p><strong><a href="{{ url_for('.view_product', productid=branch._product._productid) }}">{{ branch._product._name }}</a></strong></p>
+      {% else %}
+        {{ loop(branch._childs) }}
+      {% endif %}
+      {% if branch.isOrphaned() %}
+        <p class="text-danger">This branch is <em>orphaned</em>. A <a href="{{ url_for('.add_product') }}">product</a> or a new <a href="{{ url_for('.add_branch') }}">branch</a> should be created as child of this one.</p>
+      {% endif %}
+    {% endcall %}
+  {% endfor %}
+  <div class="pull-right">{{ add_button(url_for('.add_branch')) }}</div>
+{% endcall %}
+{% call panel(heading="Products", badge=producttree.nbProducts(), title=4) %}
+  {% for product in producttree._products if product._parent is sameas producttree %}
+    <p><strong><a href="{{ url_for('.view_product', productid=product._productid) }}">{{ product._name }}</a></strong></p>
+  {% endfor %}
+  <div class="pull-right">{{ add_button(url_for('.add_product')) }}</div>
+{% endcall %}
+{% call panel(heading="Relationships", badge=producttree._relationships | length, title=4) %}
+  {% for relationship in producttree._relationships %}
+    {% call panel() %}
+      <div>
+        <em>{{ cvrf.getProductForID(relationship._productreference)._name }}</em> as {{ relationship._relationtype | lower }} <em>{{ cvrf.getProductForID(relationship._relatestoproductreference)._name }}</em>
+        {{ edit_button(url_for('.edit_relationship', index=loop.index0)) }}
+        {% if relationship.isOrphaned() %}
+          {{ delete_button(url_for('.del_relationship', index=loop.index0)) }}
+        {% endif %}
+      </div>
+      {% if relationship._product %}
+        <p><strong><a href="{{ url_for('.view_product', productid=relationship._product._productid) }}">{{ relationship._product._name }}</a></strong></p>
+      {% endif %}
+      {% if relationship.isOrphaned() %}
+        <p class="text-danger">This relationship is <em>orphaned</em>. A product should be <a href="{{ url_for('.add_product') }}">created</a> as child of this one.</p>
+      {% endif %}
+    {% endcall %}
+  {% endfor %}
+  <div class="pull-right">{{ add_button(url_for('.add_relationship')) }}</div>
+{% endcall %}
+{% call panel(heading="Groups", badge=producttree._groups | length, title=4) %}
+  {% for group in producttree._groups %}
+    {% call panel() %}
+      <div class="pull-right">
+        {{ edit_button(url_for('.edit_group', groupid=group._groupid)) }}
+        {{ delete_button(url_for('.del_group', groupid=group._groupid)) }}
+      </div>
+      <ul>
+        {% for productid in group._productids %}
+          {% with product = cvrf.getProductForID(productid) %}
+            <li><a href="{{ url_for('.view_product', productid=product._productid) }}">{{ product._name }}</a></li>
+          {% endwith %}
+        {% endfor %}
+      </ul>
+      {% if group._description %}<p class="small">{{ group._description }}</p>{% endif %}
+    {% endcall %}
+  {% endfor %}
+  <div class="pull-right">{{ add_button(url_for('.add_group')) }}</div>
+{% endcall %}
+<div class="pull-right">{{ delete_button(url_for('.delete'), text="delete whole Product Tree") }}</div>
+{% endblock %}
--- a/farol/templates/producttree/view_product.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/producttree/view_product.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,24 +24,23 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import panel %}
+{% from "macros.j2" import panel, edit_button, delete_button %}
 {% block title %}{{ product._name }}{% endblock %}
 
 {% set active = 'product' %}
 
 {% block content %}
-<a class="pull-right" href="{{ url_for('.edit_product', productid=product._productid) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_product', productid=product._productid)) }}</div>
 <div class='page-header'>
   <h1>{{ product._name }}{% if product._cpe %} <small>{{ product._cpe }}</small>{% endif %}</h1>
 </div>
-{% if product.isRoot() %}
-  <p>This product is defined without relation to other elements</p>
-{% elif product.isRelationship() %}
-  <p>This product is part of the following relationship:</p>
+
+{% if product.isRelationship() %}
+  <p>This product is defined by the following relationship:</p>
   {% set prodref = cvrf.getProductForID(product._parent._productreference) %}
   {% set relatesto = cvrf.getProductForID(product._parent._relatestoproductreference) %}
   <p><a href="{{ url_for('.view_product', productid=prodref._productid) }}">{{ prodref._name }}</a> as <em>{{ product._parent._relationtype | lower }}</em> <a href="{{ url_for('.view_product', productid=relatesto._productid) }}">{{ relatesto._name }}</a><p>
-{% else %}
+{% elif not product.isRoot() %}
   {% call panel(heading="Tree", title=3, collapsible=False) %}
   {% for type, name in product.getTree() %}
     <ul><li>{{ type }}: <em>{{ name }}</em></li>
@@ -49,12 +48,60 @@
   {% endfor %}
   {% endcall %}
 {% endif %}
-<div>
-<p>This product belong to the following groups:</p>
-<ul>
-{% for group in cvrf._producttree._groups if product._productid in group._productids %}
-  <li>{{ group.getTitle() }}</li>
-{% endfor %}
-</ul>
+
+{% call panel(heading="Relationships", title=3, collapsible=False) %}
+  <p>This product is part of the following relationships:</p>
+  <ul>
+  {% for relationship in cvrf._producttree._relationships if product._productid in (relationship._productreference, relationship._relatestoproductreference) %}
+    {% set prodref2 = cvrf.getProductForID(relationship._productreference) %}
+    {% set relatesto2 = cvrf.getProductForID(relationship._relatestoproductreference) %}
+    <li><a href="{{ url_for('.view_product', productid=prodref2._productid) }}">{{ prodref2._name }}</a> as <em>{{ relationship._relationtype | lower }}</em> <a href="{{ url_for('.view_product', productid=relatesto2._productid) }}">{{ relatesto2._name }}</a></li>
+  {% else %}
+    <li><em>None</em></li>
+  {% endfor %}
+  </ul>
+{% endcall %}
+
+{% call panel(heading="Groups", title=3, collapsible=False) %}
+  <p>This product belong to the following groups:</p>
+  <ul>
+  {% for group in groups %}
+    <li>{{ group.getTitle() }}</li>
+  {% else %}
+    <li><em>None</em></li>
+  {% endfor %}
+  </ul>
+{% endcall %}
+
+{% call panel(heading="Vulnerabilities", title="3", collapsible=False) %}
+  <p>The following Vulneralibities are mentionning this product:</p>
+  <ul>
+    {% for vulnerability in cvrf._vulnerabilities if (
+               vulnerability.isMentioningProdId(product._productid) or
+               vulnerability.isMentioningGroupId(groups | map(attribute="_groupid") | list)) %}
+      <li>
+        <a href="{{ url_for('vulnerability.view', ordinal=vulnerability._ordinal) }}">{{ vulnerability.getTitle() }}</a>
+        {% set elements = vulnerability.mentionsProdId(product._productid) | list %}
+        {% for group in groups %}
+          {{ elements.extend(vulnerability.mentionsGroupId(group._groupid) | list) or '' }}
+        {% endfor %}
+        {% set comma = joiner(', ') %}
+        (
+        {%- for grouper, list in elements | groupby('NAME') %}{{ comma() -}}
+          {{ grouper }}{% if list | length > 1 %}(x{{ list | length }}){% endif %}
+        {%- endfor -%}
+        )
+      </li>
+    {% else %}
+      <li><em>None</em></li>
+    {% endfor %}
+  </ul>
+{% endcall %}
+<div class="pull-right">
+  {% if cvrf.isProductOrphan(product._productid) %}
+    {{ delete_button(url_for('.del_product', productid=product._productid)) }}
+  {% else %}
+    <p class="text-danger"><small>This product cannot be deleted, as it is referenced in the document</small></p>
+  {% endif %}
 </div>
 {% endblock %}
--- a/farol/templates/vulnerability/edit.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,24 +24,43 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, panel %}
+{% from "macros.j2" import textinput, panel, examples %}
 {% block title %}{{ vulnerability._title }}{% endblock %}
 
 {% set active="vulnerability" %}
 
 {% block content %}
+<p><strong>Vulnerability</strong> is a container for the aggregation of all fields that are related to a single vulnerability in the document</p>
 <form role="form" method="POST">
-  {{ textinput('ordinal', "Ordinal", value=vulnerability._ordinal, required=True, type="number", extras={'min': '1'}) }}
-  {{ textinput('title', "Title", value=vulnerability._title or '') }}
+  {% call textinput('ordinal', "Ordinal", value=vulnerability._ordinal, required=True, type="number", extras={'min': '1'}) %}
+  <p><em>Ordinal</em> is a locally significant value used to track vulnerabilities inside a CVRF document. It is provided to enable specific vulnerabilities to be referenced from elsewhere in the document (or even outside the namespace of a document provided that a unique <strong>Document Title</strong> and Revision information are provided). There should be one of these values for every <strong>Vulnerability</strong> container in a document, and it is recommended that <em>Ordinal</em> should be instantiated as a monotonically increasing counter, indexed from 1.</p>
+  {% endcall %}
+  {% call textinput('title', "Title", value=vulnerability._title or '') %}
+  <p><strong>Title</strong> gives the document producer the ability to apply a canonical name or title to the vulnerability. To avoid confusion, it is recommended that, if employed, this element commensurately match the nomenclature used by any numbering or cataloging systems references elsewhere, such as the <strong>Document Title</strong> or <strong>CVE</strong>.</p>
+  {{ examples(['February 2011 TelePresence Vulnerability Bundle']) }}
+  {% endcall %}
   {% call panel(heading="ID", title=4, collapsible=False) %}
+    <p>ID gives the document producer a place to publish a unique label or tracking ID for the vulnerability (if such information exists).</p>
+    <p>General examples may include an identifier from a vulnerability tracking system that is available to customers, such as a Cisco bug ID, an ID from a Bugzilla system, or an ID from a public vulnerability database such as the X-Force Database. The <strong>ID</strong> may be a vendor-specific value.</p>
     {% with id = vulnerability._id %}
-    {{ textinput('systemname', "System Name", value= id and (id._systemname or '') or '') }}
+    {% call textinput('systemname', "System Name", value= id and (id._systemname or '') or '') %}
+    <p>The attribute <em>System Name</em> indicates the name of the vulnerability tracking or numbering system that this <strong>ID</strong> comes from. Every <strong>ID</strong> value should have exactly one <em>System Name</em>. It is helpful if document producers use unique and consistent system names.</p>
+    {{ examples(['Cisco Bug ID']) }}
+    {% endcall %}
     {{ textinput('id_value', "Value", value= id and (id._value or '') or '') }}
     {% endwith %}
   {% endcall %}
-  {{ textinput('discoverydate', "Discovery Date", now.isoformat(), vulnerability._discoverydate and vulnerability._discoverydate.isoformat() or '', type="datetime") }}
-  {{ textinput('releasedate', "Release Date", now.isoformat(), vulnerability._releasedate and vulnerability._releasedate.isoformat() or '', type="datetime") }}
-  {{ textinput('cve', "CVE", 'CVE-xxxx-yyyy', vulnerability._cve) }}
+  {% call textinput('discoverydate', "Discovery Date", now.isoformat(), vulnerability._discoverydate and vulnerability._discoverydate.isoformat() or '', type="datetime") %}
+  <p>The <strong>Discovery Date</strong> is the date the vulnerability was originally discovered. All dateTime values in CVRF require a time, and we recommend the inclusion of a time zone as well (ICASI endorses the use of GMT or “Zulu time”). If a time zone is excluded, Zulu should be assumed.</p>
+  {% endcall %}
+  {% call textinput('releasedate', "Release Date", now.isoformat(), vulnerability._releasedate and vulnerability._releasedate.isoformat() or '', type="datetime") %}
+  <p>The <strong>Release Date</strong> is the date the vulnerability was originally released into the wild. All dateTime values in CVRF require a time, and we recommend the inclusion of a time zone as well (ICASI endorses the use of GMT or “Zulu time”). If a time zone is excluded, Zulu should be assumed.</p>
+  {% endcall %}
+  {% call textinput('cve', "CVE", 'CVE-xxxx-yyyy', vulnerability._cve) %}
+  <p><strong>CVE</strong> contains the MITRE standard Common Vulnerabilities and Exposures (CVE) tracking number for the vulnerability. CVE is a standard for vulnerability naming that provides improved tracking of vulnerabilities over time across different reporting sources. More information about CVE is available at {{ 'http://cve.mitre.org/' | urlize }}.</p>
+  {{ examples(['CVE-2006-0010']) }}
+  {% endcall %}
   <button type="submit" class="btn btn-primary">{{ action or 'Update' }}</button>
+  <a class="btn btn-danger" href="{% if action=='Add' %}{{ url_for('document.view') }}{% else %}{{ url_for('.view', ordinal=vulnerability._ordinal) }}{% endif %}">Cancel</a>
 </form>
 {% endblock %}
--- a/farol/templates/vulnerability/edit_acknowledgment.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_acknowledgment.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,18 +24,15 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, selectinput %}
-{% block title %}Edit the title{% endblock %}
+{% from "common_edits.j2" import edit_acknowledgment %}
+{% block title %}Edit an Acknowledgment{% endblock %}
 
 {% set active = 'vulnerability' %}
 
 {% block content %}
 <form role="form" method="POST">
 
-{{ textinput("name", "Name", "", name) }}
-{{ textinput("organization", "Organization", "", organization) }}
-{{ textinput("description", "Description", "", description) }}
-{{ textinput("url", "URL", "http://...", url, type="url") }}
+  {{ edit_acknowledgment(names, organizations, description, url) }}
 
 <button class="btn btn-primary" type="submit">{{ action }}</button>
 <a class="btn btn-danger" href="{{ url_for('.view', ordinal=ordinal) }}">Cancel</a>
--- a/farol/templates/vulnerability/edit_cvss.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_cvss.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,18 +24,29 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import selectinput, textinput, textarea, selectinput2 %}
+{% from "macros.j2" import selectinput, textinput, textarea, selectinput2, examples %}
 {% block title %}Edit CVSS{% endblock %}
 
 {% set active = 'vulnerability' %}
 
 {% block content %}
+<p>The <strong>Score Set</strong> container holds actual CVSS metrics. For more details about CVSS, see {{ 'http://www.first.org/cvss/cvss-guide.html' | urlize }}. The only required element of CVSS is the <strong>Base Score</strong>. If a value of the temporal or environmental score is set to “not defined,” either <strong>Temporal Score</strong> or <strong>Environmental Score</strong> can be omitted.</p>
+<p>A <strong>Score Set</strong> container can be tied to one or more specific products by referencing these products using the <strong>Product ID</strong> child element. If the <strong>Score Set</strong> is meant to be applied for all products, the <em>Product ID</em> attribute should be omitted.</p>
 <form role="form" method="POST">
 
-{{ textinput('basescore', "Base Score", '0.0', basescore, type="number", extras={'step':'0.1'}, required=True) }}
-{{ textinput('temporalscore', "Temporal Score", '0.0', temporalscore, type="number", extras={'step':'0.1'}) }}
-{{ textinput('environmentalscore', "Environmental Score", '0.0', environmentalscore, type="number", extras={'step':'0.1'}) }}
-{{ textinput('vector', "Vector", value=vector) }}
+{% call textinput('basescore', "Base Score", '0.0', basescore, type="number", extras={'step':'0.1', 'min': '0', 'max': '10'}, required=True) %}
+<p><strong>Base Score</strong> contains the numeric value of the computed CVSS base score, which should be a float from 0 to 10.0.</p>
+{% endcall %}
+{% call textinput('temporalscore', "Temporal Score", '0.0', temporalscore, type="number", extras={'step':'0.1', 'min': '0', 'max': '10'}) %}
+<p><strong>Temporal Score</strong> contains the numeric value of the computed CVSS temporal score, which should be a float from 0 to 10.0.</p>
+{% endcall %}
+{% call textinput('environmentalscore', "Environmental Score", '0.0', environmentalscore, type="number", extras={'step':'0.1', 'min': '0', 'max': '10'}) %}
+<p><strong>Environmental Score</strong> contains the numeric value of the computed CVSS environmental score, which should be a float from 0 to 10.0. This metric is typically reserved for use by the end user and is specific to the environment in which the affected product is deployed.</p>
+{% endcall %}
+{% call textinput('vector', "Vector", value=vector, extras={'maxlength': '76'}) %}
+<p><strong>Vector</strong> contains the official notation that displays all the values used to compute the CVSS base, temporal, and environmental scores. This notation will follow the guidelines set forth in the CVSS v2 documentation at {{ 'http://www.first.org/cvss/cvss-guide.html#i2.4' | urlize }}.</p>
+{{ examples(['AV:N/AC:L/Au:N/C:P/I:P/A:C/E:P/RL:O/RC:C/CDP:H/TD:M/CR:H/IR:H/AR:H']) }}
+{% endcall %}
 
 {{ selectinput2('products', "Products", products, productids, multiple=True) }}
 
--- a/farol/templates/vulnerability/edit_cwe.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_cwe.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -30,6 +30,15 @@
 {% set active = 'vulnerability' %}
 
 {% block content %}
+<p><strong>CWE</strong> contains the MITRE standard Common Weakness Enumeration (CWE). MITRE describes CWE in this way:</p>
+<blockquote><p>[CWE] is a formal list of software weakness types created to:</p>
+<ul>
+<li>Serve as a common language for describing software security weaknesses in architecture, design, or code.</li>
+<li>Serve as a standard measuring stick for software security tools targeting these weaknesses.</li>
+<li>Provide a common baseline standard for weakness identification, mitigation, and prevention efforts.</li>
+</ul>
+</blockquote>
+<p>More information about CWE is available at {{ 'http://cwe.mitre.org/' | urlize }}.</p>
 <form role="form" method="POST">
 
 {{ textinput("id", "CWE ID", "CWE-xxx", _id, required=True) }}
--- a/farol/templates/vulnerability/edit_involvement.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_involvement.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,17 +24,50 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, textarea, selectinput %}
+{% from "macros.j2" import textinput, textarea, selectinput, examples %}
 {% block title %}Edit Involvement{% endblock %}
 
 {% set active = 'vulnerability' %}
 
 {% block content %}
+<p>The <strong>Involvement</strong> container allows the document producers (or third party) to comment on their level of involvement in the vulnerability identification, scoping, and remediation process.</p>
 <form role="form" method="POST">
 
-{{ selectinput("party", "Party", parties, party) }}
-{{ selectinput("status", "Status", statuses, status) }}
-{{ textarea("description", "Description", "", description, 10) }}
+{% call selectinput("party", "Party", parties, party) %}
+<p>The attribute <em>Party</em> indicates the type of the producer issuing the status. It is identical to the <strong>Document Publisher</strong> attribute <em>Type</em>. Most of the time, both attributes will be the same because document producers will issue an <string>Involvement</strong> status on their own behalf. However, if the document producer wants to issue a status on behalf of a third party and use a different type from that used in <strong>Document Publisher</strong>, that use is allowed by the schema. If this is the case, <strong>Description</strong> should contain additional context regarding what is going on.</p>
+<dl class="dl-horizontal">
+  <dt>Vendor:</dt>
+  <dd>Developers or maintainers of information system products or services. This includes all authoritative product vendors, Product Security Incident Response Teams (PSIRTs), and product resellers and distributors, including authoritative vendor partners.</dd>
+  <dt>Discoverer:</dt>
+  <dd>Individuals or organizations that find vulnerabilities or security weaknesses. This includes all manner of researchers.</dd>
+  <dt>Coordinator:</dt>
+  <dd>Individuals or organizations that manage a single vendor’s response or multiple vendors’ responses to a vulnerability, a security flaw, or an incident. This includes all Computer Emergency/Incident Response Teams (CERTs/CIRTs) or agents acting on the behalf of a researcher.</dd>
+  <dt>User:</dt>
+  <dd>Everyone using a vendor’s product.</dd>
+  <dt>Other:</dt>
+  <dd>Catchall for everyone else. Currently this includes forwarders, republishers, language translators, and miscellaneous contributors.</dd>
+</dl>
+{% endcall %}
+
+{% call selectinput("status", "Status", statuses, status) %}
+<p>The attribute <em>Status</em> indicates the level of involvement of Party.</p>
+<p>The child <strong>Description</strong> (below) is an optional element used to give context about the involvement or engagement of the <em>Party</em>.</p>
+<p>The final two status states, <samp>Contact Attempted</samp> and <samp>Not Contacted</samp>, are intended for use by document producers other than vendors (such as research or coordinating entities).</p>
+<p>Status types include:</p>
+<dl class="dl-horizontal">
+  <dt>Open:</dt><dd>This is the default status. It doesn’t indicate anything about the vulnerability remediation effort other than the fact that the vendor has acknowledged awareness of the vulnerability report. The use of this status by a vendor indicates that future updates from the vendor about the vulnerability are to be expected.</dd>
+  <dt>Disputed:</dt><dd>This status indicates that the vendor disputes the vulnerability report in its entirety. Vendors should indicate this status when they believe that a vulnerability report regarding their product is completely inaccurate (that there is no real underlying security vulnerability) or that the technical issue being reported has no security implications.</dd>
+  <dt>In Progress:</dt><dd>This status indicates that some hotfixes, permanent fixes, mitigations, workarounds, or patches may have been made available by the vendor, but more information or fixes may be released in the future. The use of this status by a vendor indicates that future information from the vendor about the vulnerability is to be expected.</dd>
+  <dt>Completed:</dt><dd>The vendor asserts that investigation of the vulnerability is complete. No additional information, fixes, or documentation from the vendor about the vulnerability should be expected to be released.</dd>
+  <dt>Contact Attempted:</dt><dd>The document producer attempted to contact the affected vendor.</dd>
+  <dt>Not Contacted:</dt><dd>The document producer has not attempted to make contact with the affected vendor.</dd>
+</dl>
+<p>Each status is mutually exclusive—only one status is valid for a particular vulnerability at a particular time. As the vulnerability ages, a party’s involvement could move from state to state. However, in many cases, a document producer may choose not to issue CVRF documents at each state, or simply omit this element altogether. It is recommended, however, that vendors that issue CVRF documents indicating an open or in-progress <strong>Involvement</strong> should eventually expect to issue a document as Disputed or Completed.</p>
+{% endcall %}
+{% call textarea("description", "Description", "", description, 10) %}
+<p>The <strong>Description</strong> element will contain a thorough human-readable discussion of the <strong>Involvement</strong>.</p>
+{{ examples(['Cisco acknowledges that the IronPort Email Security Appliances (ESA) and Cisco IronPort Security Management Appliances (SMA) contain a vulnerability that may allow a remote, unauthenticated attacker to execute arbitrary code with elevated privileges. A Mitigation is available.', 'We emailed the vendor on February 14, 2012 when the vulnerability was first discovered by our team.']) }}
+{% endcall %}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
 <a class="btn btn-danger" href="{% if action=='Add' %}{{ url_for('.view', ordinal=ordinal) }}{% else %}{{ url_for('.view_involvement', ordinal=ordinal, index=index) }}{% endif %}">Cancel</a>
--- a/farol/templates/vulnerability/edit_note.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_note.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,7 +24,7 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, textarea, selectinput %}
+{% from "common_edits.j2" import edit_note %}
 {% block title %}Edit the type{% endblock %}
 
 {% set active = 'vulnerability' %}
@@ -32,11 +32,7 @@
 {% block content %}
 <form role="form" method="POST">
 
-{{ selectinput("type", "Type", types, note and note._type or '') }}
-{{ textinput("ordinal", "Ordinal", "", note_ordinal or note._ordinal, type="number", extras={'min': '1'}) }}
-{{ textinput("title", "Title", "", note and note._title or '') }}
-{{ textinput("audience", "Audience", "", note and note._audience or '') }}
-{{ textarea("note", "Note", "", note and note._note or '', 10, required=True) }}
+ {{ edit_note(types, note, note_ordinal) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
 <a class="btn btn-danger" href="{% if action=='Add' %}{{ url_for('.view', ordinal=ordinal) }}{% else %}{{ url_for('.view_note', ordinal=ordinal, note_ordinal=note._ordinal) }}{% endif %}">Cancel</a>
--- a/farol/templates/vulnerability/edit_productstatus.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_productstatus.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -30,9 +30,21 @@
 {% set active = 'vulnerability' %}
 
 {% block content %}
+<p>The <strong>Status</strong> element contains one or more products as chosen from the <strong>Product Tree</strong>, and defines the status of this product in the mandatory <em>Status</em> attribute.</p>
 <form role="form" method="POST">
 
-{{ selectinput('status', "Status", statuses, status) }}
+{% call selectinput('status', "Status", statuses, status) %}
+<p>The <em>Type</em> attribute is an enumerated value that contains all the possible permutations of fixed, affected, and recommended versions of the products referenced inside the <strong>Status</strong> container. <em>Type</em> values include:</p>
+<dl class="dl-horizontal">
+  <dt>First Affected:</dt><dd>This is first version of the affected release known to be affected by the vulnerability.</dd>
+  <dt>Known Affected:</dt><dd>This version is known to be affected by the vulnerability.</dd>
+  <dt>Known Not Affected:</dt><dd>This version is known not to be affected by the vulnerability.</dd>
+  <dt>First Fixed:</dt><dd>This version contains the first fix for the vulnerability but may not be the recommended fixed version.</dd>
+  <dt>Fixed:</dt><dd>This version contains a fix for the vulnerability but may not be the recommended fixed version.</dd>
+  <dt>Recommended:</dt><dd>This version has a fix for the vulnerability and is the vendor-recommended version for fixing the vulnerability.</dd>
+  <dt>Last Affected:</dt><dd>This is the last version in a release train known to be affected by the vulnerability. Subsequently released versions would contain a fix for the vulnerability.</dd>
+</dl>
+{% endcall %}
 {{ selectinput2('products', "Products", products, productids, multiple=True) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
--- a/farol/templates/vulnerability/edit_reference.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_reference.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,17 +24,14 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import textinput, selectinput %}
-{% block title %}Edit the title{% endblock %}
-
+{% block title %}Edit a Reference{% endblock %}
+{% from "common_edits.j2" import edit_reference %}
 {% set active = 'vulnerability' %}
 
 {% block content %}
 <form role="form" method="POST">
 
-{{ selectinput("type", "Type", types, _type) }}
-{{ textinput("url", "URL", "http://...", url, type="url") }}
-{{ textinput("description", "Description", "", description) }}
+  {{ edit_reference(types, _type, url, description) }}
 
 <button class="btn btn-primary" type="submit">{{ action or 'Update' }}</button>
 <a class="btn btn-danger" href="{{ url_for('.view', ordinal=ordinal) }}">Cancel</a>
--- a/farol/templates/vulnerability/edit_remediation.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_remediation.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,19 +24,40 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import selectinput, textinput, textarea, selectinput2 %}
+{% from "macros.j2" import selectinput, textinput, textarea, selectinput2, examples %}
 {% block title %}Edit Remediation{% endblock %}
 
 {% set active = 'vulnerability' %}
 
 {% block content %}
+<p>The <strong>Remediation</strong> container holds specific details on how to handle (and presumably, fix) a vulnerability.</p>
+<p>A <strong>Remediation</strong> container can be tied to one or more specific products by referencing these products using either the <strong>Product ID</strong> or <strong>Group ID</strong> child elements. If the <strong>Remediation</strong> is meant to be general or nonspecific for all products, the <strong>Product ID</strong> and <strong>Group ID</strong> child elements should be omitted.</p>
 <form role="form" method="POST">
 
-{{ selectinput('type', "Type", types, type) }}
-{{ textinput('date', "Date", now.isoformat(), date, type="datetime") }}
-{{ textarea('description', "Description", '', description, 5, required=True) }}
-{{ textarea('entitlement', "Entitlement", '', entitlement, 5) }}
-{{ textinput('url', "URL", 'https://...', url, type="url") }}
+{% call selectinput('type', "Type", types, type) %}
+<p>The <em>Type</em> attribute is required and can be one of the following:</p>
+<dl class="dl-horizontal">
+  <dt>Workaround:</dt><dd>Workaround contains information about a configuration or specific deployment scenario that can be used to avoid exposure to the vulnerability. There may be none, one, or more workarounds available. This is typically the “first line of defense” against a new vulnerability before a mitigation or vendor fix has been issued or even discovered.</dd>
+  <dt>Mitigation:</dt><dd>Mitigation contains information about a configuration or deployment scenario that helps to reduce the risk of the vulnerability but that does not resolve the vulnerability on the affected product. Mitigations may include using devices or access controls external to the affected product. Mitigations may or may not be issued by the original author of the affected product, and they may or may not be officially sanctioned by the document producer.</dd>
+  <dt>Vendor Fix:</dt><dd>Vendor Fix contains information about an official fix that is issued by the original author of the affected product. Unless otherwise noted, it is assumed that this fix fully resolves the vulnerability.</dd>
+  <dt>None Available:</dt><dd>Currently there is no fix available. Description should contain details about why there is no fix.</dd>
+  <dt>Will Not Fix:</dt><dd>There is no fix for the vulnerability and there never will be one. This is often the case when a product has been orphaned, end-of-lifed, or otherwise deprecated. Description should contain details about why there will be no fix issued.</dd>
+</dl>
+{% endcall %}
+{% call textinput('date', "Date", now.isoformat(), date, type="datetime") %}
+<p><strong>Date</strong> is the date Remedy was last updated, if omitted it is deemed to be unknown, unimportant, or irrelevant. All dateTime values in CVRF require a time, and we recommend the inclusion of a time zone as well (ICASI endorses the use of GMT or “Zulu time”). If a time zone is excluded, Zulu should be assumed.</p>
+{% endcall %}
+{% call textarea('description', "Description", '', description, 5, required=True) %}
+<p>The <strong>Description</strong> element will contain a thorough human-readable discussion of the Remediation.</p>
+{% endcall %}
+{% call textarea('entitlement', "Entitlement", '', entitlement, 5) %}
+<p><strong>Entitlement</strong> contains any possible vendor-defined constraints for obtaining fixed software or hardware that fully resolves the vulnerability. This element will often contain information about service contracts or service-level agreements that is directed toward customers of large vendors.</p>
+{{ examples(['Cisco customers with service contracts that entitle them to regular software updates should obtain security fixes through their usual update channels, generally from the Cisco website. Cisco recommends contacting the TAC only with specific and imminent problems or questions.<br>As a special customer service, and to improve the overall security of the Internet, Cisco may offer customers free of charge software updates to address security problems. If Cisco has offered a free software update to address a specific issue, noncontract customers who are eligible for the update may obtain it by contacting the Cisco TAC using any of the means described in the Contact Summary section of this document. To verify their entitlement, individuals who contact the TAC should have available the URL of the Cisco document that is offering the upgrade.<br>All aspects of this process are subject to change without notice and on a case-by-case basis. No particular level of response is guaranteed for any specific issue or class of issues.']) }}
+{% endcall %}
+{% call textinput('url', "URL", 'https://...', url, type="url") %}
+<p><strong>URL</strong> is the optional URL to the Remediation.</p>
+{% endcall %}
+
 {{ selectinput2('products', "Products", products, productids, multiple=True) }}
 {% if groups %}
   {{ selectinput2('groups', "Groups", groups, groupids, multiple=True)}}
--- a/farol/templates/vulnerability/edit_threat.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/edit_threat.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,17 +24,34 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import selectinput, textinput, textarea, selectinput2 %}
+{% from "macros.j2" import selectinput, textinput, textarea, selectinput2, examples %}
 {% block title %}Edit Threat{% endblock %}
 
 {% set active = 'vulnerability' %}
 
 {% block content %}
+<p><strong>Threat</strong> contains the vulnerability kinetic information. This information can change as the vulnerability ages and new information becomes available.</p>
+
+<p>A <strong>Threat</strong> container can be tied to one or more specific products by referencing these products using either the <strong>Product ID</strong> or <strong>Group ID</strong> child elements. If the <strong>Threat</strong> is meant to be general or nonspecific for all products, the <strong>Product ID</strong> and <strong>Group ID</strong> child elements should be omitted.</p>
 <form role="form" method="POST">
 
-{{ selectinput('type', "Type", types, type) }}
-{{ textinput('date', "Date", now.isoformat(), date, type="datetime") }}
-{{ textarea('description', "Description", '', description, 5, required=True) }}
+{% call selectinput('type', "Type", types, type) %}
+<p>The <em>Type</em> of <strong>Threat</strong> is required and can be one of the following:</p>
+<dl class="dl-horizontal">
+  <dt>Impact:</dt><dd>Impact contains an assessment of the impact on the user or the target set if the vulnerability is successfully exploited. (A description of the <samp>Target Set</samp> <em>Type</em> follows.) If applicable, for consistency and simplicity, this section can be a textual summary of the three CVSS impact metrics. These metrics measure how a vulnerability detracts from the three core security properties of an information system: Confidentiality, Integrity, and Availability.</dd>
+  <dt>Exploit Status:</dt><dd>Exploit Status contains a description of the degree to which an exploit for the vulnerability is known. This knowledge can range from information privately held among a very small group to an issue that has been described to the public at a major conference or is being widely exploited globally. For consistency and simplicity, this section can be a mirror image of the CVSS “Exploitability” metric. However, it can also contain a more contextual status, such as “Weaponized” or “Functioning Code.”</dd>
+  <dt>Target Set:</dt><dd>Target Set contains a description of the currently known victim population in whatever terms are appropriate. Such terms may include: operating system platform, types of products, user segments, and geographic distribution.</dd>
+</dl>
+{% endcall %}
+{% call textinput('date', "Date", now.isoformat(), date, type="datetime") %}
+<p>The <em>Date</em> attribute is optional. All dateTime values in CVRF require a time, and we recommend the inclusion of a time zone as well (ICASI endorses the use of GMT or “Zulu time”). If a time zone is excluded, Zulu should be assumed.</p>
+{% endcall %}
+{% call textarea('description', "Description", '', description, 5, required=True) %}
+<p>The <strong>Description</strong> element will contain a thorough human-readable discussion of the <strong>Threat</strong>.</p>
+{{ examples(['complete compromise of the integrity of affected machines'], 'Impact') }}
+{{ examples(['none', 'proof of concept'], 'Exploit Status') }}
+{{ examples(['Financial Institutions', 'US Government Agencies', 'All versions of BIND 9.4.0 and lower'], 'Target Set') }}
+{% endcall %}
 {{ selectinput2('products', "Products", products, productids, multiple=True) }}
 {% if groups %}
   {{ selectinput2('groups', "Groups", groups, groupids, multiple=True)}}
--- a/farol/templates/vulnerability/view.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,13 +24,13 @@
 -#}
 
 {% extends "base.j2" %}
-{% from "macros.j2" import panel, label_value %}
-{% block title %}{{ vulnerability._title }}{% endblock %}
+{% from "macros.j2" import panel, label_value, add_button, edit_button, delete_button %}
+{% block title %}{{ vulnerability._title or '' }}{% endblock %}
 
 {% set active = 'vulnerability' %}
 
 {% block content %}
-<a class="pull-right" href="{{ url_for('.edit', ordinal=vulnerability._ordinal) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit', ordinal=vulnerability._ordinal)) }}</div>
 <div class='page-header'>
   <h1>{{ vulnerability._title or '' }} <small>{{ vulnerability._type or '' }} (#{{ vulnerability._ordinal }})</small></h1>
 </div>
@@ -48,7 +48,7 @@
       <li><a href="{{ url_for('.view_note', ordinal=vulnerability._ordinal, note_ordinal=note._ordinal)}}">{{ note.getTitle() }}</a></li>
     {% endfor %}
   </ul>
-  <a href="{{ url_for('.add_note', ordinal=vulnerability._ordinal) }}" class="pull-right">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_note', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 <div class="form-horizontal">
   {% if vulnerability._discoverydate %}{{ label_value("Discovery Date", vulnerability._discoverydate) }}{% endif %}
@@ -60,7 +60,7 @@
       <li><a href="{{ url_for('.view_involvement', ordinal=vulnerability._ordinal, index=loop.index0) }}">{{ involvement.getTitle() }}</a></li>
     {% endfor %}
   </ul>
-  <a href="{{ url_for('.add_involvement', ordinal=vulnerability._ordinal) }}" class="pull-right">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_involvement', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 {% if vulnerability._cve %}
 <div class="form-horizontal">
@@ -70,9 +70,15 @@
 {% call panel(heading="CWE", badge=vulnerability._cwes | length, title=3) %}
   <ul>
   {% for cwe in vulnerability._cwes %}
-    <li>{{ cwe._id }} - {{ cwe._value}} (<a href="{{ url_for('.edit_cwe', ordinal=vulnerability._ordinal, index=loop.index0)  }}">edit</a>)</li>
+    <li>
+      <div>
+        {{ cwe._id }} - {{ cwe._value}}
+        {{ edit_button(url_for('.edit_cwe', ordinal=vulnerability._ordinal, index=loop.index0)) }}
+        {{ delete_button(url_for('.del_cwe', ordinal=vulnerability._ordinal, index=loop.index0)) }}
+      </div>
+    </li>
   {% endfor %}
-  <a href="{{ url_for('.add_cwe', ordinal=vulnerability._ordinal) }}" class="pull-right">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_cwe', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 {% call panel(heading="Product Statuses", badge=vulnerability._productstatuses | length, title=3) %}
   <ul>
@@ -80,7 +86,7 @@
       <li><a href="{{ url_for('.view_status', ordinal=vulnerability._ordinal, index=loop.index0) }}">{{ status.getTitle() }}</a></li>
     {% endfor %}
   </ul>
-  <a href="{{ url_for('.add_status', ordinal=vulnerability._ordinal) }}" class="pull-right">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_status', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 {% call panel(heading="Threats", badge=vulnerability._threats | length, title=3) %}
   <ul>
@@ -88,7 +94,7 @@
       <li><a href="{{ url_for('.view_threat', ordinal=vulnerability._ordinal, index=loop.index0) }}">{{ threat.getTitle() }}</a></li>
     {% endfor %}
   </ul>
-  <a href="{{ url_for('.add_threat', ordinal=vulnerability._ordinal) }}" class="pull-right">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_threat', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 {% call panel(heading="CVSS Score Sets", badge=vulnerability._cvsss | length, title=3) %}
   <ul>
@@ -96,7 +102,7 @@
       <li><a href="{{ url_for('.view_cvss', ordinal=vulnerability._ordinal, index=loop.index0) }}">{{ cvss._vector }} ({{ cvss._basescore }})</a></li>
     {% endfor %}
   </ul>
-  <a href="{{ url_for('.add_cvss', ordinal=vulnerability._ordinal) }}" class="pull-right">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_cvss', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 {% call panel(heading="Remediations", badge=vulnerability._remediations | length, title=3) %}
   <ul>
@@ -104,15 +110,23 @@
       <li><a href="{{ url_for('.view_remediation', ordinal=vulnerability._ordinal, index=loop.index0) }}">{{ remediation.getTitle() }}</a></li>
     {% endfor %}
   </ul>
-  <a href="{{ url_for('.add_remediation', ordinal=vulnerability._ordinal) }}" class="pull-right">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_remediation', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 {% call panel(heading="References", badge=vulnerability._references | length , title=3) %}
   <ul>
     {% for reference in vulnerability._references %}
-      <li><a href="{{ reference._url }}" target="_blank">{{ reference._description }}{% if reference._type %} ({{ reference._type }}){% endif %}</a> (<a href="{{ url_for('.edit_reference', ordinal=vulnerability._ordinal, index=loop.index0) }}">edit</a>)</li>
+      <li>
+        <div>
+          <a href="{{ reference._url }}" target="_blank">
+            {{ reference._description }}{% if reference._type %} ({{ reference._type }}){% endif %}
+          </a>
+          {{ edit_button(url_for('.edit_reference', ordinal=vulnerability._ordinal, index=loop.index0)) }}
+          {{ delete_button(url_for('.del_reference', ordinal=vulnerability._ordinal, index=loop.index0)) }}
+        </div>
+      </li>
     {% endfor %}
   </ul>
-  <a class="pull-right" href="{{ url_for('.add_reference', ordinal=vulnerability._ordinal) }}">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_reference', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
 {% call panel(heading="Acknowledgments", badge=vulnerability._acknowledgments | length, title=3) %}
   <ul>
@@ -120,6 +134,7 @@
       <li><a href="{{ url_for('.view_acknowledgment', ordinal=vulnerability._ordinal, index=loop.index0)}}">{{ ack.getTitle() }}</a></li>
     {% endfor %}
   </ul>
-  <a class="pull-right" href="{{ url_for('.add_acknowledgment', ordinal=vulnerability._ordinal) }}">add</a>
+  <div class="pull-right">{{ add_button(url_for('.add_acknowledgment', ordinal=vulnerability._ordinal)) }}</div>
 {% endcall %}
+<div class="pull-right">{{ delete_button(url_for('.delete', ordinal=vulnerability._ordinal), text="delete vulnerability definition") }}</div>
 {% endblock %}
--- a/farol/templates/vulnerability/view_acknowledgment.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view_acknowledgment.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,16 +24,17 @@
 -#}
 
 {% extends "vulnerability/base.j2" %}
-{% from "macros.j2" import label_value %}
+{% from "macros.j2" import label_value, edit_button, delete_button %}
 {% block title %}{{ acknowledgment.getTitle() }}{% endblock %}
 
 {% block i_content %}
-<a class="pull-right" href="{{ url_for('.edit_acknowledgment', ordinal=ordinal, index=index) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_acknowledgment', ordinal=ordinal, index=index)) }}</div>
 <div class='page-header'>
-  <h1>{{ acknowledgment._name }} <small>{{ acknowledgment._organization }}</small></h1>
+  <h1>{{ acknowledgment._names | join(', ') }} <small>{{ acknowledgment._organizations | join(', ') }}</small></h1>
 </div>
 <div class="form-horizontal">
   {{ label_value('Description', acknowledgment._description or '') }}
   {{ label_value('URL', (acknowledgment._url or '') | urlize) }}
 </div>
+<div class="pull-right">{{ delete_button(url_for('.del_acknowledgment', ordinal=ordinal, index=index)) }}</div>
 {% endblock %}
--- a/farol/templates/vulnerability/view_cvss.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view_cvss.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,11 +24,11 @@
 -#}
 
 {% extends "vulnerability/base.j2" %}
-{% from "macros.j2" import label_value %}
+{% from "macros.j2" import label_value, edit_button, delete_button %}
 {% block title %}CVSS: {{ cvss._basescore }}{% endblock %}
 
 {% block i_content %}
-<a class="pull-right" href="{{ url_for('.edit_cvss', ordinal=ordinal, index=index) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_cvss', ordinal=ordinal, index=index)) }}</div>
 <div class='page-header'>
   <h1>{{ cvss._vector }}</h1>
 </div>
@@ -47,4 +47,5 @@
     </ul>
   {% endcall %}
 {% endif %}
+<div class="pull-right">{{ delete_button(url_for('.del_cvss', ordinal=ordinal, index=index)) }}</div>
 {% endblock %}
--- a/farol/templates/vulnerability/view_involvement.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view_involvement.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,13 +24,14 @@
 -#}
 
 {% extends "vulnerability/base.j2" %}
-
+{% from "macros.j2" import edit_button, delete_button %}
 {% block title %}{{ involvement.getTitle() }}{% endblock %}
 
 {% block i_content %}
-<a class="pull-right" href="{{ url_for('.edit_involvement', ordinal=ordinal, index=index) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_involvement', ordinal=ordinal, index=index)) }}</div>
 <div class='page-header'>
   <h1>{{ involvement._party }} <small>{{ involvement._status }}</small></h1>
 </div>
 <p>{{ involvement._description or '' }}</p>
+<div class="pull-right">{{ delete_button(url_for('.del_involvement', ordinal=ordinal, index=index)) }}</div>
 {% endblock %}
--- a/farol/templates/vulnerability/view_note.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view_note.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,14 +24,15 @@
 -#}
 
 {% extends "vulnerability/base.j2" %}
-
+{% from "macros.j2" import edit_button, delete_button %}
 {% block title %}{{ note._title }}{% endblock %}
 
 {% block i_content %}
-<a class="pull-right" href="{{ url_for('.edit_note', ordinal=ordinal, note_ordinal=note._ordinal) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_note', ordinal=ordinal, note_ordinal=note._ordinal)) }}</div>
 <div class='page-header'>
   <h1>{{ note._title or '' }} <small>{{ note._type }} (#{{ note._ordinal }})</small></h1>
 </div>
 {% if note._audience %}<p>Audience: <em>{{ note._audience }}</em></p>{% endif %}
 <p>{{ note._note | replace('\n', '<br>') }}</p>
+<div class="pull-right">{{ delete_button(url_for('.del_note', ordinal=ordinal, note_ordinal=note._ordinal)) }}</div>
 {% endblock %}
--- a/farol/templates/vulnerability/view_productstatus.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view_productstatus.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,11 +24,11 @@
 -#}
 
 {% extends "vulnerability/base.j2" %}
-
+{% from "macros.j2" import edit_button, delete_button %}
 {% block title %}{{ status.getTitle() }}{% endblock %}
 
 {% block i_content %}
-<a class="pull-right" href="{{ url_for('.edit_status', ordinal=ordinal, index=index) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_status', ordinal=ordinal, index=index)) }}</div>
 <div class='page-header'>
   <h1>{{ status._type }}</h1>
 </div>
@@ -37,4 +37,5 @@
     <li><a href="{{ url_for('producttree.view_product', productid=productid) }}">{{ cvrf.getProductForID(productid)._name }}</a></li>
   {% endfor %}
 </ul>
+<div class="pull-right">{{ delete_button(url_for('.del_status', ordinal=ordinal, index=index)) }}</div>
 {% endblock %}
--- a/farol/templates/vulnerability/view_remediation.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view_remediation.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,11 +24,11 @@
 -#}
 
 {% extends "vulnerability/base.j2" %}
-{% from "macros.j2" import label_value, panel %}
+{% from "macros.j2" import label_value, panel, edit_button, delete_button %}
 {% block title %}{{ remediation.getTitle() }}{% endblock %}
 
 {% block i_content %}
-<a class="pull-right" href="{{ url_for('.edit_remediation', ordinal=ordinal, index=index) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_remediation', ordinal=ordinal, index=index)) }}</div>
 <div class='page-header'>
   <h1>{{ remediation._type }}</h1>
 </div>
@@ -39,7 +39,7 @@
   {{ label_value('URL', remediation._url | urlize) }}
 </div>
 {% if remediation._productids or remediation._groupids %}
-  {% call panel(heading="Products (%d)" % remediation._productids | length, title=2) %}
+  {% call panel(heading="Products", badge=remediation._productids | length, title=2) %}
   <ul>
     {% for productid in remediation._productids %}
       <li><a href="{{ url_for('producttree.view_product', productid=productid) }}">{{ cvrf.getProductForID(productid)._name }}</a></li>
@@ -57,4 +57,5 @@
   </ul>
   {% endcall %}
 {% endif %}
+<div class="pull-right">{{ delete_button(url_for('.del_remediation', ordinal=ordinal, index=index)) }}</div>
 {% endblock %}
--- a/farol/templates/vulnerability/view_threat.j2	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/templates/vulnerability/view_threat.j2	Wed Oct 15 15:22:59 2014 +0200
@@ -24,11 +24,11 @@
 -#}
 
 {% extends "vulnerability/base.j2" %}
-{% from "macros.j2" import panel, label_value %}
+{% from "macros.j2" import panel, label_value, edit_button, delete_button %}
 {% block title %}Edit {{ threat.getTitle() }}{% endblock %}
 
 {% block i_content %}
-<a class="pull-right" href="{{ url_for('.edit_threat', ordinal=ordinal, index=index) }}">edit</a>
+<div class="pull-right">{{ edit_button(url_for('.edit_threat', ordinal=ordinal, index=index)) }}</div>
 <div class='page-header'>
   <h1>{{ threat._type }}</h1>
 </div>
@@ -56,4 +56,5 @@
 </ul>
 {% endcall %}
 {% endif %}
+<div class="pull-right">{{ delete_button(url_for('.del_threat', ordinal=ordinal, index=index)) }}</div>
 {% endblock %}
--- a/farol/vulnerability.py	Wed Oct 15 15:22:50 2014 +0200
+++ b/farol/vulnerability.py	Wed Oct 15 15:22:59 2014 +0200
@@ -25,12 +25,14 @@
 from flask import (Blueprint, render_template, abort, redirect, request,
     url_for)
 
-from farolluz.parsers.cvrf import parseDate
 from farolluz.cvrf import (CVRFVulnerability, CVRFVulnerabilityID, CVRFNote,
-    CVRFReference, CVRFAcknowledgment, CVRFCWE, CVRFInvolvement, CVRFThreat,
-    CVRFProductStatus, CVRFCVSSSet, CVRFRemediation)
+    CVRFReference, CVRFCWE, CVRFInvolvement, CVRFThreat, CVRFProductStatus,
+    CVRFCVSSSet, CVRFRemediation)
 from farolluz.renderer import utcnow
 
+from .controller import (update_note_from_request, create_note_from_request,
+    update_reference_from_request, create_reference_from_request, parseDate,
+    update_acknowledgment_from_request, create_acknowledgment_from_request)
 from .session import document_required, get_current
 
 
@@ -102,33 +104,31 @@
     get_current().addVulnerability(vuln)
     return redirect(url_for('.view', ordinal=vuln._ordinal))
 
+@vulnerability.route('/<int:ordinal>/del', methods=['POST'])
+@document_required
+def delete(ordinal):
+    vuln = get_vuln(ordinal)
+    get_current()._vulnerabilities.remove(vuln)
+    return redirect(url_for('document.view'))
+
 @vulnerability.route('/<int:ordinal>/note/<int:note_ordinal>')
 @document_required
 def view_note(ordinal, note_ordinal):
-    for note in get_vuln(ordinal)._notes:
-        if note._ordinal != note_ordinal:
-            continue
-        return render_template('vulnerability/view_note.j2', note=note, ordinal=ordinal)
-    abort(404)
+    note = get_vuln(ordinal).getNote(note_ordinal)
+    if note is None:
+        abort(404)
+    return render_template('vulnerability/view_note.j2', note=note, ordinal=ordinal)
 
 @vulnerability.route('/<int:ordinal>/note/<int:note_ordinal>/edit', methods=['GET', 'POST'])
 @document_required
 def edit_note(ordinal, note_ordinal):
-    note = None
-    for n in get_vuln(ordinal)._notes:
-        if n._ordinal == note_ordinal:
-            note = n
-            break
+    note = get_vuln(ordinal).getNote(note_ordinal)
     if note is None:
         abort(404)
     if request.method != 'POST':
         return render_template('vulnerability/edit_note.j2', note=note, ordinal=ordinal, types=note.TYPES)
 
-    note._type = request.form['type']
-    note._ordinal = int(request.form['ordinal'])
-    note._note = request.form['note']
-    note._title = request.form['title'] or None
-    note._audience = request.form['audience'] or None
+    update_note_from_request(note)
     return redirect(url_for('.view_note', ordinal=ordinal, note_ordinal=note._ordinal))
 
 @vulnerability.route('/<int:ordinal>/note/add', methods=['GET', 'POST'])
@@ -141,11 +141,19 @@
             next_ordinal = notes[-1]._ordinal + 1
         return render_template('vulnerability/edit_note.j2', ordinal=ordinal, note_ordinal=next_ordinal, types=CVRFNote.TYPES, action='Add')
 
-    title = request.form['title'] or None
-    audience = request.form['audience'] or None
+    note = create_note_from_request()
+    get_vuln(ordinal).addNote(note)
+    return redirect(url_for('.view', ordinal=ordinal))
 
-    note = CVRFNote(request.form['type'], int(request.form['ordinal']), request.form['note'], title, audience)
-    get_vuln(ordinal).addNote(note)
+@vulnerability.route('/<int:ordinal>/note/<int:note_ordinal>/del', methods=['POST'])
+@document_required
+def del_note(ordinal, note_ordinal):
+    vuln = get_vuln(ordinal)
+    note = vuln.getNote(ordinal)
+    if note is None:
+        flash('Note not found', 'danger')
+        abort(404)
+    vuln._notes.remove(note)
     return redirect(url_for('.view', ordinal=ordinal))
 
 
@@ -169,7 +177,6 @@
     get_vuln(ordinal).addInvolvement(inv)
     return redirect(url_for('.view', ordinal=ordinal))
 
-
 @vulnerability.route('/<int:ordinal>/involvement/<int:index>/edit', methods=['GET', 'POST'])
 @document_required
 def edit_involvement(ordinal, index):
@@ -185,6 +192,18 @@
     involvement._description = request.form['description'] or None
     return redirect(url_for('.view_involvement', ordinal=ordinal, index=index))
 
+@vulnerability.route('/<int:ordinal>/involvement/<int:index>/del', methods=['POST'])
+@document_required
+def del_involvement(ordinal, index):
+    invls = get_vuln(ordinal)._involvements
+    if not ( 0 <= index < len(invls)):
+        flash('Involvement not found', 'danger')
+        abort(404)
+
+    del invls[index]
+    return redirect(url_for('.view', ordinal=ordinal))
+
+
 @vulnerability.route('/<int:ordinal>/cwe/<int:index>/edit', methods=['GET', 'POST'])
 @document_required
 def edit_cwe(ordinal, index):
@@ -199,7 +218,6 @@
     cwe._value = request.form['description']
     return redirect(url_for('.view', ordinal=ordinal))
 
-
 @vulnerability.route('/<int:ordinal>/cwe/add', methods=['GET', 'POST'])
 @document_required
 def add_cwe(ordinal):
@@ -210,6 +228,17 @@
     get_vuln(ordinal).addCWE(cwe)
     return redirect(url_for('.view', ordinal=ordinal))
 
+@vulnerability.route('/<int:ordinal>/cwe/<int:index>/del', methods=['POST'])
+@document_required
+def del_cwe(ordinal, index):
+    cwes = get_vuln(ordinal)._cwes
+    if not ( 0 <= index < len(cwes)):
+        flash('CWE not found', 'danger')
+        abort(404)
+    del cwes[index]
+    return redirect(url_for('.view', ordinal=ordinal))
+
+
 @vulnerability.route('/<int:ordinal>/productstatus/<int:index>')
 @document_required
 def view_status(ordinal, index):
@@ -231,7 +260,6 @@
     get_vuln(ordinal).addProductStatus(status)
     return redirect(url_for('.view', ordinal=ordinal))
 
-
 @vulnerability.route('/<int:ordinal>/productstatus/<int:index>/edit', methods=['GET', 'POST'])
 @document_required
 def edit_status(ordinal, index):
@@ -248,6 +276,17 @@
         status.addProductID(productid)
     return redirect(url_for('.view', ordinal=ordinal))
 
+@vulnerability.route('/<int:ordinal>/productstatus/<int:index>/del', methods=['POST'])
+@document_required
+def del_status(ordinal, index):
+    statuses = get_vuln(ordinal)._productstatuses
+    if not ( 0 <= index < len(statuses)):
+        flash('Product Status not found', 'danger')
+        abort(404)
+
+    del statuses[index]
+    return redirect(url_for('.view', ordinal=ordinal))
+
 
 @vulnerability.route('/<int:ordinal>/threat/<int:index>')
 @document_required
@@ -261,7 +300,6 @@
 @vulnerability.route('/<int:ordinal>/threat/add', methods=['GET', 'POST'])
 @document_required
 def add_threat(ordinal):
-    cvrf = get_current()
     if request.method != 'POST':
         return render_template('vulnerability/edit_threat.j2',
             ordinal=ordinal,
@@ -285,7 +323,6 @@
         threat = get_vuln(ordinal)._threats[index]
     except IndexError:
         abort(404)
-    cvrf = get_current()
     if request.method != 'POST':
         return render_template('vulnerability/edit_threat.j2',
             ordinal=ordinal, index=index,
@@ -306,6 +343,17 @@
         threat.addGroupID(groupid)
     return redirect(url_for('.view', ordinal=ordinal))
 
+@vulnerability.route('/<int:ordinal>/threat/<int:index>/del', methods=['POST'])
+@document_required
+def del_threat(ordinal, index):
+    threats = get_vuln(ordinal)._threats
+    if not (0 <= index < len(threats)):
+        flash('Threat not found', 'danger')
+        abort(404)
+
+    del threats[index]
+    return redirect(url_for('.view', ordinal=ordinal))
+
 
 @vulnerability.route('/<int:ordinal>/cvss/<int:index>')
 @document_required
@@ -335,7 +383,6 @@
     get_vuln(ordinal).addCVSSSet(cvss)
     return redirect(url_for('.view', ordinal=ordinal))
 
-
 @vulnerability.route('/<int:ordinal>/cvss/<int:index>/edit', methods=['GET', 'POST'])
 @document_required
 def edit_cvss(ordinal, index):
@@ -360,6 +407,17 @@
     cvss.setVector(request.form['vector'] or None)
     return redirect(url_for('.view', ordinal=ordinal))
 
+@vulnerability.route('/<int:ordinal>/cvss/<int:index>/del', methods=['POST'])
+@document_required
+def del_cvss(ordinal, index):
+    cvsss = get_vuln(ordinal)._cvsss
+    if not ( 0 <= index < len(cvsss)):
+        flash('CVSS not found', 'danger')
+        abort(404)
+
+    del cvsss[index]
+    return redirect(url_for('.view', ordinal=ordinal))
+
 
 @vulnerability.route('/<int:ordinal>/remediation/<int:index>')
 @document_required
@@ -425,6 +483,17 @@
         remediation.addGroupID(groupid)
     return redirect(url_for('.view', ordinal=ordinal))
 
+@vulnerability.route('/<int:ordinal>/remediation/<int:index>/del', methods=['POST'])
+@document_required
+def del_remediation(ordinal, index):
+    rems = get_vuln(ordinal)._remediations
+    if not ( 0 <= index < len(rems)):
+        flash('Remediation not found', 'danger')
+        abort(404)
+
+    del rems[index]
+    return redirect(url_for('.view', ordinal=ordinal))
+
 
 @vulnerability.route('/<int:ordinal>/reference/<int:index>/edit', methods=['GET', 'POST'])
 @document_required
@@ -436,9 +505,7 @@
     if request.method != 'POST':
         return render_template('vulnerability/edit_reference.j2', ordinal=ordinal, _type=reference._type, url=reference._url, description=reference._description, types=('',) + reference.TYPES)
 
-    reference._type = request.form['type'] or None
-    reference._url = request.form['url']
-    reference._description = request.form['description']
+    update_reference_from_request(reference)
     return redirect(url_for('.view', ordinal=ordinal))
 
 @vulnerability.route('/<int:ordinal>/reference/add', methods=['GET', 'POST'])
@@ -447,10 +514,21 @@
     if request.method != 'POST':
         return render_template('vulnerability/edit_reference.j2', action='Add', ordinal=ordinal, types=('',) + CVRFReference.TYPES)
 
-    ref = CVRFReference(request.form['url'], request.form['description'], request.form['type'] or None)
+    ref = create_reference_from_request()
     get_vuln(ordinal).addReference(ref)
     return redirect(url_for('.view', ordinal=ordinal))
 
+@vulnerability.route('/<int:ordinal>/reference/<int:index>/del', methods=['POST'])
+@document_required
+def del_reference(ordinal, index):
+    refs = get_vuln(ordinal)._references
+    if not ( 0 <= index < len(refs)):
+        flash('Reference not found', 'danger')
+        abort(404)
+
+    del refs[index]
+    return redirect(url_for('.view', ordinal=ordinal))
+
 
 @vulnerability.route('/<int:ordinal>/acknowledgment/<int:index>')
 @document_required
@@ -459,7 +537,10 @@
         ack = get_vuln(ordinal)._acknowledgments[index]
     except IndexError:
         abort(404)
-    return render_template('vulnerability/view_acknowledgment.j2', ordinal=ordinal, acknowledgment=ack, index=index, action='Update')
+    return render_template('vulnerability/view_acknowledgment.j2',
+        ordinal=ordinal,
+        acknowledgment=ack, index=index,
+        action='Update')
 
 @vulnerability.route('/<int:ordinal>/acknowledgment/<int:index>/edit', methods=['GET', 'POST'])
 @document_required
@@ -469,12 +550,13 @@
     except IndexError:
         abort(404)
     if request.method != 'POST':
-        return render_template('vulnerability/edit_acknowledgment.j2', ordinal=ordinal, name=ack._name, organization=ack._organization, description=ack._description, url=ack._url, action='Update')
+        return render_template('vulnerability/edit_acknowledgment.j2',
+            ordinal=ordinal,
+            names=ack._names, organizations=ack._organizations,
+            description=ack._description, url=ack._url,
+            action='Update')
 
-    ack._name = request.form['name'] or None
-    ack._organization = request.form['organization'] or None
-    ack._description = request.form['description'] or None
-    ack._url = request.form['url'] or None
+    update_acknowledgment_from_request(ack)
     return redirect(url_for('.view', ordinal=ordinal))
 
 @vulnerability.route('/<int:ordinal>/acknowledgment/add', methods=['GET', 'POST'])
@@ -483,10 +565,17 @@
     if request.method != 'POST':
         return render_template('vulnerability/edit_acknowledgment.j2', action='Add', ordinal=ordinal)
 
-    ack = CVRFAcknowledgment()
-    ack._name = request.form['name'] or None
-    ack._organization = request.form['organization'] or None
-    ack._description = request.form['description'] or None
-    ack._url = request.form['url'] or None
+    ack = create_acknowledgment_from_request()
     get_vuln(ordinal).addAcknowledgment(ack)
     return redirect(url_for('.view', ordinal=ordinal))
+
+@vulnerability.route('/<int:ordinal>/acknowledgment/<int:index>/del', methods=['POST'])
+@document_required
+def del_acknowledgment(ordinal, index):
+    acks = get_vuln(ordinal)._acknowledgments
+    if not( 0 <= index < len(acks)):
+        flash('Acknowledgment not found', 'danger')
+        abort(404)
+
+    del acks[index]
+    return redirect(url_for('.view', ordinal=ordinal))
--- a/tests/testBranches.py	Wed Oct 15 15:22:50 2014 +0200
+++ b/tests/testBranches.py	Wed Oct 15 15:22:59 2014 +0200
@@ -31,7 +31,7 @@
         self.assertEqual(rv.status_code, 200)
         rv = self.app.post('/producttree/create')
         rv = self.app.get('/producttree/branch/add')
-        rv = self.app.post('/producttree/branch/add', data=dict(type="Vendor", name="SecPod"))
+        rv = self.app.post('/producttree/branch/add', data=dict(type="Vendor", name="SecPod", parent=""))
 
     def testAddTwoBranch(self):
         rv = self.createDoc('Title', 'Type')
@@ -54,3 +54,12 @@
         rv = self.app.post('/producttree/branch/0/0/edit', data=dict(type="Vendor", name="Other", parent=""), follow_redirects=True)
         self._assertIn('<a href="/producttree/branch/1/edit">edit</a>', rv)
 
+    def testDeleteBranch(self):
+        rv = self.createDoc('Title', 'Type')
+        self.assertEqual(rv.status_code, 200)
+        rv = self.app.post('/producttree/create')
+        rv = self.app.get('/producttree/branch/add')
+        rv = self.app.post('/producttree/branch/add', data=dict(type="Vendor", name="SecPod", parent=""))
+        rv = self.app.post('/producttree/branch/0/del', follow_redirects=True)
+        self.assertEqual(rv.status_code, 200)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testDocument.py	Wed Oct 15 15:22:59 2014 +0200
@@ -0,0 +1,96 @@
+# -*- encoding: utf-8 -*-
+# Description:
+# Short Description
+#
+# Authors:
+# Benoît Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+from .utils import TestCase
+
+class TestTracking(TestCase):
+
+    def createTracking(self, id_):
+        return self.app.post('/document/tracking/edit', data={'id': id_, 'id_aliases': '', 'status': 'Draft', 'version': '0.0', 'initial': '2014-10-09T08:50:51+00:00', 'current': '2014-10-09T08:50:51+00:00', 'gen_engine': '', 'gen_date': ''})
+
+    def testCreateTracking(self):
+        rv = self.createDoc('Title', 'Type')
+        self.assertEqual(rv.status_code, 200)
+        rv = self.createTracking('5')
+        self.assertEqual(rv.status_code, 302)
+
+    def testCreateEditDelRev(self):
+        rv = self.createDoc('Title', 'Type')
+        self.assertEqual(rv.status_code, 200)
+        rv = self.createTracking('5')
+        self.assertEqual(rv.status_code, 302)
+
+        rv = self.app.post('/document/revision/add', data={'number': '0.0', 'date': '2014-10-09T08:57:52+00:00', 'description': "First Revision", 'update_tracking': 'on'})
+        self.assertEqual(rv.status_code, 302)
+        
+        rv = self.app.post('/document/revision/0/edit', data={'number': '0.1', 'date': '2014-10-09T08:57:52+00:00', 'description': "First Revision"})
+        self.assertEqual(rv.status_code, 302)
+
+        rv = self.app.post('/document/revision/0/del')
+        self.assertEqual(rv.status_code, 302)
+
+
+class testDocument(TestCase):
+
+    def testCreateEditDelNote(self):
+        rv = self.createDoc('Title', 'Type')
+        self.assertEqual(rv.status_code, 200)
+
+        rv = self.app.post('/document/note/add', data={'type': 'General', 'ordinal': '1', 'title': '', 'audience': '', 'note': 'Note content'})
+        self.assertEqual(rv.status_code, 302)
+        
+        rv = self.app.post('/document/note/1/edit', data={'type': 'General', 'ordinal': '5', 'title': 'title', 'audience': '', 'note': 'Note content'})
+        self.assertEqual(rv.status_code, 302)
+
+        rv = self.app.post('/document/note/5/del')
+        self.assertEqual(rv.status_code, 302)
+
+
+    def testCreateEditDelRef(self):
+        rv = self.createDoc('Title', 'Type')
+        self.assertEqual(rv.status_code, 200)
+
+        rv = self.app.post('/document/reference/add', data={'type': '', 'url': "https://example.com", 'description': "DOT com"})
+        self.assertEqual(rv.status_code, 302)
+        
+        rv = self.app.post('/document/reference/0/edit', data={'type': 'External', 'url': "https://example.com", 'description': "example DOT com"})
+        self.assertEqual(rv.status_code, 302)
+
+        rv = self.app.post('/document/reference/0/del')
+        self.assertEqual(rv.status_code, 302)
+
+
+    def testCreateEditDelAck(self):
+        rv = self.createDoc('Title', 'Type')
+        self.assertEqual(rv.status_code, 200)
+
+        rv = self.app.post('/document/acknowledgment/add', data={'names': "Antu Sanadi, Veerendra G.G", 'organizations': "SecPod Technologies Pvt. Ltd", 'description': "", 'url': ""})
+        self.assertEqual(rv.status_code, 302)
+        
+        rv = self.app.post('/document/acknowledgment/0/edit', data={'names': "Antu Sanadi", 'organizations': "SecPod Technologies Pvt. Ltd", 'description': "Thanks !", 'url': ""})
+        self.assertEqual(rv.status_code, 302)
+
+        rv = self.app.post('/document/acknowledgment/0/del')
+        self.assertEqual(rv.status_code, 302)
+

http://farol.wald.intevation.org