Răsfoiți Sursa

[search] Added copy and delete functionalities

Enrico Berti 12 ani în urmă
părinte
comite
5212bcf

+ 26 - 1
apps/search/src/search/search_controller.py

@@ -28,7 +28,7 @@ from desktop.lib.exceptions_renderable import PopupException
 from search.api import SolrApi
 from search.conf import SOLR_URL
 from search.models import Collection
-
+from django.utils.translation import ugettext as _
 
 LOG = logging.getLogger(__name__)
 
@@ -77,3 +77,28 @@ class SearchController(object):
       return hue_collection
     else:
       raise PopupException(_('Collection type does not exit: %s') % attrs)
+
+  def delete_collection(self, collection_id):
+    id = collection_id
+    try:
+      Collection.objects.get(id=collection_id).delete()
+    except Exception, e:
+      LOG.warn('Error deleting collection: %s' % e)
+      id = -1
+
+    return id
+
+  def copy_collection(self, collection_id, collection_type):
+    id = -1
+    try:
+      collection = Collection.objects.get(id=collection_id)
+      collection.name = collection.name + _('_copy')
+      collection.label = collection.label + _(' (Copy)')
+      collection.id = None
+      collection.save()
+      id = collection.id
+
+    except Exception, e:
+      LOG.warn('Error copying collection: %s' % e)
+
+    return id

+ 71 - 10
apps/search/src/search/templates/admin_collections.mako

@@ -46,6 +46,14 @@ ${ commonheader(_('Search'), "search", user, "40px") | n,unicode }
     </%def>
   </%actionbar:render>
 
+  <div class="row-fluid" data-bind="visible: collections().length == 0">
+    <div class="span10 offset1 center">
+
+      <i class="icon-plus-sign waiting"></i>
+      <h1 class="emptyMessage">${ _('There are currently no collections defined.') }<br/>${ _('Please click on Import to add one or more.') }</h1>
+
+    </div>
+  </div>
   <div class="row-fluid">
     <div class="span12">
       <ul id="collections" data-bind="template: {name: 'collectionTemplate', foreach: filteredCollections}">
@@ -61,14 +69,18 @@ ${ commonheader(_('Search'), "search", user, "40px") | n,unicode }
 
   <script id="collectionTemplate" type="text/html">
     <li style="cursor: move">
-      <a data-bind="attr: {'href': absoluteUrl}" class="pull-right" style="margin-top: 10px;margin-right: 10px"><i class="icon-edit"></i> ${_('Edit')}</a>
+      <div class="pull-right" style="margin-top: 10px;margin-right: 10px; cursor: pointer">
+        <a data-bind="attr: {'href': absoluteUrl}"><i class="icon-edit"></i> ${_('Edit')}</a> &nbsp;
+        <a data-bind="click: $root.copyCollection"><i class="icon-copy"></i> ${_('Copy')}</a> &nbsp;
+        <a data-bind="click: $root.markForDeletion"><i class="icon-remove"></i> ${_('Delete')}</a>
+      </div>
       <h4><i class="icon-list"></i> <span data-bind="text: label"></span></h4>
     </li>
   </script>
 
 </div>
 
-<div id="importDialog" class="modal hide fade">
+<div id="importModal" class="modal hide fade">
   <div class="modal-header">
     <button type="button" class="close" data-dismiss="modal">&times;</button>
     <h3>${ _('Import Collections and Cores') }</h3>
@@ -87,7 +99,7 @@ ${ commonheader(_('Search'), "search", user, "40px") | n,unicode }
   </div>
   <div class="modal-footer">
     <a href="javascript:void(0)" class="btn" data-dismiss="modal">${ _('Cancel') }</a>
-    <button href="javascript:void(0)" class="btn btn-primary" data-bind="enable: selectedImportableCollections().length > 0 || selectedImportableCores().length > 0, click: importCollectionsAndCores">${ _('Import Selected') }</button>
+    <button id="importModalBtn" href="javascript:void(0)" class="btn btn-primary disable-feedback" data-bind="enable: selectedImportableCollections().length > 0 || selectedImportableCores().length > 0, click: importCollectionsAndCores">${ _('Import Selected') }</button>
   </div>
 </div>
 
