app/soc/modules/core.py
changeset 2333 221482a54238
child 2359 d4de17ab9a1f
equal deleted inserted replaced
2332:2a6071255146 2333:221482a54238
       
     1 # Copyright 2009 the Melange authors.
       
     2 #
       
     3 # Licensed under the Apache License, Version 2.0 (the "License");
       
     4 # you may not use this file except in compliance with the License.
       
     5 # You may obtain a copy of the License at
       
     6 #
       
     7 #     http://www.apache.org/licenses/LICENSE-2.0
       
     8 #
       
     9 # Unless required by applicable law or agreed to in writing, software
       
    10 # distributed under the License is distributed on an "AS IS" BASIS,
       
    11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    12 # See the License for the specific language governing permissions and
       
    13 # limitations under the License.
       
    14 
       
    15 """The Melange Core module.
       
    16 """
       
    17 
       
    18 __authors__ = [
       
    19   '"Sverre Rabbelier" <sverre@rabbelier.nl>',
       
    20   '"Lennard de Rijk" <ljvderijk@gmail.com>',
       
    21   ]
       
    22 
       
    23 
       
    24 from django.conf.urls import defaults
       
    25 
       
    26 import settings
       
    27 import soc.cache.sidebar
       
    28 
       
    29 
       
    30 class Error(Exception):
       
    31   """Error class for the callback module.
       
    32   """
       
    33 
       
    34   pass
       
    35 
       
    36 
       
    37 class APIVersionMismatch(Error):
       
    38   """Error raised when API version mismatches.
       
    39   """
       
    40 
       
    41   MISMATCH_MSG_FMT = "API mismatch, expected '%d', got '%d'."
       
    42 
       
    43   def __init__(self, expected, actual):
       
    44     """Instantiates a new exception with a customized message.
       
    45     """
       
    46 
       
    47     msg = self.MISMATCH_MSG_FMT % (expected, actual)
       
    48     super(APIVersionMismatch, self).__init__(msg)
       
    49 
       
    50 
       
    51 class MissingService(Error):
       
    52   """Error raised when a required service is missing.
       
    53   """
       
    54 
       
    55   MISSING_SERVICE_FMT = "Required service '%s' is not registered, known: %s"
       
    56 
       
    57   def __init__(self, service, services):
       
    58     """Instantiates a new exception with a customized message.
       
    59     """
       
    60 
       
    61     msg = self.MISSING_SERVICE_FMT % (service, services)
       
    62     super(MissingService, self).__init__(msg)
       
    63 
       
    64 
       
    65 class NonUniqueService(Error):
       
    66   """Error raised when a required service is missing.
       
    67   """
       
    68 
       
    69   NON_UNIQUE_SERVICE_FMT = "Unique service '%s' called a second time, known: %s."
       
    70 
       
    71   def __init__(self, service, services):
       
    72     """Instantiates a new exception with a customized message.
       
    73     """
       
    74 
       
    75     msg = self.NON_UNIQUE_SERVICE_FMT % (service, services)
       
    76     super(NonUniqueService, self).__init__(msg)
       
    77 
       
    78 
       
    79 class Core(object):
       
    80   """The core handler that controls the Melange API.
       
    81   """
       
    82 
       
    83   def __init__(self):
       
    84     """Creates a new instance of the Core.
       
    85     """
       
    86 
       
    87     self.API_VERSION = 1
       
    88 
       
    89     self.registered_callbacks = []
       
    90     self.capability = []
       
    91     self.services = []
       
    92 
       
    93     self.sitemap = []
       
    94     self.sidebar = []
       
    95 
       
    96   ##
       
    97   ## internal
       
    98   ##
       
    99 
       
   100   def getService(self, callback, service):
       
   101    """Retrieves the specified service from the callback if supported.
       
   102 
       
   103    Args:
       
   104      callback: the callback to retrieve the capability from
       
   105      service: the service to retrieve
       
   106    """
       
   107 
       
   108    if not hasattr(callback, service):
       
   109      return False
       
   110 
       
   111    func = getattr(callback, service)
       
   112 
       
   113    if not callable(func):
       
   114      return False
       
   115 
       
   116    return func
       
   117 
       
   118   ##
       
   119   ## Core code
       
   120   ##
       
   121 
       
   122   def getPatterns(self):
       
   123     """Returns the Django patterns for this site.
       
   124     """
       
   125 
       
   126     self.callService('registerWithSitemap', True)
       
   127     return defaults.patterns(None, *self.sitemap)
       
   128 
       
   129   @soc.cache.sidebar.cache
       
   130   def getSidebar(self, id, user):
       
   131     """Constructs a sidebar for the current user.
       
   132     """
       
   133 
       
   134     self.callService('registerWithSidebar', True)
       
   135 
       
   136     sidebar = []
       
   137 
       
   138     for i in self.sidebar:
       
   139       menus = i(id, user)
       
   140 
       
   141       for menu in (menus if menus else []):
       
   142         sidebar.append(menu)
       
   143 
       
   144     return sorted(sidebar, key=lambda x: x.get('group'))
       
   145 
       
   146   def callService(self, service, unique, *args, **kwargs):
       
   147     """Calls the specified service on all callbacks.
       
   148     """
       
   149 
       
   150     if unique and (service in self.services):
       
   151       return
       
   152 
       
   153     results = []
       
   154 
       
   155     for callback in self.registered_callbacks:
       
   156       func = self.getService(callback, service)
       
   157       if not func:
       
   158         continue
       
   159 
       
   160       result = func(*args, **kwargs)
       
   161       results.append(result)
       
   162 
       
   163     self.services.append(service)
       
   164     return results
       
   165 
       
   166   def registerModuleCallbacks(self):
       
   167     """Retrieves all callbacks for the modules of this site.
       
   168 
       
   169     Callbacks for modules without a version number or the wrong API_VERSION
       
   170     number are dropped.  They won't be called.
       
   171     """
       
   172 
       
   173     fmt = settings.MODULE_FMT
       
   174     modules = ['soc_core'] + settings.MODULES
       
   175     modules = [__import__(fmt % i, fromlist=['']) for i in modules]
       
   176 
       
   177     for callback_class in [i.getCallback() for i in modules]:
       
   178       if callback_class.API_VERSION != self.API_VERSION:
       
   179         raise callback.APIVersionMismatch(self.API_VERSION,
       
   180                                           callback_class.API_VERSION)
       
   181 
       
   182 
       
   183       callback = callback_class(self)
       
   184       self.registered_callbacks.append(callback)
       
   185 
       
   186     return True
       
   187 
       
   188   ##
       
   189   ## Module code
       
   190   ##
       
   191 
       
   192   def registerCapability(self, capability):
       
   193     """Registers the specified capability.
       
   194     """
       
   195 
       
   196     self.capabilities.append(capability)
       
   197 
       
   198   def requireCapability(self, capability):
       
   199     """Requires that the specified capability is present.
       
   200     """
       
   201 
       
   202     if capability in self.capabilities:
       
   203       return True
       
   204 
       
   205     raise MissingCapability(capability, self.capability)
       
   206 
       
   207   def requireService(self, service):
       
   208     """Requires that the specified service has been called.
       
   209     """
       
   210 
       
   211     if service in self.services:
       
   212       return True
       
   213 
       
   214     raise MissingService(service, self.services)
       
   215 
       
   216   def requireUniqueService(self, service):
       
   217     """Requires that the specified service is called exactly once.
       
   218     """
       
   219 
       
   220     if service not in self.services:
       
   221       return True
       
   222 
       
   223     raise NonUniqueService(service, self.services)
       
   224 
       
   225   def registerSitemapEntry(self, entries):
       
   226     """Registers the specified entries with the sitemap.
       
   227     """
       
   228 
       
   229     self.sitemap.extend(entries)
       
   230 
       
   231   def registerSidebarEntry(self, entry):
       
   232     """Registers the specified entry with the sidebar.
       
   233     """
       
   234 
       
   235     self.sidebar.append(entry)