From 56fa0970f8d8658a34f9c76f511a4beef38bf559 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 1 Aug 2012 16:10:59 +0100 Subject: [PATCH 01/53] [#2788] Avoid unnecessary commit request The commit can be sent on the same request with the dict to index --- ckan/lib/search/index.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ckan/lib/search/index.py b/ckan/lib/search/index.py index 09ec2eec913..1d912eef1c7 100644 --- a/ckan/lib/search/index.py +++ b/ckan/lib/search/index.py @@ -222,8 +222,7 @@ def index_package(self, pkg_dict): # send to solr: try: conn = make_connection() - conn.add_many([pkg_dict]) - conn.commit(wait_flush=False, wait_searcher=False) + conn.add_many([pkg_dict], _commit=True) except Exception, e: log.exception(e) raise SearchIndexError(e) From 4ad7ac00af18f69a4ef27dc7fd6ba18627033ff1 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 2 Aug 2012 12:06:51 +0100 Subject: [PATCH 02/53] [#2788] Commit only once at the end of a search index rebuild Commiting changes in the Solr search index is a heavy task that takes significant time. We are currently commiting changes after each update, which is probably fine for individual updated (ie users editing or creating a dataset), but when rebuilding the whole index it is unnecessary. A single commit at the end of the process is needed, and that speeds the reindexing about a 85%. A flag has been added (`-e` or `--commit-each`) to allow the old behaviour (commiting after each edit). --- ckan/lib/cli.py | 21 ++++++++++++++++++--- ckan/lib/search/__init__.py | 12 +++++++++--- ckan/lib/search/index.py | 23 ++++++++++++++++++----- ckan/tests/lib/test_cli.py | 4 ++-- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/ckan/lib/cli.py b/ckan/lib/cli.py index baaa4674ba9..11272281628 100644 --- a/ckan/lib/cli.py +++ b/ckan/lib/cli.py @@ -277,7 +277,7 @@ class SearchIndexCommand(CkanCommand): '''Creates a search index for all datasets Usage: - search-index [-i] [-o] [-r] rebuild [dataset-name] - reindex dataset-name if given, if not then rebuild full search index (all datasets) + search-index [-i] [-o] [-r] [-e] rebuild [dataset-name] - reindex dataset-name if given, if not then rebuild full search index (all datasets) search-index check - checks for datasets not indexed search-index show {dataset-name} - shows index of a dataset search-index clear [dataset-name] - clears the search index for the provided dataset or for the whole ckan instance @@ -301,6 +301,13 @@ def __init__(self,name): self.parser.add_option('-r', '--refresh', dest='refresh', action='store_true', default=False, help='Refresh current index (does not clear the existing one)') + self.parser.add_option('-e', '--commit-each', dest='commit_each', + action='store_true', default=False, help= +'''Perform a commit after indexing each dataset. This ensures that changes are +immediately available on the search, but slows significantly the process. +Default is false.''' + ) + def command(self): self._load_config() @@ -322,14 +329,22 @@ def command(self): print 'Command %s not recognized' % cmd def rebuild(self): - from ckan.lib.search import rebuild + from ckan.lib.search import rebuild, commit + + # BY default we don't commit after each request to Solr, as it is + # a really heavy operation and slows things a lot if len(self.args) > 1: rebuild(self.args[1]) else: rebuild(only_missing=self.options.only_missing, force=self.options.force, - refresh=self.options.refresh) + refresh=self.options.refresh, + defer_commit=(not self.options.commit_each)) + + if not self.options.commit_each: + commit() + def check(self): from ckan.lib.search import check diff --git a/ckan/lib/search/__init__.py b/ckan/lib/search/__init__.py index f003efd9350..dfb16e301f8 100644 --- a/ckan/lib/search/__init__.py +++ b/ckan/lib/search/__init__.py @@ -131,7 +131,7 @@ def notify(self, entity, operation): log.warn("Discarded Sync. indexing for: %s" % entity) -def rebuild(package_id=None, only_missing=False, force=False, refresh=False): +def rebuild(package_id=None, only_missing=False, force=False, refresh=False, defer_commit=False): ''' Rebuilds the search index. @@ -175,12 +175,13 @@ def rebuild(package_id=None, only_missing=False, force=False, refresh=False): for pkg_id in package_ids: try: - package_index.insert_dict( + package_index.update_dict( get_action('package_show')( {'model': model, 'ignore_auth': True, 'validate': False}, {'id': pkg_id} - ) + ), + defer_commit ) except Exception, e: log.error('Error while indexing dataset %s: %s' % @@ -195,6 +196,11 @@ def rebuild(package_id=None, only_missing=False, force=False, refresh=False): log.info('Finished rebuilding search index.') +def commit(): + package_index = index_for(model.Package) + package_index.commit() + log.info('Commited pending changes on the search index') + def check(): from ckan import model package_query = query_for(model.Package) diff --git a/ckan/lib/search/index.py b/ckan/lib/search/index.py index 1d912eef1c7..dee7e27c7e8 100644 --- a/ckan/lib/search/index.py +++ b/ckan/lib/search/index.py @@ -92,10 +92,10 @@ class PackageSearchIndex(SearchIndex): def remove_dict(self, pkg_dict): self.delete_package(pkg_dict) - def update_dict(self, pkg_dict): - self.index_package(pkg_dict) + def update_dict(self, pkg_dict, defer_commit=False): + self.index_package(pkg_dict, defer_commit) - def index_package(self, pkg_dict): + def index_package(self, pkg_dict, defer_commit=False): if pkg_dict is None: return pkg_dict['data_dict'] = json.dumps(pkg_dict) @@ -222,14 +222,27 @@ def index_package(self, pkg_dict): # send to solr: try: conn = make_connection() - conn.add_many([pkg_dict], _commit=True) + commit = not defer_commit + conn.add_many([pkg_dict], _commit=commit) + except Exception, e: + log.exception(e) + raise SearchIndexError(e) + finally: + conn.close() + + commit_debug_msg = 'Not commited yet' if defer_commit else 'Commited' + log.debug('Updated index for %s [%s]' % (pkg_dict.get('name'), commit_debug_msg)) + + def commit(self): + try: + conn = make_connection() + conn.commit(wait_flush=False, wait_searcher=False) except Exception, e: log.exception(e) raise SearchIndexError(e) finally: conn.close() - log.debug("Updated index for %s" % pkg_dict.get('name')) def delete_package(self, pkg_dict): conn = make_connection() diff --git a/ckan/tests/lib/test_cli.py b/ckan/tests/lib/test_cli.py index 7bea16bb484..5aee50ace1a 100644 --- a/ckan/tests/lib/test_cli.py +++ b/ckan/tests/lib/test_cli.py @@ -80,7 +80,7 @@ def test_clear_and_rebuild_index(self): # Rebuild index self.search.args = () - self.search.options = FakeOptions(only_missing=False,force=False,refresh=False) + self.search.options = FakeOptions(only_missing=False,force=False,refresh=False,commit_each=False) self.search.rebuild() pkg_count = model.Session.query(model.Package).filter(model.Package.state==u'active').count() @@ -103,7 +103,7 @@ def test_clear_and_rebuild_only_one(self): # Rebuild index for annakarenina self.search.args = ('rebuild annakarenina').split() - self.search.options = FakeOptions(only_missing=False,force=False,refresh=False) + self.search.options = FakeOptions(only_missing=False,force=False,refresh=False,commit_each=False) self.search.rebuild() self.query.run({'q':'*:*'}) From dcd013fd6f2e59f1a36574bad6dece1dfe39fc67 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 2 Aug 2012 16:55:10 +0100 Subject: [PATCH 03/53] [#2788] Allow not returning the package dict when creating or updating This avoids calling package_show --- ckan/logic/action/create.py | 8 ++++++-- ckan/logic/action/update.py | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index 94b5f74ce32..d6f98426f9d 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -98,7 +98,8 @@ def package_create(context, data_dict): which groups exist call ``group_list()`` :type groups: list of dictionaries - :returns: the newly created dataset + :returns: the newly created dataset (if 'return_package_dict' is True in the + context, which is the default. Otherwise returns None) :rtype: dictionary ''' @@ -162,7 +163,10 @@ def package_create(context, data_dict): ## this is added so that the rest controller can make a new location context["id"] = pkg.id log.debug('Created object %s' % str(pkg.name)) - return _get_action('package_show')(context, {'id':context['id']}) + + return_package_dict = context.get('return_package_dict',True) + + return _get_action('package_show')(context, {'id':context['id']}) if return_package_dict else None def package_create_validate(context, data_dict): model = context['model'] diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index a06274c0714..bf530bd4c07 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -213,7 +213,8 @@ def package_update(context, data_dict): :param id: the name or id of the dataset to update :type id: string - :returns: the updated dataset + :returns: the updated dataset (if 'return_package_dict' is True in the + context, which is the default. Otherwise returns None) :rtype: dictionary ''' @@ -273,7 +274,10 @@ def package_update(context, data_dict): model.repo.commit() log.debug('Updated object %s' % str(pkg.name)) - return _get_action('package_show')(context, data_dict) + + return_package_dict = context.get('return_package_dict',True) + + return _get_action('package_show')(context, data_dict) if return_package_dict else None def package_update_validate(context, data_dict): model = context['model'] From c8997b6e20ca040b2146c5eedf88325eb145b7a0 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 7 Aug 2012 14:54:14 +0100 Subject: [PATCH 04/53] [#2788] Return dataset id if 'return_pacakge_dict' is False --- ckan/logic/action/create.py | 8 ++++++-- ckan/logic/action/update.py | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index d6f98426f9d..3e0a3ed8d7e 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -99,7 +99,8 @@ def package_create(context, data_dict): :type groups: list of dictionaries :returns: the newly created dataset (if 'return_package_dict' is True in the - context, which is the default. Otherwise returns None) + context, which is the default. Otherwise returns just the dataset + id) :rtype: dictionary ''' @@ -166,7 +167,10 @@ def package_create(context, data_dict): return_package_dict = context.get('return_package_dict',True) - return _get_action('package_show')(context, {'id':context['id']}) if return_package_dict else None + output = _get_action('package_show')(context, {'id':context['id']}) \ + if return_package_dict else context['id'] + + return output def package_create_validate(context, data_dict): model = context['model'] diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index bf530bd4c07..9a4f9ff0831 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -214,7 +214,8 @@ def package_update(context, data_dict): :type id: string :returns: the updated dataset (if 'return_package_dict' is True in the - context, which is the default. Otherwise returns None) + context, which is the default. Otherwise returns just the + dataset id) :rtype: dictionary ''' @@ -277,7 +278,10 @@ def package_update(context, data_dict): return_package_dict = context.get('return_package_dict',True) - return _get_action('package_show')(context, data_dict) if return_package_dict else None + output = _get_action('package_show')(context, {'id': data_dict['id']}) \ + if return_package_dict else data_dict['id'] + + return output def package_update_validate(context, data_dict): model = context['model'] From 583c073aed986a1959a5c5d66ef969923d9677d6 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 14 Aug 2012 17:33:40 +0100 Subject: [PATCH 05/53] [#2788] Use return_id_only instead of return_package_dict Suggested by @tobes, as it is more backwards compatible --- ckan/logic/action/create.py | 11 +++++------ ckan/logic/action/update.py | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index 3e0a3ed8d7e..8918b69cb2d 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -98,9 +98,8 @@ def package_create(context, data_dict): which groups exist call ``group_list()`` :type groups: list of dictionaries - :returns: the newly created dataset (if 'return_package_dict' is True in the - context, which is the default. Otherwise returns just the dataset - id) + :returns: the newly created dataset (unless 'return_id_only' is set to True + in the context, in which case just the dataset id will be returned) :rtype: dictionary ''' @@ -165,10 +164,10 @@ def package_create(context, data_dict): context["id"] = pkg.id log.debug('Created object %s' % str(pkg.name)) - return_package_dict = context.get('return_package_dict',True) + return_id_only = context.get('return_id_only', False) - output = _get_action('package_show')(context, {'id':context['id']}) \ - if return_package_dict else context['id'] + output = context['id'] if return_id_only \ + else _get_action('package_show')(context, {'id':context['id']}) return output diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 9a4f9ff0831..d60c7927728 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -276,10 +276,10 @@ def package_update(context, data_dict): log.debug('Updated object %s' % str(pkg.name)) - return_package_dict = context.get('return_package_dict',True) + return_id_only = context.get('return_id_only', False) - output = _get_action('package_show')(context, {'id': data_dict['id']}) \ - if return_package_dict else data_dict['id'] + output = data_dict['id'] if return_id_only \ + else _get_action('package_show')(context, {'id': data_dict['id']}) return output From 80be35cca9995c050e0692b5ba3ffac1c7291461 Mon Sep 17 00:00:00 2001 From: tobes Date: Tue, 18 Sep 2012 10:54:42 +0100 Subject: [PATCH 06/53] [#2257] Cleanup deprecated helper functions --- ckan/lib/helpers.py | 103 ++---------------- .../functional/api/test_revision_search.py | 6 +- ckan/tests/lib/test_helpers.py | 4 +- test-core.ini | 2 - 4 files changed, 11 insertions(+), 104 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index cda89f90fd0..0c1c8440423 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -37,7 +37,7 @@ from pylons.i18n import _, ungettext import ckan.lib.fanstatic_resources as fanstatic_resources -from lib.maintain import deprecated +#from lib.maintain import deprecated import ckan.model as model import ckan.lib.formatters as formatters @@ -295,19 +295,7 @@ def are_there_flash_messages(): return flash.are_there_messages() -def nav_link(*args, **kwargs): - # nav_link() used to need c passing as the first arg - # this is deprecated as pointless - # throws error if ckan.restrict_template_vars is True - # When we move to strict helpers then this should be removed as a wrapper - if len(args) > 2 or (len(args) > 1 and 'controller' in kwargs): - if not asbool(config.get('ckan.restrict_template_vars', 'false')): - return _nav_link(*args[1:], **kwargs) - raise Exception('nav_link() calling has been changed. remove c in template %s or included one' % _get_template_name()) - return _nav_link(*args, **kwargs) - - -def _nav_link(text, controller, **kwargs): +def nav_link(text, controller, **kwargs): ''' params class_: pass extra class(s) to add to the tag @@ -343,20 +331,7 @@ def _link_class(kwargs): return kwargs.pop('class_', '') + active -def nav_named_link(*args, **kwargs): - # subnav_link() used to need c passing as the first arg - # this is deprecated as pointless - # throws error if ckan.restrict_template_vars is True - # When we move to strict helpers then this should be removed as a wrapper - if len(args) > 3 or (len(args) > 0 and 'text' in kwargs) or \ - (len(args) > 1 and 'name' in kwargs): - if not asbool(config.get('ckan.restrict_template_vars', 'false')): - return _nav_named_link(*args[1:], **kwargs) - raise Exception('nav_named_link() calling has been changed. remove c in template %s or included one' % _get_template_name()) - return _nav_named_link(*args, **kwargs) - - -def _nav_named_link(text, name, **kwargs): +def nav_named_link(text, name, **kwargs): class_ = _link_class(kwargs) return link_to( text, @@ -365,19 +340,7 @@ def _nav_named_link(text, name, **kwargs): ) -def subnav_link(*args, **kwargs): - # subnav_link() used to need c passing as the first arg - # this is deprecated as pointless - # throws error if ckan.restrict_template_vars is True - # When we move to strict helpers then this should be removed as a wrapper - if len(args) > 2 or (len(args) > 1 and 'action' in kwargs): - if not asbool(config.get('ckan.restrict_template_vars', 'false')): - return _subnav_link(*args[1:], **kwargs) - raise Exception('subnav_link() calling has been changed. remove c in template %s or included one' % _get_template_name()) - return _subnav_link(*args, **kwargs) - - -def _subnav_link(text, action, **kwargs): +def subnav_link(text, action, **kwargs): kwargs['action'] = action class_ = _link_class(kwargs) return link_to( @@ -387,20 +350,7 @@ def _subnav_link(text, action, **kwargs): ) -def subnav_named_route(*args, **kwargs): - # subnav_link() used to need c passing as the first arg - # this is deprecated as pointless - # throws error if ckan.restrict_template_vars is True - # When we move to strict helpers then this should be removed as a wrapper - if len(args) > 2 or (len(args) > 0 and 'text' in kwargs) or \ - (len(args) > 1 and 'routename' in kwargs): - if not asbool(config.get('ckan.restrict_template_vars', 'false')): - return _subnav_named_route(*args[1:], **kwargs) - raise Exception('subnav_named_route() calling has been changed. remove c in template %s or included one' % _get_template_name()) - return _subnav_named_route(*args, **kwargs) - - -def _subnav_named_route(text, routename, **kwargs): +def subnav_named_route(text, routename, **kwargs): """ Generate a subnav element based on a named route """ # FIXME this is the same as _nav_named_link # they should be combined @@ -521,18 +471,6 @@ def unselected_facet_items(facet, limit=10): ''' return get_facet_items_dict(facet, limit=limit, exclude_active=True) -@deprecated('Please use get_facet_title(name) for i18n improvements.') -def facet_title(name): - '''Returns a title for the given facet name. - - If a mapping is declared in the config, this is used. Otherwise it falls - back to capitalizing the given name. - - This function is deprecated, use `get_facet_title` instead. - ''' - # FIXME this looks like an i18n issue - return config.get('search.facets.%s.title' % name, name.capitalize()) - def get_facet_title(name): # if this is set in the config use this @@ -574,14 +512,6 @@ def sorted_extras(list_): output.append((k, v)) return output -@deprecated('Please use check_access instead.') -def am_authorized(c, action, domain_object=None): - ''' Deprecated. Please use check_access instead''' - from ckan.authz import Authorizer - if domain_object is None: - domain_object = model.System() - return Authorizer.am_authorized(c, action, domain_object) - def check_access(action, data_dict=None): from ckan.logic import check_access as check_access_logic, NotAuthorized @@ -772,14 +702,6 @@ def render_datetime(datetime_, date_format=None, with_hours=False): return '' -@deprecated() -def datetime_to_date_str(datetime_): - '''DEPRECATED: Takes a datetime.datetime object and returns a string of it - in ISO format. - ''' - return datetime_.isoformat() - - def date_str_to_datetime(date_str): '''Convert ISO-like formatted datestring to datetime object. @@ -965,21 +887,13 @@ def dump_json(obj, **kw): return json.dumps(obj, **kw) -def auto_log_message(*args): - # auto_log_message() used to need c passing as the first arg - # this is deprecated as pointless - # throws error if ckan.restrict_template_vars is True - # When we move to strict helpers then this should be removed as a wrapper - if len(args) and asbool(config.get('ckan.restrict_template_vars', 'false')): - raise Exception('auto_log_message() calling has been changed. remove c in template %s or included one' % _get_template_name()) - return _auto_log_message() - def _get_template_name(): #FIX ME THIS IS BROKEN ''' helper function to get the currently/last rendered template name ''' return c.__debug_info[-1]['template_name'] -def _auto_log_message(): + +def auto_log_message(): if (c.action == 'new'): return _('Created new dataset.') elif (c.action == 'editresources'): @@ -1322,8 +1236,6 @@ def format_resource_items(items): 'subnav_link', 'subnav_named_route', 'default_group_type', - # 'facet_title', # deprecated - # am_authorized, # deprecated 'check_access', 'linked_user', 'group_name_to_title', @@ -1338,7 +1250,6 @@ def format_resource_items(items): 'pager_url', 'render_datetime', 'date_str_to_datetime', - 'datetime_to_date_str', 'parse_rfc_2822_date', 'time_ago_in_words_from_str', 'button_attr', diff --git a/ckan/tests/functional/api/test_revision_search.py b/ckan/tests/functional/api/test_revision_search.py index f376c36db0c..d223537b1e2 100644 --- a/ckan/tests/functional/api/test_revision_search.py +++ b/ckan/tests/functional/api/test_revision_search.py @@ -1,5 +1,3 @@ -from ckan.lib.helpers import datetime_to_date_str - from ckan.tests.functional.api.base import * from ckan.tests import TestController as ControllerTestCase @@ -48,7 +46,7 @@ def test_12_search_revision_since_time(self): revs = model.Session.query(model.Revision).all() # Check since time of first. rev_first = revs[-1] - params = "?since_time=%s" % datetime_to_date_str(rev_first.timestamp) + params = "?since_time=%s" % rev_first.timestamp.isoformat() res = self.app.get(offset+params, status=200) res_list = self.data_from_res(res) assert rev_first.id not in res_list @@ -56,7 +54,7 @@ def test_12_search_revision_since_time(self): assert rev.id in res_list, (rev.id, res_list) # Check since time of last. rev_last = revs[0] - params = "?since_time=%s" % datetime_to_date_str(rev_last.timestamp) + params = "?since_time=%s" % rev_last.timestamp.isoformat() res = self.app.get(offset+params, status=200) res_list = self.data_from_res(res) assert res_list == [], res_list diff --git a/ckan/tests/lib/test_helpers.py b/ckan/tests/lib/test_helpers.py index d501c8e7837..68859977c62 100644 --- a/ckan/tests/lib/test_helpers.py +++ b/ckan/tests/lib/test_helpers.py @@ -35,7 +35,7 @@ def test_render_datetime_blank(self): assert_equal(res, '') def test_datetime_to_date_str(self): - res = h.datetime_to_date_str(datetime.datetime(2008, 4, 13, 20, 40, 20, 123456)) + res = datetime.datetime(2008, 4, 13, 20, 40, 20, 123456).isoformat() assert_equal(res, '2008-04-13T20:40:20.123456') def test_date_str_to_datetime_date_only(self): @@ -73,7 +73,7 @@ def test_date_str_to_datetime_with_ambiguous_microseconds(self): def test_time_ago_in_words_from_str(self): two_months_ago = datetime.datetime.now() - datetime.timedelta(days=65) - two_months_ago_str = h.datetime_to_date_str(two_months_ago) + two_months_ago_str = two_months_ago.isoformat() res = h.time_ago_in_words_from_str(two_months_ago_str) assert_equal(res, '2 months') diff --git a/test-core.ini b/test-core.ini index c6f8cd7535a..03e997e4568 100644 --- a/test-core.ini +++ b/test-core.ini @@ -33,8 +33,6 @@ package_edit_return_url = http://localhost/dataset/?test=edit rdf_packages = http://test.com/package/ ckan.extra_resource_fields = alt_url -# disable this so we can test all types of indexing -ckan.restrict_template_vars = false # we need legacy templates for many tests to pass ckan.legacy_templates = yes # FIXME this is to allow some tests to pass but should be removed when possible From c3e3a6b981e8665143de535c7f6326ef725c85b3 Mon Sep 17 00:00:00 2001 From: tobes Date: Tue, 18 Sep 2012 11:05:22 +0100 Subject: [PATCH 07/53] [#2257] Remove the restrict_template_vars config option --- ckan/config/environment.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ckan/config/environment.py b/ckan/config/environment.py index 78e647c65bd..0abb2018274 100644 --- a/ckan/config/environment.py +++ b/ckan/config/environment.py @@ -32,7 +32,7 @@ class _Helpers(object): missing functions from causing template exceptions. Useful if templates have helper functions provided by extensions that have not been enabled. ''' - def __init__(self, helpers, restrict=True): + def __init__(self, helpers): functions = {} allowed = helpers.__allowed_functions__[:] # list of functions due to be deprecated @@ -41,8 +41,7 @@ def __init__(self, helpers, restrict=True): for helper in dir(helpers): if helper not in allowed: self.deprecated.append(helper) - if restrict: - continue + continue functions[helper] = getattr(helpers, helper) if helper in allowed: allowed.remove(helper) @@ -181,9 +180,7 @@ def find_controller(self, controller): config['pylons.app_globals']._init() # add helper functions - restrict_helpers = asbool( - config.get('ckan.restrict_template_vars', 'true')) - helpers = _Helpers(h, restrict_helpers) + helpers = _Helpers(h) config['pylons.h'] = helpers ## redo template setup to use genshi.search_path From ae5d0a7b0d75213f653faeeaf3f6520f6e46e47c Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 25 Sep 2012 12:16:25 +0100 Subject: [PATCH 08/53] reformat /ckan/ckan/config/middleware.py --- ckan/config/middleware.py | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ckan/config/middleware.py b/ckan/config/middleware.py index 9e004a5806a..4702122e3fa 100644 --- a/ckan/config/middleware.py +++ b/ckan/config/middleware.py @@ -26,6 +26,7 @@ from ckan.config.environment import load_environment import ckan.lib.app_globals as app_globals + def make_app(global_conf, full_stack=True, static_files=True, **app_conf): """Create a Pylons WSGI application and return it @@ -64,30 +65,29 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): app = RoutesMiddleware(app, config['routes.map']) app = SessionMiddleware(app, config) app = CacheMiddleware(app, config) - + # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) #app = QueueLogMiddleware(app) - + # Fanstatic if asbool(config.get('debug', False)): fanstatic_config = { - 'versioning' : True, - 'recompute_hashes' : True, - 'minified' : False, - 'bottom' : True, - 'bundle' : False, + 'versioning': True, + 'recompute_hashes': True, + 'minified': False, + 'bottom': True, + 'bundle': False, } else: fanstatic_config = { - 'versioning' : True, - 'recompute_hashes' : False, - 'minified' : True, - 'bottom' : True, - 'bundle' : True, + 'versioning': True, + 'recompute_hashes': False, + 'minified': True, + 'bottom': True, + 'bundle': True, } app = Fanstatic(app, **fanstatic_config) - if asbool(full_stack): # Handle Python exceptions app = ErrorHandler(app, global_conf, **config['pylons.errorware']) @@ -98,7 +98,7 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): app = StatusCodeRedirect(app, [400, 404]) else: app = StatusCodeRedirect(app, [400, 404, 500]) - + # Initialize repoze.who who_parser = WhoConfig(global_conf['here']) who_parser.parse(open(app_conf['who.config_file'])) @@ -106,7 +106,7 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): if asbool(config.get('openid_enabled', 'true')): from repoze.who.plugins.openid.identification import OpenIdIdentificationPlugin # Monkey patches for repoze.who.openid - # Fixes #1659 - enable log-out when CKAN mounted at non-root URL + # Fixes #1659 - enable log-out when CKAN mounted at non-root URL from ckan.lib import repoze_patch OpenIdIdentificationPlugin.identify = repoze_patch.identify OpenIdIdentificationPlugin.redirect_to_logged_in = repoze_patch.redirect_to_logged_in @@ -117,7 +117,7 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): not isinstance(i, OpenIdIdentificationPlugin)] who_parser.challengers = [i for i in who_parser.challengers if \ not isinstance(i, OpenIdIdentificationPlugin)] - + app = PluggableAuthenticationMiddleware(app, who_parser.identifiers, who_parser.authenticators, @@ -126,10 +126,10 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): who_parser.request_classifier, who_parser.challenge_decider, logging.getLogger('repoze.who'), - logging.WARN, # ignored + logging.WARN, # ignored who_parser.remote_user_key, ) - + # Establish the Registry for this application app = RegistryManager(app) @@ -152,7 +152,7 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): StaticURLParser(public_path.strip(), cache_max_age=static_max_age) ) - app = Cascade(extra_static_parsers+static_parsers) + app = Cascade(extra_static_parsers + static_parsers) # Page cache if asbool(config.get('ckan.page_cache_enabled')): @@ -164,6 +164,7 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): return app + class I18nMiddleware(object): """I18n Middleware selects the language based on the url eg /fr/home is French""" @@ -198,7 +199,7 @@ def __call__(self, environ, start_response): # Current application url path_info = environ['PATH_INFO'] # sort out weird encodings - path_info = '/'.join(urllib.quote(pce,'') for pce in path_info.split('/')) + path_info = '/'.join(urllib.quote(pce, '') for pce in path_info.split('/')) qs = environ.get('QUERY_STRING') @@ -317,7 +318,6 @@ def __init__(self, app, config): self.app = app self.engine = sa.create_engine(config.get('sqlalchemy.url')) - def __call__(self, environ, start_response): path = environ['PATH_INFO'] if path == '/_tracking': From b838d56089a300653a370e10307faaf79e1efa5b Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 25 Sep 2012 12:19:18 +0100 Subject: [PATCH 09/53] reformat and removed imports from ckan/ckan/lib/plugins.py --- ckan/lib/plugins.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/ckan/lib/plugins.py b/ckan/lib/plugins.py index 5acc733eeaf..691a4830689 100644 --- a/ckan/lib/plugins.py +++ b/ckan/lib/plugins.py @@ -78,7 +78,7 @@ def register_package_plugins(map): controller='package', action='read') map.connect('%s_action' % package_type, '/%s/{action}/{id}' % package_type, controller='package', - requirements=dict(action='|'.join(['edit', 'authz', 'history' ])) + requirements=dict(action='|'.join(['edit', 'authz', 'history'])) ) if package_type in _package_plugins: @@ -92,8 +92,6 @@ def register_package_plugins(map): _default_package_plugin = DefaultDatasetForm() - - def register_group_plugins(map): """ Register the various IGroupForm instances. @@ -138,7 +136,7 @@ def register_group_plugins(map): controller='group', action='read') map.connect('%s_action' % group_type, '/%s/{action}/{id}' % group_type, controller='group', - requirements=dict(action='|'.join(['edit', 'authz', 'history' ])) + requirements=dict(action='|'.join(['edit', 'authz', 'history'])) ) if group_type in _group_plugins: @@ -203,7 +201,6 @@ def history_template(self): """ return 'package/history.html' - def package_form(self): return 'package/new_package_form.html' @@ -215,7 +212,7 @@ def form_to_db_schema_options(self, options): If a context is provided, and it contains a schema, it will be returned. ''' - schema = options.get('context',{}).get('schema',None) + schema = options.get('context', {}).get('schema', None) if schema: return schema @@ -248,7 +245,7 @@ def db_to_form_schema_options(self, options): If a context is provided, and it contains a schema, it will be returned. ''' - schema = options.get('context',{}).get('schema',None) + schema = options.get('context', {}).get('schema', None) if schema: return schema return self.db_to_form_schema() @@ -273,11 +270,9 @@ def check_data_dict(self, data_dict, schema=None): raise dictization_functions.DataError(data_dict) def setup_template_variables(self, context, data_dict): - from pylons import config - authz_fn = logic.get_action('group_list_authz') c.groups_authz = authz_fn(context, data_dict) - data_dict.update({'available_only':True}) + data_dict.update({'available_only': True}) c.groups_available = authz_fn(context, data_dict) @@ -300,7 +295,6 @@ def setup_template_variables(self, context, data_dict): c.auth_for_change_state = False - class DefaultGroupForm(object): """ Provides a default implementation of the pluggable Group controller @@ -352,7 +346,6 @@ def edit_template(self): """ return 'group/edit.html' - def group_form(self): return 'group/new_group_form.html' @@ -364,7 +357,7 @@ def form_to_db_schema_options(self, options): If a context is provided, and it contains a schema, it will be returned. ''' - schema = options.get('context',{}).get('schema',None) + schema = options.get('context', {}).get('schema', None) if schema: return schema @@ -396,7 +389,7 @@ def db_to_form_schema_options(self, options): If a context is provided, and it contains a schema, it will be returned. ''' - schema = options.get('context',{}).get('schema',None) + schema = options.get('context', {}).get('schema', None) if schema: return schema return self.db_to_form_schema() From 00005581cf8d8960fe4d5fbd0958177d45fa9346 Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 25 Sep 2012 12:26:12 +0100 Subject: [PATCH 10/53] reformat and removed double imports from ckan/ckan/lib/helpers.py --- ckan/lib/helpers.py | 63 ++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index cda89f90fd0..8cf03b1f4ea 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -12,7 +12,6 @@ import urllib import pprint import copy -import logging from urllib import urlencode from paste.deploy.converters import asbool @@ -110,7 +109,6 @@ def _add_i18n_to_url(url_to_amend, **kw): # (as part of the language changing feature). # A locale of default will not add locale info to the url. - default_locale = False locale = kw.pop('locale', None) no_root = kw.pop('__ckan_no_root', False) @@ -177,6 +175,7 @@ def full_current_url(): for sharing etc ''' return(url_for(request.environ['CKAN_CURRENT_URL'], qualified=True)) + def lang(): ''' Return the language code for the current locale eg `en` ''' return request.environ.get('CKAN_LANG') @@ -331,6 +330,7 @@ def _nav_link(text, controller, **kwargs): link = '' return link + def _link_class(kwargs): ''' creates classes for the link_to calls ''' highlight_actions = kwargs.pop('highlight_actions', @@ -411,6 +411,7 @@ def _subnav_named_route(text, routename, **kwargs): class_=class_ ) + def build_nav_main(*args): ''' build a set of menu items. @@ -446,22 +447,20 @@ def default_group_type(): return str(config.get('ckan.default.group_type', 'group')) - _menu_items = { - 'add dataset' : dict(controller='package', action='new'), - 'search' : dict(controller='package', + 'add dataset': dict(controller='package', action='new'), + 'search': dict(controller='package', action='search', - highlight_actions = 'index search'), + highlight_actions='index search'), 'default_group': dict(name='%s_index' % default_group_type(), controller='group', highlight_actions='index search'), - 'about' : dict(controller='home', action='about'), - 'login' : dict(controller='user', action='login'), - 'register' : dict(controller='user', action='register'), + 'about': dict(controller='home', action='about'), + 'login': dict(controller='user', action='login'), + 'register': dict(controller='user', action='register'), } - def get_facet_items_dict(facet, limit=10, exclude_active=False): '''Return the list of unselected facet items for the given facet, sorted by count. @@ -501,6 +500,7 @@ def get_facet_items_dict(facet, limit=10, exclude_active=False): else: return facets + def unselected_facet_items(facet, limit=10): '''Return the list of unselected facet items for the given facet, sorted by count. @@ -521,10 +521,11 @@ def unselected_facet_items(facet, limit=10): ''' return get_facet_items_dict(facet, limit=limit, exclude_active=True) + @deprecated('Please use get_facet_title(name) for i18n improvements.') def facet_title(name): '''Returns a title for the given facet name. - + If a mapping is declared in the config, this is used. Otherwise it falls back to capitalizing the given name. @@ -533,6 +534,7 @@ def facet_title(name): # FIXME this looks like an i18n issue return config.get('search.facets.%s.title' % name, name.capitalize()) + def get_facet_title(name): # if this is set in the config use this @@ -540,15 +542,17 @@ def get_facet_title(name): if config_title: return config_title - facet_titles = {'groups' : _('Groups'), - 'tags' : _('Tags'), - 'res_format' : _('Formats'), - 'license' : _('Licence'), } + facet_titles = {'groups': _('Groups'), + 'tags': _('Tags'), + 'res_format': _('Formats'), + 'license': _('Licence'), } return facet_titles.get(name, name.capitalize()) + def get_param_int(name, default=10): return int(request.params.get(name, default)) + def _url_with_params(url, params): if not params: return url @@ -556,14 +560,16 @@ def _url_with_params(url, params): for k, v in params] return url + u'?' + urlencode(params) + def _search_url(params): url = url_for(controller='package', action='search') return _url_with_params(url, params) + def sorted_extras(list_): ''' Used for outputting package extras ''' output = [] - for extra in sorted(list_, key=lambda x:x['key']): + for extra in sorted(list_, key=lambda x: x['key']): if extra.get('state') == 'deleted': continue k, v = extra['key'], extra['value'] @@ -574,6 +580,7 @@ def sorted_extras(list_): output.append((k, v)) return output + @deprecated('Please use check_access instead.') def am_authorized(c, action, domain_object=None): ''' Deprecated. Please use check_access instead''' @@ -629,6 +636,7 @@ def markdown_extract(text, extract_length=190): plain = re.sub(r'<.*?>', '', markdown(text)) return literal(unicode(truncate(plain, length=extract_length, indicator='...', whole_word=True))) + def icon_url(name): return url_for_static('/images/icons/%s.png' % name) @@ -667,6 +675,7 @@ def format_icon(_format): if ('xml' in _format): return 'page_white_code' return 'page_white' + def dict_list_reduce(list_, key, unique=True): ''' Take a list of dicts and create a new one containing just the values for the key with unique values if requested. ''' @@ -703,6 +712,7 @@ def gravatar(email_hash, size=100, default=None): % (email_hash, size, default, size, size) ) + def pager_url(page, partial=None, **kwargs): routes_dict = _pylons_default_url.environ['pylons.routes_dict'] kwargs['controller'] = routes_dict['controller'] @@ -721,7 +731,7 @@ def pager(self, *args, **kwargs): kwargs.update( format=u"", symbol_previous=u'«', symbol_next=u'»', - curpage_attr={'class':'active'}, link_attr={} + curpage_attr={'class': 'active'}, link_attr={} ) return super(Page, self).pager(*args, **kwargs) @@ -974,11 +984,13 @@ def auto_log_message(*args): raise Exception('auto_log_message() calling has been changed. remove c in template %s or included one' % _get_template_name()) return _auto_log_message() + def _get_template_name(): #FIX ME THIS IS BROKEN ''' helper function to get the currently/last rendered template name ''' return c.__debug_info[-1]['template_name'] + def _auto_log_message(): if (c.action == 'new'): return _('Created new dataset.') @@ -1095,6 +1107,7 @@ def follow_count(obj_type, obj_id): context = {'model': model, 'session': model.Session, 'user': c.user} return logic.get_action(action)(context, {'id': obj_id}) + def _create_url_with_params(params=None, controller=None, action=None, extras=None): ''' internal function for building urls with parameters. ''' @@ -1123,7 +1136,7 @@ def add_url_param(alternative_url=None, controller=None, action=None, via url_for(controller=controller, action=action, **extras) controller & action default to the current ones ''' - params_nopage = [(k, v) for k,v in request.params.items() if k != 'page'] + params_nopage = [(k, v) for k, v in request.params.items() if k != 'page'] params = set(params_nopage) if new_params: params |= set(new_params.items()) @@ -1132,6 +1145,7 @@ def add_url_param(alternative_url=None, controller=None, action=None, return _create_url_with_params(params=params, controller=controller, action=action, extras=extras) + def remove_url_param(key, value=None, replace=None, controller=None, action=None, extras=None): ''' Remove a key from the current parameters. A specific key/value @@ -1143,25 +1157,28 @@ def remove_url_param(key, value=None, replace=None, controller=None, via url_for(controller=controller, action=action, **extras) controller & action default to the current ones ''' - params_nopage = [(k, v) for k,v in request.params.items() if k != 'page'] + params_nopage = [(k, v) for k, v in request.params.items() if k != 'page'] params = list(params_nopage) if value: params.remove((key, value)) else: - [params.remove((k, v)) for (k, v) in params[:] if k==key] + [params.remove((k, v)) for (k, v) in params[:] if k == key] if replace is not None: params.append((key, replace)) return _create_url_with_params(params=params, controller=controller, action=action, extras=extras) + def include_resource(resource): r = getattr(fanstatic_resources, resource) r.need() + def debug_inspect(arg): ''' Output pprint.pformat view of supplied arg ''' return literal('
') + pprint.pformat(arg) + literal('
') + def debug_full_info_as_list(debug_info): ''' This dumps the template variables for debugging purposes only. ''' out = [] @@ -1204,6 +1221,7 @@ def popular(type_, number, min=1, title=None): raise Exception('popular() did not recieve a valid type_ or title') return snippet('snippets/popular.html', title=title, number=number, min=min) + def groups_available(): ''' return a list of available groups ''' import ckan.logic as logic @@ -1212,6 +1230,7 @@ def groups_available(): data_dict = {'available_only': True} return logic.get_action('group_list_authz')(context, data_dict) + def dashboard_activity_stream(user_id): '''Return the dashboard activity stream of the given user. @@ -1240,6 +1259,7 @@ def escape_js(str_to_escape): .replace('\'', '\\\'') \ .replace('"', '\\\"') + def get_pkg_dict_extra(pkg_dict, key, default=None): '''Returns the value for the dataset extra with the provided key. @@ -1259,6 +1279,7 @@ def get_pkg_dict_extra(pkg_dict, key, default=None): return default + def get_request_param(parameter_name, default=None): ''' This function allows templates to access query string parameters from the request. This is useful for things like sort order in @@ -1301,7 +1322,7 @@ def format_resource_items(items): value = formatters.localised_number(value) key = key.replace('_', ' ') output.append((key, value)) - return sorted(output, key=lambda x:x[0]) + return sorted(output, key=lambda x: x[0]) # these are the functions that will end up in `h` template helpers From c58215e5a2ed475ae5ab82f49358ff274827e08d Mon Sep 17 00:00:00 2001 From: John Martin Date: Wed, 3 Oct 2012 14:22:31 +0100 Subject: [PATCH 11/53] Fixes in order to prevent the flickering from happening with the data-viewer --- .../base/javascript/modules/data-viewer.js | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/ckan/public/base/javascript/modules/data-viewer.js b/ckan/public/base/javascript/modules/data-viewer.js index dedf1e24d39..d9e5235dbc4 100644 --- a/ckan/public/base/javascript/modules/data-viewer.js +++ b/ckan/public/base/javascript/modules/data-viewer.js @@ -4,8 +4,7 @@ this.ckan.module('data-viewer', function (jQuery) { return { options: { timeout: 200, - minHeight: 400, - padding: 42 + minHeight: 400 }, initialize: function () { @@ -19,39 +18,22 @@ this.ckan.module('data-viewer', function (jQuery) { var loc = $('body').data('site-root'); // see if page is in part of the same domain if (this.el.attr('src').substring(0, loc.length) === loc) { + // hide the scrollbar to prevent jumping + $('').appendTo(this.el.contents().find('body')); this._recalibrate(); setInterval(function() { - if (!self.el.is(':animated')) { - self._recalibrate(); - } + self._recalibrate(); }, this.options.timeout); } else { - this.el.animate({height: 600}, 600); + this.el.css('height', 600); } }, - _showDebugInfo: function() { - var iframe = this.el; - console.log('================='); - console.log($(iframe.get(0), window.top.document).contents().find('body')[0].scrollHeight); - console.log($(iframe.get(0).contentWindow.document).height()); - console.log($(iframe.get(0).contentWindow.document.body).height()); - console.log($(iframe.contents().height())); - console.log($(iframe.contents().innerHeight())); - console.log($(iframe.contents().find('html').height())); - console.log($(iframe.contents().find('body').height())); - console.log($(iframe.contents().find('body').innerHeight())); - }, - _recalibrate: function() { // save reference to this to use in timeout - var self = this; - var height = self.el.contents().find('body').height(); - height = Math.max(height, self.options.minHeight); - var deltaHeight = height - (self.el.height() - self.options.padding); - if (deltaHeight > 1 || deltaHeight < -10) { - self.el.animate({height: height+self.options.padding}, Math.min(700, height*2)); - } + var height = this.el.contents().find('body').outerHeight(); + height = Math.max(height, this.options.minHeight); + this.el.css('height', height); }, // firefox caches iframes so force it to get fresh content From dc46a41f163faa9f70f47c0217e4f731c75c097d Mon Sep 17 00:00:00 2001 From: John Martin Date: Wed, 3 Oct 2012 14:51:08 +0100 Subject: [PATCH 12/53] Remove fix for `overflow:hidden` on iframe body It wasn't need as it was a problem that I spotted that only occured when we used `jQuery.animate` and since we moved to `jQuery.css` it doesn't happen any more. --- ckan/public/base/javascript/modules/data-viewer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/ckan/public/base/javascript/modules/data-viewer.js b/ckan/public/base/javascript/modules/data-viewer.js index d9e5235dbc4..34d4315da74 100644 --- a/ckan/public/base/javascript/modules/data-viewer.js +++ b/ckan/public/base/javascript/modules/data-viewer.js @@ -18,8 +18,6 @@ this.ckan.module('data-viewer', function (jQuery) { var loc = $('body').data('site-root'); // see if page is in part of the same domain if (this.el.attr('src').substring(0, loc.length) === loc) { - // hide the scrollbar to prevent jumping - $('').appendTo(this.el.contents().find('body')); this._recalibrate(); setInterval(function() { self._recalibrate(); From 3a3fc1f1344ef60f68444a84f4cb396c559d0768 Mon Sep 17 00:00:00 2001 From: John Martin Date: Wed, 3 Oct 2012 15:45:39 +0100 Subject: [PATCH 13/53] Added deltaHeight back in... as it was the simplest solution to the problem - Tried https://github.com/house9/jquery-iframe-auto-height but it only works onload and there is no api to allow for refiring a resize event - Tried using `document.scrollHeight` but it was too flaky in returning correct values and could cause the iframe to resize by 1px every timer tick --- ckan/public/base/javascript/modules/data-viewer.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ckan/public/base/javascript/modules/data-viewer.js b/ckan/public/base/javascript/modules/data-viewer.js index 34d4315da74..33809618a69 100644 --- a/ckan/public/base/javascript/modules/data-viewer.js +++ b/ckan/public/base/javascript/modules/data-viewer.js @@ -4,7 +4,8 @@ this.ckan.module('data-viewer', function (jQuery) { return { options: { timeout: 200, - minHeight: 400 + minHeight: 400, + padding: 30 }, initialize: function () { @@ -28,10 +29,12 @@ this.ckan.module('data-viewer', function (jQuery) { }, _recalibrate: function() { - // save reference to this to use in timeout - var height = this.el.contents().find('body').outerHeight(); + var height = this.el.contents().find('body').outerHeight(true); height = Math.max(height, this.options.minHeight); - this.el.css('height', height); + var deltaHeight = height - (this.el.height() - this.options.padding); + if (deltaHeight > 1 || deltaHeight < -10) { + this.el.css('height', height + this.options.padding); + } }, // firefox caches iframes so force it to get fresh content From 7841c45f8694b32f5f1064ca0de7a9a403e9e0d6 Mon Sep 17 00:00:00 2001 From: John Martin Date: Wed, 3 Oct 2012 15:52:33 +0100 Subject: [PATCH 14/53] Removed deltaHeight as it was uneeded, it was only really the padding that was really needed --- ckan/public/base/javascript/modules/data-viewer.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ckan/public/base/javascript/modules/data-viewer.js b/ckan/public/base/javascript/modules/data-viewer.js index 33809618a69..f928ed1c0d3 100644 --- a/ckan/public/base/javascript/modules/data-viewer.js +++ b/ckan/public/base/javascript/modules/data-viewer.js @@ -31,10 +31,7 @@ this.ckan.module('data-viewer', function (jQuery) { _recalibrate: function() { var height = this.el.contents().find('body').outerHeight(true); height = Math.max(height, this.options.minHeight); - var deltaHeight = height - (this.el.height() - this.options.padding); - if (deltaHeight > 1 || deltaHeight < -10) { - this.el.css('height', height + this.options.padding); - } + this.el.css('height', height + this.options.padding); }, // firefox caches iframes so force it to get fresh content From ca5882a8f5ecd3b55eb21174223e6c29c26dd19a Mon Sep 17 00:00:00 2001 From: John Martin Date: Thu, 4 Oct 2012 11:06:31 +0100 Subject: [PATCH 15/53] Added resources the the side bar of the edit dataset page --- ckan/templates/package/edit.html | 4 ++++ ckan/templates/package/snippets/resources.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ckan/templates/package/edit.html b/ckan/templates/package/edit.html index c2c6c321221..722df8812a9 100644 --- a/ckan/templates/package/edit.html +++ b/ckan/templates/package/edit.html @@ -8,6 +8,10 @@
  • {% link_for _('Edit Dataset'), controller='package', action='edit', id=pkg.name %}
  • {% endblock %} +{% block resources_module %} + {% snippet 'package/snippets/resources.html', pkg=pkg, action='resource_edit' %} +{% endblock %} + {% block actions_content %} {% snippet 'package/snippets/back_to_package_action.html', pkg=pkg %} {% endblock %} diff --git a/ckan/templates/package/snippets/resources.html b/ckan/templates/package/snippets/resources.html index 9def65b71f0..b6585018dc2 100644 --- a/ckan/templates/package/snippets/resources.html +++ b/ckan/templates/package/snippets/resources.html @@ -14,7 +14,7 @@ {% set resources = pkg.resources or [] %} {% if resources %}
    -

    {{ _("Resources") }}

    +

    {{ _("Resources") }}