@@ -100,6 +112,21 @@ ${ commonheader(_('Search'), "search", user, "40px") | n,unicode }
   </tr>
 </script>
 
+<div id="deleteModal" class="modal hide fade">
+  <div class="modal-header">
+    <a href="#" class="close" data-dismiss="modal">&times;</a>
+    <h3>${_('Confirm Delete')}</h3>
+  </div>
+  <div class="modal-body">
+    <p>${_('Are you sure you want to delete this collection?')}</p>
+  </div>
+  <div class="modal-footer">
+    <a class="btn" data-dismiss="modal">${_('No')}</a>
+    <a id="deleteModalBtn" class="btn btn-danger disable-feedback" data-bind="click: deleteCollection">${_('Yes')}</a>
+  </div>
+</div>
+
+
 <style type="text/css">
   #collections {
     list-style-type: none;
@@ -132,7 +159,9 @@ ${ commonheader(_('Search'), "search", user, "40px") | n,unicode }
     labels: [],
     listCollectionsUrl: "${ url("search:admin_collections") }?format=json",
     listImportablesUrl: "${ url("search:admin_collections_import") }?format=json",
-    importUrl: "${ url("search:admin_collections_import") }"
+    importUrl: "${ url("search:admin_collections_import") }",
+    deleteUrl: "${ url("search:admin_collection_delete") }",
+    copyUrl: "${ url("search:admin_collection_copy") }"
   }
 
   var viewModel = new SearchCollectionsModel(appProperties);
@@ -169,25 +198,57 @@ ${ commonheader(_('Search'), "search", user, "40px") | n,unicode }
       }, 300);
     });
 
