diff --git a/django/website/data_layer/migrations/0004_auto_20150720_1723.py b/django/website/data_layer/migrations/0004_auto_20150720_1723.py
new file mode 100644
index 0000000000000000000000000000000000000000..aaa9d331e09f6d9ddf98b5d9f0d4fbb9f5113da9
--- /dev/null
+++ b/django/website/data_layer/migrations/0004_auto_20150720_1723.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('data_layer', '0003_message_terms'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='message',
+            name='terms',
+            field=models.ManyToManyField(related_name='items', to='taxonomies.Term'),
+        ),
+    ]
diff --git a/django/website/data_layer/models.py b/django/website/data_layer/models.py
index 244aa6e90c65b97aea1b225cfcaa0f256b748d89..fa04cfe55ba847dac221853f1f24ea3953ad2e7d 100644
--- a/django/website/data_layer/models.py
+++ b/django/website/data_layer/models.py
@@ -12,7 +12,7 @@ class DataLayerModel(models.Model):
 class Message(DataLayerModel):
     body = models.TextField()
     timestamp = models.DateTimeField(null=True)
-    terms = models.ManyToManyField(Term)
+    terms = models.ManyToManyField(Term, related_name="items")
 
     def apply_term(self, term):
         # TODO: test this
diff --git a/django/website/hid/assets.py b/django/website/hid/assets.py
index 36acaff8819dc7821232f071f2a4c49393d5c5de..309cd040267230b84221aed44ebe74d1628ee333 100644
--- a/django/website/hid/assets.py
+++ b/django/website/hid/assets.py
@@ -37,7 +37,8 @@ _assets = [
     'hid/widgets/chart.js',
     'hid/js/spinner.js',
     'hid/js/messages.js',
-    'hid/js/automatic_file_upload.js'
+    'hid/js/automatic_file_upload.js',
+    'hid/js/select_all_checkbox.js'
 ]
 
 
diff --git a/django/website/hid/migrations/0002_rename_question_type_terms.py b/django/website/hid/migrations/0002_rename_question_type_terms.py
new file mode 100644
index 0000000000000000000000000000000000000000..db0f99d21e0ee8bf72420b9424f20e5e9b06a167
--- /dev/null
+++ b/django/website/hid/migrations/0002_rename_question_type_terms.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+QUESTION_TYPES = (
+    ('Ebola updates', 'What are the current updates on Ebola?'),
+    ('Ebola authenticity', 'Is Ebola a real disease?'),
+    ('Ebola prevention', 'What measures could be put in place to end Ebola?'),
+    ('Ebola origins', 'What is the origin of Ebola?'),
+    ('Non-Ebola concerns', 'What are the non-Ebola related concerns?'),
+    ('Ebola symptoms', 'What are the symptoms of Ebola?'),
+    ('Ebola vaccine', 'What are the stakes of the Ebola vaccine?'),
+    ('Liberia Ebola-free', 'Can Liberia be Ebola free?'),
+    ('Unknown', 'Unknown.'),
+)
+
+
+def rename_question_type_terms(apps, schema_editor):
+    Taxonomy = apps.get_model('taxonomies', 'Taxonomy')
+    Term = apps.get_model('taxonomies', 'Term')
+    (taxonomy, _) = Taxonomy.objects.get_or_create(
+        slug="ebola-questions",
+        name="Ebola Questions",
+    )
+    Term.objects.filter(taxonomy=taxonomy).delete()
+    new_terms = [
+        Term(name=qt[0], long_name=qt[1], taxonomy=taxonomy)
+        for qt in QUESTION_TYPES
+    ]
+
+    Term.objects.bulk_create(new_terms)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hid', '0001_question_types'),
+    ]
+
+    operations = [
+        migrations.RunPython(rename_question_type_terms)
+    ]
diff --git a/django/website/hid/static/hid/js/automatic_file_upload.js b/django/website/hid/static/hid/js/automatic_file_upload.js
index a72b5151f7db862ad2f46cd9d9d14dcde29f1314..35a49bc06ed6efab9da78c22c5bfeaaff890e4d4 100644
--- a/django/website/hid/static/hid/js/automatic_file_upload.js
+++ b/django/website/hid/static/hid/js/automatic_file_upload.js
@@ -7,6 +7,11 @@
  * - The form element should have a 'auto-upload-file' class;
  * - The upload button replacement should have an 'upload-button' class
  *   (optional, we can also use the normal upload button).
+ * - The file input tag should be a descendant of a .form-group element
+ *   (optional, this will be hidden if we have a button replacement).
+ *
+ * This is progressive enhancement, so the upload-button should be hidden
+ * to be begin with, and will only be displayed if the javascript executes.
  *
  * Note that this will only work if the form has one file upload element.
  *
