X-Git-Url: http://git.ithinksw.org/philo.git/blobdiff_plain/c5ec4492ee19b3c624c654d29f04938e748510cd..90bcb8a34f4fcfc52b21da7c82430f06524318b8:/contrib/gilbert/sites.py diff --git a/contrib/gilbert/sites.py b/contrib/gilbert/sites.py index 76c1d9a..e181fdf 100644 --- a/contrib/gilbert/sites.py +++ b/contrib/gilbert/sites.py @@ -1,171 +1,242 @@ -from django.contrib.admin.sites import AdminSite -from django.contrib.auth import authenticate, login, logout -from django.conf.urls.defaults import url, patterns +from django.conf.urls.defaults import url, patterns, include from django.core.urlresolvers import reverse from django.shortcuts import render_to_response from django.conf import settings from django.utils import simplejson as json from django.utils.datastructures import SortedDict -from django.http import HttpResponse + from django.db.models.base import ModelBase from philo.utils import fattr -from philo.contrib.gilbert.options import GilbertModelAdmin, GilbertPlugin, GilbertClass -from philo.contrib.gilbert.exceptions import AlreadyRegistered, NotRegistered +from .exceptions import AlreadyRegistered, NotRegistered from django.forms.models import model_to_dict import sys -from traceback import format_tb from inspect import getargspec -from philo.contrib.gilbert.utils import is_gilbert_plugin, is_gilbert_class, is_gilbert_method, gilbert_method, call_gilbert_method +from django.views.decorators.cache import never_cache +from . import __version__ as gilbert_version +import staticmedia +import os +import datetime +from .extdirect import ExtAction, ExtRouter + +from functools import partial +from django.http import HttpResponse, HttpResponseRedirect +from django.template import RequestContext +from django.core.context_processors import csrf +from django.utils.functional import update_wrapper +from django.contrib.auth import authenticate, login +from .plugins.models import Models, ModelAdmin +from .plugins.auth import Auth __all__ = ('GilbertSite', 'site') -class GilbertSitePlugin(GilbertPlugin): - class auth(GilbertClass): - @gilbert_method(restricted=False) - def login(self, request, username, password): - user = authenticate(username=username, password=password) - if user is not None and user.is_active: - login(request, user) - return True - else: - return False - - @gilbert_method(restricted=False) - def logout(self, request): - logout(request) - return True - - @gilbert_method - def passwd(self, request, current_password, new_password, new_password_confirm): - user = request.user - if user.check_password(current_password) and (new_password == new_password_confirm): - user.set_password(new_password) - user.save() - return True - return False +class CoreRouter(ExtRouter): + def __init__(self, site): + self.site = site + self._actions = {} + + @property + def namespace(self): + return 'Gilbert.api.plugins' + + @property + def url(self): + return reverse('%s:router' % self.site.namespace, current_app=self.site.app_name) + + @property + def type(self): + return 'remoting' + + @property + def actions(self): + return self._actions + + @property + def plugins(self): + return list(action.obj for action in self._actions.itervalues()) + + def register_plugin(self, plugin): + action = ExtAction(plugin) + self._actions[action.name] = action + + +class ModelRouter(ExtRouter): + def __init__(self, site, app_label): + self.site = site + self.app_label = app_label + self._actions = {} + + @property + def namespace(self): + return 'Gilbert.api.models.%s' % self.app_label + + @property + def url(self): + return reverse('%s:model_router' % self.site.namespace, current_app=self.site.app_name, kwargs={'app_label': self.app_label}) + + @property + def type(self): + return 'remoting' + + @property + def actions(self): + return self._actions + + @property + def models(self): + return dict((name, action.obj) for name, action in self._actions.iteritems()) + + def register_admin(self, name, admin): + action = ExtAction(admin) + action.name = name + self._actions[action.name] = action class GilbertSite(object): - def __init__(self, namespace='gilbert', app_name='gilbert', title='Gilbert'): + version = gilbert_version + + def __init__(self, namespace='gilbert', app_name='gilbert', title=None): self.namespace = namespace self.app_name = app_name - self.title = title - self.core_api = GilbertSitePlugin(self) - self.model_registry = SortedDict() - self.plugin_registry = SortedDict() + if title is None: + self.title = getattr(settings, 'GILBERT_TITLE', 'Gilbert') + else: + self.title = title + + self.core_router = CoreRouter(self) + self.model_routers = SortedDict() + + self.register_plugin(Models) + self.register_plugin(Auth) def register_plugin(self, plugin): - if is_gilbert_plugin(plugin): - if plugin.gilbert_plugin_name in self.plugin_registry: - raise AlreadyRegistered('A plugin named \'%s\' is already registered' % plugin.gilbert_plugin_name) - self.plugin_registry[plugin.gilbert_plugin_name] = plugin(self) - else: - raise ValueError('register_plugin must be provided a valid plugin class or instance') + self.core_router.register_plugin(plugin(self)) - def register_model(self, model_or_iterable, admin_class=GilbertModelAdmin, **admin_attrs): + def register_model(self, model_or_iterable, admin_class=ModelAdmin, **admin_attrs): if isinstance(model_or_iterable, ModelBase): model_or_iterable = [model_or_iterable] for model in model_or_iterable: - if model._meta.app_label not in self.model_registry: - self.model_registry[model._meta.app_label] = SortedDict() - if model._meta.object_name in self.model_registry[model._meta.app_label]: - raise AlreadyRegistered('The model %s is already registered' % model.__name__) + app_label = model._meta.app_label + name = model._meta.object_name + + if app_label not in self.model_routers: + self.model_routers[app_label] = ModelRouter(self, app_label) + router = self.model_routers[app_label] + if admin_attrs: admin_attrs['__module__'] = __name__ admin_class = type('%sAdmin' % model.__name__, (admin_class,), admin_attrs) - self.model_registry[model._meta.app_label][model._meta.object_name] = admin_class(self, model) + + router.register_admin(name, admin_class(self, model)) def has_permission(self, request): return request.user.is_active and request.user.is_staff + def protected_view(self, view, login_page=True, cacheable=False): + def inner(request, *args, **kwargs): + if not self.has_permission(request): + if login_page: + return self.login(request) + else: + return HttpResponse(status=403) + return view(request, *args, **kwargs) + if not cacheable: + inner = never_cache(inner) + return update_wrapper(inner, view) + @property def urls(self): - return (patterns('', - url(r'^$', self.index, name='index'), - url(r'^css.css$', self.css, name='css'), - url(r'^api.js$', self.api, name='api'), - url(r'^router/?$', self.router, name='router'), - url(r'^models/(?P\w+)/?$', self.router, name='models'), - url(r'^plugins/(?P\w+)/?$', self.router, name='plugins'), - url(r'^login$', self.router, name='login'), - ), self.app_name, self.namespace) - - def request_context(self, request, extra_context=None): - from django.template import RequestContext - context = RequestContext(request, current_app=self.namespace) - context.update(extra_context or {}) - context.update({'gilbert': self, 'user': request.user, 'logged_in': self.has_permission(request)}) - return context - - def index(self, request, extra_context=None): - return render_to_response('gilbert/index.html', context_instance=self.request_context(request, extra_context)) - - def css(self, request, extra_context=None): - return render_to_response('gilbert/css.css', context_instance=self.request_context(request, extra_context), mimetype='text/css') - - def api(self, request, extra_context=None): - return render_to_response('gilbert/api.js', context_instance=self.request_context(request, extra_context), mimetype='text/javascript') - - def router(self, request, app_label=None, plugin_name=None, extra_context=None): - submitted_form = False - if request.META['CONTENT_TYPE'].startswith('application/x-www-form-urlencoded'): - submitted_form = True - - if submitted_form: - post_dict = dict(request.POST) - ext_request = { - 'action': post_dict.pop('extAction'), - 'method': post_dict.pop('extMethod'), - 'type': post_dict.pop('extType'), - 'tid': post_dict.pop('extTID'), - 'data': None, - 'kwdata': post_dict, - } - if 'extUpload' in request.POST: - ext_request['upload'] = request.POST['extUpload'] - else: - ext_request = json.loads(request.raw_post_data) - ext_request['kwdata'] = None + urlpatterns = patterns('', + url(r'^$', self.protected_view(self.index), name='index'), + url(r'^api.js$', self.protected_view(self.api, login_page=False), name='api'), + url(r'^icons.css$', self.protected_view(self.icons, login_page=False), name='icons'), + url(r'^router$', self.protected_view(self.router, login_page=False), name='router'), + url(r'^router/models/(?P\w+)$', self.protected_view(self.router, login_page=False), name='model_router'), + ) + return (urlpatterns, self.app_name, self.namespace) + + def login(self, request): + context = { + 'gilbert': self, + 'form_url': request.get_full_path(), + } + context.update(csrf(request)) - try: - gilbert_class = None - - if app_label is not None: - try: - gilbert_class = self.model_registry[app_label][ext_request['action']] - except KeyError: - raise NotImplementedError('A model named \'%s\' has not been registered' % ext_request['action']) - elif plugin_name is not None: - try: - gilbert_plugin = self.plugin_registry[plugin_name] - except KeyError: - raise NotImplementedError('A plugin named \'%s\' has not been registered' % plugin_name) - try: - gilbert_class = gilbert_plugin.gilbert_plugin_classes[ext_request['action']] - except KeyError: - raise NotImplementedError('The plugin named \'%s\' does not provide a class named \'%s\'' % (plugin_name, ext_request['action'])) + if request.POST: + if request.session.test_cookie_worked(): + request.session.delete_test_cookie() + username = request.POST.get('username', None) + password = request.POST.get('password', None) + user = authenticate(username=username, password=password) + if user is not None: + if user.is_active and user.is_staff: + login(request, user) + return HttpResponseRedirect(request.get_full_path()) + else: + context.update({ + 'error_message_short': 'Not staff', + 'error_message': 'You do not have access to this page.', + }) + else: + context.update({ + 'error_message_short': 'Invalid credentials', + 'error_message': 'Unable to authenticate using the provided credentials. Please try again.', + }) else: - try: - gilbert_class = self.core_api.gilbert_plugin_classes[ext_request['action']] - except KeyError: - raise NotImplementedError('Gilbert does not provide a class named \'%s\'' % ext_request['action']) - - try: - method = gilbert_class.gilbert_class_methods[ext_request['method']] - except KeyError: - raise NotImplementedError('The class named \'%s\' does not implement a method named \'%\'' % (gilbert_class.gilbert_class_name, ext_request['method'])) - if method.gilbert_method_restricted and not self.has_permission(request): - raise NotImplementedError('The method named \'%s\' is not available' % method.gilbert_method_name) - response = {'type': 'rpc', 'tid': ext_request['tid'], 'action': ext_request['action'], 'method': ext_request['method'], 'result': call_gilbert_method(method, gilbert_class, request, *(ext_request['data'] or []), **(ext_request['kwdata'] or {}))} - except: - exc_type, exc_value, exc_traceback = sys.exc_info() - response = {'type': 'exception', 'tid': ext_request['tid'], 'message': ('%s: %s' % (exc_type, exc_value)), 'where': format_tb(exc_traceback)[0]} + context.update({ + 'error_message_short': 'Cookies disabled', + 'error_message': 'Please enable cookies, reload this page, and try logging in again.', + }) + + request.session.set_test_cookie() + return render_to_response('gilbert/login.html', context, context_instance=RequestContext(request)) + + def index(self, request): + return render_to_response('gilbert/index.html', { + 'gilbert': self, + 'plugins': self.core_router.plugins # needed as the template language will not traverse callables + }, context_instance=RequestContext(request)) + + def api(self, request): + providers = [] + model_registry = {} + + for app_label, router in self.model_routers.items(): + if request.user.has_module_perms(app_label): + providers.append(router.spec) + model_registry[app_label] = dict((model_name, admin) for model_name, admin in router.models.items() if admin.has_permission(request)) + + providers.append(self.core_router.spec) - if submitted_form: - return HttpResponse(('' % json.dumps(response))) - return HttpResponse(json.dumps(response), content_type=('application/json; charset=%s' % settings.DEFAULT_CHARSET)) + context = { + 'gilbert': self, + 'providers': [json.dumps(provider, separators=(',', ':')) for provider in providers], + 'model_registry': model_registry, + } + context.update(csrf(request)) + + return render_to_response('gilbert/api.js', context, mimetype='text/javascript') + + def icons(self, request): + icon_names = [] + + for plugin in self.core_router.plugins: + icon_names.extend(plugin.icon_names) + + for router in self.model_routers.values(): + for admin in router.models.values(): + icon_names.extend(admin.icon_names) + + return render_to_response('gilbert/icons.css', { + 'icon_names': set(icon_names), + }, mimetype='text/css') + + def router(self, request, app_label=None, extra_context=None): + if app_label is None: + return self.core_router.render_to_response(request) + else: + return self.model_routers[app_label].render_to_response(request) site = GilbertSite() \ No newline at end of file