From f366ba684340adbb903b78bc5beb871069a6a0c2 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 27 Nov 2013 18:03:53 +0000 Subject: [PATCH 01/47] [#1188] Allow organization dropdown to be displayed The current logic was unclear. If a user belongs to at least one organization, the dropdown will be displayed, both on the created and edit forms. Same for the visibility one. --- ckan/templates/package/snippets/package_basic_fields.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckan/templates/package/snippets/package_basic_fields.html b/ckan/templates/package/snippets/package_basic_fields.html index d1ac7e25577..7cb56ec7e51 100644 --- a/ckan/templates/package/snippets/package_basic_fields.html +++ b/ckan/templates/package/snippets/package_basic_fields.html @@ -56,8 +56,8 @@ {% set dataset_has_organization = data.owner_org or data.group_id %} {% set organizations_available = h.organizations_available('create_dataset') %} {% set user_is_sysadmin = h.check_access('sysadmin') %} - {% set show_organizations_selector = organizations_available and (user_is_sysadmin or dataset_is_draft) %} - {% set show_visibility_selector = dataset_has_organization or (organizations_available and (user_is_sysadmin or dataset_is_draft)) %} + {% set show_organizations_selector = organizations_available %} + {% set show_visibility_selector = dataset_has_organization or organizations_available %} {% if show_organizations_selector and show_visibility_selector %}
From 62fba1241be09e7ff6bd197a79e0c8258a00b357 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 27 Nov 2013 18:36:37 +0000 Subject: [PATCH 02/47] [#1188] Allow empty orgs if datasets can be unowned If the "create_unowned_dataset" is set to True and you are not a sysadmin and select the "No organization" option on the dataset form, you get a validation error. --- ckan/logic/validators.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py index c4c9b6d3aca..c860b6f0c2b 100644 --- a/ckan/logic/validators.py +++ b/ckan/logic/validators.py @@ -32,7 +32,9 @@ def owner_org_validator(key, data, errors, context): model = context['model'] user = context['user'] user = model.User.get(user) - if value == '': + if value == '' : + if new_authz.check_config_permission('create_unowned_dataset'): + return # only sysadmins can remove datasets from org if not user.sysadmin: raise Invalid(_('You cannot remove a dataset from an existing organization')) From acd7976f67f769483d615d0a77422e59007a086d Mon Sep 17 00:00:00 2001 From: Samuele Santi Date: Mon, 9 Dec 2013 13:16:32 +0100 Subject: [PATCH 03/47] Removed limit of number of arguments passed to ``user add`` command. This is needed as the ``user add`` command is supposed to accept a variable number of arguments. --- ckan/lib/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/lib/cli.py b/ckan/lib/cli.py index 3378bce73e9..389570b4921 100644 --- a/ckan/lib/cli.py +++ b/ckan/lib/cli.py @@ -697,7 +697,7 @@ class UserCmd(CkanCommand): ''' summary = __doc__.split('\n')[0] usage = __doc__ - max_args = 4 + max_args = None min_args = 0 def command(self): From 5ad5b582a9f4126d9f485163c581d4d668dee861 Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Tue, 10 Dec 2013 15:59:25 +0100 Subject: [PATCH 04/47] [#1022] Fix cross-references in API docstrings Use :py:func:, :py:class: etc to do proper hyperlinked cross-references. Also a few other minor docstrings formatting fixes. --- ckan/logic/action/create.py | 53 ++++++++++++++++++++++--------------- ckan/logic/action/get.py | 43 +++++++++++++++--------------- ckan/logic/action/update.py | 46 ++++++++++++++++++++------------ 3 files changed, 82 insertions(+), 60 deletions(-) diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index 1c8c0065ab6..9b99c4a42c5 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -45,7 +45,8 @@ def package_create(context, data_dict): for the new dataset, you must also be authorized to edit these groups. Plugins may change the parameters of this function depending on the value - of the ``type`` parameter, see the ``IDatasetForm`` plugin interface. + of the ``type`` parameter, see the + :py:class:`~ckan.plugins.interfaces.IDatasetForm` plugin interface. :param name: the name of the new dataset, must be between 2 and 100 characters long and contain only lowercase alphanumeric characters, @@ -63,8 +64,9 @@ def package_create(context, data_dict): :param maintainer_email: the email address of the dataset's maintainer (optional) :type maintainer_email: string - :param license_id: the id of the dataset's license, see ``license_list()`` - for available values (optional) + :param license_id: the id of the dataset's license, see + :py:func:`~ckan.logic.action.get.license_list` for available values + (optional) :type license_id: license id string :param notes: a description of the dataset (optional) :type notes: string @@ -78,14 +80,16 @@ def package_create(context, data_dict): authorized to change the state of the dataset (optional, default: ``'active'``) :type state: string - :param type: the type of the dataset (optional), ``IDatasetForm`` plugins + :param type: the type of the dataset (optional), + :py:class:`~ckan.plugins.interfaces.IDatasetForm` plugins associate themselves with different dataset types and provide custom dataset handling behaviour for these types :type type: string - :param resources: the dataset's resources, see ``resource_create()`` - for the format of resource dictionaries (optional) + :param resources: the dataset's resources, see + :py:func:`resource_create` for the format of resource dictionaries + (optional) :type resources: list of resource dictionaries - :param tags: the dataset's tags, see ``tag_create()`` for the format + :param tags: the dataset's tags, see :py:func:`tag_create` for the format of tag dictionaries (optional) :type tags: list of tag dictionaries :param extras: the dataset's extras (optional), extras are arbitrary @@ -93,26 +97,28 @@ def package_create(context, data_dict): dictionary should have keys ``'key'`` (a string), ``'value'`` (a string) :type extras: list of dataset extra dictionaries - :param relationships_as_object: see ``package_relationship_create()`` for - the format of relationship dictionaries (optional) + :param relationships_as_object: see :py:func:`package_relationship_create` + for the format of relationship dictionaries (optional) :type relationships_as_object: list of relationship dictionaries - :param relationships_as_subject: see ``package_relationship_create()`` for - the format of relationship dictionaries (optional) + :param relationships_as_subject: see :py:func:`package_relationship_create` + for the format of relationship dictionaries (optional) :type relationships_as_subject: list of relationship dictionaries :param groups: the groups to which the dataset belongs (optional), each group dictionary should have one or more of the following keys which identify an existing group: ``'id'`` (the id of the group, string), ``'name'`` (the name of the group, string), ``'title'`` (the title of the group, string), to see - which groups exist call ``group_list()`` + which groups exist call :py:func:`~ckan.logic.action.get.group_list` :type groups: list of dictionaries :param owner_org: the id of the dataset's owning organization, see - ``organization_list()`` or ``organization_list_for_user`` for + :py:func:`~ckan.logic.action.get.organization_list` or + :py:func:`~ckan.logic.action.get.organization_list_for_user` for available values (optional) :type owner_org: string - :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) + :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 ''' @@ -617,7 +623,8 @@ def group_create(context, data_dict): You must be authorized to create groups. Plugins may change the parameters of this function depending on the value - of the ``type`` parameter, see the ``IGroupForm`` plugin interface. + of the ``type`` parameter, see the + :py:class:`~ckan.plugins.interfaces.IGroupForm` plugin interface. :param name: the name of the group, a string between 2 and 100 characters long, containing only lowercase alphanumeric characters, ``-`` and @@ -632,7 +639,8 @@ def group_create(context, data_dict): :param image_url: the URL to an image to be displayed on the group's page (optional) :type image_url: string - :param type: the type of the group (optional), ``IGroupForm`` plugins + :param type: the type of the group (optional), + :py:class:`~ckan.plugins.interfaces.IGroupForm` plugins associate themselves with different group types and provide custom group handling behaviour for these types Cannot be 'organization' @@ -683,7 +691,8 @@ def organization_create(context, data_dict): You must be authorized to create organizations. Plugins may change the parameters of this function depending on the value - of the ``type`` parameter, see the ``IGroupForm`` plugin interface. + of the ``type`` parameter, see the + :py:class:`~ckan.plugins.interfaces.IGroupForm` plugin interface. :param name: the name of the organization, a string between 2 and 100 characters long, containing only lowercase alphanumeric characters, ``-`` and @@ -962,7 +971,7 @@ def vocabulary_create(context, data_dict): :param name: the name of the new vocabulary, e.g. ``'Genre'`` :type name: string :param tags: the new tags to add to the new vocabulary, for the format of - tag dictionaries see ``tag_create()`` + tag dictionaries see :py:func:`tag_create` :type tags: list of tag dictionaries :returns: the newly-created vocabulary @@ -1001,8 +1010,7 @@ def activity_create(context, activity_dict, **kw): ``'my_dataset'`` :param activity_type: the type of the activity, this must be an activity type that CKAN knows how to render, e.g. ``'new package'``, - ``'changed user'``, ``'deleted group'`` etc. (for a full list see - ``activity_renderers`` in ``ckan/logic/action/get.py`` + ``'changed user'``, ``'deleted group'`` etc. :type activity_type: string :param data: any additional data about the activity :type data: dictionary @@ -1066,7 +1074,8 @@ def tag_create(context, data_dict): You can only use this function to create tags that belong to a vocabulary, not to create free tags. (To create a new free tag simply add the tag to - a package, e.g. using the ``package_update`` function.) + a package, e.g. using the + :py:func:`~ckan.logic.action.update.package_update` function.) :param name: the name for the new tag, a string between 2 and 100 characters long containing only alphanumeric characters and ``-``, diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index 502b1bf92c9..239c7de4b9c 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -447,10 +447,10 @@ def group_list_authz(context, data_dict): (optional, default: ``False``) :type available_only: boolean - :param am_member: if True return only the groups the logged-in user is a - member of, otherwise return all groups that the user is authorized to + :param am_member: if ``True`` return only the groups the logged-in user is + a member of, otherwise return all groups that the user is authorized to edit (for example, sysadmin users are authorized to edit all groups) - (optional, default: False) + (optional, default: ``False``) :type am-member: boolean :returns: list of dictized groups that the user is authorized to edit @@ -742,8 +742,8 @@ def package_relationships_list(context, data_dict): :param id2: the id or name of the second package :type id: string :param rel: relationship as string see - :func:`ckan.logic.action.create.package_relationship_create()` for the - relationship types (optional) + :py:func:`~ckan.logic.action.create.package_relationship_create` for + the relationship types (optional) :rtype: list of dictionaries @@ -1330,13 +1330,13 @@ def package_search(context, data_dict): This action accepts a *subset* of solr's search query parameters: - :param q: the solr query. Optional. Default: `"*:*"` + :param q: the solr query. Optional. Default: ``"*:*"`` :type q: string - :param fq: any filter queries to apply. Note: `+site_id:{ckan_site_id}` + :param fq: any filter queries to apply. Note: ``+site_id:{ckan_site_id}`` is added to this string prior to the query being executed. :type fq: string :param sort: sorting of the search results. Optional. Default: - 'relevance asc, metadata_modified desc'. As per the solr + ``'relevance asc, metadata_modified desc'``. As per the solr documentation, this is a comma-separated string of field names and sort-orderings. :type sort: string @@ -1345,7 +1345,7 @@ def package_search(context, data_dict): :param start: the offset in the complete result for where the set of returned datasets should begin. :type start: int - :param facet: whether to enable faceted results. Default: "true". + :param facet: whether to enable faceted results. Default: ``True``. :type facet: string :param facet.mincount: the minimum counts for facet fields should be included in the results. @@ -1616,7 +1616,7 @@ def resource_search(context, data_dict): queries (rather than ValidationErrors). :param query: The search criteria. See above for description. - :type query: string or list of strings of the form "{field}:{term1}" + :type query: string or list of strings of the form ``{field}:{term1}`` :param fields: Deprecated :type fields: dict of fields to search terms. :param order_by: A field on the Resource model that orders the results. @@ -2664,11 +2664,12 @@ def followee_list(context, data_dict): (optional) :type q: string - :rtype: list of dictionaries, each with keys 'type' (e.g. 'user', - 'dataset' or 'group'), 'display_name' (e.g. a user's display name, - or a package's title) and 'dict' (e.g. a dict representing the - followed user, package or group, the same as the dict that would be - returned by user_show, package_show or group_show) + :rtype: list of dictionaries, each with keys ``'type'`` (e.g. ``'user'``, + ``'dataset'`` or ``'group'``), ``'display_name'`` (e.g. a user's + display name, or a package's title) and ``'dict'`` (e.g. a dict + representing the followed user, package or group, the same as the dict + that would be returned by :py:func:`user_show`, + :py:func:`package_show` or :py:func:`group_show`) ''' _check_access('followee_list', context, data_dict) @@ -2824,7 +2825,7 @@ def dashboard_activity_list(context, data_dict): :type offset: int :param limit: the maximum number of activities to return (optional, default: 31, the default value is configurable via the - ``ckan.activity_list_limit`` setting) + :ref:`ckan.activity_list_limit` setting) :rtype: list of activity dictionaries @@ -2952,12 +2953,12 @@ def _unpick_search(sort, allowed_fields=None, total=None): def member_roles_list(context, data_dict): '''Return the possible roles for members of groups and organizations. - :param group_type: the group type, either "group" or "organization" - (optional, default "organization") + :param group_type: the group type, either ``"group"`` or ``"organization"`` + (optional, default ``"organization"``) :type id: string - :returns: a list of dictionaries each with two keys: "text" (the display - name of the role, e.g. "Admin") and "value" (the internal name of the - role, e.g. "admin") + :returns: a list of dictionaries each with two keys: ``"text"`` (the + display name of the role, e.g. ``"Admin"``) and ``"value"`` (the + internal name of the role, e.g. ``"admin"``) :rtype: list of dictionaries ''' diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 920c68c4092..76dbf30b9ca 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -67,7 +67,9 @@ def _make_latest_rev_active(context, q): context['latest_revision'] = latest_rev.revision_id def make_latest_pending_package_active(context, data_dict): - '''TODO: What does this function do? + ''' + + .. todo:: What does this function do? You must be authorized to update the dataset. @@ -120,7 +122,8 @@ def related_update(context, data_dict): You must be the owner of a related item to update it. - For further parameters see ``related_create()``. + For further parameters see + :py:func:`~ckan.logic.action.create.related_create`. :param id: the id of the related item to update :type id: string @@ -187,7 +190,8 @@ def resource_update(context, data_dict): To update a resource you must be authorized to update the dataset that the resource belongs to. - For further parameters see ``resource_create()``. + For further parameters see + :py:func:`~ckan.logic.action.create.resource_create`. :param id: the id of the resource to update :type id: string @@ -245,16 +249,17 @@ def package_update(context, data_dict): to. Plugins may change the parameters of this function depending on the value - of the dataset's ``type`` attribute, see the ``IDatasetForm`` plugin - interface. + of the dataset's ``type`` attribute, see the + :py:class:`~ckan.plugins.interfaces.IDatasetForm` plugin interface. - For further parameters see ``package_create()``. + For further parameters see + :py:func:`~ckan.logic.action.create.package_create`. :param id: the name or id of the dataset to update :type id: string - :returns: the updated dataset (if 'return_package_dict' is True in the - context, which is the default. Otherwise returns just the + :returns: the updated dataset (if ``'return_package_dict'`` is ``True`` in + the context, which is the default. Otherwise returns just the dataset id) :rtype: dictionary @@ -598,9 +603,11 @@ def group_update(context, data_dict): You must be authorized to edit the group. Plugins may change the parameters of this function depending on the value - of the group's ``type`` attribute, see the ``IGroupForm`` plugin interface. + of the group's ``type`` attribute, see the + :py:class:`~ckan.plugins.interfaces.IGroupForm` plugin interface. - For further parameters see ``group_create()``. + For further parameters see + :py:func:`~ckan.logic.action.create.group_create`. :param id: the name or id of the group to update :type id: string @@ -616,7 +623,8 @@ def organization_update(context, data_dict): You must be authorized to edit the organization. - For further parameters see ``organization_create()``. + For further parameters see + :py:func:`~ckan.logic.action.create.organization_create`. :param id: the name or id of the organization to update :type id: string @@ -633,7 +641,8 @@ def user_update(context, data_dict): Normal users can only update their own user accounts. Sysadmins can update any user account. - For further parameters see ``user_create()``. + For further parameters see + :py:func:`~ckan.logic.action.create.user_create`. :param id: the name or id of the user to update :type id: string @@ -740,7 +749,8 @@ def task_status_update_many(context, data_dict): '''Update many task statuses at once. :param data: the task_status dictionaries to update, for the format of task - status dictionaries see ``task_status_update()`` + status dictionaries see + :py:func:`~task_status_update` :type data: list of dictionaries :returns: the updated task statuses @@ -814,7 +824,7 @@ def term_translation_update_many(context, data_dict): :param data: the term translation dictionaries to create or update, for the format of term translation dictionaries see - ``term_translation_update()`` + :py:func:`~term_translation_update` :type data: list of dictionaries :returns: a dictionary with key ``'success'`` whose value is a string @@ -898,7 +908,8 @@ def vocabulary_update(context, data_dict): You must be a sysadmin to update vocabularies. - For further parameters see ``vocabulary_create()``. + For further parameters see + :py:func:`~ckan.logic.action.create.vocabulary_create`. :param id: the id of the vocabulary to update :type id: string @@ -1023,7 +1034,7 @@ def user_role_bulk_update(context, data_dict): You must be authorized to update the domain object. :param user_roles: the updated user roles, for the format of user role - dictionaries see ``user_role_update()`` + dictionaries see :py:func:`~user_role_update` :type user_roles: list of dictionaries :returns: the updated roles of all users and authorization groups for the @@ -1052,7 +1063,8 @@ def user_role_bulk_update(context, data_dict): def dashboard_mark_activities_old(context, data_dict): '''Mark all the authorized user's new dashboard activities as old. - This will reset dashboard_new_activities_count to 0. + This will reset + :py:func:`~ckan.logic.action.get.dashboard_new_activities_count` to 0. ''' _check_access('dashboard_mark_activities_old', context, From 2d23040c0a727f1a0bcceb6e9ea38601292387b8 Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Tue, 10 Dec 2013 17:46:57 +0100 Subject: [PATCH 05/47] [#1380] Encourage installing latest release in source install docs Needed an automated way to get the latest version number, so I added functions to doc/conf.py for this, and then used them in the source and package install docs. --- doc/.gitignore | 1 + doc/conf.py | 90 +++++++++++++++++++++++++++++++++++- doc/install-from-package.rst | 14 ++++-- doc/install-from-source.rst | 22 ++++++--- 4 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 doc/.gitignore diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000000..49531e3f612 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +_latest_release.rst diff --git a/doc/conf.py b/doc/conf.py index e5c33312cfc..0df7c8ce669 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -14,7 +14,9 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import sys +import os +import subprocess # If your extensions (or modules documented by autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -106,6 +108,92 @@ # The full version, including alpha/beta/rc tags. release = ckan.__version__ + +def latest_release_tag(): + '''Return the name of the git tag for the latest stable release. + + e.g.: "ckan-2.1.1" + + This requires git to be installed. + + ''' + git_tags = subprocess.check_output(['git', 'tag', '-l']).split() + + # FIXME: We could do more careful pattern matching against ckan-X.Y.Z here. + release_tags = [tag for tag in git_tags if tag.startswith('ckan-')] + + # git tag -l prints out the tags in the right order anyway, but don't rely + # on that, sort them again here for good measure. + release_tags.sort() + + return release_tags[-1] + + +def latest_release_version(): + '''Return the version number of the latest stable release. + + e.g. "2.1.1" + + ''' + version = latest_release_tag()[len('ckan-'):] + + # TODO: We could assert here that latest_version matches X.Y.Z. + + return version + + +def latest_package_name(): + '''Return the filename of the Ubuntu package for the latest stable release. + + e.g. "python-ckan_2.1_amd64.deb" + + ''' + # We don't create a new package file name for a patch release like 2.1.1, + # instead we just update the existing 2.1 package. So package names only + # have the X.Y part of the version number in them, not X.Y.Z. + latest_minor_version = latest_release_version()[:3] + + return 'python-ckan_{version}_amd64.deb'.format( + version=latest_minor_version) + + +def write_latest_release_file(): + '''Write a file in the doc/ dir containing reStructuredText substitutions + for the latest release tag name and version number. + + ''' + filename = '_latest_release.rst' + template = ''':orphan: + +.. Some common reStructuredText substitutions. + + **This file is autogenerated!** So don't edit it by hand. + + You can include this file at the top of your ``*.rst`` file with a line + like:: + + .. include:: {filename} + + Then use the substitutions in this file, e.g.:: + + |latest_release_version| + +.. |latest_release_tag| replace:: {latest_tag} +.. |latest_release_version| replace:: {latest_version} +.. |latest_package_name| replace:: {package_name} + +''' + open('_latest_release.rst', 'w').write(template.format( + filename=filename, + latest_tag=latest_release_tag(), + latest_version=latest_release_version(), + package_name=latest_package_name(), + )) + + +write_latest_release_file() + + # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None diff --git a/doc/install-from-package.rst b/doc/install-from-package.rst index 8447b3bf1af..2d6e8f59c24 100644 --- a/doc/install-from-package.rst +++ b/doc/install-from-package.rst @@ -1,3 +1,5 @@ +.. include:: _latest_release.rst + ============================ Installing CKAN from package ============================ @@ -24,18 +26,22 @@ CKAN: sudo apt-get install -y nginx apache2 libapache2-mod-wsgi libpq5 -#. Download the CKAN package:: +#. Download the CKAN package: + + .. parsed-literal:: - wget http://packaging.ckan.org/python-ckan_2.0_amd64.deb + wget \http://packaging.ckan.org/|latest_package_name| .. note:: If ``wget`` is not present, you can install it via:: sudo apt-get install wget -#. Install the CKAN package:: +#. Install the CKAN package: + + .. parsed-literal:: - sudo dpkg -i python-ckan_2.0_amd64.deb + sudo dpkg -i |latest_package_name| .. note:: If you get the following error it means that for some reason the Apache WSGI module was not enabled:: diff --git a/doc/install-from-source.rst b/doc/install-from-source.rst index f34449f9105..78e167c2e86 100644 --- a/doc/install-from-source.rst +++ b/doc/install-from-source.rst @@ -1,3 +1,5 @@ +.. include:: _latest_release.rst + =========================== Installing CKAN from source =========================== @@ -90,19 +92,27 @@ a. Create a Python `virtual environment `_ |activate| -b. Install the CKAN source code into your virtualenv. To install the latest - development version of CKAN (the most recent commit on the master branch of - the CKAN git repository), run: +b. Install the CKAN source code into your virtualenv. + To install the latest stable release of CKAN (CKAN |latest_release_version|), + run: .. parsed-literal:: - pip install -e 'git+\ |git_url|\#egg=ckan' + pip install -e 'git+\ |git_url|\@\ |latest_release_tag|\#egg=ckan' - Alternatively, to install a specific version such as CKAN 2.0 run: + If you're installing CKAN for development, you may want to install the + latest development version (the most recent commit on the master branch of + the CKAN git repository). In that case, run this command instead: .. parsed-literal:: - pip install -e 'git+\ |git_url|\@ckan-2.0#egg=ckan' + pip install -e 'git+\ |git_url|\#egg=ckan' + + .. warning:: + + The development version may contain bugs and should not be used for + production websites! Only install this version if you're doing CKAN + development. c. Install the Python modules that CKAN requires into your virtualenv: From 79a218f0818031567b6ce1903a73ae98e27ddb5b Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Tue, 10 Dec 2013 17:51:26 +0100 Subject: [PATCH 06/47] [#1380] Remove hardcoded filename --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 0df7c8ce669..fc38bbd5206 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -183,7 +183,7 @@ def write_latest_release_file(): .. |latest_package_name| replace:: {package_name} ''' - open('_latest_release.rst', 'w').write(template.format( + open(filename, 'w').write(template.format( filename=filename, latest_tag=latest_release_tag(), latest_version=latest_release_version(), From 10a379881fb6784014265c6e7371de434c3c8898 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 12 Dec 2013 15:54:50 +0000 Subject: [PATCH 07/47] Revert "[#1188] Allow organization dropdown to be displayed" There is certain logic, albeit a bit flacky, in hiding the organization dropwdown, in that if shown, any user belonging to an organization could take ownership of any unowned dataset in the instance. We need to properly define the auth rules and enforce them at the validator level as well. For the time being, showing the dropdown only to sysadmins makes sense (and of course fixing the original bug on the visibility field validator, which is done on the next commit) This reverts commit f366ba684340adbb903b78bc5beb871069a6a0c2. --- ckan/templates/package/snippets/package_basic_fields.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckan/templates/package/snippets/package_basic_fields.html b/ckan/templates/package/snippets/package_basic_fields.html index 8db5763da95..585d6450270 100644 --- a/ckan/templates/package/snippets/package_basic_fields.html +++ b/ckan/templates/package/snippets/package_basic_fields.html @@ -56,8 +56,8 @@ {% set dataset_has_organization = data.owner_org or data.group_id %} {% set organizations_available = h.organizations_available('create_dataset') %} {% set user_is_sysadmin = h.check_access('sysadmin') %} - {% set show_organizations_selector = organizations_available %} - {% set show_visibility_selector = dataset_has_organization or organizations_available %} + {% set show_organizations_selector = organizations_available and (user_is_sysadmin or dataset_is_draft) %} + {% set show_visibility_selector = dataset_has_organization or (organizations_available and (user_is_sysadmin or dataset_is_draft)) %} {% if show_organizations_selector and show_visibility_selector %}
From 864e1a68c8183b35cb2e7fcb0db43b51d1f6cc58 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 12 Dec 2013 17:09:47 +0000 Subject: [PATCH 08/47] [#1188] Fix visibility validator To not rely on owner_org being present, when the validator gets a value of private and there is no owner_org, we request the dataset anyway and check if it actually has an owner_org. Added new validation tests --- ckan/logic/validators.py | 25 ++++++++- ckan/new_tests/logic/test_validators.py | 71 +++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py index c239af796cd..d4d52045561 100644 --- a/ckan/logic/validators.py +++ b/ckan/logic/validators.py @@ -692,7 +692,28 @@ def role_exists(role, context): def datasets_with_no_organization_cannot_be_private(key, data, errors, context): - if data[key] is True and data.get(('owner_org',)) is None: + + dataset_id = data.get(('id',)) + owner_org = data.get(('owner_org',)) + private = data[key] is True + + check_passed = True + from nose.tools import set_trace; set_trace() + if not dataset_id and private and owner_org is None: + # When creating a dataset, enforce it directly + check_passed = False + elif dataset_id and private and owner_org is None: + # Check if the dataset actually has an owner_org, even if not provided + try: + dataset_dict = logic.get_action('package_show')({}, + {'id': dataset_id}) + if not dataset_dict.get('owner_org'): + check_passed = False + + except logic.NotFound: + check_passed = False + + if not check_passed: errors[key].append( _("Datasets with no organization can't be private.")) @@ -723,4 +744,4 @@ def no_loops_in_hierarchy(key, data, errors, context): not in allowable_parents: raise Invalid(_('This parent would create a loop in the ' 'hierarchy')) - + diff --git a/ckan/new_tests/logic/test_validators.py b/ckan/new_tests/logic/test_validators.py index 4aab0fad23c..8009b0f62d1 100644 --- a/ckan/new_tests/logic/test_validators.py +++ b/ckan/new_tests/logic/test_validators.py @@ -317,3 +317,74 @@ def call_validator(*args, **kwargs): # TODO: Test user_name_validator()'s behavior when there's a 'user_obj' in # the context dict. + + def test_datasets_with_org_can_be_private_when_creating(self): + + import ckan.logic.validators as validators + + data = factories.validator_data_dict() + errors = factories.validator_errors_dict() + + key = ('private',) + data[key] = True + errors[key] = [] + + data[('owner_org',)] = 'some_org_id' + + # Mock ckan.model. + mock_model = mock.MagicMock() + + @t.does_not_modify_errors_dict + @t.does_not_modify_data_dict + @t.returns_None + def call_validator(*args, **kwargs): + return validators.datasets_with_no_organization_cannot_be_private(*args, **kwargs) + call_validator(key, data, errors, context={'model': mock_model}) + + + def test_datasets_with_no_org_cannot_be_private_when_creating(self): + + import ckan.logic.validators as validators + + data = factories.validator_data_dict() + errors = factories.validator_errors_dict() + + key = ('private',) + data[key] = True + errors[key] = [] + + # Mock ckan.model. + mock_model = mock.MagicMock() + + @t.does_not_modify_data_dict + @adds_message_to_errors_dict('Datasets with no organization can\'t be private.') + def call_validator(*args, **kwargs): + return validators.datasets_with_no_organization_cannot_be_private(*args, **kwargs) + call_validator(key, data, errors, context={'model': mock_model}) + + def test_datasets_with_org_can_be_private_when_updating(self): + + import ckan.logic.validators as validators + + data = factories.validator_data_dict() + errors = factories.validator_errors_dict() + + key = ('private',) + data[key] = True + errors[key] = [] + + data[('id',)] = 'some_dataset_id' + data[('owner_org',)] = 'some_org_id' + + # Mock ckan.model. + mock_model = mock.MagicMock() + + @t.does_not_modify_errors_dict + @t.does_not_modify_data_dict + @t.returns_None + def call_validator(*args, **kwargs): + return validators.datasets_with_no_organization_cannot_be_private(*args, **kwargs) + call_validator(key, data, errors, context={'model': mock_model}) + + #TODO: Need to test when you are not providing owner_org and the validator + # queries for the dataset with package_show From 42e17b5edd5799f0b876e848ec44b7aae816a4c1 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 12 Dec 2013 18:52:08 +0000 Subject: [PATCH 09/47] [#1188] Remove debugger call --- ckan/logic/validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py index d4d52045561..fab4a68d946 100644 --- a/ckan/logic/validators.py +++ b/ckan/logic/validators.py @@ -698,7 +698,7 @@ def datasets_with_no_organization_cannot_be_private(key, data, errors, private = data[key] is True check_passed = True - from nose.tools import set_trace; set_trace() + if not dataset_id and private and owner_org is None: # When creating a dataset, enforce it directly check_passed = False From 7bd7062ac6b494f221e4157c339a4990b8afb344 Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Thu, 12 Dec 2013 21:25:30 +0100 Subject: [PATCH 10/47] [#1380] Fix some broken hyperlinks in the docs Stop Sphinx from hyperlinking part of the URL in pip install commands. --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index fc38bbd5206..774152dd048 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -35,7 +35,7 @@ .. |config_dir| replace:: |config_parent_dir|/default .. |production.ini| replace:: |config_dir|/production.ini .. |development.ini| replace:: |config_dir|/development.ini -.. |git_url| replace:: https://github.com/okfn/ckan.git +.. |git_url| replace:: \https://github.com/okfn/ckan.git .. |postgres| replace:: PostgreSQL .. |database| replace:: ckan_default .. |database_user| replace:: ckan_default From 2570f454b9dcd30af80ef627557874dbf1c43e46 Mon Sep 17 00:00:00 2001 From: amercader Date: Fri, 13 Dec 2013 17:26:09 +0000 Subject: [PATCH 11/47] [#1188] PEP8 fixes --- ckan/new_tests/logic/test_validators.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ckan/new_tests/logic/test_validators.py b/ckan/new_tests/logic/test_validators.py index 8009b0f62d1..dcdc46cb046 100644 --- a/ckan/new_tests/logic/test_validators.py +++ b/ckan/new_tests/logic/test_validators.py @@ -338,10 +338,10 @@ def test_datasets_with_org_can_be_private_when_creating(self): @t.does_not_modify_data_dict @t.returns_None def call_validator(*args, **kwargs): - return validators.datasets_with_no_organization_cannot_be_private(*args, **kwargs) + return validators.datasets_with_no_organization_cannot_be_private( + *args, **kwargs) call_validator(key, data, errors, context={'model': mock_model}) - def test_datasets_with_no_org_cannot_be_private_when_creating(self): import ckan.logic.validators as validators @@ -357,9 +357,12 @@ def test_datasets_with_no_org_cannot_be_private_when_creating(self): mock_model = mock.MagicMock() @t.does_not_modify_data_dict - @adds_message_to_errors_dict('Datasets with no organization can\'t be private.') + @adds_message_to_errors_dict( + "Datasets with no organization can't be private.") def call_validator(*args, **kwargs): - return validators.datasets_with_no_organization_cannot_be_private(*args, **kwargs) + return validators.datasets_with_no_organization_cannot_be_private( + *args, **kwargs) + call_validator(key, data, errors, context={'model': mock_model}) def test_datasets_with_org_can_be_private_when_updating(self): @@ -383,7 +386,8 @@ def test_datasets_with_org_can_be_private_when_updating(self): @t.does_not_modify_data_dict @t.returns_None def call_validator(*args, **kwargs): - return validators.datasets_with_no_organization_cannot_be_private(*args, **kwargs) + return validators.datasets_with_no_organization_cannot_be_private( + *args, **kwargs) call_validator(key, data, errors, context={'model': mock_model}) #TODO: Need to test when you are not providing owner_org and the validator From 9207cafd4ed91d8c232a58d08e8f3284c6909c89 Mon Sep 17 00:00:00 2001 From: John Martin Date: Tue, 17 Dec 2013 11:43:51 +0000 Subject: [PATCH 12/47] [#1392] Fixes member add page within groups --- ckan/templates/group/member_new.html | 31 +++++++++------------------- ckan/templates/group/members.html | 5 ++++- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/ckan/templates/group/member_new.html b/ckan/templates/group/member_new.html index bead208c5ec..f7b24c236fe 100644 --- a/ckan/templates/group/member_new.html +++ b/ckan/templates/group/member_new.html @@ -50,29 +50,18 @@

{{ form.select('role', label=_('Role'), options=c.roles, selected=c.user_role, error='', attrs=format_attrs) }}
{% if user %} - - {% set format_attrs = {'disabled': true} %} - {{ form.input('username', label=_('User'), value=user.name, classes=['control-medium'], attrs=format_attrs) }} + {% set locale = h.dump_json({'content': _('Are you sure you want to delete this member?')}) %} + {{ _('Delete') }} + {% else %} - {% set format_attrs = {'data-module': 'autocomplete', 'data-module-source': '/api/2/util/user/autocomplete?q=?'} %} - {{ form.input('username', id='field-username', label=_('User'), placeholder=_('Username'), value='', error='', classes=['control-medium'], attrs=format_attrs) }} + {% endif %} - {% set format_attrs = {'data-module': 'autocomplete'} %} - {{ form.select('role', label=_('Role'), options=c.roles, selected=c.user_role, error='', attrs=format_attrs) }} -
- {% if user %} - {% set locale = h.dump_json({'content': _('Are you sure you want to delete this member?')}) %} - {{ _('Delete') }} - - {% else %} - - {% endif %} -
- +
+ {% endblock %} {% endblock %} diff --git a/ckan/templates/group/members.html b/ckan/templates/group/members.html index c758972d54b..210562903b7 100644 --- a/ckan/templates/group/members.html +++ b/ckan/templates/group/members.html @@ -2,8 +2,11 @@ {% block subtitle %}{{ _('Members') }} - {{ c.group_dict.display_name }} - {{ _('Groups') }}{% endblock %} +{% block page_primary_action %} + {% link_for _('Add Member'), controller='group', action='member_new', id=c.group_dict.id, class_='btn btn-primary', icon='plus-sign-alt' %} +{% endblock %} + {% block primary_content_inner %} - {% link_for _('Add Member'), controller='group', action='member_new', id=c.group_dict.id, class_='btn pull-right', icon='plus-sign-alt' %}

{{ _('{0} members'.format(c.members|length)) }}

From 9d8454964ea4e79066901cdc16785e5d4c1a63ef Mon Sep 17 00:00:00 2001 From: Nigel Babu Date: Fri, 20 Dec 2013 15:01:49 +0530 Subject: [PATCH 13/47] Fix test failures associted with related_list The related_list dictization changes returned them as returned by the database. This change gives it a default order if they're not previously sorted. --- ckan/lib/dictization/model_dictize.py | 5 +++-- ckan/logic/action/get.py | 1 + ckan/tests/functional/test_related.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ckan/lib/dictization/model_dictize.py b/ckan/lib/dictization/model_dictize.py index 59f9bc97cb4..ce0f4df75ab 100644 --- a/ckan/lib/dictization/model_dictize.py +++ b/ckan/lib/dictization/model_dictize.py @@ -90,8 +90,9 @@ def related_list_dictize(related_list, context): for res in related_list: related_dict = related_dictize(res, context) result_list.append(related_dict) - - return result_list + if context.get('sorted'): + return result_list + return sorted(result_list, key=lambda x: x["created"], reverse=True) def extras_dict_dictize(extras_dict, context): diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index 91aa1aee3f4..03f99888fd8 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -263,6 +263,7 @@ def related_list(context, data_dict=None): if data_dict.get('featured', False): related_list = related_list.filter(model.Related.featured == 1) related_items = related_list.all() + context['sorted'] = True else: relateds = model.Related.get_for_dataset(dataset, status='active') related_items = (r.related for r in relateds) diff --git a/ckan/tests/functional/test_related.py b/ckan/tests/functional/test_related.py index 78085b24a11..9e65917fb2a 100644 --- a/ckan/tests/functional/test_related.py +++ b/ckan/tests/functional/test_related.py @@ -467,7 +467,7 @@ def test_api_list(self): r = json.loads(res.body) assert r['success'] == True, r assert r['result'][0]['type'] == "idea" - assert r['result'][0]['title'] == "one", r + assert r['result'][0]['title'] == "two", r p.related.remove(one) p.related.remove(two) From 59aae2cb68eca43fb0fd300d40672929fb7bb5df Mon Sep 17 00:00:00 2001 From: John Martin Date: Tue, 24 Dec 2013 11:46:23 +0000 Subject: [PATCH 14/47] [#1398] Adds: 'word-break: break-word;' for better word breaking on long words --- ckan/public/base/less/layout.less | 1 + 1 file changed, 1 insertion(+) diff --git a/ckan/public/base/less/layout.less b/ckan/public/base/less/layout.less index 0a74dfbe311..9ae4d86796d 100644 --- a/ckan/public/base/less/layout.less +++ b/ckan/public/base/less/layout.less @@ -114,6 +114,7 @@ margin-top: 15px; padding-top: 10px; border-top: 1px dotted #DDD; + word-break: break-word; dl dd { margin-top: 3px; margin-left: 0; From 87fc0807ec5aa7aa5c8a93b744bcf0daa75e2609 Mon Sep 17 00:00:00 2001 From: John Martin Date: Tue, 24 Dec 2013 13:54:13 +0000 Subject: [PATCH 15/47] [#1302] Removes mentions of 'no description' from templates --- ckan/templates/group/about.html | 2 -- ckan/templates/group/snippets/group_item.html | 2 -- ckan/templates/group/snippets/info.html | 2 -- ckan/templates/organization/about.html | 2 -- ckan/templates/organization/bulk_process.html | 2 -- ckan/templates/organization/snippets/organization_item.html | 2 -- ckan/templates/package/resource_read.html | 2 -- ckan/templates/package/snippets/package_context.html | 2 -- ckan/templates/package/snippets/resource_item.html | 2 -- ckan/templates/related/snippets/related_item.html | 2 -- ckan/templates/snippets/group.html | 2 -- ckan/templates/snippets/group_item.html | 2 -- ckan/templates/snippets/organization_item.html | 2 -- ckan/templates/snippets/package_item.html | 2 -- 14 files changed, 28 deletions(-) diff --git a/ckan/templates/group/about.html b/ckan/templates/group/about.html index a7d3683700d..bec97ae26e6 100644 --- a/ckan/templates/group/about.html +++ b/ckan/templates/group/about.html @@ -7,8 +7,6 @@

{% block page_heading %}{{ c.group_dict.display_name }}{% endblock %}

{% block group_description %} {% if c.group_dict.description %} {{ h.render_markdown(c.group_dict.description) }} - {% else %} -

{{ _('There is no description for this group') }}

{% endif %} {% endblock %} diff --git a/ckan/templates/group/snippets/group_item.html b/ckan/templates/group/snippets/group_item.html index 04351329f2b..ca90dedd647 100644 --- a/ckan/templates/group/snippets/group_item.html +++ b/ckan/templates/group/snippets/group_item.html @@ -23,8 +23,6 @@

{{ group.display_name }}

{% block description %} {% if group.description %}

{{ h.markdown_extract(group.description, extract_length=80) }}

- {% else %} -

{{ _('This group has no description') }}

{% endif %} {% endblock %} {% block datasets %} diff --git a/ckan/templates/group/snippets/info.html b/ckan/templates/group/snippets/info.html index dc86036bb4c..72db2cf8c5e 100644 --- a/ckan/templates/group/snippets/info.html +++ b/ckan/templates/group/snippets/info.html @@ -11,8 +11,6 @@

{{ group.display_name }}

{{ h.markdown_extract(group.description, 180) }} {% link_for _('read more'), controller='group', action='about', id=group.name %}

- {% else %} -

{{ _('There is no description for this group') }}

{% endif %} {% if show_nums %}
diff --git a/ckan/templates/organization/about.html b/ckan/templates/organization/about.html index 6d3fcfe1bfd..ed0e7b718ae 100644 --- a/ckan/templates/organization/about.html +++ b/ckan/templates/organization/about.html @@ -7,8 +7,6 @@

{% block page_heading %}{{ c.group_dict.display_name }}{% endblock %}

{% block organization_description %} {% if c.group_dict.description %} {{ h.render_markdown(c.group_dict.description) }} - {% else %} -

{{ _('There is no description for this organization') }}

{% endif %} {% endblock %} {% block organization_extras %} diff --git a/ckan/templates/organization/bulk_process.html b/ckan/templates/organization/bulk_process.html index 13c2001e45c..d4e4fa54bd1 100644 --- a/ckan/templates/organization/bulk_process.html +++ b/ckan/templates/organization/bulk_process.html @@ -77,8 +77,6 @@

{% if notes %}

{{ notes|urlize }}

- {% else %} -

{{ _("This dataset has no description") }}

{% endif %} diff --git a/ckan/templates/organization/snippets/organization_item.html b/ckan/templates/organization/snippets/organization_item.html index 5d774cb7f39..1d7ca32e308 100644 --- a/ckan/templates/organization/snippets/organization_item.html +++ b/ckan/templates/organization/snippets/organization_item.html @@ -22,8 +22,6 @@

{{ organization.display_name }}

{% block description %} {% if organization.description %}

{{ h.markdown_extract(organization.description, extract_length=80) }}

- {% else %} -

{{ _('This organization has no description') }}

{% endif %} {% endblock %} {% block datasets %} diff --git a/ckan/templates/package/resource_read.html b/ckan/templates/package/resource_read.html index dee83c32e0d..2eca1e62fbc 100644 --- a/ckan/templates/package/resource_read.html +++ b/ckan/templates/package/resource_read.html @@ -61,8 +61,6 @@
{% if res.description %} {{ h.render_markdown(res.description) }} - {% else %} -

{{ _('There is no description for this resource') }}

{% endif %} {% if not res.description and c.package.notes %}

{{ _('From the dataset abstract') }}

diff --git a/ckan/templates/package/snippets/package_context.html b/ckan/templates/package/snippets/package_context.html index 3640468c51e..110bbd5201b 100644 --- a/ckan/templates/package/snippets/package_context.html +++ b/ckan/templates/package/snippets/package_context.html @@ -6,8 +6,6 @@

{{ pkg.title or pkg.name }}

{{ h.markdown_extract(pkg.notes, 180) }} {% link_for _('read more'), controller='package', action='about', id=pkg.name %}

- {% else %} -

{{ _('There is no description for this dataset') }}

{% endif %}
diff --git a/ckan/templates/package/snippets/resource_item.html b/ckan/templates/package/snippets/resource_item.html index da03ec496f9..1a7c43490a4 100644 --- a/ckan/templates/package/snippets/resource_item.html +++ b/ckan/templates/package/snippets/resource_item.html @@ -12,8 +12,6 @@

{% if res.description %} {{ h.markdown_extract(res.description, extract_length=80) }} - {% else %} - {{ _('No description for this resource') }} {% endif %}

{% block resource_item_explore %} diff --git a/ckan/templates/related/snippets/related_item.html b/ckan/templates/related/snippets/related_item.html index df1c1300025..2053f7c0406 100644 --- a/ckan/templates/related/snippets/related_item.html +++ b/ckan/templates/related/snippets/related_item.html @@ -19,8 +19,6 @@

{{ related.title }}

{% if related.description %}
{{ h.render_markdown(related.description) }}
- {% else %} -

{{ _('This item has no description') }}

{% endif %} {{ tooltip }} diff --git a/ckan/templates/snippets/group.html b/ckan/templates/snippets/group.html index db758d9bc96..df0d44ca031 100644 --- a/ckan/templates/snippets/group.html +++ b/ckan/templates/snippets/group.html @@ -20,8 +20,6 @@

{{ group.title or group.name }}

{% if group.description %}

{{ h.markdown_extract(group.description, truncate) }}

- {% else %} -

{{ _('There is no description for this group') }}

{% endif %}
diff --git a/ckan/templates/snippets/group_item.html b/ckan/templates/snippets/group_item.html index 60f43f63b79..032f05fad15 100644 --- a/ckan/templates/snippets/group_item.html +++ b/ckan/templates/snippets/group_item.html @@ -14,8 +14,6 @@

{{ group.title or group.name }}{{ h.markdown_extract(group.description, truncate)|urlize }}

{% endif %} - {% else %} -

{{ _('There is no description for this group') }}

{% endif %} {% set list_class = "unstyled dataset-list" %} diff --git a/ckan/templates/snippets/organization_item.html b/ckan/templates/snippets/organization_item.html index 06579756f32..2b066464b77 100644 --- a/ckan/templates/snippets/organization_item.html +++ b/ckan/templates/snippets/organization_item.html @@ -12,8 +12,6 @@

{{ organization.title or organizatio {% else %}

{{ h.markdown_extract(organization.description, truncate)|urlize }}

{% endif %} - {% else %} -

{{ _('There is no description for this organization') }}

{% endif %} {% set list_class = "unstyled dataset-list" %} diff --git a/ckan/templates/snippets/package_item.html b/ckan/templates/snippets/package_item.html index b6ae1b95168..42bb4fc0244 100644 --- a/ckan/templates/snippets/package_item.html +++ b/ckan/templates/snippets/package_item.html @@ -41,8 +41,6 @@

{% endif %} {% if notes %}
{{ notes|urlize }}
- {% else %} -

{{ _("This dataset has no description") }}

{% endif %}

{% if package.resources and not hide_resources %} From 46c53d5093ef998337f434da2cf141573002fb72 Mon Sep 17 00:00:00 2001 From: Nigel Babu Date: Wed, 1 Jan 2014 13:02:01 +0530 Subject: [PATCH 16/47] [#1360] Update package_update docstring. --- ckan/logic/action/update.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index a0b80db7c6e..e60504d9b2d 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -242,13 +242,15 @@ def package_update(context, data_dict): '''Update a dataset (package). You must be authorized to edit the dataset and the groups that it belongs - to. + to. It is recommended to call + :py:func:`ckan.logic.action.get.package_show`, make the desired changes to + the result, and then call ``package_update()`` with it. Plugins may change the parameters of this function depending on the value of the dataset's ``type`` attribute, see the ``IDatasetForm`` plugin interface. - For further parameters see ``package_create()``. + For further parameters see :py:func:`ckan.logic.action.create.package_create()`. :param id: the name or id of the dataset to update :type id: string From 6ced7cbffaa256ba6d1117012cdb078ecc83191d Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 7 Jan 2014 11:41:45 +0000 Subject: [PATCH 17/47] [#1422] More secure default for the repoze secret key The who.ini file has a secret key used during authentication. To make sure users don't forget to update it we can use the beaker session secret which is generated randomly when creating the ckan ini file. If users define a secret in the who.ini file, this one will be used. To do this, we use a small custom plugin that checks the secret key and calls the core repoze plugin afterwards. --- ckan/config/middleware.py | 6 ++++++ ckan/config/who.ini | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ckan/config/middleware.py b/ckan/config/middleware.py index 5ba02933d46..ba3f1f15d2e 100644 --- a/ckan/config/middleware.py +++ b/ckan/config/middleware.py @@ -18,6 +18,7 @@ from routes.middleware import RoutesMiddleware from repoze.who.config import WhoConfig from repoze.who.middleware import PluggableAuthenticationMiddleware +from repoze.who.plugins.auth_tkt import make_plugin as auth_tkt_make_plugin from fanstatic import Fanstatic from ckan.plugins import PluginImplementations @@ -183,6 +184,11 @@ def make_app(conf, full_stack=True, static_files=True, **app_conf): return app +def ckan_auth_tkt_make_app(**kw): + if not len(kw.get('secret', '')) or kw.get('secret') == 'somesecret': + kw['secret'] = config['beaker.session.secret'] + return auth_tkt_make_plugin(**kw) + class I18nMiddleware(object): """I18n Middleware selects the language based on the url diff --git a/ckan/config/who.ini b/ckan/config/who.ini index 54528f27c3b..5282e20d9b1 100644 --- a/ckan/config/who.ini +++ b/ckan/config/who.ini @@ -1,6 +1,7 @@ [plugin:auth_tkt] -use = repoze.who.plugins.auth_tkt:make_plugin -secret = somesecret +use = ckan.config.middleware:ckan_auth_tkt_make_app +# If no secret key is defined here, beaker.session.secret will be used +#secret = somesecret [plugin:friendlyform] use = repoze.who.plugins.friendlyform:FriendlyFormPlugin From a10adfd64aee7e81aa3b70f0577f91a682385efa Mon Sep 17 00:00:00 2001 From: Nigel Babu Date: Wed, 8 Jan 2014 13:08:31 +0530 Subject: [PATCH 18/47] [#1425] Call logic.NotFound --- ckan/lib/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 39cde049f62..2484ba4323a 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -1713,7 +1713,7 @@ def get_group(id): try: out = logic.get_action(get_action)(context, data_dict) - except logic.ObjectNotFound: + except logic.NotFound: return None return out From 0677c8bf9a614c969af23b96537c39c82b92be28 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 8 Jan 2014 12:55:30 +0000 Subject: [PATCH 19/47] [#1389] Rebuild css after merge --- ckan/public/base/css/fuchsia.css | 66 ++++++++++++++++---------------- ckan/public/base/css/green.css | 66 ++++++++++++++++---------------- ckan/public/base/css/main.css | 66 ++++++++++++++++---------------- ckan/public/base/css/maroon.css | 66 ++++++++++++++++---------------- ckan/public/base/css/red.css | 66 ++++++++++++++++---------------- 5 files changed, 170 insertions(+), 160 deletions(-) diff --git a/ckan/public/base/css/fuchsia.css b/ckan/public/base/css/fuchsia.css index 2017ab41be6..b22ecef39d5 100644 --- a/ckan/public/base/css/fuchsia.css +++ b/ckan/public/base/css/fuchsia.css @@ -6238,44 +6238,45 @@ textarea { top: 14px; right: 10px; } -.dataset-resource-form .dataset-form-resource-types { - margin-bottom: 5px; +.resource-list.reordering .resource-item { + border: 1px solid #dddddd; + margin-bottom: 10px; + cursor: move; } -.dataset-form-resource-types .ckan-icon { - position: relative; - top: 3px; +.resource-list.reordering .resource-item .handle { + display: block; + position: absolute; + color: #888888; + left: -31px; + top: 50%; + margin-top: -15px; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + border: 1px solid #dddddd; + border-width: 1px 0 1px 1px; + background-color: #ffffff; + -webkit-border-radius: 20px 0 0 20px; + -moz-border-radius: 20px 0 0 20px; + border-radius: 20px 0 0 20px; } -.dataset-form-resource-types .radio { - font-weight: normal; - padding-left: 0; - padding-right: 18px; +.resource-list.reordering .resource-item .handle:hover { + text-decoration: none; } -.dataset-form-resource-types label { - position: relative; +.resource-list.reordering .resource-item:hover .handle { + background-color: #eeeeee; } -.dataset-form-resource-types input[type=radio]:checked + label { - font-weight: bold; +.resource-list.reordering .resource-item.ui-sortable-helper { + background-color: #eeeeee; + border: 1px solid #e73892; } -.dataset-form-resource-types input[type=radio]:checked + label:after { - *margin-right: .3em; - display: inline-block; - vertical-align: text-bottom; - position: relative; - top: 2px; - background-image: url("../../../base/images/sprite-ckan-icons.png"); - background-repeat: no-repeat; - background-position: 16px 16px; - width: 16px; - height: 16px; - background-position: -192px 0; - display: block; - content: ""; - position: absolute; - top: auto; - left: 0; - bottom: -12px; +.resource-list.reordering .resource-item.ui-sortable-helper .handle { + background-color: #eeeeee; + border-color: #e73892; + color: #333333; } -.dataset-form-resource-types input[type=radio] { +.resource-item .handle { display: none; } .tag-list { @@ -7118,6 +7119,7 @@ h4 small { margin-top: 15px; padding-top: 10px; border-top: 1px dotted #DDD; + word-break: break-word; } .context-info .info dl dd { margin-top: 3px; diff --git a/ckan/public/base/css/green.css b/ckan/public/base/css/green.css index 0b6f45d9109..061794d7a3d 100644 --- a/ckan/public/base/css/green.css +++ b/ckan/public/base/css/green.css @@ -6238,44 +6238,45 @@ textarea { top: 14px; right: 10px; } -.dataset-resource-form .dataset-form-resource-types { - margin-bottom: 5px; +.resource-list.reordering .resource-item { + border: 1px solid #dddddd; + margin-bottom: 10px; + cursor: move; } -.dataset-form-resource-types .ckan-icon { - position: relative; - top: 3px; +.resource-list.reordering .resource-item .handle { + display: block; + position: absolute; + color: #888888; + left: -31px; + top: 50%; + margin-top: -15px; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + border: 1px solid #dddddd; + border-width: 1px 0 1px 1px; + background-color: #ffffff; + -webkit-border-radius: 20px 0 0 20px; + -moz-border-radius: 20px 0 0 20px; + border-radius: 20px 0 0 20px; } -.dataset-form-resource-types .radio { - font-weight: normal; - padding-left: 0; - padding-right: 18px; +.resource-list.reordering .resource-item .handle:hover { + text-decoration: none; } -.dataset-form-resource-types label { - position: relative; +.resource-list.reordering .resource-item:hover .handle { + background-color: #eeeeee; } -.dataset-form-resource-types input[type=radio]:checked + label { - font-weight: bold; +.resource-list.reordering .resource-item.ui-sortable-helper { + background-color: #eeeeee; + border: 1px solid #2f9b45; } -.dataset-form-resource-types input[type=radio]:checked + label:after { - *margin-right: .3em; - display: inline-block; - vertical-align: text-bottom; - position: relative; - top: 2px; - background-image: url("../../../base/images/sprite-ckan-icons.png"); - background-repeat: no-repeat; - background-position: 16px 16px; - width: 16px; - height: 16px; - background-position: -192px 0; - display: block; - content: ""; - position: absolute; - top: auto; - left: 0; - bottom: -12px; +.resource-list.reordering .resource-item.ui-sortable-helper .handle { + background-color: #eeeeee; + border-color: #2f9b45; + color: #333333; } -.dataset-form-resource-types input[type=radio] { +.resource-item .handle { display: none; } .tag-list { @@ -7118,6 +7119,7 @@ h4 small { margin-top: 15px; padding-top: 10px; border-top: 1px dotted #DDD; + word-break: break-word; } .context-info .info dl dd { margin-top: 3px; diff --git a/ckan/public/base/css/main.css b/ckan/public/base/css/main.css index df4c0110922..7b75e40fb3e 100644 --- a/ckan/public/base/css/main.css +++ b/ckan/public/base/css/main.css @@ -6238,44 +6238,45 @@ textarea { top: 14px; right: 10px; } -.dataset-resource-form .dataset-form-resource-types { - margin-bottom: 5px; +.resource-list.reordering .resource-item { + border: 1px solid #dddddd; + margin-bottom: 10px; + cursor: move; } -.dataset-form-resource-types .ckan-icon { - position: relative; - top: 3px; +.resource-list.reordering .resource-item .handle { + display: block; + position: absolute; + color: #888888; + left: -31px; + top: 50%; + margin-top: -15px; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + border: 1px solid #dddddd; + border-width: 1px 0 1px 1px; + background-color: #ffffff; + -webkit-border-radius: 20px 0 0 20px; + -moz-border-radius: 20px 0 0 20px; + border-radius: 20px 0 0 20px; } -.dataset-form-resource-types .radio { - font-weight: normal; - padding-left: 0; - padding-right: 18px; +.resource-list.reordering .resource-item .handle:hover { + text-decoration: none; } -.dataset-form-resource-types label { - position: relative; +.resource-list.reordering .resource-item:hover .handle { + background-color: #eeeeee; } -.dataset-form-resource-types input[type=radio]:checked + label { - font-weight: bold; +.resource-list.reordering .resource-item.ui-sortable-helper { + background-color: #eeeeee; + border: 1px solid #187794; } -.dataset-form-resource-types input[type=radio]:checked + label:after { - *margin-right: .3em; - display: inline-block; - vertical-align: text-bottom; - position: relative; - top: 2px; - background-image: url("../../../base/images/sprite-ckan-icons.png"); - background-repeat: no-repeat; - background-position: 16px 16px; - width: 16px; - height: 16px; - background-position: -192px 0; - display: block; - content: ""; - position: absolute; - top: auto; - left: 0; - bottom: -12px; +.resource-list.reordering .resource-item.ui-sortable-helper .handle { + background-color: #eeeeee; + border-color: #187794; + color: #333333; } -.dataset-form-resource-types input[type=radio] { +.resource-item .handle { display: none; } .tag-list { @@ -7118,6 +7119,7 @@ h4 small { margin-top: 15px; padding-top: 10px; border-top: 1px dotted #DDD; + word-break: break-word; } .context-info .info dl dd { margin-top: 3px; diff --git a/ckan/public/base/css/maroon.css b/ckan/public/base/css/maroon.css index c93b5426b72..73f14b214d9 100644 --- a/ckan/public/base/css/maroon.css +++ b/ckan/public/base/css/maroon.css @@ -6238,44 +6238,45 @@ textarea { top: 14px; right: 10px; } -.dataset-resource-form .dataset-form-resource-types { - margin-bottom: 5px; +.resource-list.reordering .resource-item { + border: 1px solid #dddddd; + margin-bottom: 10px; + cursor: move; } -.dataset-form-resource-types .ckan-icon { - position: relative; - top: 3px; +.resource-list.reordering .resource-item .handle { + display: block; + position: absolute; + color: #888888; + left: -31px; + top: 50%; + margin-top: -15px; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + border: 1px solid #dddddd; + border-width: 1px 0 1px 1px; + background-color: #ffffff; + -webkit-border-radius: 20px 0 0 20px; + -moz-border-radius: 20px 0 0 20px; + border-radius: 20px 0 0 20px; } -.dataset-form-resource-types .radio { - font-weight: normal; - padding-left: 0; - padding-right: 18px; +.resource-list.reordering .resource-item .handle:hover { + text-decoration: none; } -.dataset-form-resource-types label { - position: relative; +.resource-list.reordering .resource-item:hover .handle { + background-color: #eeeeee; } -.dataset-form-resource-types input[type=radio]:checked + label { - font-weight: bold; +.resource-list.reordering .resource-item.ui-sortable-helper { + background-color: #eeeeee; + border: 1px solid #810606; } -.dataset-form-resource-types input[type=radio]:checked + label:after { - *margin-right: .3em; - display: inline-block; - vertical-align: text-bottom; - position: relative; - top: 2px; - background-image: url("../../../base/images/sprite-ckan-icons.png"); - background-repeat: no-repeat; - background-position: 16px 16px; - width: 16px; - height: 16px; - background-position: -192px 0; - display: block; - content: ""; - position: absolute; - top: auto; - left: 0; - bottom: -12px; +.resource-list.reordering .resource-item.ui-sortable-helper .handle { + background-color: #eeeeee; + border-color: #810606; + color: #333333; } -.dataset-form-resource-types input[type=radio] { +.resource-item .handle { display: none; } .tag-list { @@ -7118,6 +7119,7 @@ h4 small { margin-top: 15px; padding-top: 10px; border-top: 1px dotted #DDD; + word-break: break-word; } .context-info .info dl dd { margin-top: 3px; diff --git a/ckan/public/base/css/red.css b/ckan/public/base/css/red.css index 975d4d07065..3105bbda479 100644 --- a/ckan/public/base/css/red.css +++ b/ckan/public/base/css/red.css @@ -6238,44 +6238,45 @@ textarea { top: 14px; right: 10px; } -.dataset-resource-form .dataset-form-resource-types { - margin-bottom: 5px; +.resource-list.reordering .resource-item { + border: 1px solid #dddddd; + margin-bottom: 10px; + cursor: move; } -.dataset-form-resource-types .ckan-icon { - position: relative; - top: 3px; +.resource-list.reordering .resource-item .handle { + display: block; + position: absolute; + color: #888888; + left: -31px; + top: 50%; + margin-top: -15px; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + border: 1px solid #dddddd; + border-width: 1px 0 1px 1px; + background-color: #ffffff; + -webkit-border-radius: 20px 0 0 20px; + -moz-border-radius: 20px 0 0 20px; + border-radius: 20px 0 0 20px; } -.dataset-form-resource-types .radio { - font-weight: normal; - padding-left: 0; - padding-right: 18px; +.resource-list.reordering .resource-item .handle:hover { + text-decoration: none; } -.dataset-form-resource-types label { - position: relative; +.resource-list.reordering .resource-item:hover .handle { + background-color: #eeeeee; } -.dataset-form-resource-types input[type=radio]:checked + label { - font-weight: bold; +.resource-list.reordering .resource-item.ui-sortable-helper { + background-color: #eeeeee; + border: 1px solid #c14531; } -.dataset-form-resource-types input[type=radio]:checked + label:after { - *margin-right: .3em; - display: inline-block; - vertical-align: text-bottom; - position: relative; - top: 2px; - background-image: url("../../../base/images/sprite-ckan-icons.png"); - background-repeat: no-repeat; - background-position: 16px 16px; - width: 16px; - height: 16px; - background-position: -192px 0; - display: block; - content: ""; - position: absolute; - top: auto; - left: 0; - bottom: -12px; +.resource-list.reordering .resource-item.ui-sortable-helper .handle { + background-color: #eeeeee; + border-color: #c14531; + color: #333333; } -.dataset-form-resource-types input[type=radio] { +.resource-item .handle { display: none; } .tag-list { @@ -7118,6 +7119,7 @@ h4 small { margin-top: 15px; padding-top: 10px; border-top: 1px dotted #DDD; + word-break: break-word; } .context-info .info dl dd { margin-top: 3px; From 747f2cd24e45aaf0471be1973cf15ebe9feae2d3 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 8 Jan 2014 13:10:06 +0000 Subject: [PATCH 20/47] [#1426] Remove duplicated require notice --- ckan/templates/group/snippets/group_form.html | 1 - 1 file changed, 1 deletion(-) diff --git a/ckan/templates/group/snippets/group_form.html b/ckan/templates/group/snippets/group_form.html index a6d5fbcbf95..f8d69c7d0af 100644 --- a/ckan/templates/group/snippets/group_form.html +++ b/ckan/templates/group/snippets/group_form.html @@ -81,7 +81,6 @@ {% block delete_button_text %}{{ _('Delete') }}{% endblock %} {% endif %} {% endblock %} - {{ form.required_message() }} From 39662c35a1ab89a35e7d73bb0d129a42ac0d3e80 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 8 Jan 2014 15:50:07 +0000 Subject: [PATCH 21/47] [#1188] Tweak checks to support empty strings --- ckan/logic/validators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py index fab4a68d946..977fd80fb19 100644 --- a/ckan/logic/validators.py +++ b/ckan/logic/validators.py @@ -699,10 +699,10 @@ def datasets_with_no_organization_cannot_be_private(key, data, errors, check_passed = True - if not dataset_id and private and owner_org is None: + if not dataset_id and private and not owner_org: # When creating a dataset, enforce it directly check_passed = False - elif dataset_id and private and owner_org is None: + elif dataset_id and private and not owner_org: # Check if the dataset actually has an owner_org, even if not provided try: dataset_dict = logic.get_action('package_show')({}, From 6eeb2b1bd0605cc470e4ad742b2dbdedf4d74b79 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 8 Jan 2014 16:06:44 +0000 Subject: [PATCH 22/47] [#1421] Ensure that check_access is called on activity_create Otherwise when disabling the activity streams the action returned before calling the auth function, which made the auth audit fail --- ckan/logic/action/create.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index b078ed485c6..48fc83941b6 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -1011,6 +1011,8 @@ def activity_create(context, activity_dict, **kw): ''' + _check_access('activity_create', context, activity_dict) + # this action had a ignore_auth param which has been removed # removed in 2.2 if 'ignore_auth' in kw: @@ -1031,8 +1033,6 @@ def activity_create(context, activity_dict, **kw): else: activity_dict['revision_id'] = None - _check_access('activity_create', context, activity_dict) - schema = context.get('schema') or ckan.logic.schema.default_create_activity_schema() data, errors = _validate(activity_dict, schema, context) if errors: From 337b5c9b7010f897d622779f508478eb96c879e8 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 8 Jan 2014 16:18:09 +0000 Subject: [PATCH 23/47] [#1421] Better exception message for auth audit --- ckan/logic/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ckan/logic/__init__.py b/ckan/logic/__init__.py index 7227da77429..872cd7e4b7b 100644 --- a/ckan/logic/__init__.py +++ b/ckan/logic/__init__.py @@ -402,7 +402,7 @@ def get_action(action): def make_wrapped(_action, action_name): def wrapped(context=None, data_dict=None, **kw): if kw: - log.critical('%s was pass extra keywords %r' + log.critical('%s was passed extra keywords %r' % (_action.__name__, kw)) context = _prepopulate_context(context) @@ -423,7 +423,9 @@ def wrapped(context=None, data_dict=None, **kw): if action_name not in new_authz.auth_functions_list(): log.debug('No auth function for %s' % action_name) elif not getattr(_action, 'auth_audit_exempt', False): - raise Exception('Action Auth Audit: %s' % action_name) + raise Exception( + 'Action function {0} did not call its auth function' + .format(action_name)) # remove from audit stack context['__auth_audit'].pop() except IndexError: From 3a00b572e795e03b915f6250dfed8b83aad768f7 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 8 Jan 2014 16:32:14 +0000 Subject: [PATCH 24/47] [#1368] Fix dataset ordering on org and group pages Match dataset ordering options with those on the main search --- ckan/templates/group/read.html | 9 ++++++++- ckan/templates/organization/read.html | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ckan/templates/group/read.html b/ckan/templates/group/read.html index 81b967e53d7..0fa87eb8d7b 100644 --- a/ckan/templates/group/read.html +++ b/ckan/templates/group/read.html @@ -11,7 +11,14 @@ 'translated_fields': c.translated_fields, 'remove_field': c.remove_field } %} - {% snippet 'snippets/search_form.html', type='dataset', query=c.q, sorting_selected=c.sort_by_selected, count=c.page.item_count, facets=facets, placeholder=_('Search datasets...'), show_empty=request.params %} + {% set sorting = [ + (_('Relevance'), 'score desc, metadata_modified desc'), + (_('Name Ascending'), 'title_string asc'), + (_('Name Descending'), 'title_string desc'), + (_('Last Modified'), 'metadata_modified desc'), + (_('Popular'), 'views_recent desc') if g.tracking_enabled else (false, false) ] + %} + {% snippet 'snippets/search_form.html', type='dataset', query=c.q, sorting=sorting, sorting_selected=c.sort_by_selected, count=c.page.item_count, facets=facets, placeholder=_('Search datasets...'), show_empty=request.params %} {% endblock %} {% block packages_list %} {% if c.page.items %} diff --git a/ckan/templates/organization/read.html b/ckan/templates/organization/read.html index aa3a0388a4d..e2a6a04b090 100644 --- a/ckan/templates/organization/read.html +++ b/ckan/templates/organization/read.html @@ -15,7 +15,14 @@ 'translated_fields': c.translated_fields, 'remove_field': c.remove_field } %} - {% snippet 'snippets/search_form.html', type='dataset', query=c.q, sorting_selected=c.sort_by_selected, count=c.page.item_count, facets=facets, placeholder=_('Search datasets...'), show_empty=request.params %} + {% set sorting = [ + (_('Relevance'), 'score desc, metadata_modified desc'), + (_('Name Ascending'), 'title_string asc'), + (_('Name Descending'), 'title_string desc'), + (_('Last Modified'), 'metadata_modified desc'), + (_('Popular'), 'views_recent desc') if g.tracking_enabled else (false, false) ] + %} + {% snippet 'snippets/search_form.html', type='dataset', query=c.q, sorting=sorting, sorting_selected=c.sort_by_selected, count=c.page.item_count, facets=facets, placeholder=_('Search datasets...'), show_empty=request.params %} {% endblock %} {% block packages_list %} {% if c.page.items %} From 917e4c6d775a9bc92efe4aa205f3a535ed826c12 Mon Sep 17 00:00:00 2001 From: kindly Date: Thu, 9 Jan 2014 11:46:30 +0000 Subject: [PATCH 25/47] [#1427] add contitional to allow 0.8 sqlalchemy --- ckan/lib/dictization/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ckan/lib/dictization/__init__.py b/ckan/lib/dictization/__init__.py index 6f726ad3436..5c9f77346d1 100644 --- a/ckan/lib/dictization/__init__.py +++ b/ckan/lib/dictization/__init__.py @@ -3,6 +3,12 @@ import sqlalchemy from pylons import config +try: + RowProxy = sqlalchemy.engine.result.RowProxy +except AttributeError: + RowProxy = sqlalchemy.engine.base.RowProxy + + # NOTE # The functions in this file contain very generic methods for dictizing objects # and saving dictized objects. If a specialised use is needed please do NOT extend @@ -17,7 +23,7 @@ def table_dictize(obj, context, **kw): model = context["model"] session = model.Session - if isinstance(obj, sqlalchemy.engine.base.RowProxy): + if isinstance(obj, RowProxy): fields = obj.keys() else: ModelClass = obj.__class__ From ed05bad70c03b3dc79429160ecb6ee5847c704bf Mon Sep 17 00:00:00 2001 From: David Read Date: Tue, 14 Jan 2014 20:10:06 +0000 Subject: [PATCH 26/47] [#1420] Fix group_show by taking schema out of the context when making sub-calls. --- ckan/controllers/group.py | 3 ++- ckan/lib/dictization/model_dictize.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ckan/controllers/group.py b/ckan/controllers/group.py index 387732426c4..a640cd719c6 100644 --- a/ckan/controllers/group.py +++ b/ckan/controllers/group.py @@ -323,7 +323,8 @@ def pager_url(q=None, page=None): 'extras': search_extras } - query = get_action('package_search')(context, data_dict) + context_ = dict((k, v) for (k, v) in context.items() if k != 'schema') + query = get_action('package_search')(context_, data_dict) c.page = h.Page( collection=query['results'], diff --git a/ckan/lib/dictization/model_dictize.py b/ckan/lib/dictization/model_dictize.py index ce0f4df75ab..e5c6707a4ea 100644 --- a/ckan/lib/dictization/model_dictize.py +++ b/ckan/lib/dictization/model_dictize.py @@ -376,7 +376,8 @@ def group_dictize(group, context): if include_datasets: q['rows'] = 1000 # Only the first 1000 datasets are returned - search_results = logic.get_action('package_search')(context, q) + context_ = dict((k, v) for (k, v) in context.items() if k != 'schema') + search_results = logic.get_action('package_search')(context_, q) if include_datasets: result_dict['packages'] = search_results['results'] From 2da633fa09b64362aed5d4b22057ae0ad90da452 Mon Sep 17 00:00:00 2001 From: Nigel Babu Date: Sun, 19 Jan 2014 19:24:57 +0000 Subject: [PATCH 27/47] [#1437] Instruction to restart apache and nginx --- doc/install-from-package.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/install-from-package.rst b/doc/install-from-package.rst index 2d6e8f59c24..f42bb92e30f 100644 --- a/doc/install-from-package.rst +++ b/doc/install-from-package.rst @@ -92,8 +92,17 @@ CKAN: #. Also optionally, you can enable file uploads by following the instructions in :doc:`filestore`. +--------------------------- +3. Restart Apache and Nginx +--------------------------- + +Restart Apache and Nginx by running this command in a terminal:: + + sudo service apache2 restart + sudo service nginx restart + --------------- -3. You're done! +4. You're done! --------------- Open http://localhost in your web browser. You should see the CKAN front From b5b751028b582938c9d207ae60f4b408b29d40a6 Mon Sep 17 00:00:00 2001 From: Nigel Babu Date: Mon, 20 Jan 2014 11:18:24 +0000 Subject: [PATCH 28/47] Add restart instructions in upgrade docs too --- doc/upgrade-package-to-minor-release.rst | 7 ++++--- doc/upgrade-package-to-patch-release.rst | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/upgrade-package-to-minor-release.rst b/doc/upgrade-package-to-minor-release.rst index c7754352589..2caa4369338 100644 --- a/doc/upgrade-package-to-minor-release.rst +++ b/doc/upgrade-package-to-minor-release.rst @@ -46,10 +46,10 @@ respectively. .. note:: The install process will uninstall any existing CKAN extensions or other - libraries located in the ``src`` directory of the CKAN virtualenv. To + libraries located in the ``src`` directory of the CKAN virtualenv. To enable them again, the installation process will iterate over all folders in the ``src`` directory, reinstall the requirements listed in - ``pip-requirements.txt`` and ``requirements.txt`` files and run + ``pip-requirements.txt`` and ``requirements.txt`` files and run ``python setup.py develop`` for each. If you are using a custom extension which does not use this requirements file name or is located elsewhere, you will need to manually reinstall it. @@ -96,8 +96,9 @@ respectively. See :ref:`rebuild search index` for details of the ``ckan search-index rebuild`` command. -#. Finally, restart Apache: +#. Finally, restart Apache and Nginx: .. parsed-literal:: |restart_apache| + sudo service nginx restart diff --git a/doc/upgrade-package-to-patch-release.rst b/doc/upgrade-package-to-patch-release.rst index c79e18b452b..4dc7ac93530 100644 --- a/doc/upgrade-package-to-patch-release.rst +++ b/doc/upgrade-package-to-patch-release.rst @@ -77,11 +77,12 @@ minor release they belong to, so for example CKAN ``2.0``, ``2.0.1``, These are due to vdm not longer being installed from source. You can ignore them and delete the folder manually if you want. -#. Finally, restart Apache: +#. Finally, restart Apache and Nginx: .. parsed-literal:: |restart_apache| + sudo service nginx restart #. You're done! From 47b38e5645b6cc1309d2c933827064756d1e462a Mon Sep 17 00:00:00 2001 From: amercader Date: Mon, 20 Jan 2014 18:58:26 +0000 Subject: [PATCH 29/47] [#1428] Fix superimposing resource items This was caused by removing the "This resource has no description" message in #1302. --- ckan/public/base/less/dataset.less | 1 + 1 file changed, 1 insertion(+) diff --git a/ckan/public/base/less/dataset.less b/ckan/public/base/less/dataset.less index 0089e991b5c..47e6c70ac22 100644 --- a/ckan/public/base/less/dataset.less +++ b/ckan/public/base/less/dataset.less @@ -85,6 +85,7 @@ .resource-item .description { font-size: 12px; margin-bottom: 0; + min-height: 12px; } .resource-item .btn-group { From 412fba44ace2bef514f54891afdec897dd702e6e Mon Sep 17 00:00:00 2001 From: Rachel Knowler Date: Tue, 21 Jan 2014 17:12:46 +0100 Subject: [PATCH 30/47] Changed license to licenses in facet titles where this makes more sense. --- ckan/controllers/group.py | 2 +- ckan/controllers/home.py | 2 +- ckan/lib/helpers.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ckan/controllers/group.py b/ckan/controllers/group.py index 387732426c4..748a206dd42 100644 --- a/ckan/controllers/group.py +++ b/ckan/controllers/group.py @@ -290,7 +290,7 @@ def pager_url(q=None, page=None): 'groups': _('Groups'), 'tags': _('Tags'), 'res_format': _('Formats'), - 'license_id': _('License')} + 'license_id': _('Licenses')} for facet in g.facets: if facet in default_facet_titles: diff --git a/ckan/controllers/home.py b/ckan/controllers/home.py index 5d04fd91aef..33d5df95016 100644 --- a/ckan/controllers/home.py +++ b/ckan/controllers/home.py @@ -71,7 +71,7 @@ def index(self): 'groups': _('Groups'), 'tags': _('Tags'), 'res_format': _('Formats'), - 'license': _('License'), + 'license': _('Licenses'), } data_dict = {'sort': 'packages', 'all_fields': 1} diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 2484ba4323a..a96004b35e6 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -604,7 +604,7 @@ def get_facet_title(name): 'groups': _('Groups'), 'tags': _('Tags'), 'res_format': _('Formats'), - 'license': _('License'), } + 'license': _('Licenses'), } return facet_titles.get(name, name.capitalize()) From ae5c63a48670611cb04afa75b13a673357a6f132 Mon Sep 17 00:00:00 2001 From: Gael Pasgrimaud Date: Mon, 2 Dec 2013 21:03:04 +0100 Subject: [PATCH 31/47] avoid UnicodeEncodeError when params contains non ascii characters --- ckan/controllers/feed.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/ckan/controllers/feed.py b/ckan/controllers/feed.py index 42626ef3585..3ba8b050947 100644 --- a/ckan/controllers/feed.py +++ b/ckan/controllers/feed.py @@ -21,7 +21,6 @@ # TODO fix imports import logging import urlparse -from urllib import urlencode import webhelpers.feedgenerator from pylons import config @@ -277,8 +276,6 @@ def custom(self): search_params[param] = value fq += ' %s:"%s"' % (param, value) - search_url_params = urlencode(search_params) - try: page = int(request.params.get('page', 1)) except ValueError: @@ -307,6 +304,9 @@ def custom(self): controller='feed', action='custom') + atom_url = h._url_with_params('/feeds/custom.atom', + search_params.items()) + alternate_url = self._alternate_url(request.params) return self.output_feed(results, @@ -315,8 +315,7 @@ def custom(self): ' datasets on %s. Custom query: \'%s\'' % (g.site_title, q), feed_link=alternate_url, - feed_guid=_create_atom_id - (u'/feeds/custom.atom?%s' % search_url_params), + feed_guid=_create_atom_id(atom_url), feed_url=feed_url, navigation_urls=navigation_urls) @@ -376,11 +375,7 @@ def _feed_url(self, query, controller, action, **kwargs): parameters. """ path = h.url_for(controller=controller, action=action, **kwargs) - query = [(k, v.encode('utf-8') if isinstance(v, basestring) - else str(v)) for k, v in query.items()] - - # a trailing '?' is valid. - return self.base_url + path + u'?' + urlencode(query) + return h._url_with_params(self.base_url + path, query.items()) def _navigation_urls(self, query, controller, action, item_count, limit, **kwargs): From fd257d06b5f689cb70b4e6b40cfab0f9a9e2a888 Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Wed, 22 Jan 2014 15:22:12 -0500 Subject: [PATCH 32/47] we need try, except to handle local datastore use This reverts commit 129aec805528f1cba3c6ac73c7363421d35e38d2. --- ckanext/datastore/db.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ckanext/datastore/db.py b/ckanext/datastore/db.py index 82353c9e550..d3c2cac3683 100644 --- a/ckanext/datastore/db.py +++ b/ckanext/datastore/db.py @@ -836,12 +836,11 @@ def _insert_links(data_dict, limit, offset): and the resource page.''' data_dict['_links'] = {} - # no links required for local actions - if not toolkit.request.environ: - return - # get the url from the request - urlstring = toolkit.request.environ['CKAN_CURRENT_URL'] + try: + urlstring = toolkit.request.environ['CKAN_CURRENT_URL'] + except TypeError: + return # no links required for local actions # change the offset in the url parsed = list(urlparse.urlparse(urlstring)) From b6b981c07744c1193f82a64c08087d6d934c7d8a Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 23 Jan 2014 09:53:37 +0000 Subject: [PATCH 33/47] [#1446] Require ckan.site_url when using the DataPusher --- ckanext/datapusher/plugin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ckanext/datapusher/plugin.py b/ckanext/datapusher/plugin.py index 50c9cd864f5..f0b03936359 100644 --- a/ckanext/datapusher/plugin.py +++ b/ckanext/datapusher/plugin.py @@ -80,10 +80,11 @@ def configure(self, config): datapusher_formats = config.get('ckan.datapusher.formats', '').lower() self.datapusher_formats = datapusher_formats.split() or DEFAULT_FORMATS - datapusher_url = config.get('ckan.datapusher.url') - if not datapusher_url: - raise Exception( - 'Config option `ckan.datapusher.url` has to be set.') + for config_option in ('ckan.site_url', 'ckan.datapusher.url',): + if not config.get(config_option): + raise Exception( + 'Config option `{0}` must be set to use the DataPusher.' + .format(config_option)) def notify(self, entity, operation=None): if isinstance(entity, model.Resource): From 0230587df78bae50288272f78b0a69527335e1fa Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 23 Jan 2014 11:25:55 +0000 Subject: [PATCH 34/47] [#1446] Better defaults for DataPusher config options + docs --- ckan/config/deployment.ini_tmpl | 4 ++-- doc/configuration.rst | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ckan/config/deployment.ini_tmpl b/ckan/config/deployment.ini_tmpl index b3148cc6d68..a8c05cb92f6 100644 --- a/ckan/config/deployment.ini_tmpl +++ b/ckan/config/deployment.ini_tmpl @@ -130,8 +130,8 @@ ckan.feeds.author_link = # Make sure you have set up the DataStore -ckan.datapusher.formats = csv -ckan.datapusher.url = http://datapusher.ckan.org/ +#ckan.datapusher.formats = +#ckan.datapusher.url = http://127.0.0.1:8800/ ## Activity Streams Settings diff --git a/doc/configuration.rst b/doc/configuration.rst index a9c7a581cc7..cd05f2b777e 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -1066,9 +1066,15 @@ ckan.datapusher.formats ^^^^^^^^^^^^^^^^^^^^^^^ Example:: - ckan.datapusher.formats = csv xls xlsx -.. todo:: Expand + ckan.datapusher.formats = csv xls + +Default value: ``csv xls application/csv application/vnd.ms-excel`` + +File formats that will be pushed to the DataStore by the DataPusher. When +adding or editing a resource which links to a file in one of these formats, +the DataPusher will automatically try to import its contents to the DataStore. + .. _ckan.datapusher.url: @@ -1076,9 +1082,13 @@ ckan.datapusher.url ^^^^^^^^^^^^^^^^^^^ Example:: - ckan.datapusher.url = http://datapusher.ckan.org/ -.. todo:: Expand + ckan.datapusher.url = http://127.0.0.1:8800/ + +DataPusher endpoint to use when enabling the ``datapusher`` extension. If you +installed CKAN via :doc:`install-from-package`, the DataPusher was installed for you +running on port 8800. If you want to manually install the DataPusher, follow +the installation `instructions `_. Activity Streams Settings From b128ebb1437c4f882be1304feb2020eebddc95a8 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 23 Jan 2014 11:52:08 +0000 Subject: [PATCH 35/47] [#1446] Clarify DataPusher issues in docs --- doc/datastore.rst | 25 +++++++++++++++++++++---- doc/install-from-package.rst | 9 +++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/doc/datastore.rst b/doc/datastore.rst index 80fe5955bc3..026c1f303fd 100644 --- a/doc/datastore.rst +++ b/doc/datastore.rst @@ -15,6 +15,11 @@ When a resource is added to the DataStore, you get: The DataStore is integrated into the :doc:`CKAN API ` and authorization system. +The DataStore is generally used alongside the +`DataPusher `_, which will +automatically upload data to the DataStore from suitable files, whether +uploaded to CKAN's FileStore or externally linked. + .. contents:: :depth: 1 :local: @@ -126,7 +131,16 @@ Option 1: Paster command This option is preferred if CKAN and PostgreSQL are on the same server. -To set the permissions, use this paster command after you've set the database URLs (make sure to have your virtualenv activated): +To set the permissions, use the following paster command after you've set the database URLs. + +If you did a package install, the easiest way is to use the ``ckan`` command wrapper: + +.. parsed-literal:: + + sudo ckan datastore set-permissions postgres + +If you did a source install, make sure to have your virtualenv activated and +run the command from the CKAN source directory: .. parsed-literal:: @@ -230,10 +244,13 @@ DataStore. This requires some processing, to extract the data from your files and to add it to the DataStore in the format the DataStore can handle. This task of automatically parsing and then adding data to the DataStore is -performed by a DataPusher, a service that runs asynchronously and can be installed -allongside CKAN. +performed by the `DataPusher `_, a service that runs asynchronously and can be installed +alongside CKAN. + +To install this please look at the docs here: http://docs.ckan.org/projects/datapusher + -To install this please look at the docs here: http://datapusher.readthedocs.org +.. _DataPusher_docs: http://docs.ckan.org/projects/datapusher ----------------- diff --git a/doc/install-from-package.rst b/doc/install-from-package.rst index f42bb92e30f..690ad7577c2 100644 --- a/doc/install-from-package.rst +++ b/doc/install-from-package.rst @@ -9,6 +9,11 @@ and easiest way to install CKAN, but it requires **Ubuntu 12.04 64-bit**. If you're not using Ubuntu 12.04 64-bit, or if you're installing CKAN for development, you should follow :doc:`install-from-source` instead. +At the end of the installation process you will end up with two running web +applications, CKAN itself and the DataPusher, a separate service for automatically +importing data to CKAN's :doc:`datastore`. + + .. _run-package-installer: --------------------------- @@ -86,8 +91,8 @@ CKAN: sudo ckan db init -#. Optionally, setup the DataStore by following the instructions in - :doc:`/datastore`. +#. Optionally, setup the DataStore and DataPusher by following the + instructions in :doc:`/datastore`. #. Also optionally, you can enable file uploads by following the instructions in :doc:`filestore`. From 853568a023662308113fb1756d606f1c0c4b216c Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 23 Jan 2014 11:54:12 +0000 Subject: [PATCH 36/47] [#1446] Remove SQLite urls from template ini file --- ckan/config/deployment.ini_tmpl | 2 -- 1 file changed, 2 deletions(-) diff --git a/ckan/config/deployment.ini_tmpl b/ckan/config/deployment.ini_tmpl index a8c05cb92f6..b15d1c5657b 100644 --- a/ckan/config/deployment.ini_tmpl +++ b/ckan/config/deployment.ini_tmpl @@ -44,8 +44,6 @@ who.log_file = %(cache_dir)s/who_log.ini ## Database Settings sqlalchemy.url = postgresql://ckan_default:pass@localhost/ckan_default -#sqlalchemy.url = sqlite:/// -#sqlalchemy.url = sqlite:///%(here)s/somedb.db #ckan.datastore.write_url = postgresql://ckan_default:pass@localhost/datastore_default #ckan.datastore.read_url = postgresql://datastore_default:pass@localhost/datastore_default From 213e14f37d40163a541348dd80550258d76c8f5d Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Thu, 23 Jan 2014 11:00:40 -0500 Subject: [PATCH 37/47] pep8 --- ckanext/datastore/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckanext/datastore/db.py b/ckanext/datastore/db.py index d3c2cac3683..53775000752 100644 --- a/ckanext/datastore/db.py +++ b/ckanext/datastore/db.py @@ -840,7 +840,7 @@ def _insert_links(data_dict, limit, offset): try: urlstring = toolkit.request.environ['CKAN_CURRENT_URL'] except TypeError: - return # no links required for local actions + return # no links required for local actions # change the offset in the url parsed = list(urlparse.urlparse(urlstring)) From 1613ed86d45a7694281ba9b161cb5a2c23dc8acf Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 23 Jan 2014 16:50:38 +0000 Subject: [PATCH 38/47] [#1446] Enable DataPusher for private datasets There's no need to prevent the import to the datastore on them, as the datastore will manage the authorization, and logged in users still expect these files to get imported to the DataStore. --- ckanext/datapusher/plugin.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ckanext/datapusher/plugin.py b/ckanext/datapusher/plugin.py index f0b03936359..56fb44d2122 100644 --- a/ckanext/datapusher/plugin.py +++ b/ckanext/datapusher/plugin.py @@ -95,10 +95,7 @@ def notify(self, entity, operation=None): # 1 parameter context = {'model': model, 'ignore_auth': True, 'defer_commit': True} - package = p.toolkit.get_action('package_show')(context, { - 'id': entity.get_package_id() - }) - if (not package['private'] and entity.format and + if (entity.format and entity.format.lower() in self.datapusher_formats and entity.url_type != 'datapusher'): try: From 699dac215996c01afecc93fc062d3a230d8e810e Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 23 Jan 2014 16:53:00 +0000 Subject: [PATCH 39/47] [#1446] Remove misleading datastore warning in docs --- doc/datastore.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/datastore.rst b/doc/datastore.rst index 026c1f303fd..41519aa6ed5 100644 --- a/doc/datastore.rst +++ b/doc/datastore.rst @@ -51,10 +51,6 @@ Setting up the DataStore available and the set-up is slightly different. Make sure, you read :ref:`legacy-mode` for more details. -.. warning:: - - The DataStore does not support hiding resources in a private dataset. - 1. Enable the plugin ==================== From 6247a8110807c7a9daa92adfb1dcbe0ea46c75f5 Mon Sep 17 00:00:00 2001 From: amercader Date: Thu, 23 Jan 2014 17:16:03 +0000 Subject: [PATCH 40/47] Recompile main.css (should have been done after #1428 merge) --- ckan/public/base/css/main.css | 1 + 1 file changed, 1 insertion(+) diff --git a/ckan/public/base/css/main.css b/ckan/public/base/css/main.css index 7b75e40fb3e..911b02d6c98 100644 --- a/ckan/public/base/css/main.css +++ b/ckan/public/base/css/main.css @@ -6232,6 +6232,7 @@ textarea { .resource-item .description { font-size: 12px; margin-bottom: 0; + min-height: 12px; } .resource-item .btn-group { position: absolute; From f9638b4a01b961f5afd6a5658889287a96606b8d Mon Sep 17 00:00:00 2001 From: kindly Date: Fri, 24 Jan 2014 11:48:25 +0000 Subject: [PATCH 41/47] [#1450] remove min file as it does not work properly with fanstatic --- ckan/public/base/vendor/jquery.ui.widget.min.js | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 ckan/public/base/vendor/jquery.ui.widget.min.js diff --git a/ckan/public/base/vendor/jquery.ui.widget.min.js b/ckan/public/base/vendor/jquery.ui.widget.min.js deleted file mode 100644 index 12d47c2f79c..00000000000 --- a/ckan/public/base/vendor/jquery.ui.widget.min.js +++ /dev/null @@ -1,13 +0,0 @@ -(function(factory){if(typeof define==="function"&&define.amd){define(["jquery"],factory);}else{factory(jQuery);}}(function($,undefined){if($.cleanData){var _cleanData=$.cleanData;$.cleanData=function(elems){for(var i=0,elem;(elem=elems[i])!=null;i++){try{$(elem).triggerHandler("remove");}catch(e){}} -_cleanData(elems);};}else{var _remove=$.fn.remove;$.fn.remove=function(selector,keepData){return this.each(function(){if(!keepData){if(!selector||$.filter(selector,[this]).length){$("*",this).add([this]).each(function(){try{$(this).triggerHandler("remove");}catch(e){}});}} -return _remove.call($(this),selector,keepData);});};} -$.widget=function(name,base,prototype){var namespace=name.split(".")[0],fullName;name=name.split(".")[1];fullName=namespace+"-"+name;if(!prototype){prototype=base;base=$.Widget;} -$.expr[":"][fullName]=function(elem){return!!$.data(elem,name);};$[namespace]=$[namespace]||{};$[namespace][name]=function(options,element){if(arguments.length){this._createWidget(options,element);}};var basePrototype=new base();basePrototype.options=$.extend(true,{},basePrototype.options);$[namespace][name].prototype=$.extend(true,basePrototype,{namespace:namespace,widgetName:name,widgetEventPrefix:$[namespace][name].prototype.widgetEventPrefix||name,widgetBaseClass:fullName},prototype);$.widget.bridge(name,$[namespace][name]);};$.widget.bridge=function(name,object){$.fn[name]=function(options){var isMethodCall=typeof options==="string",args=Array.prototype.slice.call(arguments,1),returnValue=this;options=!isMethodCall&&args.length?$.extend.apply(null,[true,options].concat(args)):options;if(isMethodCall&&options.charAt(0)==="_"){return returnValue;} -if(isMethodCall){this.each(function(){var instance=$.data(this,name),methodValue=instance&&$.isFunction(instance[options])?instance[options].apply(instance,args):instance;if(methodValue!==instance&&methodValue!==undefined){returnValue=methodValue;return false;}});}else{this.each(function(){var instance=$.data(this,name);if(instance){instance.option(options||{})._init();}else{$.data(this,name,new object(options,this));}});} -return returnValue;};};$.Widget=function(options,element){if(arguments.length){this._createWidget(options,element);}};$.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(options,element){$.data(element,this.widgetName,this);this.element=$(element);this.options=$.extend(true,{},this.options,this._getCreateOptions(),options);var self=this;this.element.bind("remove."+this.widgetName,function(){self.destroy();});this._create();this._trigger("create");this._init();},_getCreateOptions:function(){return $.metadata&&$.metadata.get(this.element[0])[this.widgetName];},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled "+"ui-state-disabled");},widget:function(){return this.element;},option:function(key,value){var options=key;if(arguments.length===0){return $.extend({},this.options);} -if(typeof key==="string"){if(value===undefined){return this.options[key];} -options={};options[key]=value;} -this._setOptions(options);return this;},_setOptions:function(options){var self=this;$.each(options,function(key,value){self._setOption(key,value);});return this;},_setOption:function(key,value){this.options[key]=value;if(key==="disabled"){this.widget() -[value?"addClass":"removeClass"](this.widgetBaseClass+"-disabled"+" "+"ui-state-disabled").attr("aria-disabled",value);} -return this;},enable:function(){return this._setOption("disabled",false);},disable:function(){return this._setOption("disabled",true);},_trigger:function(type,event,data){var prop,orig,callback=this.options[type];data=data||{};event=$.Event(event);event.type=(type===this.widgetEventPrefix?type:this.widgetEventPrefix+type).toLowerCase();event.target=this.element[0];orig=event.originalEvent;if(orig){for(prop in orig){if(!(prop in event)){event[prop]=orig[prop];}}} -this.element.trigger(event,data);return!($.isFunction(callback)&&callback.call(this.element[0],event,data)===false||event.isDefaultPrevented());}};})); \ No newline at end of file From a0b7780bd20298b0c8c26624f1e708fb3e69cada Mon Sep 17 00:00:00 2001 From: kindly Date: Fri, 24 Jan 2014 12:01:38 +0000 Subject: [PATCH 42/47] [#1451] use site url to get callback url for datapusher --- ckanext/datapusher/logic/action.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/datapusher/logic/action.py b/ckanext/datapusher/logic/action.py index 359fcb3d21f..6931d36ef38 100644 --- a/ckanext/datapusher/logic/action.py +++ b/ckanext/datapusher/logic/action.py @@ -45,9 +45,9 @@ def datapusher_submit(context, data_dict): datapusher_url = pylons.config.get('ckan.datapusher.url') - callback_url = p.toolkit.url_for( + callback_url = pylons.config['ckan.site_url'].rstrip('/') + p.toolkit.url_for( controller='api', action='action', logic_function='datapusher_hook', - ver=3, qualified=True) + ver=3) user = p.toolkit.get_action('user_show')(context, {'id': context['user']}) From d65d22d8c53cd3b8822a21a110bf035aa3db318c Mon Sep 17 00:00:00 2001 From: amercader Date: Fri, 24 Jan 2014 13:21:47 +0000 Subject: [PATCH 43/47] [#1452] Fix text preview tests --- ckanext/textpreview/tests/test_preview.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/textpreview/tests/test_preview.py b/ckanext/textpreview/tests/test_preview.py index 4467c0a21e4..5dbc07002b7 100644 --- a/ckanext/textpreview/tests/test_preview.py +++ b/ckanext/textpreview/tests/test_preview.py @@ -114,8 +114,8 @@ def test_css_included(self): result = self.app.get(url, status='*') assert result.status == 200, result.status - assert 'text.css' in result.body, result.body - assert 'github.css' in result.body, result.body + assert (('text.css' in result.body) or ('text.min.css' in result.body)), result.body + assert (('github.css' in result.body) or ('github.min.css' in result.body)), result.body def test_iframe_is_shown(self): url = h.url_for(controller='package', action='resource_read', From bc9d7fbd2557bf6a2094a01af0bdee7c6d3f0a42 Mon Sep 17 00:00:00 2001 From: kindly Date: Fri, 24 Jan 2014 13:52:49 +0000 Subject: [PATCH 44/47] [#1453] remove item for select list if config not set by adding helper and change validator to fail when config is set with empty string --- ckan/lib/helpers.py | 4 ++++ ckan/logic/validators.py | 4 ++-- ckan/templates/package/snippets/package_basic_fields.html | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index a96004b35e6..91556fa5d7a 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -38,6 +38,7 @@ import ckan.lib.datapreview as datapreview import ckan.logic as logic import ckan.lib.uploader as uploader +import ckan.new_authz as new_authz from ckan.common import ( _, ungettext, g, c, request, session, json, OrderedDict @@ -1753,6 +1754,8 @@ def get_site_statistics(): return stats +def check_config_permission(permission): + return new_authz.check_config_permission(permission) # these are the functions that will end up in `h` template helpers __allowed_functions__ = [ @@ -1855,4 +1858,5 @@ def get_site_statistics(): 'get_featured_organizations', 'get_featured_groups', 'get_site_statistics', + 'check_config_permission', ] diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py index 977fd80fb19..8c32940c5f3 100644 --- a/ckan/logic/validators.py +++ b/ckan/logic/validators.py @@ -33,8 +33,8 @@ def owner_org_validator(key, data, errors, context): user = context['user'] user = model.User.get(user) if value == '' : - if new_authz.check_config_permission('create_unowned_dataset'): - return + if not new_authz.check_config_permission('create_unowned_dataset'): + raise Invalid(_('A organization must be supplied')) # only sysadmins can remove datasets from org if not user.sysadmin: raise Invalid(_('You cannot remove a dataset from an existing organization')) diff --git a/ckan/templates/package/snippets/package_basic_fields.html b/ckan/templates/package/snippets/package_basic_fields.html index 585d6450270..0418e3be2f8 100644 --- a/ckan/templates/package/snippets/package_basic_fields.html +++ b/ckan/templates/package/snippets/package_basic_fields.html @@ -69,7 +69,9 @@