Index: ow/templates/bulk_files.pt
===================================================================
--- ow/templates/bulk_files.pt	(revision 62111629eafee9888a762344d0e949c0317ada40)
+++ ow/templates/bulk_files.pt	(revision 83406611f1eb52c6cdf00f77f31010314ee2da76)
@@ -60,4 +60,9 @@
           </p>
 
+          <p>
+            <a href="" tal:attributes="href request.resource_url(bulk_file, 'delete')"
+               i18n:translate="">delete</a>
+          </p>
+
           <!--!
                <tal:workouts tal:condition="bulk_file.loaded and bulk_file.workout_ids">
Index: ow/templates/delete_bulk_file.pt
===================================================================
--- ow/templates/delete_bulk_file.pt	(revision 83406611f1eb52c6cdf00f77f31010314ee2da76)
+++ ow/templates/delete_bulk_file.pt	(revision 83406611f1eb52c6cdf00f77f31010314ee2da76)
@@ -0,0 +1,66 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="OpenWorkouts"
+      metal:use-macro="load: base.pt"
+      tal:attributes="lang request.locale_name">
+
+  <metal:head-title metal:fill-slot="head-title">
+    <tal:t i18n:translate="">Delete bulk file</tal:t> -
+    <tal:w tal:content="context.file_name"></tal:w>
+  </metal:head-title>
+
+  <metal:title metal:fill-slot="title">
+    <tal:t i18n:translate="">Delete bulk file</tal:t> -
+    <tal:w tal:content="context.file_name"></tal:w>
+  </metal:title>
+
+  <metal:content metal:fill-slot="content">
+    <div id="delete_workout" class="ow-forms">
+      <a class="back" href="" tal:attributes="href request.resource_url(user, 'bulk-files')"
+         i18n:translate="">Cancel</a>
+      <form action="" method="POST">
+        <fieldset>
+          <input id="_csrf" name="_csrf" type="hidden" value=""
+                 tal:attributes="value request.session.get_csrf_token()" />
+          <input id="delete" name="delete" type="hidden" value="yes" />
+
+          <div id="workout"
+               tal:define="timezone request.root[request.authenticated_userid].timezone">
+            <h2 tal:content="context.file_name"></h2>
+
+            <ul class="workout-activity-summary">
+              <li>
+                <span i18n:translate="">Uploaded:</span>
+                <span tal:content="context.uploaded_in_timezone(timezone)"></span>
+              </li>
+              <tal:loaded tal:condition="context.loaded">
+                <li>
+                  <span i18n:translate="">File loaded on:</span>
+                  <span tal:content="context.loaded_in_timezone(timezone)"></span>
+                </li>
+              </tal:loaded>
+              <tal:not_loaded tal:condition="not context.loaded">
+                <li>
+                  <span i18n:translate="">This file has not been yet processed</span>
+                </li>
+              </tal:not_loaded>
+
+            </ul>
+          </div>
+        </fieldset>
+        <div>
+          <p i18n:translate="">Are you sure you want to delete this file?</p>
+        </div>
+        <div>
+          <input class="button button-normal" type="submit" name="submit" value="Yes, delete"
+                 i18n:attributes="value" />
+        </div>
+      </form>
+    </div>
+
+  </metal:content>
+
+</html>
Index: ow/tests/views/test_bulk.py
===================================================================
--- ow/tests/views/test_bulk.py	(revision 62111629eafee9888a762344d0e949c0317ada40)
+++ ow/tests/views/test_bulk.py	(revision 83406611f1eb52c6cdf00f77f31010314ee2da76)
@@ -216,2 +216,70 @@
         assert response == {'bulk_files': [bulk_file]}
         assert response['bulk_files'][0].uid == str(user.uid)
