[ckan-changes] [okfn/ckan] ec1d21: extend plugin.toolkit functions

GitHub noreply at github.com
Fri Apr 20 14:27:22 UTC 2012


  Branch: refs/heads/feature-2302-simple-theming
  Home:   https://github.com/okfn/ckan
  Commit: ec1d2113a66ed66312ad6b56fe441afb633cf8b1
      https://github.com/okfn/ckan/commit/ec1d2113a66ed66312ad6b56fe441afb633cf8b1
  Author: Toby <toby.junk at gmail.com>
  Date:   2012-04-19 (Thu, 19 Apr 2012)

  Changed paths:
    M ckan/plugins/toolkit.py

  Log Message:
  -----------
  extend plugin.toolkit functions


diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
index 64a80d7..59de1c9 100644
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -1,21 +1,28 @@
-## This file is intended to make functions consistently available to
-## plugins whilst giving developers the ability move code around or
-## change underlying frameworks etc. It should not be used internaly
-## within ckan only by extensions. Functions should only be removed from
-## this file after reasonable depreciation notice has been given.
+## This file is intended to make functions/objects consistently
+## available to plugins whilst giving developers the ability move code
+## around or change underlying frameworks etc. It should not be used
+## internaly within ckan only by extensions. Functions should only be
+## removed from this file after reasonable depreciation notice has
+## been given.
 
 import inspect
 import os
+import re
 
 import pylons
 import paste.deploy.converters as converters
 import webhelpers.html.tags
 
-import lib.base as base
+import ckan
+import ckan.lib.base as base
+import ckan.logic as logic
+import ckan.lib.cli as cli
+
 
 
 __all__ = [
-    ## Imported functions ##
+    ## Imported functions/objects ##
+    '_',                    # i18n translation
     'c',                    # template context
     'request',              # http request object
     'render',               # template render function
@@ -25,12 +32,22 @@
     'asint',                # converts an object to an integer
     'aslist',               # converts an object to a list
     'literal',              # stop tags in a string being escaped
+    'get_action',           # get logic action function
+    'check_access',         # check logic function authorisation
+    'NotFound',             # action not found exception
+    'NotAuthorized',        # action not authourized exception
+    'ValidationError',      # model update validation error
+    'CkanCommand',          # class for providing cli interfaces
 
-    ## Functions fully defined here ##
+    ## Fully defined in this file ##
     'add_template_directory',
     'add_public_directory',
+    'requires_ckan_version',
+    'check_ckan_version',
+    'CkanVersionException',
 ]
 
+_ = pylons.i18n._
 c = pylons.c
 request = pylons.request
 render = base.render
@@ -40,6 +57,13 @@
 aslist = converters.aslist
 literal = webhelpers.html.tags.literal
 
+get_action = logic.get_action
+check_access = logic.check_access
+NotFound = logic.NotFound
+NotAuthorized = logic.NotAuthorized
+ValidationError = logic.ValidationError
+
+CkanCommand = cli.CkanCommand
 
 # wrappers
 def render_snippet(template, data=None):
@@ -73,3 +97,38 @@ def _add_served_directory(config, relative_path, config_var):
             config[config_var] += ',' + absolute_path
         else:
             config[config_var] = absolute_path
+
+class CkanVersionException(Exception):
+    ''' Exception raised if required ckan version is not available. '''
+    pass
+
+
+def _version_str_2_list(v_str):
+    ''' conver a version string into a list of ints
+    eg 1.6.1b --> [1, 6, 1] '''
+    v_str = re.sub(r'[^0-9.]', '', v_str)
+    return [int(part) for part in v_str.split('.')]
+
+def check_ckan_version(min_version=None, max_version=None):
+    ''' Check that the ckan version is correct for the plugin. '''
+    current = _version_str_2_list(ckan.__version__)
+
+    if min_version:
+        min_required = _version_str_2_list(min_version)
+        if current < min_required:
+            return False
+    if max_version:
+        max_required = _version_str_2_list(max_version)
+        if current > max_required:
+            return False
+    return True
+
+def requires_ckan_version(min_version, max_version=None):
+    ''' Check that the ckan version is correct for the plugin. '''
+    if not check_ckan_version(min_version=min_version, max_version=max_version):
+        if not max_version:
+            error = 'Requires ckan version %s or higher' % min_version
+        else:
+            error = 'Requires ckan version  between %s and %s' % \
+                        (min_version, max_version)
+        raise CkanVersionException(error)


================================================================
  Commit: 413f680238f204f791d81a384b52748612f58284
      https://github.com/okfn/ckan/commit/413f680238f204f791d81a384b52748612f58284
  Author: Toby <toby.junk at gmail.com>
  Date:   2012-04-19 (Thu, 19 Apr 2012)

  Changed paths:
    M ckan/plugins/toolkit.py

  Log Message:
  -----------
  [xs] learn to spell


diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
index 59de1c9..27e0fd0 100644
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -35,7 +35,7 @@
     'get_action',           # get logic action function
     'check_access',         # check logic function authorisation
     'NotFound',             # action not found exception
-    'NotAuthorized',        # action not authourized exception
+    'NotAuthorized',        # action not authorized exception
     'ValidationError',      # model update validation error
     'CkanCommand',          # class for providing cli interfaces
 


