from django.core.urlresolvers import reverse from rapid.models import Application from django.conf.urls import url import inspect import logging from os import path def _split_all_path(file_name): file_name = path.splitdrive(file_name)[1] p = 'a' while p: file_name, p = path.split(file_name) yield p def _caller_urls_module(): st = inspect.stack() for rec in st: file_name = rec[1] segments = list(_split_all_path(file_name)) i = 0 for i in xrange(0, len(segments) - 2): p = path.normcase(segments[i]) p = path.splitext(p)[0] if p == "urls": return segments[i+1] return None def _model_name(model): if hasattr(model, "url_name"): return model.url_name return model._meta.verbose_name class MenuEntry: """ The data that goes on a menu item. Model, permissions and url """ def __init__(self, model, permission_set, name=None): self.name = name self.model = model self.permission_set = permission_set def get_url(self, instance=None): ats = [(x, getattr(instance, x)) for x in self.action.query_parameters] return reverse(self.url, kwargs=dict(ats)) def __unicode__(self): return u"Menu entry: " + self.name + " -> " + unicode(self.action) def __str__(self): return str(unicode(self)) class ModuleEntry: """ Module data used at menu construction """ def __init__(self, python_name, menu_name, base_url): self.python_name = python_name self.menu_name = menu_name self.base_url = url self.models = set() class Action: """ An action to be done over a model. Default actions are "list", "view", "edit", "add", "delete", and "select", those are defined at the views module. """ def __init__(self, name, url_parameters, query_parameters, view_factory, verbose_name=None, icon=None, visibility=None): self.name = name self.url_parameters = url_parameters self.query_parameters = query_parameters self.view_factory = view_factory self.verbose_name = verbose_name if verbose_name else name self.icon = icon self.visibility = self.Visibility.details if visibility is None else visibility def __unicode__(self): return u"Action: " + self.name def __str__(self): return str(unicode(self)) class Visibility: hidden = 1 details = 2 list = 3 class _Registry: """ Registry of URLs, models and views present on the menu The registry must: -- for the menu creation list registered modules list registered models list menu entries by module list actions by model reverse url of action and model -- for crud generation query if model is registered list actions by model reverse url of action and model """ def __init__(self): """ Populates the menu registry """ self._modules = {} # ModuleEntry by python_name self._models = {} # {'action name': MenuEntry} by model class for a in Application.objects.filter(enabled=True): m = ModuleEntry(a.python_name, a.name, a.url) self._modules[a.python_name] = m def register_action(self, action, entry, **kwargs): """ Registers an action at this registry, so it will appear on the menu and can be reversed at the cruds. :param action: Action type :param entry: The menu entry where it will appear :param kwargs: Arguments (besides model) that'll be passed to the view_factory of the action :return: A Django URL pattern that should be added to the patterns of a urls.py module """ from django.contrib.auth.models import User module_name = _caller_urls_module() model = entry.model if not module_name: raise Exception("Unidentified python module registering " + str(model)) if not registry._modules.has_key(module_name): logging.error("Module " + module_name + " is not set-up for registering cruds") return None module_entry = registry._modules[module_name] module_entry.models.add(model) if not entry.name: entry.name = _model_name(model) entry.action = action entry_url = module_entry.menu_name + '_' + entry.name + '_' + action.name entry.url = entry_url model_actions = self._models.get(model, {}) if model_actions.has_key(action.name): raise Exception("Action " + action.name + " already registered for model " + str(model)) model_actions[action.name] = entry self._models[model] = model_actions return url(r'^/%s/%s/%s$' % (entry.name, action.name, action.url_parameters), action.view_factory(model=entry.model, **kwargs), name=entry_url) def get_url_of_action(self, model, action_name, **kwargs): acts = self._models.get(model) if acts and acts.has_key(action_name): return reverse(acts[action_name].url, kwargs=kwargs) def modules(self): return self._modules.values() def entry_names(self): return [x.menu_name for x in self._modules.values()] def is_controlled(self, model): return self._models.has_key(model) def model_entry(self, model): return self._models.get(model) def module_models(self, module): return self._modules[module].models registry = _Registry()