123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- 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()
|