-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 import settings
+from django.contrib.auth import authenticate, login
+from django.core.context_processors import csrf
+from django.conf.urls.defaults import url, patterns, include
from django.core.urlresolvers import reverse
+from django.db.models.base import ModelBase
+from django.forms.models import model_to_dict
+from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
-from django.conf import settings
+from django.template import RequestContext
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 django.views.decorators.cache import never_cache
from philo.utils import fattr
-from philo.contrib.gilbert.options import GilbertModelAdmin, GilbertPlugin, GilbertClass
-from philo.contrib.gilbert.exceptions import AlreadyRegistered, NotRegistered
-from django.forms.models import model_to_dict
-import sys
-from traceback import format_tb
+from . import __version__ as gilbert_version
+from .exceptions import AlreadyRegistered, NotRegistered
+from .extdirect import ExtAction, ExtRouter
+from .plugins.auth import Auth
+from .plugins.models import Models, ModelAdmin
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 functools import partial, update_wrapper
+import sys, os, datetime
+
__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<app_label>\w+)/?$', self.router, name='models'),
- url(r'^plugins/(?P<plugin_name>\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<app_label>\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(('<html><body><textarea>%s</textarea></body></html>' % 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),
+ 'STATIC_URL': settings.STATIC_URL
+ }, 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