-    $("#importDialog").modal({
+    $("#importModal").modal({
+      show: false
+    });
+
+    $("#deleteModal").modal({
       show: false
     });
 
     % if is_redirect:
-        showImportDialog();
+        showImportModal();
     % endif
 
     $("#importBtn").on("click", function () {
-      showImportDialog();
+      showImportModal();
     });
 
-    function showImportDialog() {
-      $("#importDialog").modal('show');
+    function showImportModal() {
+      $("#importModal").modal("show");
       viewModel.updateImportables();
     }
 
+    $(document).on("importing", function () {
+      var _btn = $("#importModalBtn");
+      _btn.attr("data-loading-text", _btn.text() + " ...");
+      _btn.button("loading");
+    });
+
     $(document).on("imported", function () {
-      $("#importDialog").modal('hide');
+      $("#importModal").modal("hide");
+      $("#importModalBtn").button("reset");
+      $.jHueNotify.info("${ _("Collections imported successfully.") }");
+    });
+
+    $(document).on("deleting", function () {
+      var _btn = $("#deleteModalBtn");
+      _btn.attr("data-loading-text", _btn.text() + " ...");
+      _btn.button("loading");
+    });
+
+    $(document).on("collectionDeleted", function () {
+      $("#deleteModal").modal("hide");
+      $("#deleteModalBtn").button("reset");
+      $.jHueNotify.info("${ _("Collection deleted successfully.") }");
+    });
+
+    $(document).on("collectionCopied", function () {
+      $.jHueNotify.info("${ _("Collection copied successfully.") }");
+    });
+
+    $(document).on("confirmDelete", function () {
+      $("#deleteModal").modal('show');
     });
 
   });

+ 2 - 0
apps/search/src/search/urls.py

@@ -35,4 +35,6 @@ urlpatterns = patterns('search.views',
   url(r'^suggest/(?P<collection>\w+)/(?P<query>\w+)?$', 'query_suggest', name='query_suggest'),
   url(r'^admin/collection/(?P<collection>\w+)/schema$', 'admin_collection_schema', name='admin_collection_schema'),
   url(r'^admin/collection/(?P<collection>\w+)/solr_properties$', 'admin_collection_solr_properties', name='admin_collection_solr_properties'),
+  url(r'^admin/collection_delete$', 'admin_collection_delete', name='admin_collection_delete'),
+  url(r'^admin/collection_copy$', 'admin_collection_copy', name='admin_collection_copy'),
 )

+ 28 - 0
apps/search/src/search/views.py

@@ -165,6 +165,34 @@ def admin_collections_import(request):
     else:
       return admin_collections(request, True)
 
+@allow_admin_only
+def admin_collection_delete(request):
+  if request.method != 'POST':
+    raise PopupException(_('POST request required.'))
+
+  id = request.POST.get('id')
+  searcher = SearchController()
+  response = {
+    'id': searcher.delete_collection(id)
+  }
+
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+
+@allow_admin_only
+def admin_collection_copy(request):
+  if request.method != 'POST':
+    raise PopupException(_('POST request required.'))
+
+  id = request.POST.get('id')
+  type = request.POST.get('type')
+  searcher = SearchController()
+  response = {
+    'id': searcher.copy_collection(id, type)
+  }
+
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
 
 @allow_admin_only
 def admin_collection_properties(request, collection):

+ 11 - 0
apps/search/static/css/admin.css

@@ -77,3 +77,14 @@ h3 {
 .search-bar h4 {
   margin-top: 4px;
 }
+
+.waiting {
+  font-size: 196px;
+  color: #DDD;
+}
+
+h1.emptyMessage {
+  margin-top: 50px;
+  color: #BBB;
+  line-height: 60px;
+}

+ 32 - 7
apps/search/static/js/search.ko.js

@@ -51,6 +51,8 @@ var SearchCollectionsModel = function (props) {
   self.LIST_COLLECTIONS_URL = props.listCollectionsUrl;
   self.LIST_IMPORTABLES_URL = props.listImportablesUrl;
   self.IMPORT_URL = props.importUrl;
+  self.DELETE_URL = props.deleteUrl;
+  self.COPY_URL = props.copyUrl;
 
   self.isLoading = ko.observable(false);
   self.isLoadingImportables = ko.observable(false);
@@ -62,6 +64,8 @@ var SearchCollectionsModel = function (props) {
   self.importableCollections = ko.observableArray([]);
   self.importableCores = ko.observableArray([]);
 
+  self.collectionToDelete = null;
+
   self.selectedCollections = ko.computed(function () {
     return ko.utils.arrayFilter(self.collections(), function (coll) {
       return coll.selected();
@@ -94,12 +98,34 @@ var SearchCollectionsModel = function (props) {
     }));
   };
 
-  self.deleteCollections = function () {
-    var ids = [];
-    $(self.selectedCollections()).each(function (index, coll) {
-      ids.push(coll.id());
-    });
-    //callDelete(ids);
+  self.markForDeletion = function (collection) {
+    self.collectionToDelete = collection;
+    $(document).trigger("confirmDelete");
+  };
+
+  self.deleteCollection = function () {
+    $(document).trigger("deleting");
+    $.post(self.DELETE_URL,
+      {
+        id: self.collectionToDelete.id()
+      },
+      function (data) {
+        self.updateCollections();
+        $(document).trigger("collectionDeleted");
+      }, "json");
+  };
+
+  self.copyCollection = function (collection) {
+    $(document).trigger("copying");
+    $.post(self.COPY_URL,
+      {
+        id: collection.id(),
+        type: collection.isCoreOnly()?"core":"collection"
+      },
+      function (data) {
+        self.updateCollections();
+        $(document).trigger("collectionCopied");
+      }, "json");
   };
 
   self.updateCollections = function () {
@@ -125,7 +151,6 @@ var SearchCollectionsModel = function (props) {
     });
   };
 
-
   self.importCollectionsAndCores = function () {
     $(document).trigger("importing");
     var selected = [];