+
+    def test_delete_bulk_file_get(self, dummy_request):
+        request = dummy_request
+        user = request.root.users[0]
+        # let's add one bulk file
+        uid = str(user.uid)
+        bulk_file = BulkFile(uid=uid)
+        bulk_file_path = os.path.join(
+            os.path.dirname(os.path.dirname(__file__)),
+            'fixtures/bulk-fit.zip')
+        with open(bulk_file_path, 'rb') as _bulk_file:
+            bulk_file.compressed_file = create_blob(
+                _bulk_file.read(), file_extension='zip', binary=True)
+        bulk_file.file_name = 'bulk-fit.zip'
+        bulk_file.file_type = 'zip'
+        request.root['_bulk_files'].add_bulk_file(bulk_file)
+        assert len(request.root['_bulk_files']) == 1
+        response = bulk_views.delete_bulk_file(bulk_file, request)
+        assert response == {'user': user}
+        assert len(request.root['_bulk_files']) == 1
+
+    def test_delete_bulk_file_post_invalid(self, dummy_request):
+        request = dummy_request
+        request.method = 'POST'
+        # invalid, missing confirmation delete hidden value
+        request.POST = MultiDict({'submit': True})
+        user = request.root.users[0]
+        # let's add one bulk file
+        uid = str(user.uid)
+        bulk_file = BulkFile(uid=uid)
+        bulk_file_path = os.path.join(
+            os.path.dirname(os.path.dirname(__file__)),
+            'fixtures/bulk-fit.zip')
+        with open(bulk_file_path, 'rb') as _bulk_file:
+            bulk_file.compressed_file = create_blob(
+                _bulk_file.read(), file_extension='zip', binary=True)
+        bulk_file.file_name = 'bulk-fit.zip'
+        bulk_file.file_type = 'zip'
+        request.root['_bulk_files'].add_bulk_file(bulk_file)
+        assert len(request.root['_bulk_files']) == 1
+        response = bulk_views.delete_bulk_file(bulk_file, request)
+        assert response == {'user': user}
+        assert len(request.root['_bulk_files']) == 1
+
+    def test_delete_bulk_file_post_valid(self, dummy_request):
+        request = dummy_request
+        request.method = 'POST'
+        # invalid, missing confirmation delete hidden value
+        request.POST = MultiDict({'submit': True, 'delete': 'yes'})
+        user = request.root.users[0]
+        # let's add one bulk file
+        uid = str(user.uid)
+        bulk_file = BulkFile(uid=uid)
+        bulk_file_path = os.path.join(
+            os.path.dirname(os.path.dirname(__file__)),
+            'fixtures/bulk-fit.zip')
+        with open(bulk_file_path, 'rb') as _bulk_file:
+            bulk_file.compressed_file = create_blob(
+                _bulk_file.read(), file_extension='zip', binary=True)
+        bulk_file.file_name = 'bulk-fit.zip'
+        bulk_file.file_type = 'zip'
+        request.root['_bulk_files'].add_bulk_file(bulk_file)
+        assert len(request.root['_bulk_files']) == 1
+        response = bulk_views.delete_bulk_file(bulk_file, request)
+        # after a successful delete, we send the user back to his dashboard
+        assert isinstance(response, HTTPFound)
+        assert response.location == request.resource_url(user, 'bulk-files')
+        assert len(request.root['_bulk_files']) == 0
Index: ow/views/bulk.py
===================================================================
--- ow/views/bulk.py	(revision 62111629eafee9888a762344d0e949c0317ada40)
+++ ow/views/bulk.py	(revision 83406611f1eb52c6cdf00f77f31010314ee2da76)
@@ -54,4 +54,22 @@
 
 @view_config(
+    context=BulkFile,
+    permission='delete',
+    name='delete',
+    renderer='ow:templates/delete_bulk_file.pt')
+def delete_bulk_file(context, request):
+    """
+    Delete a bulk file
+    """
+    uid = context.uid
+    user = request.root.get_user_by_uid(uid)
+    if 'submit' in request.POST:
+        if request.POST.get('delete', None) == 'yes':
+            del request.root['_bulk_files'][str(context.bfid)]
+            return HTTPFound(location=request.resource_url(user, 'bulk-files'))
+    return {'user': user}
+
+
+@view_config(
     context=User,
     permission='edit',