================================================================
  Commit: 5ceee26d8cb0b7c6df390d09f978b0acfa6275e5
      https://github.com/okfn/ckan/commit/5ceee26d8cb0b7c6df390d09f978b0acfa6275e5
  Author: Toby <toby.junk at gmail.com>
  Date:   2012-04-19 (Thu, 19 Apr 2012)

  Changed paths:
    M ckan/plugins/toolkit.py

  Log Message:
  -----------
  [xs] spelling


diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
index 27e0fd0..de72479 100644
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -1,7 +1,7 @@
 ## This file is intended to make functions/objects consistently
 ## available to plugins whilst giving developers the ability move code
 ## around or change underlying frameworks etc. It should not be used
-## internaly within ckan only by extensions. Functions should only be
+## internally within ckan only by extensions. Functions should only be
 ## removed from this file after reasonable depreciation notice has
 ## been given.
 


================================================================
  Commit: cf1211145f7cc1896f4bae49c37936dccce112a9
      https://github.com/okfn/ckan/commit/cf1211145f7cc1896f4bae49c37936dccce112a9
  Author: Toby <toby.junk at gmail.com>
  Date:   2012-04-19 (Thu, 19 Apr 2012)

  Changed paths:
    M ckan/plugins/toolkit.py

  Log Message:
  -----------
  plugin.toolkit NotFound -> ActionNotFound


diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
index de72479..e635c43 100644
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -34,7 +34,7 @@
     'literal',              # stop tags in a string being escaped
     'get_action',           # get logic action function
     'check_access',         # check logic function authorisation
-    'NotFound',             # action not found exception
+    'ActionNotFound',       # action not found exception (ckan.logic.NotFound)
     'NotAuthorized',        # action not authorized exception
     'ValidationError',      # model update validation error
     'CkanCommand',          # class for providing cli interfaces
@@ -59,7 +59,7 @@
 
 get_action = logic.get_action
 check_access = logic.check_access
-NotFound = logic.NotFound
+ActionNotFound = logic.NotFound  ## Name change intentional
 NotAuthorized = logic.NotAuthorized
 ValidationError = logic.ValidationError
 


================================================================
  Commit: 66b596ce01b1613a9398bb25d6b266e4b4828b6e
      https://github.com/okfn/ckan/commit/66b596ce01b1613a9398bb25d6b266e4b4828b6e
  Author: Toby <toby.junk at gmail.com>
  Date:   2012-04-20 (Fri, 20 Apr 2012)

  Changed paths:
    M ckan/lib/base.py

  Log Message:
  -----------
  import clean


diff --git a/ckan/lib/base.py b/ckan/lib/base.py
index da9d69f..075029c 100644
--- a/ckan/lib/base.py
+++ b/ckan/lib/base.py
@@ -22,7 +22,7 @@
 
 import ckan.exceptions
 import ckan
-from ckan import authz
+import ckan.authz as authz
 from ckan.lib import i18n
 import ckan.lib.helpers as h
 from ckan.plugins import PluginImplementations, IGenshiStreamFilter


================================================================
  Commit: 79e42821a2eddfaedb12e6d6a5376ae4a6354a70
      https://github.com/okfn/ckan/commit/79e42821a2eddfaedb12e6d6a5376ae4a6354a70
  Author: Toby <toby.junk at gmail.com>
  Date:   2012-04-20 (Fri, 20 Apr 2012)

  Changed paths:
    M ckan/lib/cli.py

  Log Message:
  -----------
  import clean in lib/cli


