1 from django.contrib.admin.sites import AdminSite
2 from django.contrib.auth import authenticate, login, logout
3 from django.conf.urls.defaults import url, patterns
4 from django.core.urlresolvers import reverse
5 from django.shortcuts import render_to_response
6 from django.conf import settings
7 from django.utils import simplejson as json
8 from django.utils.datastructures import SortedDict
9 from django.http import HttpResponse
10 from django.db.models.base import ModelBase
11 from philo.utils import fattr
12 from philo.contrib.gilbert.options import GilbertModelAdmin, GilbertPlugin, GilbertClass
13 from philo.contrib.gilbert.exceptions import AlreadyRegistered, NotRegistered
14 from django.forms.models import model_to_dict
16 from traceback import format_tb
17 from inspect import getargspec
18 from philo.contrib.gilbert.utils import is_gilbert_plugin, is_gilbert_class, is_gilbert_method, gilbert_method, call_gilbert_method
21 __all__ = ('GilbertSite', 'site')
24 class GilbertSitePlugin(GilbertPlugin):
25 class auth(GilbertClass):
26 @gilbert_method(restricted=False)
27 def login(self, request, username, password):
28 user = authenticate(username=username, password=password)
29 if user is not None and user.is_active:
35 @gilbert_method(restricted=False)
36 def logout(self, request):
41 def passwd(self, request, current_password, new_password, new_password_confirm):
43 if user.check_password(current_password) and (new_password == new_password_confirm):
44 user.set_password(new_password)
50 class GilbertSite(object):
51 def __init__(self, namespace='gilbert', app_name='gilbert', title='Gilbert'):
52 self.namespace = namespace
53 self.app_name = app_name
55 self.core_api = GilbertSitePlugin(self)
56 self.model_registry = SortedDict()
57 self.plugin_registry = SortedDict()
59 def register_plugin(self, plugin):
60 if is_gilbert_plugin(plugin):
61 if plugin.gilbert_plugin_name in self.plugin_registry:
62 raise AlreadyRegistered('A plugin named \'%s\' is already registered' % plugin.gilbert_plugin_name)
63 self.plugin_registry[plugin.gilbert_plugin_name] = plugin(self)
65 raise ValueError('register_plugin must be provided a valid plugin class or instance')
67 def register_model(self, model_or_iterable, admin_class=GilbertModelAdmin, **admin_attrs):
68 if isinstance(model_or_iterable, ModelBase):
69 model_or_iterable = [model_or_iterable]
70 for model in model_or_iterable:
71 if model._meta.app_label not in self.model_registry:
72 self.model_registry[model._meta.app_label] = SortedDict()
73 if model._meta.object_name in self.model_registry[model._meta.app_label]:
74 raise AlreadyRegistered('The model %s is already registered' % model.__name__)
76 admin_attrs['__module__'] = __name__
77 admin_class = type('%sAdmin' % model.__name__, (admin_class,), admin_attrs)
78 self.model_registry[model._meta.app_label][model._meta.object_name] = admin_class(self, model)
80 def has_permission(self, request):
81 return request.user.is_active and request.user.is_staff
86 url(r'^$', self.index, name='index'),
87 url(r'^css.css$', self.css, name='css'),
88 url(r'^api.js$', self.api, name='api'),
89 url(r'^router/?$', self.router, name='router'),
90 url(r'^models/(?P<app_label>\w+)/?$', self.router, name='models'),
91 url(r'^plugins/(?P<plugin_name>\w+)/?$', self.router, name='plugins'),
92 url(r'^login$', self.router, name='login'),
93 ), self.app_name, self.namespace)
95 def request_context(self, request, extra_context=None):
96 from django.template import RequestContext
97 context = RequestContext(request, current_app=self.namespace)
98 context.update(extra_context or {})
99 context.update({'gilbert': self, 'user': request.user, 'logged_in': self.has_permission(request)})
102 def index(self, request, extra_context=None):
103 return render_to_response('gilbert/index.html', context_instance=self.request_context(request, extra_context))
105 def css(self, request, extra_context=None):
106 return render_to_response('gilbert/css.css', context_instance=self.request_context(request, extra_context), mimetype='text/css')
108 def api(self, request, extra_context=None):
109 return render_to_response('gilbert/api.js', context_instance=self.request_context(request, extra_context), mimetype='text/javascript')
111 def router(self, request, app_label=None, plugin_name=None, extra_context=None):
112 submitted_form = False
113 if request.META['CONTENT_TYPE'].startswith('application/x-www-form-urlencoded'):
114 submitted_form = True
117 post_dict = dict(request.POST)
119 'action': post_dict.pop('extAction'),
120 'method': post_dict.pop('extMethod'),
121 'type': post_dict.pop('extType'),
122 'tid': post_dict.pop('extTID'),
126 if 'extUpload' in request.POST:
127 ext_request['upload'] = request.POST['extUpload']
129 ext_request = json.loads(request.raw_post_data)
130 ext_request['kwdata'] = None
135 if app_label is not None:
137 gilbert_class = self.model_registry[app_label][ext_request['action']]
139 raise NotImplementedError('A model named \'%s\' has not been registered' % ext_request['action'])
140 elif plugin_name is not None:
142 gilbert_plugin = self.plugin_registry[plugin_name]
144 raise NotImplementedError('A plugin named \'%s\' has not been registered' % plugin_name)
146 gilbert_class = gilbert_plugin.gilbert_plugin_classes[ext_request['action']]
148 raise NotImplementedError('The plugin named \'%s\' does not provide a class named \'%s\'' % (plugin_name, ext_request['action']))
151 gilbert_class = self.core_api.gilbert_plugin_classes[ext_request['action']]
153 raise NotImplementedError('Gilbert does not provide a class named \'%s\'' % ext_request['action'])
156 method = gilbert_class.gilbert_class_methods[ext_request['method']]
158 raise NotImplementedError('The class named \'%s\' does not implement a method named \'%\'' % (gilbert_class.gilbert_class_name, ext_request['method']))
159 if method.gilbert_method_restricted and not self.has_permission(request):
160 raise NotImplementedError('The method named \'%s\' is not available' % method.gilbert_method_name)
161 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 {}))}
163 exc_type, exc_value, exc_traceback = sys.exc_info()
164 response = {'type': 'exception', 'tid': ext_request['tid'], 'message': ('%s: %s' % (exc_type, exc_value)), 'where': format_tb(exc_traceback)[0]}
167 return HttpResponse(('<html><body><textarea>%s</textarea></body></html>' % json.dumps(response)))
168 return HttpResponse(json.dumps(response), content_type=('application/json; charset=%s' % settings.DEFAULT_CHARSET))