From 85b904f176c11cd4af52afaf1bdd35509ce49193 Mon Sep 17 00:00:00 2001 From: Chris Dodge Date: Thu, 13 Jun 2013 16:01:06 -0400 Subject: [PATCH] fix sizing of the delete column --- .../commands/empty_asset_trashcan.py | 28 +++++++++++ .../commands/restore_asset_from_trashcan.py | 17 +++++++ cms/djangoapps/contentstore/views/assets.py | 16 ++++-- cms/static/js/base.js | 24 +++++++++ cms/static/sass/views/_assets.scss | 4 ++ cms/templates/asset_index.html | 28 ++++++++++- cms/urls.py | 4 +- .../lib/xmodule/xmodule/contentstore/utils.py | 49 +++++++++++++++++++ 8 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 cms/djangoapps/contentstore/management/commands/empty_asset_trashcan.py create mode 100644 cms/djangoapps/contentstore/management/commands/restore_asset_from_trashcan.py create mode 100644 common/lib/xmodule/xmodule/contentstore/utils.py diff --git a/cms/djangoapps/contentstore/management/commands/empty_asset_trashcan.py b/cms/djangoapps/contentstore/management/commands/empty_asset_trashcan.py new file mode 100644 index 0000000000..c10700c7af --- /dev/null +++ b/cms/djangoapps/contentstore/management/commands/empty_asset_trashcan.py @@ -0,0 +1,28 @@ +### +### Script for cloning a course +### +from django.core.management.base import BaseCommand, CommandError +from xmodule.course_module import CourseDescriptor +from xmodule.contentstore.utils import empty_asset_trashcan +from xmodule.modulestore.django import modulestore +from .prompt import query_yes_no + + +class Command(BaseCommand): + help = '''Empty the trashcan. Can pass an optional course_id to limit the damage.''' + + def handle(self, *args, **options): + if len(args) != 1 and len(args) != 0: + raise CommandError("empty_asset_trashcan requires one or no arguments: ||") + + locs = [] + + if len(args) == 1: + locs.append(CourseDescriptor.id_to_location(args[0])) + else: + courses = modulestore('direct').get_courses() + for course in courses: + locs.append(course.location) + + if query_yes_no("Emptying trashcan. Confirm?", default="no"): + empty_asset_trashcan(locs) diff --git a/cms/djangoapps/contentstore/management/commands/restore_asset_from_trashcan.py b/cms/djangoapps/contentstore/management/commands/restore_asset_from_trashcan.py new file mode 100644 index 0000000000..0a4be40efc --- /dev/null +++ b/cms/djangoapps/contentstore/management/commands/restore_asset_from_trashcan.py @@ -0,0 +1,17 @@ +### +### Script for cloning a course +### +from django.core.management.base import BaseCommand, CommandError +from xmodule.contentstore.utils import restore_asset_from_trashcan +from xmodule.modulestore import Location + + +class Command(BaseCommand): + help = '''Restore a deleted asset from the trashcan back to it's original course''' + + def handle(self, *args, **options): + if len(args) != 1 and len(args) != 0: + raise CommandError("restore_asset_from_trashcan requires one argument: ") + + restore_asset_from_trashcan(args[0]) + diff --git a/cms/djangoapps/contentstore/views/assets.py b/cms/djangoapps/contentstore/views/assets.py index f1f51b3ca9..62dee1ba21 100644 --- a/cms/djangoapps/contentstore/views/assets.py +++ b/cms/djangoapps/contentstore/views/assets.py @@ -80,7 +80,12 @@ def asset_index(request, org, course, name): 'active_tab': 'assets', 'context_course': course_module, 'assets': asset_display, - 'upload_asset_callback_url': upload_asset_callback_url + 'upload_asset_callback_url': upload_asset_callback_url, + 'remove_asset_callback_url': reverse('remove_asset', kwargs={ + 'org': org, + 'course': course, + 'name': name + }) }) @@ -151,16 +156,19 @@ def upload_asset(request, org, course, coursename): @ensure_csrf_cookie @login_required -def remove_asset(request, org, course, name, location): +def remove_asset(request, org, course, name): ''' This method will perform a 'soft-delete' of an asset, which is basically to copy the asset from the main GridFS collection and into a Trashcan ''' get_location_and_verify_access(request, org, course, name) + location = request.POST['location'] + logging.debug('location = {0}'.format(location)) + # make sure the location is valid try: - loc = StaticContent.get_location_from_path(request.path) + loc = StaticContent.get_location_from_path(location) except InvalidLocationError: # return a 'Bad Request' to browser as we have a malformed Location response = HttpResponse() @@ -195,6 +203,8 @@ def remove_asset(request, org, course, name, location): # remove from cache del_cached_content(content.location) + return HttpResponse() + @ensure_csrf_cookie @login_required diff --git a/cms/static/js/base.js b/cms/static/js/base.js index c626fa1b3f..9cb70592cb 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -146,6 +146,7 @@ $(document).ready(function() { $('.edit-section-start-save').bind('click', saveSetSectionScheduleDate); $('.upload-modal .choose-file-button').bind('click', showFileSelectionMenu); + $('.remove-asset-button').bind('click', removeAsset); $body.on('click', '.section-published-date .edit-button', editSectionPublishDate); $body.on('click', '.section-published-date .schedule-button', editSectionPublishDate); @@ -398,6 +399,29 @@ function _deleteItem($el) { }); } +function removeAsset(e) { + e.preventDefault(); + + // replace with new notification moodal + if (!confirm('Are you sure you wish to delete this item. It cannot be reversed!')) return; + + var remove_asset_url = $('.asset-library').data('remove-asset-callback-url'); + var location = $(this).closest('tr').data('id'); + var that = this; + $.post(remove_asset_url, + { 'location': location }, + function() { + // show the alert + $(".wrapper-alert-confirmation").addClass("is-shown").attr('aria-hidden','false'); + $(that).closest('tr').remove(); + analytics.track('Deleted Asset', { + 'course': course_location_analytics, + 'id': location + }); + } + ); +} + function showUploadModal(e) { e.preventDefault(); $modal = $('.upload-modal').show(); diff --git a/cms/static/sass/views/_assets.scss b/cms/static/sass/views/_assets.scss index d01dd988ef..d4cff42ee9 100644 --- a/cms/static/sass/views/_assets.scss +++ b/cms/static/sass/views/_assets.scss @@ -76,6 +76,10 @@ body.course.uploads { width: 250px; } + .delete-col { + width: 20px; + } + .embeddable-xml-input { @include box-shadow(none); width: 100%; diff --git a/cms/templates/asset_index.html b/cms/templates/asset_index.html index f03a9012f8..0de38510f5 100644 --- a/cms/templates/asset_index.html +++ b/cms/templates/asset_index.html @@ -1,5 +1,6 @@ <%inherit file="base.html" /> <%! from django.core.urlresolvers import reverse %> +<%! from django.utils.translation import ugettext as _ %> <%block name="bodyclass">is-signedin course uploads <%block name="title">Files & Uploads @@ -30,6 +31,9 @@ + + + @@ -56,7 +60,7 @@
-
+
@@ -64,6 +68,7 @@ + @@ -86,6 +91,9 @@ + % endfor @@ -129,3 +137,21 @@ + +<%block name="view_alerts"> + +
+
+ + +
+

