diff --git a/rodan-client/code/src/js/Views/Master/Dialog/ViewConfirmationDialog.js b/rodan-client/code/src/js/Views/Master/Dialog/ViewConfirmationDialog.js new file mode 100644 index 000000000..ec2cb8780 --- /dev/null +++ b/rodan-client/code/src/js/Views/Master/Dialog/ViewConfirmationDialog.js @@ -0,0 +1,54 @@ +import $ from 'jquery'; +import _ from 'underscore'; +import RODAN_EVENTS from 'js/Shared/RODAN_EVENTS'; +import Marionette from 'backbone.marionette'; +import Radio from 'backbone.radio'; + +/** + * Password view. + */ +export default class ViewConfirmationDialog extends Marionette.CollectionView +{ + initialize(options) + { + this._message = options.message; + this._onConfirm = options.onConfirm; + } + + templateContext() + { + return { + message: this._message + }; + } + +/////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS +/////////////////////////////////////////////////////////////////////////////////////// + _handleButtonConfirm() + { + this._onConfirm(); + } + + _handleButtonCancel() + { + Radio.channel("rodan").request(RODAN_EVENTS.REQUEST__MODAL_HIDE); + } + +} + +ViewConfirmationDialog.prototype.modelEvents = { + 'all': 'render' + }; + +ViewConfirmationDialog.prototype.ui = { + buttonConfirm: '#button-confirm', + buttonCancel: '#button-cancel' + }; + +ViewConfirmationDialog.prototype.events = { + 'click @ui.buttonConfirm': '_handleButtonConfirm', + 'click @ui.buttonCancel': '_handleButtonCancel' + }; + +ViewConfirmationDialog.prototype.template = _.template($('#template-dialog_confirm').text()); diff --git a/rodan-client/code/src/js/Views/Master/Main/WorkflowRun/Individual/LayoutViewIndividualWorkflowRun.js b/rodan-client/code/src/js/Views/Master/Main/WorkflowRun/Individual/LayoutViewIndividualWorkflowRun.js index 62e7e40b3..2feef317f 100644 --- a/rodan-client/code/src/js/Views/Master/Main/WorkflowRun/Individual/LayoutViewIndividualWorkflowRun.js +++ b/rodan-client/code/src/js/Views/Master/Main/WorkflowRun/Individual/LayoutViewIndividualWorkflowRun.js @@ -8,6 +8,7 @@ import ViewResourceCollection from 'js/Views/Master/Main/Resource/Collection/Vie import ViewResourceCollectionItem from 'js/Views/Master/Main/Resource/Collection/ViewResourceCollectionItem'; import ViewRunJobCollection from 'js/Views/Master/Main/RunJob/Collection/ViewRunJobCollection'; import ViewRunJobCollectionItem from 'js/Views/Master/Main/RunJob/Collection/ViewRunJobCollectionItem'; +import ViewConfirmationDialog from '../../../Dialog/ViewConfirmationDialog'; /** * WorkflowRun view. @@ -113,7 +114,11 @@ export default class LayoutViewIndividualWorkflowRun extends Marionette.View */ _handleButtonDelete() { - Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOWRUN_DELETE, {workflowrun: this.model}); + const content = new ViewConfirmationDialog({ + message: 'Are you sure you want to delete this WorkflowRun? This will delete all related Run Jobs and Resources.', + onConfirm: () => Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__WORKFLOWRUN_DELETE, { workflowrun: this.model }), + }); + Radio.channel('rodan').request(RODAN_EVENTS.REQUEST__MODAL_SHOW, { title: 'Delete WorkflowRun', content }); } } LayoutViewIndividualWorkflowRun.prototype.modelEvents = { diff --git a/rodan-client/code/templates/Views/Master/Main/Dialog/template-dialog_confirm.html b/rodan-client/code/templates/Views/Master/Main/Dialog/template-dialog_confirm.html new file mode 100644 index 000000000..b08086f3c --- /dev/null +++ b/rodan-client/code/templates/Views/Master/Main/Dialog/template-dialog_confirm.html @@ -0,0 +1,6 @@ +
+

<%- message %>

+
+ + +
diff --git a/rodan-main/code/rodan/models/resource.py b/rodan-main/code/rodan/models/resource.py index 422cf1547..b2de6f97e 100644 --- a/rodan-main/code/rodan/models/resource.py +++ b/rodan-main/code/rodan/models/resource.py @@ -6,7 +6,10 @@ from django.urls import reverse from rodan.constants import task_status from rodan.models.resourcelabel import ResourceLabel +from rodan.models.output import Output from rodan.models.user import User +from django.db.models.signals import post_delete +from django.dispatch import receiver import logging @@ -298,3 +301,12 @@ def get_viewer(self): def viewer_relurl(self): if self.get_viewer() is not None: return reverse("resource-viewer-acquire", args=(self.uuid,)) + +@receiver(post_delete, sender=Output) +def post_output_delete(sender, instance, **kwargs): + """ + Deletes resource when associated output is deleted. Cascade delete does not work due to on_delete=PROTECT in Output model. + In newer Django versions, we can use on_delete=models.RESTRICT in Output model to allow cascade delete to work. + """ + if instance.resource: + instance.resource.delete()