diff --git a/ckan/lib/cli.py b/ckan/lib/cli.py
index 5146aa9..6798cc4 100644
--- a/ckan/lib/cli.py
+++ b/ckan/lib/cli.py
@@ -84,7 +84,7 @@ class ManageDb(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         import ckan.lib.search as search
 
         cmd = self.args[0]
@@ -164,7 +164,7 @@ def _postgres_dump(self, filepath):
         self._run_cmd(pg_dump_cmd)
 
     def _postgres_load(self, filepath):
-        from ckan import model
+        import ckan.model as model
         assert not model.repo.are_tables_created(), "Tables already found. You need to 'db clean' before a load."
         pg_cmd = self._get_psql_cmd() + ' -f %s' % filepath
         self._run_cmd(pg_cmd)
@@ -194,7 +194,7 @@ def load(self, only_load=False):
         pg_cmd = self._postgres_load(dump_path)
         if not only_load:
             print 'Upgrading DB'
-            from ckan import model
+            import ckan.model as model
             model.repo.upgrade_db()
 
             print 'Rebuilding search index'
@@ -205,7 +205,7 @@ def load(self, only_load=False):
         print 'Done'
 
     def simple_dump_csv(self):
-        from ckan import model
+        import ckan.model as model
         if len(self.args) < 2:
             print 'Need csv file path'
             return
@@ -215,7 +215,7 @@ def simple_dump_csv(self):
         dumper.SimpleDumper().dump(dump_file, format='csv')
 
     def simple_dump_json(self):
-        from ckan import model
+        import ckan.model as model
         if len(self.args) < 2:
             print 'Need json file path'
             return
@@ -452,7 +452,7 @@ class Sysadmin(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         cmd = self.args[0] if self.args else None
         if cmd == None or cmd == 'list':
@@ -465,7 +465,7 @@ def command(self):
             print 'Command %s not recognized' % cmd
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Sysadmins:'
         sysadmins = model.Session.query(model.SystemRole).filter_by(role=model.Role.ADMIN)
         print 'count = %i' % sysadmins.count()
@@ -477,7 +477,7 @@ def list(self):
                                         user_or_authgroup.id)
 
     def add(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user to be made sysadmin.'
@@ -501,7 +501,7 @@ def add(self):
         print 'Added %s as sysadmin' % username
 
     def remove(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user to be made sysadmin.'
@@ -537,7 +537,7 @@ class UserCmd(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         if not self.args:
             self.list()
@@ -563,7 +563,7 @@ def get_user_str(self, user):
         return user_str
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Users:'
         users = model.Session.query(model.User)
         print 'count = %i' % users.count()
@@ -571,14 +571,14 @@ def list(self):
             print self.get_user_str(user)
 
     def show(self):
-        from ckan import model
+        import ckan.model as model
 
         username = self.args[0]
         user = model.User.get(unicode(username))
         print 'User: \n', user
 
     def setpass(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -593,7 +593,7 @@ def setpass(self):
         print 'Done'
 
     def search(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need user name query string.'
@@ -618,7 +618,7 @@ def password_prompt(cls):
         return password1
 
     def add(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -671,7 +671,7 @@ def add(self):
         print user
 
     def remove(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -704,7 +704,7 @@ class DatasetCmd(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         if not self.args:
             print self.usage
@@ -722,7 +722,7 @@ def command(self):
                 self.show(self.args[0])
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Datasets:'
         datasets = model.Session.query(model.Package)
         print 'count = %i' % datasets.count()
@@ -732,19 +732,19 @@ def list(self):
             print '%s %s %s' % (dataset.id, dataset.name, state)
 
     def _get_dataset(self, dataset_ref):
-        from ckan import model
+        import ckan.model as model
         dataset = model.Package.get(unicode(dataset_ref))
         assert dataset, 'Could not find dataset matching reference: %r' % dataset_ref
         return dataset
 
     def show(self, dataset_ref):
-        from ckan import model
         import pprint
         dataset = self._get_dataset(dataset_ref)
         pprint.pprint(dataset.as_dict())
 
     def delete(self, dataset_ref):
-        from ckan import model, plugins
+        from ckan import plugins
+        import ckan.model as model
         dataset = self._get_dataset(dataset_ref)
         old_state = dataset.state
 
@@ -756,7 +756,8 @@ def delete(self, dataset_ref):
         print '%s %s -> %s' % (dataset.name, old_state, dataset.state)
 
     def purge(self, dataset_ref):
-        from ckan import model, plugins
+        from ckan import plugins
+        import ckan.model as model
         dataset = self._get_dataset(dataset_ref)
         name = dataset.name
 
@@ -802,7 +803,7 @@ def run_(self):
 
     def view(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         from kombu.transport.sqlalchemy.models import Message
         q = model.Session.query(Message)
         q_visible = q.filter_by(visible=True)
@@ -816,7 +817,7 @@ def view(self):
 
     def clean(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         import pprint
         tasks_initially = model.Session.execute("select * from kombu_message").rowcount
         if not tasks_initially:
@@ -847,7 +848,7 @@ class Ratings(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         cmd = self.args[0]
         if cmd == 'count':
@@ -860,14 +861,14 @@ def command(self):
             print 'Command %s not recognized' % cmd
 
     def count(self):
-        from ckan import model
+        import ckan.model as model
         q = model.Session.query(model.Rating)
         print "%i ratings" % q.count()
         q = q.filter(model.Rating.user_id == None)
         print "of which %i are anonymous ratings" % q.count()
 
     def clean(self, user_ratings=True):
-        from ckan import model
+        import ckan.model as model
         q = model.Session.query(model.Rating)
         print "%i ratings" % q.count()
         if not user_ratings:


================================================================
  Commit: e3c3c004ff21fa3848d49251310a8a4b399ab496
      https://github.com/okfn/ckan/commit/e3c3c004ff21fa3848d49251310a8a4b399ab496
  Author: Toby <toby.junk at gmail.com>
  Date:   2012-04-20 (Fri, 20 Apr 2012)

  Changed paths:
    M ckan/plugins/__init__.py

  Log Message:
  -----------
  avoid circular imports in plugins.toolkit


diff --git a/ckan/plugins/__init__.py b/ckan/plugins/__init__.py
index fcb6055..e11e909 100644
--- a/ckan/plugins/__init__.py
+++ b/ckan/plugins/__init__.py
@@ -1,3 +1,19 @@
 from ckan.plugins.core import *
 from ckan.plugins.interfaces import *
-import toolkit
+
+
+class _Toolkit(object):
+    ''' This object allows us to avoid circular imports while making
+    functions/objects available to plugins. '''
+
+    def __init__(self):
+        self.toolkit = None
+
+    def __getattr__(self, name):
+        if not self.toolkit:
+            import toolkit
+            self.toolkit = toolkit
+        return getattr(self.toolkit, name)
+
+toolkit = _Toolkit()
+del _Toolkit


================================================================
  Commit: 762901f7d21dbaecf3f120c21d9136375c632a77
      https://github.com/okfn/ckan/commit/762901f7d21dbaecf3f120c21d9136375c632a77
  Author: Ross Jones <rossdjones at gmail.com>
  Date:   2012-04-20 (Fri, 20 Apr 2012)

  Changed paths:
    M ckan/lib/base.py
    M ckan/lib/cli.py
    M ckan/plugins/__init__.py
    M ckan/plugins/toolkit.py

  Log Message:
  -----------
  Merge branch 'master' of https://github.com/okfn/ckan


diff --git a/ckan/lib/base.py b/ckan/lib/base.py
index da9d69f..075029c 100644
--- a/ckan/lib/base.py
+++ b/ckan/lib/base.py
@@ -22,7 +22,7 @@
 
 import ckan.exceptions
 import ckan
-from ckan import authz
+import ckan.authz as authz
 from ckan.lib import i18n
 import ckan.lib.helpers as h
 from ckan.plugins import PluginImplementations, IGenshiStreamFilter
diff --git a/ckan/lib/cli.py b/ckan/lib/cli.py
index 5146aa9..6798cc4 100644
--- a/ckan/lib/cli.py
+++ b/ckan/lib/cli.py
@@ -84,7 +84,7 @@ class ManageDb(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         import ckan.lib.search as search
 
         cmd = self.args[0]
@@ -164,7 +164,7 @@ def _postgres_dump(self, filepath):
         self._run_cmd(pg_dump_cmd)
 
     def _postgres_load(self, filepath):
-        from ckan import model
+        import ckan.model as model
         assert not model.repo.are_tables_created(), "Tables already found. You need to 'db clean' before a load."
         pg_cmd = self._get_psql_cmd() + ' -f %s' % filepath
         self._run_cmd(pg_cmd)
@@ -194,7 +194,7 @@ def load(self, only_load=False):
         pg_cmd = self._postgres_load(dump_path)
         if not only_load:
             print 'Upgrading DB'
-            from ckan import model
+            import ckan.model as model
             model.repo.upgrade_db()
 
             print 'Rebuilding search index'
@@ -205,7 +205,7 @@ def load(self, only_load=False):
         print 'Done'
 
     def simple_dump_csv(self):
-        from ckan import model
+        import ckan.model as model
         if len(self.args) < 2:
             print 'Need csv file path'
             return
@@ -215,7 +215,7 @@ def simple_dump_csv(self):
         dumper.SimpleDumper().dump(dump_file, format='csv')
 
     def simple_dump_json(self):
-        from ckan import model
+        import ckan.model as model
         if len(self.args) < 2:
             print 'Need json file path'
             return
@@ -452,7 +452,7 @@ class Sysadmin(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         cmd = self.args[0] if self.args else None
         if cmd == None or cmd == 'list':
@@ -465,7 +465,7 @@ def command(self):
             print 'Command %s not recognized' % cmd
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Sysadmins:'
         sysadmins = model.Session.query(model.SystemRole).filter_by(role=model.Role.ADMIN)
         print 'count = %i' % sysadmins.count()
@@ -477,7 +477,7 @@ def list(self):
                                         user_or_authgroup.id)
 
     def add(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user to be made sysadmin.'
@@ -501,7 +501,7 @@ def add(self):
         print 'Added %s as sysadmin' % username
 
     def remove(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user to be made sysadmin.'
@@ -537,7 +537,7 @@ class UserCmd(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         if not self.args:
             self.list()
@@ -563,7 +563,7 @@ def get_user_str(self, user):
         return user_str
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Users:'
         users = model.Session.query(model.User)
         print 'count = %i' % users.count()
@@ -571,14 +571,14 @@ def list(self):
             print self.get_user_str(user)
 
     def show(self):
-        from ckan import model
+        import ckan.model as model
 
         username = self.args[0]
         user = model.User.get(unicode(username))
         print 'User: \n', user
 
     def setpass(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -593,7 +593,7 @@ def setpass(self):
         print 'Done'
 
     def search(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need user name query string.'
@@ -618,7 +618,7 @@ def password_prompt(cls):
         return password1
 
     def add(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -671,7 +671,7 @@ def add(self):
         print user
 
     def remove(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -704,7 +704,7 @@ class DatasetCmd(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         if not self.args:
             print self.usage
@@ -722,7 +722,7 @@ def command(self):
                 self.show(self.args[0])
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Datasets:'
         datasets = model.Session.query(model.Package)
         print 'count = %i' % datasets.count()
@@ -732,19 +732,19 @@ def list(self):
             print '%s %s %s' % (dataset.id, dataset.name, state)
 
     def _get_dataset(self, dataset_ref):
-        from ckan import model
+        import ckan.model as model
         dataset = model.Package.get(unicode(dataset_ref))
         assert dataset, 'Could not find dataset matching reference: %r' % dataset_ref
         return dataset
 
     def show(self, dataset_ref):
-        from ckan import model
         import pprint
         dataset = self._get_dataset(dataset_ref)
         pprint.pprint(dataset.as_dict())
 
     def delete(self, dataset_ref):
-        from ckan import model, plugins
+        from ckan import plugins
+        import ckan.model as model
         dataset = self._get_dataset(dataset_ref)
         old_state = dataset.state
 
@@ -756,7 +756,8 @@ def delete(self, dataset_ref):
         print '%s %s -> %s' % (dataset.name, old_state, dataset.state)
 
     def purge(self, dataset_ref):
-        from ckan import model, plugins
+        from ckan import plugins
+        import ckan.model as model
         dataset = self._get_dataset(dataset_ref)
         name = dataset.name
 
@@ -802,7 +803,7 @@ def run_(self):
 
     def view(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         from kombu.transport.sqlalchemy.models import Message
         q = model.Session.query(Message)
         q_visible = q.filter_by(visible=True)
@@ -816,7 +817,7 @@ def view(self):
 
     def clean(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         import pprint
         tasks_initially = model.Session.execute("select * from kombu_message").rowcount
         if not tasks_initially:
@@ -847,7 +848,7 @@ class Ratings(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         cmd = self.args[0]
         if cmd == 'count':
@@ -860,14 +861,14 @@ def command(self):
             print 'Command %s not recognized' % cmd
 
     def count(self):
-        from ckan import model
+        import ckan.model as model
         q = model.Session.query(model.Rating)
         print "%i ratings" % q.count()
         q = q.filter(model.Rating.user_id == None)
         print "of which %i are anonymous ratings" % q.count()
 
     def clean(self, user_ratings=True):
-        from ckan import model
+        import ckan.model as model
         q = model.Session.query(model.Rating)
         print "%i ratings" % q.count()
         if not user_ratings:
diff --git a/ckan/plugins/__init__.py b/ckan/plugins/__init__.py
index fcb6055..e11e909 100644
--- a/ckan/plugins/__init__.py
+++ b/ckan/plugins/__init__.py
@@ -1,3 +1,19 @@
 from ckan.plugins.core import *
 from ckan.plugins.interfaces import *
-import toolkit
+
+
+class _Toolkit(object):
+    ''' This object allows us to avoid circular imports while making
+    functions/objects available to plugins. '''
+
+    def __init__(self):
+        self.toolkit = None
+
+    def __getattr__(self, name):
+        if not self.toolkit:
+            import toolkit
+            self.toolkit = toolkit
+        return getattr(self.toolkit, name)
+
+toolkit = _Toolkit()
+del _Toolkit
diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
index 64a80d7..e635c43 100644
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -1,21 +1,28 @@
-## This file is intended to make functions consistently available to
-## plugins whilst giving developers the ability move code around or
-## change underlying frameworks etc. It should not be used internaly
-## within ckan only by extensions. Functions should only be removed from
-## this file after reasonable depreciation notice has been given.
+## This file is intended to make functions/objects consistently
+## available to plugins whilst giving developers the ability move code
+## around or change underlying frameworks etc. It should not be used
+## internally within ckan only by extensions. Functions should only be
+## removed from this file after reasonable depreciation notice has
+## been given.
 
 import inspect
 import os
+import re
 
 import pylons
 import paste.deploy.converters as converters
 import webhelpers.html.tags
 
-import lib.base as base
+import ckan
+import ckan.lib.base as base
+import ckan.logic as logic
+import ckan.lib.cli as cli
+
 
 
 __all__ = [
-    ## Imported functions ##
+    ## Imported functions/objects ##
+    '_',                    # i18n translation
     'c',                    # template context
     'request',              # http request object
     'render',               # template render function
@@ -25,12 +32,22 @@
     'asint',                # converts an object to an integer
     'aslist',               # converts an object to a list
     'literal',              # stop tags in a string being escaped
+    'get_action',           # get logic action function
+    'check_access',         # check logic function authorisation
+    'ActionNotFound',       # action not found exception (ckan.logic.NotFound)
+    'NotAuthorized',        # action not authorized exception
+    'ValidationError',      # model update validation error
+    'CkanCommand',          # class for providing cli interfaces
 
-    ## Functions fully defined here ##
+    ## Fully defined in this file ##
     'add_template_directory',
     'add_public_directory',
+    'requires_ckan_version',
+    'check_ckan_version',
+    'CkanVersionException',
 ]
 
+_ = pylons.i18n._
 c = pylons.c
 request = pylons.request
 render = base.render
@@ -40,6 +57,13 @@
 aslist = converters.aslist
 literal = webhelpers.html.tags.literal
 
+get_action = logic.get_action
+check_access = logic.check_access
+ActionNotFound = logic.NotFound  ## Name change intentional
+NotAuthorized = logic.NotAuthorized
+ValidationError = logic.ValidationError
+
+CkanCommand = cli.CkanCommand
 
 # wrappers
 def render_snippet(template, data=None):
@@ -73,3 +97,38 @@ def _add_served_directory(config, relative_path, config_var):
             config[config_var] += ',' + absolute_path
         else:
             config[config_var] = absolute_path
+
+class CkanVersionException(Exception):
+    ''' Exception raised if required ckan version is not available. '''
+    pass
+
+
+def _version_str_2_list(v_str):
+    ''' conver a version string into a list of ints
+    eg 1.6.1b --> [1, 6, 1] '''
+    v_str = re.sub(r'[^0-9.]', '', v_str)
+    return [int(part) for part in v_str.split('.')]
+
+def check_ckan_version(min_version=None, max_version=None):
+    ''' Check that the ckan version is correct for the plugin. '''
+    current = _version_str_2_list(ckan.__version__)
+
+    if min_version:
+        min_required = _version_str_2_list(min_version)
+        if current < min_required:
+            return False
+    if max_version:
+        max_required = _version_str_2_list(max_version)
+        if current > max_required:
+            return False
+    return True
+
+def requires_ckan_version(min_version, max_version=None):
+    ''' Check that the ckan version is correct for the plugin. '''
+    if not check_ckan_version(min_version=min_version, max_version=max_version):
+        if not max_version:
+            error = 'Requires ckan version %s or higher' % min_version
+        else:
+            error = 'Requires ckan version  between %s and %s' % \
+                        (min_version, max_version)
+        raise CkanVersionException(error)


================================================================
  Commit: 9a20c1438585f0365cfe2a9a0df348071785f4c5
      https://github.com/okfn/ckan/commit/9a20c1438585f0365cfe2a9a0df348071785f4c5
  Author: Ross Jones <rossdjones at gmail.com>
  Date:   2012-04-20 (Fri, 20 Apr 2012)

  Changed paths:
    M ckan/lib/base.py
    M ckan/lib/cli.py
    M ckan/plugins/__init__.py
    M ckan/plugins/toolkit.py

  Log Message:
  -----------
  Merge branch 'master' into feature-2302-simple-theming


diff --git a/ckan/lib/base.py b/ckan/lib/base.py
index da9d69f..075029c 100644
--- a/ckan/lib/base.py
+++ b/ckan/lib/base.py
@@ -22,7 +22,7 @@
 
 import ckan.exceptions
 import ckan
-from ckan import authz
+import ckan.authz as authz
 from ckan.lib import i18n
 import ckan.lib.helpers as h
 from ckan.plugins import PluginImplementations, IGenshiStreamFilter
diff --git a/ckan/lib/cli.py b/ckan/lib/cli.py
index 5146aa9..6798cc4 100644
--- a/ckan/lib/cli.py
+++ b/ckan/lib/cli.py
@@ -84,7 +84,7 @@ class ManageDb(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         import ckan.lib.search as search
 
         cmd = self.args[0]
@@ -164,7 +164,7 @@ def _postgres_dump(self, filepath):
         self._run_cmd(pg_dump_cmd)
 
     def _postgres_load(self, filepath):
-        from ckan import model
+        import ckan.model as model
         assert not model.repo.are_tables_created(), "Tables already found. You need to 'db clean' before a load."
         pg_cmd = self._get_psql_cmd() + ' -f %s' % filepath
         self._run_cmd(pg_cmd)
@@ -194,7 +194,7 @@ def load(self, only_load=False):
         pg_cmd = self._postgres_load(dump_path)
         if not only_load:
             print 'Upgrading DB'
-            from ckan import model
+            import ckan.model as model
             model.repo.upgrade_db()
 
             print 'Rebuilding search index'
@@ -205,7 +205,7 @@ def load(self, only_load=False):
         print 'Done'
 
     def simple_dump_csv(self):
-        from ckan import model
+        import ckan.model as model
         if len(self.args) < 2:
             print 'Need csv file path'
             return
@@ -215,7 +215,7 @@ def simple_dump_csv(self):
         dumper.SimpleDumper().dump(dump_file, format='csv')
 
     def simple_dump_json(self):
-        from ckan import model
+        import ckan.model as model
         if len(self.args) < 2:
             print 'Need json file path'
             return
@@ -452,7 +452,7 @@ class Sysadmin(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         cmd = self.args[0] if self.args else None
         if cmd == None or cmd == 'list':
@@ -465,7 +465,7 @@ def command(self):
             print 'Command %s not recognized' % cmd
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Sysadmins:'
         sysadmins = model.Session.query(model.SystemRole).filter_by(role=model.Role.ADMIN)
         print 'count = %i' % sysadmins.count()
@@ -477,7 +477,7 @@ def list(self):
                                         user_or_authgroup.id)
 
     def add(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user to be made sysadmin.'
@@ -501,7 +501,7 @@ def add(self):
         print 'Added %s as sysadmin' % username
 
     def remove(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user to be made sysadmin.'
@@ -537,7 +537,7 @@ class UserCmd(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         if not self.args:
             self.list()
@@ -563,7 +563,7 @@ def get_user_str(self, user):
         return user_str
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Users:'
         users = model.Session.query(model.User)
         print 'count = %i' % users.count()
@@ -571,14 +571,14 @@ def list(self):
             print self.get_user_str(user)
 
     def show(self):
-        from ckan import model
+        import ckan.model as model
 
         username = self.args[0]
         user = model.User.get(unicode(username))
         print 'User: \n', user
 
     def setpass(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -593,7 +593,7 @@ def setpass(self):
         print 'Done'
 
     def search(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need user name query string.'
@@ -618,7 +618,7 @@ def password_prompt(cls):
         return password1
 
     def add(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -671,7 +671,7 @@ def add(self):
         print user
 
     def remove(self):
-        from ckan import model
+        import ckan.model as model
 
         if len(self.args) < 2:
             print 'Need name of the user.'
@@ -704,7 +704,7 @@ class DatasetCmd(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         if not self.args:
             print self.usage
@@ -722,7 +722,7 @@ def command(self):
                 self.show(self.args[0])
 
     def list(self):
-        from ckan import model
+        import ckan.model as model
         print 'Datasets:'
         datasets = model.Session.query(model.Package)
         print 'count = %i' % datasets.count()
@@ -732,19 +732,19 @@ def list(self):
             print '%s %s %s' % (dataset.id, dataset.name, state)
 
     def _get_dataset(self, dataset_ref):
-        from ckan import model
+        import ckan.model as model
         dataset = model.Package.get(unicode(dataset_ref))
         assert dataset, 'Could not find dataset matching reference: %r' % dataset_ref
         return dataset
 
     def show(self, dataset_ref):
-        from ckan import model
         import pprint
         dataset = self._get_dataset(dataset_ref)
         pprint.pprint(dataset.as_dict())
 
     def delete(self, dataset_ref):
-        from ckan import model, plugins
+        from ckan import plugins
+        import ckan.model as model
         dataset = self._get_dataset(dataset_ref)
         old_state = dataset.state
 
@@ -756,7 +756,8 @@ def delete(self, dataset_ref):
         print '%s %s -> %s' % (dataset.name, old_state, dataset.state)
 
     def purge(self, dataset_ref):
-        from ckan import model, plugins
+        from ckan import plugins
+        import ckan.model as model
         dataset = self._get_dataset(dataset_ref)
         name = dataset.name
 
@@ -802,7 +803,7 @@ def run_(self):
 
     def view(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         from kombu.transport.sqlalchemy.models import Message
         q = model.Session.query(Message)
         q_visible = q.filter_by(visible=True)
@@ -816,7 +817,7 @@ def view(self):
 
     def clean(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
         import pprint
         tasks_initially = model.Session.execute("select * from kombu_message").rowcount
         if not tasks_initially:
@@ -847,7 +848,7 @@ class Ratings(CkanCommand):
 
     def command(self):
         self._load_config()
-        from ckan import model
+        import ckan.model as model
 
         cmd = self.args[0]
         if cmd == 'count':
@@ -860,14 +861,14 @@ def command(self):
             print 'Command %s not recognized' % cmd
 
     def count(self):
-        from ckan import model
+        import ckan.model as model
         q = model.Session.query(model.Rating)
         print "%i ratings" % q.count()
         q = q.filter(model.Rating.user_id == None)
         print "of which %i are anonymous ratings" % q.count()
 
     def clean(self, user_ratings=True):
-        from ckan import model
+        import ckan.model as model
         q = model.Session.query(model.Rating)
         print "%i ratings" % q.count()
         if not user_ratings:
diff --git a/ckan/plugins/__init__.py b/ckan/plugins/__init__.py
index fcb6055..e11e909 100644
--- a/ckan/plugins/__init__.py
+++ b/ckan/plugins/__init__.py
@@ -1,3 +1,19 @@
 from ckan.plugins.core import *
 from ckan.plugins.interfaces import *
-import toolkit
+
+
+class _Toolkit(object):
+    ''' This object allows us to avoid circular imports while making
+    functions/objects available to plugins. '''
+
+    def __init__(self):
+        self.toolkit = None
+
+    def __getattr__(self, name):
+        if not self.toolkit:
+            import toolkit
+            self.toolkit = toolkit
+        return getattr(self.toolkit, name)
+
+toolkit = _Toolkit()
+del _Toolkit
diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
index 64a80d7..e635c43 100644
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -1,21 +1,28 @@
-## This file is intended to make functions consistently available to
-## plugins whilst giving developers the ability move code around or
-## change underlying frameworks etc. It should not be used internaly
-## within ckan only by extensions. Functions should only be removed from
-## this file after reasonable depreciation notice has been given.
+## This file is intended to make functions/objects consistently
+## available to plugins whilst giving developers the ability move code
+## around or change underlying frameworks etc. It should not be used
+## internally within ckan only by extensions. Functions should only be
+## removed from this file after reasonable depreciation notice has
+## been given.
 
 import inspect
 import os
+import re
 
 import pylons
 import paste.deploy.converters as converters
 import webhelpers.html.tags
 
-import lib.base as base
+import ckan
+import ckan.lib.base as base
+import ckan.logic as logic
+import ckan.lib.cli as cli
+
 
 
 __all__ = [
-    ## Imported functions ##
+    ## Imported functions/objects ##
+    '_',                    # i18n translation
     'c',                    # template context
     'request',              # http request object
     'render',               # template render function
@@ -25,12 +32,22 @@
     'asint',                # converts an object to an integer
     'aslist',               # converts an object to a list
     'literal',              # stop tags in a string being escaped
+    'get_action',           # get logic action function
+    'check_access',         # check logic function authorisation
+    'ActionNotFound',       # action not found exception (ckan.logic.NotFound)
+    'NotAuthorized',        # action not authorized exception
+    'ValidationError',      # model update validation error
+    'CkanCommand',          # class for providing cli interfaces
 
-    ## Functions fully defined here ##
+    ## Fully defined in this file ##
     'add_template_directory',
     'add_public_directory',
+    'requires_ckan_version',
+    'check_ckan_version',
+    'CkanVersionException',
 ]
 
+_ = pylons.i18n._
 c = pylons.c
 request = pylons.request
 render = base.render
@@ -40,6 +57,13 @@
 aslist = converters.aslist
 literal = webhelpers.html.tags.literal
 
+get_action = logic.get_action
+check_access = logic.check_access
+ActionNotFound = logic.NotFound  ## Name change intentional
+NotAuthorized = logic.NotAuthorized
+ValidationError = logic.ValidationError
+
+CkanCommand = cli.CkanCommand
 
 # wrappers
 def render_snippet(template, data=None):
@@ -73,3 +97,38 @@ def _add_served_directory(config, relative_path, config_var):
             config[config_var] += ',' + absolute_path
         else:
             config[config_var] = absolute_path
+
+class CkanVersionException(Exception):
+    ''' Exception raised if required ckan version is not available. '''
+    pass
+
+
+def _version_str_2_list(v_str):
+    ''' conver a version string into a list of ints
+    eg 1.6.1b --> [1, 6, 1] '''
+    v_str = re.sub(r'[^0-9.]', '', v_str)
+    return [int(part) for part in v_str.split('.')]
+
+def check_ckan_version(min_version=None, max_version=None):
+    ''' Check that the ckan version is correct for the plugin. '''
+    current = _version_str_2_list(ckan.__version__)
+
+    if min_version:
+        min_required = _version_str_2_list(min_version)
+        if current < min_required:
+            return False
+    if max_version:
+        max_required = _version_str_2_list(max_version)
+        if current > max_required:
+            return False
+    return True
+
+def requires_ckan_version(min_version, max_version=None):
+    ''' Check that the ckan version is correct for the plugin. '''
+    if not check_ckan_version(min_version=min_version, max_version=max_version):
+        if not max_version:
+            error = 'Requires ckan version %s or higher' % min_version
+        else:
+            error = 'Requires ckan version  between %s and %s' % \
+                        (min_version, max_version)
+        raise CkanVersionException(error)


================================================================
  Commit: e1c747a7ff9e4f494bbbdd221614e82193bad87c
      https://github.com/okfn/ckan/commit/e1c747a7ff9e4f494bbbdd221614e82193bad87c
  Author: Ross Jones <rossdjones at gmail.com>
  Date:   2012-04-20 (Fri, 20 Apr 2012)

  Changed paths:
    M ckan/controllers/settings.py
    A ckan/migration/versions/054_add_setting_table.py
    A ckan/model/settings.py
    M ckan/templates/settings/index.html

  Log Message:
  -----------
  [2302] Added model and migration


diff --git a/ckan/controllers/settings.py b/ckan/controllers/settings.py
index b84fc7b..7ddaf07 100644
--- a/ckan/controllers/settings.py
+++ b/ckan/controllers/settings.py
@@ -14,6 +14,12 @@ def _ensure_hex(key, data, errors, context):
     value = data.get(key)
 
     def is_hex(value):
+        v = ''.join(c for c in value if c != '#')
+        try:
+            x = int(v, 16)
+        except:
+            return False
+        data[key] = v
         return True
 
     if not value or not is_hex(value):
@@ -22,7 +28,6 @@ def is_hex(value):
         raise dictfunc.StopOnError
 
 class SettingsController(BaseController):
-    repo = model.repo
 
     def __before__(self, action, **env):
         BaseController.__before__(self, action, **env)
diff --git a/ckan/migration/versions/054_add_setting_table.py b/ckan/migration/versions/054_add_setting_table.py
new file mode 100644
index 0000000..d8e3cf8
--- /dev/null
+++ b/ckan/migration/versions/054_add_setting_table.py
@@ -0,0 +1,16 @@
+from sqlalchemy import *
+from migrate import *
+
+def upgrade(migrate_engine):
+    metadata = MetaData()
+    metadata.bind = migrate_engine
+    migrate_engine.execute('''
+        CREATE TABLE setting (
+        	id text NOT NULL,
+        	key text,
+        	value text
+        );
+
+        ALTER TABLE setting
+	    ADD CONSTRAINT setting_pkey PRIMARY KEY (id);
+    ''')
\ No newline at end of file
diff --git a/ckan/model/settings.py b/ckan/model/settings.py
new file mode 100644
index 0000000..2e71bad
--- /dev/null
+++ b/ckan/model/settings.py
@@ -0,0 +1,20 @@
+import datetime
+
+from meta import *
+from core import *
+from package import *
+from types import make_uuid
+from user import User
+
+__all__ = ['Setting']
+
+setting_table = Table('setting', metadata,
+                     Column('id', UnicodeText, primary_key=True, default=make_uuid),
+                     Column('key', UnicodeText),
+                     Column('value', UnicodeText),
+                     )
+
+class Setting(DomainObject):
+    pass
+
+mapper(Setting, setting_table)
diff --git a/ckan/templates/settings/index.html b/ckan/templates/settings/index.html
index f96923b..225b8d1 100644
--- a/ckan/templates/settings/index.html
+++ b/ckan/templates/settings/index.html
@@ -20,7 +20,6 @@
   </py:match>
 
   <div py:match="content">
-
     <div class="alert alert-error" py:if="error_summary">
         The form contains invalid entries please correct them and submit again
     </div>
@@ -81,7 +80,7 @@
                   <label class="control-label" for="input01">Footer background</label>
                   <div class="controls">
                       <div class="input-prepend">
-                      <span class='add-on'>#</span><input type="text" class="input-xlarge" id="css_footer" name="css_footer"  value="${data.get('css_footer','')}"/>
+                          <span class='add-on'>#</span><input type="text" class="input-xlarge" id="css_footer" name="css_footer"  value="${data.get('css_footer','')}"/>
                       </div>
                       <span class="help-inline" py:if="not errors.get('css_footer')">Hexadecimal value for the footer</span>
                       <span class="help-inline" py:if="errors.get('css_footer')">${errors['css_footer']}</span>


================================================================
Compare: https://github.com/okfn/ckan/compare/f9e151d...e1c747a


More information about the ckan-changes mailing list