diff --git a/horizon/api/swift.py b/horizon/api/swift.py index 056839987..2e09065a5 100644 --- a/horizon/api/swift.py +++ b/horizon/api/swift.py @@ -102,6 +102,42 @@ def swift_get_objects(request, container_name, prefix=None, path=None, return (objects, False) +def swift_filter_objects(request, filter_string, container_name, prefix=None, + path=None, marker=None): + #FIXME(kewu): Cloudfiles currently has no filtering API, thus the marker + #parameter here won't actually help the pagination. For now I am just + #getting the largest number of objects from a container and filtering based + #on those objects. + limit = 10000 + container = swift_api(request).get_container(container_name) + objects = container.get_objects(prefix=prefix, + marker=marker, + limit=limit, + delimiter="/", + path=path) + filter_string_list = filter_string.lower().strip().split(' ') + + return filter(lambda obj: any([ + obj.content_type != "application/directory" + and wildcard_search(obj.name.lower(), q) + for q in filter_string_list if q != '' + ]), objects) + + +def wildcard_search(string, q): + q_list = q.split('*') + if all(map(lambda x: x == '', q_list)): + return True + elif q_list[0] not in string: + return False + else: + if q_list[0] == '': + tail = string + else: + head, delimiter, tail = string.partition(q_list[0]) + return wildcard_search(tail, '*'.join(q_list[1:])) + + def swift_copy_object(request, orig_container_name, orig_object_name, new_container_name, new_object_name): try: diff --git a/horizon/dashboards/nova/containers/tables.py b/horizon/dashboards/nova/containers/tables.py index 5b03be896..3b357b6cc 100644 --- a/horizon/dashboards/nova/containers/tables.py +++ b/horizon/dashboards/nova/containers/tables.py @@ -138,15 +138,14 @@ class DownloadObject(tables.LinkAction): class ObjectFilterAction(tables.FilterAction): def filter(self, table, objects, filter_string): - """ Really naive case-insensitive search. """ - q = filter_string.lower() - - def comp(object): - if q in object.name.lower(): - return True - return False - - return filter(comp, objects) + request = table._meta.request + container = self.table.kwargs['container_name'] + subfolder = self.table.kwargs['subfolder_path'] + path = subfolder + '/' if subfolder else '' + return api.swift_filter_objects(request, + filter_string, + container, + path=path) def sanitize_name(name): diff --git a/horizon/static/horizon/js/tables.js b/horizon/static/horizon/js/tables.js index 3e9c07318..7ec282503 100644 --- a/horizon/static/horizon/js/tables.js +++ b/horizon/static/horizon/js/tables.js @@ -25,6 +25,28 @@ horizon.datatables.add_table_checkboxes = function(parent) { }); }; +horizon.datatables.set_table_filter = function (parent) { + $(parent).find('table').each(function (index, elm) { + var input = $($(elm).find('div.table_search input')); + if (input) { + input.quicksearch('table#' + $(elm).attr('id') + ' tbody tr', { + 'delay': 300, + 'loader': 'span.loading', + 'bind': 'keyup click', + 'show': this.show, + 'hide': this.hide, + 'prepareQuery': function (val) { + return new RegExp(val, "i"); + }, + 'testQuery': function (query, txt, _row) { + return query.test($(_row).find('td:not(.hidden)').text()); + } + }); + } + }); +}; + + horizon.addInitFunction(function() { $('div.table_wrapper, div.modal_wrapper').on('click', 'table thead .multi_select_column :checkbox', function(evt) { var $this = $(this), @@ -33,31 +55,16 @@ horizon.addInitFunction(function() { checkboxes = $table.find('tbody :checkbox'); checkboxes.prop('checked', is_checked); }); - $('.table_search input').quicksearch('tbody tr', { - 'delay': 300, - 'loader': 'span.loading', - 'bind': 'keyup click', - 'show': function () { - this.style.display = ''; - }, - 'hide': function () { - this.style.display = 'none'; - }, - 'prepareQuery': function (val) { - return new RegExp(val, "i"); - }, - 'testQuery': function (query, txt, _row) { - return query.test($(_row).find('td:not(.hidden)').text()); - } - }); horizon.datatables.add_table_checkboxes($('body')); horizon.datatables.set_table_sorting($('body')); + horizon.datatables.set_table_filter($('body')); // Also apply on tables in modal views $('div.modal_wrapper').on('shown', '.modal', function(evt) { horizon.datatables.add_table_checkboxes(this); horizon.datatables.set_table_sorting(this); + horizon.datatables.set_table_filter(this); }); horizon.datatables.update(); diff --git a/horizon/tests/api_tests/swift_tests.py b/horizon/tests/api_tests/swift_tests.py index afe8b69c4..97b64e33e 100644 --- a/horizon/tests/api_tests/swift_tests.py +++ b/horizon/tests/api_tests/swift_tests.py @@ -77,6 +77,29 @@ class SwiftApiTests(test.APITestCase): self.assertEqual(len(objs), len(objects)) self.assertFalse(more) + def test_swift_filter_objects(self): + container = self.containers.first() + objects = self.objects.list() + first_obj = self.objects.first() + expected_objs = [obj.name.encode('utf8') for obj in + self.objects.filter(name=first_obj.name)] + + swift_api = self.stub_swiftclient() + swift_api.get_container(container.name).AndReturn(container) + self.mox.StubOutWithMock(container, 'get_objects') + container.get_objects(limit=10000, + marker=None, + prefix=None, + delimiter='/', + path=None).AndReturn(objects) + self.mox.ReplayAll() + + result_objs = api.swift_filter_objects(self.request, + first_obj.name, + container.name) + self.assertQuerysetEqual(result_objs, expected_objs, + lambda obj: obj.name.encode('utf8')) + def test_swift_upload_object(self): container = self.containers.first() obj = self.objects.first()