Added task deletion. GUI still ugly, though.
This commit is contained in:
@@ -20,6 +20,26 @@
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fades out the element, then erases its contents and shows the now-empty element again.
|
||||
*/
|
||||
$.fn.fadeOutAndClear = function(fade_speed) {
|
||||
var target = this;
|
||||
this
|
||||
.fadeOut(fade_speed, function() {
|
||||
target
|
||||
.html('')
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.hideAndRemove = function(hide_speed, finished) {
|
||||
this.slideUp(hide_speed, function() {
|
||||
this.remove();
|
||||
if (typeof finished !== 'undefined') finished();
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.removeClassPrefix = function(prefix) {
|
||||
this.each(function(i, el) {
|
||||
var classes = el.className.split(" ").filter(function(c) {
|
||||
@@ -31,6 +51,20 @@
|
||||
};
|
||||
}(jQuery));
|
||||
|
||||
/**
|
||||
* Removes the task from the task list and shot list, and show the 'task-add-link'
|
||||
* when this was the last task in its category.
|
||||
*/
|
||||
function _remove_task_from_list(task_id) {
|
||||
var $task_link = $('#task-' + task_id)
|
||||
var $task_link_parent = $task_link.parent();
|
||||
$task_link.hideAndRemove(300, function() {
|
||||
if ($task_link_parent.children('.task-link').length == 0) {
|
||||
$task_link_parent.find('.task-add-link').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a task in the #task-details div.
|
||||
*/
|
||||
@@ -220,6 +254,36 @@ function shot_save(shot_id, shot_url) {
|
||||
});
|
||||
}
|
||||
|
||||
function task_delete(task_id, task_etag, task_delete_url) {
|
||||
if (task_id === undefined || task_etag === undefined || task_delete_url === undefined) {
|
||||
throw new ReferenceError("task_delete(" + task_id + ", " + task_etag + ", " + task_delete_url + ") called.");
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: task_delete_url,
|
||||
data: {'etag': task_etag}
|
||||
})
|
||||
.done(function(e) {
|
||||
if (console) console.log('Task', task_id, 'was deleted.');
|
||||
$('#task-details').fadeOutAndClear();
|
||||
_remove_task_from_list(task_id);
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
$('#status-bar').text('Unable to delete task, code ' + xhr.status);
|
||||
if (xhr.status == 412) {
|
||||
alert('Someone else edited this task before you deleted it; refresh to try again.');
|
||||
// TODO: implement something nice here. Just make sure we don't throw
|
||||
// away the user's edits. It's up to the user to handle this.
|
||||
// TODO: refresh activity feed and point user to it.
|
||||
} else {
|
||||
// TODO: find a better place to put this error message, without overwriting the
|
||||
// task the user is looking at in-place.
|
||||
$('#task-view-feed').html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$("a.shot-link[data-shot-id]").click(function(e) {
|
||||
e.preventDefault();
|
||||
|
@@ -85,6 +85,13 @@ class TaskManager(object):
|
||||
task.update(api=api)
|
||||
return task
|
||||
|
||||
def delete_task(self, task_id, etag):
|
||||
api = pillar_api()
|
||||
|
||||
self._log.info('Deleting task %s', task_id)
|
||||
task = pillarsdk.Node({'_id': task_id, '_etag': etag})
|
||||
task.delete(api=api)
|
||||
|
||||
|
||||
def setup_app(app):
|
||||
from . import eve_hooks
|
||||
|
@@ -22,6 +22,16 @@ def index():
|
||||
return render_template('attract/tasks/index.html')
|
||||
|
||||
|
||||
@blueprint.route('/<task_id>', methods=['DELETE'])
|
||||
def delete(task_id):
|
||||
log.info('Deleting task %s', task_id)
|
||||
|
||||
etag = request.form['etag']
|
||||
current_attract.task_manager.delete_task(task_id, etag)
|
||||
|
||||
return '', 204
|
||||
|
||||
|
||||
@perproject_blueprint.route('/', endpoint='index')
|
||||
@attract_project_view()
|
||||
def for_project(project, task_id=None):
|
||||
|
@@ -48,11 +48,10 @@
|
||||
href="{{ url_for('attract.shots.perproject.with_task', project_url=project.url, task_id=task._id) }}",
|
||||
class="status-{{ task.properties.status }} task-link")
|
||||
| {% endfor %}
|
||||
| {% if not tasks_for_shots[shot._id][task_type] %}
|
||||
a.task-add(
|
||||
class="task-add-link {% if tasks_for_shots[shot._id][task_type] %}hidden{% endif %}"
|
||||
href="javascript:task_create('{{ shot._id }}', '{{ project.url }}', '{{ task_type }}');")
|
||||
| + Task
|
||||
| {% endif %}
|
||||
| {% endfor %}
|
||||
| {% endfor %}
|
||||
|
||||
|
@@ -48,8 +48,11 @@
|
||||
| {% endfor %}
|
||||
|
||||
.input-transparent-group
|
||||
| {% if 'DELETE' in task.allowed_methods %}
|
||||
button.btn.btn-danger(type='button',onclick="task_delete('{{ task._id }}', '{{ task._etag }}', '{{ url_for('attract.tasks.delete', task_id=task._id, _method='DELETE') }}')") Delete
|
||||
| {% endif %}
|
||||
| {% if 'PUT' in task.allowed_methods %}
|
||||
button.btn.btn-default.btn-block(type='submit') Save Changes
|
||||
button.btn.btn-default(type='submit') Save Changes
|
||||
| {% endif %}
|
||||
|
||||
|
||||
|
@@ -102,3 +102,32 @@ class TaskWorkflowTest(AbstractAttractTest):
|
||||
json=remove_private_keys(json_task),
|
||||
auth_token='token',
|
||||
headers={'If-Match': json_task['_etag']})
|
||||
|
||||
@responses.activate
|
||||
def test_delete_task(self):
|
||||
task = self.create_task()
|
||||
task_id = task['_id']
|
||||
|
||||
self.create_valid_auth_token(ctd.EXAMPLE_PROJECT_OWNER_ID, 'token')
|
||||
node_url = '/api/nodes/%s' % task_id
|
||||
self.get(node_url, auth_token='token')
|
||||
|
||||
with self.app.test_request_context():
|
||||
# Log in as project admin user
|
||||
pillar.auth.login_user(ctd.EXAMPLE_PROJECT_OWNER_ID)
|
||||
|
||||
self.mock_blenderid_validate_happy()
|
||||
self.assertRaises(sdk_exceptions.PreconditionFailed,
|
||||
self.mngr.delete_task,
|
||||
task._id,
|
||||
'jemoeder')
|
||||
self.mngr.delete_task(task._id, task._etag)
|
||||
|
||||
# Test directly with MongoDB
|
||||
with self.app.test_request_context():
|
||||
nodes_coll = self.app.data.driver.db['nodes']
|
||||
found = nodes_coll.find_one(ObjectId(task_id))
|
||||
self.assertTrue(found['_deleted'])
|
||||
|
||||
# Test with Eve
|
||||
self.get(node_url, auth_token='token', expected_status=404)
|
||||
|
Reference in New Issue
Block a user