diff --git a/README.rst b/README.rst index c454a4d..46bb3de 100644 --- a/README.rst +++ b/README.rst @@ -46,7 +46,14 @@ To install ckanext-rating: 5. Initialize database tables used by Rating:: + ON CKAN >= 2.9: + + ckan --config=production.ini rating init + + ON CKAN < 2.8: + paster --plugin=ckanext-rating rating init --config=production.ini + 6. If you want to use this extension for ckanext-showcase, install it into your environment by following the instructions at https://github.com/ckan/ckanext-showcase diff --git a/ckanext/rating/public/css/rating.css b/ckanext/rating/assets/css/rating.css similarity index 100% rename from ckanext/rating/public/css/rating.css rename to ckanext/rating/assets/css/rating.css diff --git a/ckanext/rating/public/js/rating.js b/ckanext/rating/assets/js/rating.js similarity index 100% rename from ckanext/rating/public/js/rating.js rename to ckanext/rating/assets/js/rating.js diff --git a/ckanext/rating/assets/resource.config b/ckanext/rating/assets/resource.config new file mode 100644 index 0000000..f249038 --- /dev/null +++ b/ckanext/rating/assets/resource.config @@ -0,0 +1,4 @@ +[groups] +rating = + js/rating.js + css/rating.css \ No newline at end of file diff --git a/ckanext/rating/assets/webassets.yml b/ckanext/rating/assets/webassets.yml new file mode 100644 index 0000000..67dbd08 --- /dev/null +++ b/ckanext/rating/assets/webassets.yml @@ -0,0 +1,12 @@ +rating-js: + filters: rjsmin + output: ckanext-rating/rating.js + extra: + preload: + - base/main + contents: + - js/rating.js +rating-css: + output: ckanext-rating/rating.css + contents: + - css/rating.css \ No newline at end of file diff --git a/ckanext/rating/blueprint.py b/ckanext/rating/blueprint.py new file mode 100644 index 0000000..a0b26c6 --- /dev/null +++ b/ckanext/rating/blueprint.py @@ -0,0 +1,15 @@ +from flask import Blueprint + +import ckanext.rating.utils as utils + +rating = Blueprint('rating', __name__) + +def submit_package_rating(package, rating): + return utils.submit_package_rating(package, rating) + +def submit_showcase_rating(package, rating): + return utils.submit_showcase_rating(package, rating) + + +rating.add_url_rule('/rating/dataset//', view_func=submit_package_rating, endpoint='submit_package_rating') +rating.add_url_rule('/rating/showcase//', view_func=submit_showcase_rating, endpoint='submit_showcase_rating') \ No newline at end of file diff --git a/ckanext/rating/cli.py b/ckanext/rating/cli.py new file mode 100644 index 0000000..c10d2fb --- /dev/null +++ b/ckanext/rating/cli.py @@ -0,0 +1,17 @@ +import click + +import ckanext.rating.utils as utils + + +def get_commands(): + return [rating] + + +@click.group() +def rating(): + pass + + +@rating.command() +def init(): + utils.init_db() \ No newline at end of file diff --git a/ckanext/rating/commands.py b/ckanext/rating/commands.py index 53cec71..8d41a69 100644 --- a/ckanext/rating/commands.py +++ b/ckanext/rating/commands.py @@ -1,5 +1,7 @@ -from ckan.lib.cli import CkanCommand +import sys +from ckan.plugins.toolkit import CkanCommand +import ckanext.rating.utils as utils class RatingCommand(CkanCommand): ''' @@ -21,8 +23,8 @@ def command(self): Parse command line arguments and call appropriate method. """ if not self.args or self.args[0] in ['--help', '-h', 'help']: - print(RatingCommand.__doc__) - return + self.parser.print_usage() + sys.exit(1) cmd = self.args[0] self._load_config() @@ -33,6 +35,4 @@ def command(self): self.log.error('Command "%s" not recognized' % (cmd,)) def init_db(self): - import ckan.model as model - from ckanext.rating.model import init_tables - init_tables(model.meta.engine) + utils.init_db() diff --git a/ckanext/rating/controller.py b/ckanext/rating/controller.py index 2d66dea..ccbc1dc 100644 --- a/ckanext/rating/controller.py +++ b/ckanext/rating/controller.py @@ -1,48 +1,11 @@ import ckan.plugins as p -import ckan.model as model -import ckan.logic as logic -from ckan.lib.base import h from ckan.controllers.package import PackageController -from ckan.common import request, _ -import ckan.lib.base as base - -c = p.toolkit.c -flatten_to_string_key = logic.flatten_to_string_key -NotAuthorized = logic.NotAuthorized -abort = base.abort - +import ckanext.rating.utils as utils class RatingController(p.toolkit.BaseController): def submit_package_rating(self, package, rating): - context = {'model': model, 'user': c.user or c.author} - data_dict = {'package': package, 'rating': rating} - try: - p.toolkit.check_access('check_access_user', context, data_dict) - p.toolkit.get_action('rating_package_create')(context, data_dict) - h.redirect_to(controller='package', action='read', id=package) - except NotAuthorized: - abort(403, _('Unauthenticated user not allowed to submit ratings.')) - + return utils.submit_package_rating(package, rating) + def submit_showcase_rating(self, package, rating): - context = {'model': model, 'user': c.user or c.author} - data_dict = {'package': package, 'rating': rating} - try: - p.toolkit.check_access('check_access_user', context, data_dict) - p.toolkit.get_action('rating_package_create')(context, data_dict) - h.redirect_to(controller='ckanext.sixodp_showcase.controller:Sixodp_ShowcaseController', action='read', id=package) - except NotAuthorized: - abort(403, _('Unauthenticated user not allowed to submit ratings.')) - - -class RatingPackageController(PackageController): - - def search(self): - cur_page = request.params.get('page') - if cur_page is not None: - c.current_page = h.get_page_number(request.params) - else: - c.current_page = 1 - c.pkg_type = 'dataset' - result = super(RatingPackageController, self).search() - return result + return utils.submit_showcase_rating(package, rating) diff --git a/ckanext/rating/helpers.py b/ckanext/rating/helpers.py index 17862e3..f5da487 100644 --- a/ckanext/rating/helpers.py +++ b/ckanext/rating/helpers.py @@ -1,10 +1,9 @@ from ckanext.rating.model import Rating from ckan.plugins import toolkit -from pylons import config +from ckantoolkit import config c = toolkit.c - def get_user_rating(package_id): if not c.userobj: user = toolkit.request.environ.get('REMOTE_ADDR') diff --git a/ckanext/rating/logic/auth/create.py b/ckanext/rating/logic/auth/create.py index a63d8f6..61ba7ea 100644 --- a/ckanext/rating/logic/auth/create.py +++ b/ckanext/rating/logic/auth/create.py @@ -1,9 +1,8 @@ -import pylons.config as config +from ckantoolkit import config from ckan.plugins import toolkit import ckan.logic as logic c = toolkit.c - def rating_create_auth(): return { 'check_access_user': check_access_user, diff --git a/ckanext/rating/model.py b/ckanext/rating/model.py index 113c272..2aa5ef5 100644 --- a/ckanext/rating/model.py +++ b/ckanext/rating/model.py @@ -18,7 +18,11 @@ def make_uuid(): - return str(uuid.uuid4()) + try: + unicode('') + except NameError: + unicode = str + return unicode(uuid.uuid4()) class Rating(Base): diff --git a/ckanext/rating/plugin.py b/ckanext/rating/plugin/__init__.py similarity index 76% rename from ckanext/rating/plugin.py rename to ckanext/rating/plugin/__init__.py index 0a76e07..fb9c667 100644 --- a/ckanext/rating/plugin.py +++ b/ckanext/rating/plugin/__init__.py @@ -5,17 +5,32 @@ import ckan.model as model from ckanext.rating.logic import action -from ckanext.rating import helpers import ckanext.rating.logic.auth as rating_auth from ckanext.rating.model import Rating -from ckan.lib.plugins import DefaultTranslation from ckan.plugins.toolkit import get_action -from helpers import show_rating_in_type +from ckanext.rating import helpers +from ckanext.rating.helpers import show_rating_in_type import logging log = logging.getLogger(__name__) +if toolkit.check_ckan_version(min_version='2.5'): + from ckan.lib.plugins import DefaultTranslation + + class RatingPluginBase(plugins.SingletonPlugin, DefaultTranslation): + plugins.implements(plugins.ITranslation, inherit=True) +else: + class RatingPluginBase(plugins.SingletonPlugin): + pass + +if toolkit.check_ckan_version(u'2.9'): + from ckanext.rating.plugin.flask_plugin import MixinPlugin + ckan_29_or_higher = True +else: + from ckanext.rating.plugin.pylons_plugin import MixinPlugin + ckan_29_or_higher = False + def sort_by_rating(sort): limit = g.datasets_per_page @@ -56,25 +71,19 @@ def sort_by_rating(sort): q = tmp[:-4] + ')' return q - -class RatingPlugin(plugins.SingletonPlugin, DefaultTranslation): +class RatingPlugin(RatingPluginBase, MixinPlugin): plugins.implements(plugins.IConfigurer) plugins.implements(plugins.IActions) plugins.implements(plugins.ITemplateHelpers) plugins.implements(plugins.IAuthFunctions) plugins.implements(plugins.IPackageController, inherit=True) - plugins.implements(plugins.IRoutes, inherit=True) - if toolkit.check_ckan_version(min_version='2.5.0'): - plugins.implements(plugins.ITranslation, inherit=True) # IConfigurer def update_config(self, config_): - toolkit.add_template_directory(config_, 'templates') - toolkit.add_public_directory(config_, 'public') - toolkit.add_resource('fanstatic', 'rating') - toolkit.add_resource('public/css/', 'rating_css') - toolkit.add_resource('public/js/', 'rating_js') + toolkit.add_template_directory(config_, '../templates') + toolkit.add_public_directory(config_, '../public') + toolkit.add_resource('../assets', 'rating') # IActions @@ -124,22 +133,4 @@ def after_search(self, search_results, search_params): pkg['ratings_count'] = rating_dict.get('ratings_count', 0) return search_results - # IRoutes - - def before_map(self, map): - map.connect('/rating/dataset/:package/:rating', - controller='ckanext.rating.controller:RatingController', - action='submit_package_rating') - - map.connect('/rating/showcase/:package/:rating', - controller='ckanext.rating.controller:RatingController', - action='submit_showcase_rating') - - map.connect( - '/dataset', - controller='ckanext.rating.controller:RatingPackageController', - action='search', - highlight_actions='index search' - ) - - return map + diff --git a/ckanext/rating/plugin/flask_plugin.py b/ckanext/rating/plugin/flask_plugin.py new file mode 100644 index 0000000..22944e3 --- /dev/null +++ b/ckanext/rating/plugin/flask_plugin.py @@ -0,0 +1,15 @@ +import ckan.plugins as p + +from ckanext.rating.blueprint import rating as rating_blueprint +import ckanext.rating.cli as cli + +class MixinPlugin(p.SingletonPlugin): + + p.implements(p.IBlueprint) + p.implements(p.IClick) + + def get_blueprint(self): + return [rating_blueprint] + + def get_commands(self): + return cli.get_commands() \ No newline at end of file diff --git a/ckanext/rating/i18n/ckanext-rating.pot b/ckanext/rating/plugin/i18n/ckanext-rating.pot similarity index 100% rename from ckanext/rating/i18n/ckanext-rating.pot rename to ckanext/rating/plugin/i18n/ckanext-rating.pot diff --git a/ckanext/rating/i18n/en_GB/LC_MESSAGES/ckanext-rating.mo b/ckanext/rating/plugin/i18n/en_GB/LC_MESSAGES/ckanext-rating.mo similarity index 100% rename from ckanext/rating/i18n/en_GB/LC_MESSAGES/ckanext-rating.mo rename to ckanext/rating/plugin/i18n/en_GB/LC_MESSAGES/ckanext-rating.mo diff --git a/ckanext/rating/i18n/en_GB/LC_MESSAGES/ckanext-rating.po b/ckanext/rating/plugin/i18n/en_GB/LC_MESSAGES/ckanext-rating.po similarity index 100% rename from ckanext/rating/i18n/en_GB/LC_MESSAGES/ckanext-rating.po rename to ckanext/rating/plugin/i18n/en_GB/LC_MESSAGES/ckanext-rating.po diff --git a/ckanext/rating/i18n/fi/LC_MESSAGES/ckanext-rating.mo b/ckanext/rating/plugin/i18n/fi/LC_MESSAGES/ckanext-rating.mo similarity index 100% rename from ckanext/rating/i18n/fi/LC_MESSAGES/ckanext-rating.mo rename to ckanext/rating/plugin/i18n/fi/LC_MESSAGES/ckanext-rating.mo diff --git a/ckanext/rating/i18n/fi/LC_MESSAGES/ckanext-rating.po b/ckanext/rating/plugin/i18n/fi/LC_MESSAGES/ckanext-rating.po similarity index 100% rename from ckanext/rating/i18n/fi/LC_MESSAGES/ckanext-rating.po rename to ckanext/rating/plugin/i18n/fi/LC_MESSAGES/ckanext-rating.po diff --git a/ckanext/rating/i18n/sv/LC_MESSAGES/ckanext-rating.mo b/ckanext/rating/plugin/i18n/sv/LC_MESSAGES/ckanext-rating.mo similarity index 100% rename from ckanext/rating/i18n/sv/LC_MESSAGES/ckanext-rating.mo rename to ckanext/rating/plugin/i18n/sv/LC_MESSAGES/ckanext-rating.mo diff --git a/ckanext/rating/i18n/sv/LC_MESSAGES/ckanext-rating.po b/ckanext/rating/plugin/i18n/sv/LC_MESSAGES/ckanext-rating.po similarity index 100% rename from ckanext/rating/i18n/sv/LC_MESSAGES/ckanext-rating.po rename to ckanext/rating/plugin/i18n/sv/LC_MESSAGES/ckanext-rating.po diff --git a/ckanext/rating/plugin/pylons_plugin.py b/ckanext/rating/plugin/pylons_plugin.py new file mode 100644 index 0000000..40e1439 --- /dev/null +++ b/ckanext/rating/plugin/pylons_plugin.py @@ -0,0 +1,19 @@ +import ckan.plugins as p + + +class MixinPlugin(p.SingletonPlugin): + + p.implements(p.IRoutes, inherit=True) + + # IRoutes + + def before_map(self, map): + map.connect('/rating/dataset/:package/:rating', + controller='ckanext.rating.controller:RatingController', + action='submit_package_rating') + + map.connect('/rating/showcase/:package/:rating', + controller='ckanext.rating.controller:RatingController', + action='submit_showcase_rating') + + return map \ No newline at end of file diff --git a/ckanext/rating/templates/rating/snippets/rating.html b/ckanext/rating/templates/rating/snippets/rating.html index e9cf621..82868eb 100644 --- a/ckanext/rating/templates/rating/snippets/rating.html +++ b/ckanext/rating/templates/rating/snippets/rating.html @@ -6,7 +6,7 @@ {% snippet "rating/snippets/rating.html", package=pkg %} #} -{% resource "rating_css/rating.css" %} +{% asset "rating/rating-css" %} {% if h.show_rating_in_type(package.type) %}
{% block general_rating %} diff --git a/ckanext/rating/templates/rating/snippets/rating_single.html b/ckanext/rating/templates/rating/snippets/rating_single.html index 68614aa..22e300d 100644 --- a/ckanext/rating/templates/rating/snippets/rating_single.html +++ b/ckanext/rating/templates/rating/snippets/rating_single.html @@ -7,9 +7,9 @@ {% snippet "rating/snippets/rating_single.html", package=pkg %} #} -{% resource "rating_css/rating.css" %} +{% asset "rating/rating-css" %} {% if enable_input %} - {% resource "rating_js/rating.js" %} + {% asset "rating/rating-js" %} {% endif %} {% set action = 'submit_package_rating' %} diff --git a/ckanext/rating/templates/rating/snippets/rating_single_flat.html b/ckanext/rating/templates/rating/snippets/rating_single_flat.html index a872839..b4aa592 100644 --- a/ckanext/rating/templates/rating/snippets/rating_single_flat.html +++ b/ckanext/rating/templates/rating/snippets/rating_single_flat.html @@ -9,9 +9,9 @@ {% snippet "rating/snippets/rating_single_flat.html", package=pkg %} #} -{% resource "rating_css/rating.css" %} +{% asset "rating/rating-css" %} {% if enable_input %} -{% resource "rating_js/rating.js" %} +{% asset "rating/rating-js" %} {% endif %} {% set action = 'submit_package_rating' %} diff --git a/ckanext/rating/templates/rating/snippets/stars.html b/ckanext/rating/templates/rating/snippets/stars.html index 076ac8a..e39d56f 100644 --- a/ckanext/rating/templates/rating/snippets/stars.html +++ b/ckanext/rating/templates/rating/snippets/stars.html @@ -7,9 +7,10 @@ {% snippet "rating/snippets/stars.html", package=pkg %} #} +{% set ckan_29_or_higher = h.ckan_version().split('.')[1] | int >= 9 %} -{% resource "rating_css/rating.css" %} -{% resource "rating_js/rating.js" %} +{% asset "rating/rating-css" %} +{% asset "rating/rating-js" %} {% set action = 'submit_package_rating' %} {% if package.type == 'showcase' %} @@ -20,10 +21,20 @@ {%- for index in range(stars|int) -%} - + {% if ckan_29_or_higher %} + {% set url = h.url_for('rating.'+action, package=package.name, rating=index+1) %} + {% else %} + {% set url = h.url_for(controller='ckanext.rating.controller:RatingController', action=action, package=package.name, rating=index+1) %} + {% endif %} + {%- endfor -%} {%- for index in range(stars|int, 5) -%} - + {% if ckan_29_or_higher %} + {% set url = h.url_for('rating.'+action, package=package.name, rating=index+1) %} + {% else %} + {% set url = h.url_for(controller='ckanext.rating.controller:RatingController', action=action, package=package.name, rating=index+1) %} + {% endif %} + {%- endfor -%} diff --git a/ckanext/rating/templates/snippets/package_item.html b/ckanext/rating/templates/snippets/package_item.html index 26cd774..146c5ea 100644 --- a/ckanext/rating/templates/snippets/package_item.html +++ b/ckanext/rating/templates/snippets/package_item.html @@ -1,4 +1,4 @@ -{% resource "rating_css/rating.css" %} +{% asset "rating/rating-css" %} {% ckan_extends %} diff --git a/ckanext/rating/utils.py b/ckanext/rating/utils.py new file mode 100644 index 0000000..bf2abf4 --- /dev/null +++ b/ckanext/rating/utils.py @@ -0,0 +1,41 @@ +import ckan.plugins as p +import ckan.model as model +import ckan.logic as logic +from ckan.lib.base import h +from ckan.common import request, _ +import ckan.lib.base as base +from ckanext.rating.model import init_tables +import ckantoolkit as tk + +c = p.toolkit.c +flatten_to_string_key = logic.flatten_to_string_key +NotAuthorized = logic.NotAuthorized +abort = base.abort + +ckan_29_or_higher = tk.check_ckan_version(min_version='2.9.0') + +def submit_package_rating(package, rating): + context = {'model': model, 'user': c.user or c.author} + data_dict = {'package': package, 'rating': rating} + try: + p.toolkit.check_access('check_access_user', context, data_dict) + p.toolkit.get_action('rating_package_create')(context, data_dict) + if ckan_29_or_higher: + return h.redirect_to(controller='dataset', action='read', id=package) + else: + h.redirect_to(controller='package', action='read', id=package) + except NotAuthorized: + abort(403, _('Unauthenticated user not allowed to submit ratings.')) + +def submit_showcase_rating(package, rating): + context = {'model': model, 'user': c.user or c.author} + data_dict = {'package': package, 'rating': rating} + try: + p.toolkit.check_access('check_access_user', context, data_dict) + p.toolkit.get_action('rating_package_create')(context, data_dict) + return h.redirect_to(controller='ckanext.sixodp_showcase.controller:Sixodp_ShowcaseController', action='read', id=package) + except NotAuthorized: + abort(403, _('Unauthenticated user not allowed to submit ratings.')) + +def init_db(): + init_tables(model.meta.engine) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b83ee74 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +ckantoolkit +six diff --git a/setup.py b/setup.py index 04b9a18..cb3d1a8 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,9 @@ # that you indicate whether you support Python 2, Python 3 or both. 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ], @@ -61,7 +64,9 @@ # project is installed. For an analysis of "install_requires" vs pip's # requirements files see: # https://packaging.python.org/en/latest/technical.html#install-requires-vs-requirements-files - install_requires=[], + install_requires=[ + 'six', 'ckantoolkit', + ], # If there are data files included in your packages that need to be # installed, specify them here. If using Python 2.6 or less, then these