${_('Your file has been deleted.')}

+
+ + + + close alert + +
+
+ diff --git a/cms/urls.py b/cms/urls.py index ebd5e33323..a9a7f0a68a 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -76,8 +76,8 @@ urlpatterns = ('', # nopep8 url(r'^(?P[^/]+)/(?P[^/]+)/assets/(?P[^/]+)$', 'contentstore.views.asset_index', name='asset_index'), - url(r'^(?P[^/]+)/(?P[^/]+)/assets/remove/(?P.*?)$', - 'contentstore.views.remove_asset', name='remove_asset'), + url(r'^(?P[^/]+)/(?P[^/]+)/assets/(?P[^/]+)/remove$', + 'contentstore.views.assets.remove_asset', name='remove_asset'), # this is a generic method to return the data/metadata associated with a xmodule url(r'^module_info/(?P.*)$', diff --git a/common/lib/xmodule/xmodule/contentstore/utils.py b/common/lib/xmodule/xmodule/contentstore/utils.py new file mode 100644 index 0000000000..fadc06c84e --- /dev/null +++ b/common/lib/xmodule/xmodule/contentstore/utils.py @@ -0,0 +1,49 @@ +from xmodule.modulestore import Location +from xmodule.contentstore.content import StaticContent +from .django import contentstore + + +def empty_asset_trashcan(course_locs=None): + ''' + This method will hard delete all assets (optionally within a course_id) from the trashcan + ''' + store = contentstore('trashcan') + + for course_loc in course_locs: + # first delete all of the thumbnails + thumbs = store.get_all_content_thumbnails_for_course(course_loc) + for thumb in thumbs: + thumb_loc = Location(thumb["_id"]) + id = StaticContent.get_id_from_location(thumb_loc) + print "Deleting {0}...".format(id) + store.delete(id) + + # then delete all of the assets + assets = store.get_all_content_for_course(course_loc) + for asset in assets: + asset_loc = Location(asset["_id"]) + id = StaticContent.get_id_from_location(asset_loc) + print "Deleting {0}...".format(id) + store.delete(id) + + +def restore_asset_from_trashcan(location): + ''' + This method will restore an asset which got soft deleted and put back in the original course + ''' + trash = contentstore('trashcan') + store = contentstore() + + loc = StaticContent.get_location_from_path(location) + content = trash.find(loc) + + # ok, save the content into the courseware + store.save(content) + + # see if there is a thumbnail as well, if so move that as well + if content.thumbnail_location is not None: + try: + thumbnail_content = trash.find(content.thumbnail_location) + store.save(thumbnail_content) + except: + pass # OK if this is left dangling
Name Date Added URL
+ +