@@ -17,6 +22,13 @@
             var $form = $(this);
             var $button = $('.upload-button', $form);
             var $file_input = $('[type="file"]', $form);
+            var $input_group = $file_input.closest('.form-group');
+
+            // Hide the file input group and show the alternative button.
+            if ($button.length > 0) {
+                $input_group.hide();
+                $button.show();
+            }
 
             // Auto-submit
             $file_input.on('change', function(e) {
diff --git a/django/website/hid/static/hid/js/select_all_checkbox.js b/django/website/hid/static/hid/js/select_all_checkbox.js
new file mode 100644
index 0000000000000000000000000000000000000000..cefd900538159807c72b433b040590248b01a804
--- /dev/null
+++ b/django/website/hid/static/hid/js/select_all_checkbox.js
@@ -0,0 +1,53 @@
+/**
+ * Add a checkbox to select all entries to tables with a column containing
+ * a checkbox.
+ *
+ * Usage:
+ * - The th/td elements containing the checkbox must have
+ *   a 'select_action' class. In Django tables this is done
+ *   by naming the column select_action, eg.:
+ *
+ *   select_action = NamedCheckBoxColumn(accessor='id', verbose_name='Select')
+ */
+(function($) {
+    $(document).ready(function() {
+        var selectors = {
+            td_input : 'form td.select_item input[type="checkbox"]',
+            th_select : 'form th.select_item'
+        };
+
+        function addCheckbox(selector, check_id_base) {
+            $(selector).each(function(i, column_header) {
+                var input_id = check_id_base + '-' + i;
+                var check = $('<input>', {
+                        type: 'checkbox',
+                        id: input_id
+                    });
+                $(column_header).html("")
+                           .append(check);
+            });
+        }
+
+        function toggleSelection(e) {
+            var checked = $(e.target).prop("checked");
+            $(selectors.td_input).prop("checked", checked);
+        }
+
+        function setSelectAll(e) {
+            var checked = $(selectors.td_input + ':checked').length,
+                all = $(selectors.td_input).length,
+                checked_all = all === checked;
+
+            $(selectors.th_select + ' input').prop("checked", checked_all);
+        }
+
+        function init() {
+            addCheckbox(selectors.th_select, 'select-all-items');
+
+            $(selectors.th_select + ' input').on('change', toggleSelection);
+            $(selectors.td_input).on('change', setSelectAll);
+        }
+
+        init();
+    });
+})(jQuery);
diff --git a/django/website/hid/tables.py b/django/website/hid/tables.py
index fb5c2a6fc805804283eaa0591fa4e81670e7a68b..d270635c6453bff249442c4933ba0775ed9d8b26 100644
--- a/django/website/hid/tables.py
+++ b/django/website/hid/tables.py
@@ -1,6 +1,6 @@
 import django_tables2 as tables
 from django.conf import settings
-from django.template import loader
+from django.utils.translation import ugettext_lazy as _
 
 
 class NamedCheckBoxColumn(tables.CheckBoxColumn):
@@ -15,32 +15,37 @@ class ItemTable(tables.Table):
         template = 'hid/table.html'
         order_by = ('-created',)
 
+    select_item = tables.TemplateColumn(
+        template_name='hid/select_item_id_checkbox_column.html',
+        verbose_name=_('Select')
+    )
     created = tables.columns.DateTimeColumn(
-        verbose_name='Imported',
+        verbose_name=_('Imported'),
         format=settings.SHORT_DATETIME_FORMAT,
     )
     timestamp = tables.columns.DateTimeColumn(
-        verbose_name='Created',
+        verbose_name=_('Created'),
         format=settings.SHORT_DATETIME_FORMAT,
     )
-    body = tables.Column(verbose_name='Message')
-    category = tables.TemplateColumn(
-        template_name='hid/categories_column.html',
+    body = tables.Column(verbose_name=_('Message'))
+    category = tables.Column(
+        verbose_name=_('Category'),
         accessor='terms.0.name',
+        default=_('Uncategorized')
     )
-    delete = NamedCheckBoxColumn(accessor='id', verbose_name='Delete')
 
     def __init__(self, *args, **kwargs):
         self.categories = kwargs.pop('categories')
         super(ItemTable, self).__init__(*args, **kwargs)
 
-    def render_category(self, record, value):
-        # TODO: Test this
-        Template = loader.get_template('hid/categories_column.html')
-        ctx = {
-            'categories': self.categories,
-            'category': value,
-            'record': record
-        }
-
-        return Template.render(ctx)
+    @staticmethod
+    def get_selected(params):
+        """ Given a request parameter list, return the items that were
+            selected using the select_item column.
+
+            Args:
+                - params: GET/POST parameter list
+            Returns:
+                List of selected record ids as integers
+        """
+        return [int(x) for x in params.getlist("select_item_id", [])]
diff --git a/django/website/hid/templates/hid/categories_column.html b/django/website/hid/templates/hid/categories_column.html
deleted file mode 100644
index 5d90046b97c16d24a4c1b2d0d528b3c8e1157960..0000000000000000000000000000000000000000
--- a/django/website/hid/templates/hid/categories_column.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% if categories %}
-<select name="category-{{ record.id }}" class="form-control">
-    {% if not category %}
-        <option value="" selected="selected">---------</option>
-    {% endif %}
-    {% for cat in categories %}
-    <option value="{{ cat.0 }}"{% if category == cat.0 %} selected="selected"{% endif %}>{{ cat.1 }}</option>
-    {% endfor %}
-</select>
-{% endif %}
diff --git a/django/website/hid/templates/hid/select_item_id_checkbox_column.html b/django/website/hid/templates/hid/select_item_id_checkbox_column.html
new file mode 100644
index 0000000000000000000000000000000000000000..d54a6fa5eaade5454c7e249101b8d4e23a47e484
--- /dev/null
+++ b/django/website/hid/templates/hid/select_item_id_checkbox_column.html
@@ -0,0 +1,9 @@
+<div class='select-item-id-checkbox'>
+    <input 
+        type='checkbox' 
+        id='select-item-id-checkbox-{{ record.id }}'
+        value='{{ record.id }}' 
+        name='select_item_id'
+    />
+    <label for='select-item-id-checkbox-{{ record.id }}'></label>
+</div>
diff --git a/django/website/hid/templates/hid/sources.html b/django/website/hid/templates/hid/sources.html
index abb55d883fdb45251461aeaf666828ab02cd7c9f..ee0d9e809f3286afb5340bcda308febee861e9fd 100644
--- a/django/website/hid/templates/hid/sources.html
+++ b/django/website/hid/templates/hid/sources.html
@@ -13,9 +13,13 @@
 
             <form action="{% url "sources-upload" %}" method="post" enctype="multipart/form-data" class="item-source-actions auto-upload-file pull-right">
                 {% csrf_token %}
-                <a class="btn btn-primary btn-block btn-sm" value="View/Edit data" type="button" href="{% url "data-view" %}"><span class="fa fa-pencil fa-fw"></span> View &amp; Edit data</a>
-                {% bootstrap_form source.form show_label=False %}
-                <a class="btn btn-sm item-source-upload upload-button btn-block btn-primary" type="button" value="Upload" href="{% url "data-view" %}"><span class="fa fa-upload fa-fw"></span> Upload</a>
+                <div class='form-group'>
+                    <a class="btn btn-primary btn-block btn-sm" value="View/Edit data" type="button" href="{% url "data-view" %}"><span class="fa fa-pencil fa-fw"></span> View &amp; Edit data</a>
+                </div>
+                <div class='form-group'>
+                    {% bootstrap_form source.form show_label=False %}
+                    <a class="btn btn-sm item-source-upload upload-button btn-block btn-primary" type="button" value="Upload" href="{% url "data-view" %}"><span class="fa fa-upload fa-fw"></span> Upload</a>
+                </div>
             </form>
         </li>
         {% endfor %}
diff --git a/django/website/hid/templates/hid/tests/select_all_checkbox.html b/django/website/hid/templates/hid/tests/select_all_checkbox.html
new file mode 100644
index 0000000000000000000000000000000000000000..125f151c42a2b51b49350a55756e738ba1f9f76e
--- /dev/null
+++ b/django/website/hid/templates/hid/tests/select_all_checkbox.html
@@ -0,0 +1,56 @@
+{% extends "djangojs/qunit-runner.html" %}
+{% block js_content %}
+    <script>
+        QUnit.test('Select all checbox is added to header', function(assert) {
+            var $header_checkbox = jQuery('th.select_item input[type="checkbox"]');
+            assert.equal($header_checkbox.length, 1, 'Expected a checkbox in the header');
+        });
+        QUnit.test('Clicking the select all checkbox selects all', function(assert) {
+            var $header_checkbox = jQuery('th.select_item input[type="checkbox"]');
+            var $selected_checkboxes = jQuery('td.select_item input:checked');
+
+            // Ensure none are checked
+            $selected_checkboxes.prop('checked', false);
+
+            // Simulte click
+            $header_checkbox.click();
+
+            // Ensure all are checked
+            $selected_checkboxes = jQuery('td.select_item input:checked');
+            assert.equal($selected_checkboxes.length, 2, 'Expected all checkboxes to be checked');
+        });
+    </script>
+{% endblock %}
+{% block body_content %}
+    <form>
+        <table>
+            <thead>
+                <tr>
+                    <th class="select_item">
+                    </th>
+                    <th>
+                        A field
+                    </th>
+                 </tr>
+             </thead>
+             <tbody>
+                 <tr>
+                     <td class="select_item">
+                         <input type="checkbox" />
+                     </td>
+                     <td>
+                         A value
+                     </td>
+                 </tr>
+                 <tr>
+                     <td class="select_item">
+                         <input type="checkbox" />
+                     </td>
+                     <td>
+                         Another value
+                     </td>
+                 </tr>
+             </tbody>
+         </table>
+     </form>
+{% endblock %}
diff --git a/django/website/hid/templates/hid/view.html b/django/website/hid/templates/hid/view.html
index 20ebc81b5eb65316c2b831e6256c57169fc53b9b..7e4164bdae05019b6bfb18bc9c9ff5ca114df284 100644
--- a/django/website/hid/templates/hid/view.html
+++ b/django/website/hid/templates/hid/view.html
@@ -7,86 +7,47 @@
 <h1 class="page-header"><span class="fa fa-pencil fa-fw"></span>{% trans "View &amp; Edit" %}</h1>
  <div class='row'>
     <div class="col-lg-12">
-        <form action="{% url "sources-upload" %}" method="post" enctype="multipart/form-data" class="auto-upload-file header-upload-form">
-            {% csrf_token %}
-            {% bootstrap_form upload_form show_label=False %}
-            <a class="btn btn-sm item-source-upload btn-block btn-primary upload-button" type="button" value="Upload" href="{% url "data-view" %}"><span class="fa fa-upload fa-fw"></span>{% blocktrans %}Upload {{ type_label }}{% endblocktrans %}</a>
-        </form>
-        <form action="{% url "data-view-process" %}" method="post" class="view-items-form">{% csrf_token %}
-            <div class="panel panel-default">
-                <div class="panel-heading">
-                    <span class="fa fa-table fa-fw"></span> Questions
-                    <div class="pull-right">
-                        <div class="btn-group">
-                            {% bootstrap_button "Save Changes" button_type="submit" value="Update" button_class="btn btn-success btn-sm table-submit" %}
-                        </div>
-                    </div>
+        <div class="panel panel-default">
+            <div class="panel-heading clearfix">
+                <span class="fa fa-table fa-fw"></span>{{ type_label }}
+                <div class="pull-right">
+                    <form action="{% url "sources-upload" %}"
+                          method="post"
+                          enctype="multipart/form-data"
+                          class="auto-upload-file">
+                        {% csrf_token %}
+                            {% bootstrap_form upload_form show_label=False %}
+                            <a class="btn item-source-upload btn-block btn-primary upload-button" type="button" value="Upload" href="{% url "data-view" %}"><span class="fa fa-upload fa-fw"></span>{% blocktrans %}Upload {{ type_label }}{% endblocktrans %}</a>
+                    </form>
                 </div>
-                <div class="panel-body">
+            </div>
+            <div class="panel-body">
+                <form action="{% url "data-view-process" %}"
+                      method="post"
+                      class="view-items-form">
+                    {% csrf_token %}
+                    <div class="form-group form-header-group">
+                        <select name='action' class='form-control' required>
+                            <option value="" default selected">{% trans "Actions" %}</option>
+                            {% for group in actions %}
+                                <optgroup label="{{ group.label }}">
+                                    {% for action, action_label in group.items.items %}
+                                        <option value="{{ action }}">
+                                            {{ action_label }}
+                                        </option>
+                                    {% endfor %}
+                                </optgroup>
+                            {% endfor %}
+                        </select>
+                        {% bootstrap_button "Update" button_type="submit" value="Update" button_class="btn btn-success btn-sm table-submit" %}
+                    </div>
                     {% with pagination_class="pagination-circle-nav" %}
-                    {% render_table table %}
+                        {% render_table table %}
                     {% endwith %}
-
-                </div>
-                <div class="panel-footer clearfix">
-                        {% bootstrap_button "Save Changes" button_type="submit" value="Update" button_class="btn btn-success pull-right btn-sm table-submit" %}
-
-                </div>
-                </div>
-
-        </form>
+                </form>
+            </div>
+        </div>
     </div>
 </div>
-
 {% endblock maincontent %}
 
-{% block lastjs %}
-<script type="text/javascript">
-(function ($) {
-    var selectors = {
-        td_input : '.view-items-form td.delete > input',
-        th_delete : '.view-items-form th.delete'
-    };
-
-    function addCheckbox(selector, check_id) {
-        var check = $('<input>', {
-                type: 'checkbox',
-                id: check_id
-            }),
-            label = $('<label>', {
-                for: check_id
-            });
-
-        label.html(
-            $(selector).html()
-        );
-
-        $(selector).html("")
-                   .append(check)
-                   .append(label);
-    }
-
-    function toggleSelection(e) {
-        var checked = $(e.target).prop("checked");
-        $(selectors.td_input).prop("checked", checked);
-    }
-
-    function setSelectAll(e) {
-        var checked = $(selectors.td_input + ':checked').length,
-            all = $(selectors.td_input).length,
-            checked_all = all === checked;
-
-        $(selectors.th_delete + ' input').prop("checked", checked_all);
-    }
-
-    function init() {
-        addCheckbox(selectors.th_delete, 'select-all-items');
-
-        $(selectors.th_delete + ' input').on('change', toggleSelection);
-        $(selectors.td_input).on('change', setSelectAll);
-    }
-
-    init();
-})(jQuery);
-</script>
-{% endblock lastjs %}
diff --git a/django/website/hid/tests/categorize_items_tests.py b/django/website/hid/tests/categorize_items_tests.py
index f4f80d93bc83811c787f0c443eb3d09e734aefa5..75c1ad6c332133a5e318ae4f5e1bf46da7f1adb8 100644
--- a/django/website/hid/tests/categorize_items_tests.py
+++ b/django/website/hid/tests/categorize_items_tests.py
@@ -1,10 +1,16 @@
 from __future__ import unicode_literals, absolute_import
 import pytest
 
+from django.core.urlresolvers import reverse
+from django.test import RequestFactory
 from taxonomies.tests.factories import TermFactory, TaxonomyFactory
 
 import transport
-from ..views import add_categories
+from .views_tests import fix_messages
+from ..views import add_items_categories
+
+
+ReqFactory = RequestFactory()
 
 
 @pytest.fixture
@@ -21,10 +27,12 @@ def item():
 
 
 @pytest.mark.django_db
-def test_add_categories_adds_term_to_item(term, item):
-    category_list = [(item['id'], term.name), ]
+def test_add_items_categories_adds_term_to_item(term, item):
+    url = reverse('data-view-process')
+    request = ReqFactory.post(url, {'a': 'b'})
+    request = fix_messages(request)
 
-    add_categories(category_list)
+    add_items_categories(request, [item['id']], term.name)
 
     [item_data] = transport.items.list()
     [term_data] = item_data['terms']
diff --git a/django/website/hid/tests/itemtable_tests.py b/django/website/hid/tests/itemtable_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ffc61fb4c3f61b84cda4f5a4ace17878d715ade
--- /dev/null
+++ b/django/website/hid/tests/itemtable_tests.py
@@ -0,0 +1,18 @@
+import mock
+
+
+from hid.tables import ItemTable
+
+
+def test_get_selected_returns_empty_list_on_empty_selection():
+    params = mock.MagicMock()
+    params.getlist.return_value = []
+
+    assert ItemTable.get_selected(params) == []
+
+
+def test_get_selected_returns_submitted_values_as_ints():
+    params = mock.MagicMock()
+    params.getlist.return_value = ["201", "199", "3"]
+
+    assert ItemTable.get_selected(params) == [201, 199, 3]
diff --git a/django/website/hid/tests/util_javascript_tests.py b/django/website/hid/tests/util_javascript_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..78ab552f514236017f315cb8683192747c093f30
--- /dev/null
+++ b/django/website/hid/tests/util_javascript_tests.py
@@ -0,0 +1,16 @@
+""" This file defines a number of Javascript tests
+    for a range of small javascript enhancements
+    and utilities.
+
+    Each feature should have it's own test class and
+    test template.
+"""
+from djangojs.runners import QUnitSuite, JsTemplateTestCase
+
+
+class SelectAllCheckboxTest(QUnitSuite, JsTemplateTestCase):
+    template_name = 'hid/tests/select_all_checkbox.html'
+    js_files = (
+        'js/jquery.min.js',
+        'hid/js/select_all_checkbox.js'
+    )
diff --git a/django/website/hid/tests/views_tests.py b/django/website/hid/tests/views_tests.py
index 4c8b34380d258b97a0033c1de88e71f475963d06..75c285f49e7de6d6dd46539f251f2e6952791d4f 100644
--- a/django/website/hid/tests/views_tests.py
+++ b/django/website/hid/tests/views_tests.py
@@ -1,4 +1,3 @@
-import mock
 import pytest
 
 from django.contrib.messages.storage.fallback import FallbackStorage
@@ -7,11 +6,10 @@ from django.http import HttpResponseRedirect
 from django.test import RequestFactory
 
 from ..views import (
-    get_deleted,
     process_items,
-    get_categories,
     delete_items,
     ViewItems,
+    DELETE_COMMAND
 )
 
 from taxonomies.tests.factories import (
@@ -45,64 +43,6 @@ def check_message(request, content):
     return False
 
 
-def test_get_deleted_returns_empty_list_on_empty_selection():
-    params = mock.MagicMock()
-    params.getlist.return_value = []
-
-    assert get_deleted(params) == []
-
-
-def test_get_deleted_returns_submitted_values_as_ints():
-    params = mock.MagicMock()
-    params.getlist.return_value = ["201", "199", "3"]
-
-    assert get_deleted(params) == [201, 199, 3]
-
-
-def test_get_categories_returns_id_category_pairs():
-    post_params = {
-        'category-123': "second",
-        'category-99': "third",
-        'category-56': "first",
-        'category-1': "second",
-    }
-    expected = [
-        (123, "second"),
-        (99, "third"),
-        (56, "first"),
-        (1, "second")
-    ]
-    assert sorted(get_categories(post_params)) == sorted(expected)  # Order is not important
-
-
-def test_get_categories_filters_out_non_categories():
-    post_params = {
-        'category-123': "second",
-        'category-99': "third",
-        'notcat-1': "second",
-    }
-    expected = [
-        (123, "second"),
-        (99, "third"),
-    ]
-    assert sorted(get_categories(post_params)) == sorted(expected)  # Order is not important
-
-
-def test_get_categories_filters_out_removed():
-    post_params = {
-        'category-123': "second",
-        'category-99': "third",
-        'category-56': "first",
-        'category-1': "second",
-    }
-    removed = [1, 56]
-    expected = [
-        (123, "second"),
-        (99, "third"),
-    ]
-    assert sorted(get_categories(post_params, removed)) == sorted(expected)  # Order is not important
-
-
 @pytest.fixture
 def request_item():
     '''Create item and request'''
@@ -113,7 +53,10 @@ def request_item():
     [item] = list(transport.items.list())
 
     url = reverse('data-view-process')
-    request = ReqFactory.post(url, {'delete': [item['id']]})
+    request = ReqFactory.post(url, {
+        'action': DELETE_COMMAND,
+        'select_item_id': [item['id']]}
+    )
     request = fix_messages(request)
 
     return [request, item]
@@ -151,7 +94,8 @@ def test_process_items_always_redirects_to_data_view():
     assert isinstance(response, HttpResponseRedirect) is True
 
     request.method = 'POST'
-    request = ReqFactory.post(url)
+    request = ReqFactory.post(url, {})
+    request = fix_messages(request)
     response = process_items(request)
     assert response.url == redirect_url
     assert isinstance(response, HttpResponseRedirect) is True
@@ -190,6 +134,7 @@ def test_get_category_options_uses_terms():
     assert (type_3.name, type_3.long_name) in options
     assert (other_term.name, other_term.long_name) not in options
 
+
 @pytest.mark.django_db
 def test_get_category_options_with_no_taxonomy_returns_all():
     # TODO: Rewrite tests to use transport layer
diff --git a/django/website/hid/views.py b/django/website/hid/views.py
index 97fed520e5334556c8574f608804ef625c9bb0d4..bce3045d9604d0b1ce754b9a39723bf3873a60ee 100644
--- a/django/website/hid/views.py
+++ b/django/website/hid/views.py
@@ -11,11 +11,15 @@ from django_tables2 import SingleTableView
 from chn_spreadsheet.importer import Importer, SheetImportException
 from data_layer.models import Term
 import transport
+from transport.exceptions import TransportException
+from .assets import require_assets
 from .forms import UploadForm, get_spreadsheet_choices
 from .tables import ItemTable
 
 
 QUESTION_TYPE_TAXONOMY = 'ebola-questions'
+ADD_CATEGORY_PREFIX = 'add-category-'
+DELETE_COMMAND = 'delete'
 
 
 class ListSources(TemplateView):
@@ -101,23 +105,51 @@ class ViewItems(SingleTableView):
         context = super(ViewItems, self).get_context_data(**kwargs)
         context['type_label'] = _('Questions')
         context['upload_form'] = UploadForm(initial={'source': 'geopoll'})
+        context['actions'] = [
+            self._build_action_dropdown_group(
+                items=[(DELETE_COMMAND, _('Delete Selected'))]
+            ),
+            self._build_action_dropdown_group(
+                label=_('Set question type'),
+                items=self.get_category_options(),
+                prefix=ADD_CATEGORY_PREFIX
+            )
+        ]
+
+        require_assets('hid/js/automatic_file_upload.js')
+        require_assets('hid/js/select_all_checkbox.js')
         return context
 
+    def _build_action_dropdown_group(self, label='', items=[], prefix=''):
+        """ Helper method to build a group of actions used in the
+            action dropdown.
 
-def get_deleted(params):
-    return [int(x) for x in params.getlist("delete", [])]
+            Args:
+                - label: Label of the group of action;
+                - items: List of items in the group. Each item is a tupple
+                         consisting of the command suffix and the display
+                         name;
+                - prefix: A string used to prefix the command string.
 
-
-def get_categories(params, deleted_ids=[]):
-    removed = set(deleted_ids)
-
-    categories = [(int(key[9:]), val)
-                  for key, val in params.items()
-                  if key.startswith("category-")]
-    return [cat for cat in categories if cat[1] and cat[0] not in removed]
+            Returns:
+                A dictionary representing the action group.
+        """
+        return {
+            'label': label,
+            'items': dict(
+                [(prefix + k, v) for k, v in items]
+            )
+        }
 
 
 def delete_items(request, deleted):
+    """ Delete the given items, and set a success/failure
+        on the request
+
+        Args:
+            - request: Current request object
+            - items: List of items to delete
+    """
     try:
         transport.items.bulk_delete(deleted)
         num_deleted = len(deleted)
@@ -130,38 +162,61 @@ def delete_items(request, deleted):
         messages.error(request, msg)
 
 
-def add_categories(categories):
-    """ Add specified category Terms to The items
-    as specified in categories list.
+def add_items_categories(request, items, category):
+    """ Add the given category to the given items,
+        and set a success/failure on the request
 
-    args:
-        categories: a list of item ids and term names:
-            [ (<item-id>, <term-name>), ... ]
+        Args:
+            - request: Current request object
+            - items: List of item ids to add the category too
+            - category: Category name to add
     """
-    for item_id, term_name in categories:
-        transport.items.add_term(
-            item_id,
-            QUESTION_TYPE_TAXONOMY,
-            term_name,
-        )
-    # Did we want to test for any failures or exceptions ?
-    # TODO: Add messages/success/error reporting here?
+    success = 0
+    failed = 0
+    for item_id in items:
+        try:
+            transport.items.add_term(
+                item_id,
+                QUESTION_TYPE_TAXONOMY,
+                category,
+            )
+            success += 1
+        except TransportException:
+            failed += 1
+    if success > 0:
+        msg = ungettext("Successfully updated %d item.",
+                        "Successfully updated %d items.",
+                        len(items)) % len(items)
+        messages.success(request, msg)
+    if failed > 0:
+        msg = ungettext("Failed to update %d item.",
+                        "Failed to update %d items.",
+                        len(items)) % len(items)
+        messages.success(request, msg)
 
 
 def process_items(request):
-    '''
-    If POST request, then:
-    - delete items that were checked
-    - update categories on those that weren't deleted.
-    '''
+    """ Request to process a selection of items from the
+        view & edit page.
+
+        Args:
+            - request: Request object. This should contain
+              a POST request defining:
+                  - action: The action to apply
+                  - select_action: List of items to apply
+                    the action too.
+    """
     redirect_url = reverse("data-view")
     # Just redirect back to items view on GET
     if request.method == "POST":
-        deleted = get_deleted(request.POST)
-        categories = get_categories(request.POST, deleted)
-        if len(deleted):
-            delete_items(request, deleted)
-        if len(categories):
-            add_categories(categories)
+        selected = ItemTable.get_selected(request.POST)
+        action = request.POST.get('action')
+        if action == DELETE_COMMAND:
+            delete_items(request, selected)
+        elif action and action.startswith(ADD_CATEGORY_PREFIX):
+            category = action[len(ADD_CATEGORY_PREFIX):]
+            add_items_categories(request, selected, category)
+        else:
+            messages.error(request, _('Unknown action'))
 
     return HttpResponseRedirect(redirect_url)
diff --git a/django/website/media/less/internews.less b/django/website/media/less/internews.less
index bff61e8d249d6d77bda86e920625d52658c3b121..b8733ad924397eb2a49b210d22f34070a4a97775 100644
--- a/django/website/media/less/internews.less
+++ b/django/website/media/less/internews.less
@@ -402,6 +402,12 @@ body {
     line-height: 1.33;
 }
 
+// Auto-upload button. Progressive enhencement, this
+// will be displayed if the associated js executes.
+.auto-upload-file .upload-button {
+    display: none;
+}
+
 // Grid Demo Elements
 
 .show-grid [class^="col-"] {
@@ -519,4 +525,4 @@ body {
 .fa-fw {
   color:@gray-base;
   margin-right:5px;
-}
\ No newline at end of file
+}
diff --git a/django/website/media/less/sources.less b/django/website/media/less/sources.less
index 8cd494cc98b5eef1b5a215b9a6c1ef5585474f3a..545060171168898f634957b6c08ce67607bcb86c 100644
--- a/django/website/media/less/sources.less
+++ b/django/website/media/less/sources.less
@@ -1,9 +1,5 @@
 //Styles for import, loading and sources
 
-.bootstrap3-multi-input {
-  display:none;
-}
-
 .item-source-info > h2 {
   margin:15px;
 }
@@ -12,4 +8,4 @@
   .item-source-info > h2 > img {
     max-width: 120px;
   }
-}
\ No newline at end of file
+}
diff --git a/django/website/media/less/view-edit.less b/django/website/media/less/view-edit.less
index a255a1943875f441f858cfbb741360a91e99c4b9..923bfa2bcf335e2da0bccdfdef4c1a34bdc79fe0 100644
--- a/django/website/media/less/view-edit.less
+++ b/django/website/media/less/view-edit.less
@@ -29,7 +29,7 @@ td.created, td.timestamp {
   font-size:@font-size-small;
 }
 
-.delete > label {
+.select_action > label {
   margin-bottom:0;
 }
 
@@ -82,10 +82,27 @@ td.created, td.timestamp {
   float:inherit;
 }
 
+// Form header actions
+.panel-body form .form-header-group {
+    margin-bottom: @padding-base-vertical;
+}
+
+.panel-body form .form-header-group .form-control {
+    float: left;
+    width: auto;
+    height: auto;
+    padding: @padding-xs-horizontal @padding-base-vertical;
+    margin-right: @padding-small-horizontal;
+}
+
+.panel-body .form-header-group select option[default] {
+    font-weight: bold;
+}
+
 // Checkboxes
 
-td.delete,
-th.delete {
+td.select_action,
+th.select_action {
   background-color:rgba(0,0,0,0.05);
   text-align:center;
 }
@@ -117,10 +134,3 @@ input[type="checkbox"]:checked {
  outline:0;
  border:none;
 }
-
-// Place the upload button in the header.
-form.header-upload-form {
-    position: absolute;
-    top: 10px;
-    right: 150px;
-}
diff --git a/django/website/rest_api/views.py b/django/website/rest_api/views.py
index ba74fe4ccfa5ca5a68c8a5082b084473bd23f5cd..5893fbccbee318296b6682dcaf67f833a6eac00b 100644
--- a/django/website/rest_api/views.py
+++ b/django/website/rest_api/views.py
@@ -70,7 +70,9 @@ class TaxonomyViewSet(viewsets.ModelViewSet):
             data = {'detail': message}
             return Response(data, status=status.HTTP_400_BAD_REQUEST)
 
-        terms = Term.objects.filter(taxonomy=taxonomy).annotate(count=Count('message'))
+        terms = Term.objects.filter(taxonomy=taxonomy).annotate(
+            count=Count('items')
+        )
         data = TermItemCountSerializer(terms, many=True).data
 
         return Response(data, status=status.HTTP_200_OK)
diff --git a/django/website/settings.py b/django/website/settings.py
index 854f9b9f6d030b505fccf041421b8cd80f2e0a8f..3493ef3b7112ddbdcfb56c864ce5e7ffad5cf557 100644
--- a/django/website/settings.py
+++ b/django/website/settings.py
@@ -153,7 +153,7 @@ DATA_LAYER_APPS = (
     'transport',
 )
 
-INSTALLED_APPS = LOCAL_APPS + DATA_LAYER_APPS + THIRD_PARTY_APPS + DJANGO_APPS
+INSTALLED_APPS = DATA_LAYER_APPS + LOCAL_APPS + THIRD_PARTY_APPS + DJANGO_APPS
 
 ########## END APP CONFIGURATION
 
diff --git a/django/website/templates/base_side.html b/django/website/templates/base_side.html
index 7140c4619ef76741ed4d1f7386e9605788d15210..33524e649e08cefa293c2583201a8642671cf751 100644
--- a/django/website/templates/base_side.html
+++ b/django/website/templates/base_side.html
@@ -9,7 +9,7 @@
         <nav class="sidebar-nav navbar-collapse collapse">
             <ul class="nav" id="side-menu">
                 <li><a class="active" href="{% url "dashboard" %}"><span class="fa fa-dashboard fa-fw"></span> Dashboard</a></li>
-                <li><a href="{% url "sources" %}"><span class="fa fa-download fa-fw"></span> Sources</a></li>
+                {# <li><a href="{% url "sources" %}"><span class="fa fa-download fa-fw"></span> Sources</a></li> #}
                 <li><a href="{% url "data-view" %}"><span class="fa fa-pencil fa-fw"></span> View &amp; Edit</a></li>
             </ul>
         </nav>
diff --git a/django/website/transport/items.py b/django/website/transport/items.py
index c8464efe6327b8608a1d314fed0f3f70ea01ef73..1bf54b00bc2adc0e53be8e573eade7dc6625f6a7 100644
--- a/django/website/transport/items.py
+++ b/django/website/transport/items.py
@@ -98,6 +98,9 @@ def add_term(item_id, taxonomy_slug, name):
     returns:
         response from the server
 
+    raises:
+       TransportException on failure
+
     For the moment both the taxonomy and term must already exist.
     """
     view = ItemViewSet.as_view(actions={'post': 'add_term'})