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, include
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.plugins import GilbertModelAdmin, GilbertPlugin, is_gilbert_method, gilbert_method
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 django.views.decorators.cache import never_cache
19 from philo.contrib.gilbert import __version__ as gilbert_version
23 __all__ = ('GilbertSite', 'site')
26 class GilbertAuthPlugin(GilbertPlugin):
31 return [staticmedia.url('gilbert/Gilbert.api.auth.js')]
34 def fugue_icons(self):
35 return ['user-silhouette', 'key--pencil', 'door-open-out', 'door-open-in']
37 @gilbert_method(restricted=False)
38 def login(self, request, username, password):
39 user = authenticate(username=username, password=password)
40 if user is not None and user.is_active:
47 def logout(self, request):
52 def passwd(self, request, current_password, new_password, new_password_confirm):
54 if user.check_password(current_password) and (new_password == new_password_confirm):
55 user.set_password(new_password)
61 def whoami(self, request):
63 return user.get_full_name() or user.username
66 class GilbertSite(object):
67 version = gilbert_version
69 def __init__(self, namespace='gilbert', app_name='gilbert', title='Gilbert'):
70 self.namespace = namespace
71 self.app_name = app_name
73 self.model_registry = SortedDict()
74 self.plugin_registry = SortedDict()
75 self.register_plugin(GilbertAuthPlugin)
77 def register_plugin(self, plugin):
78 if plugin.name in self.plugin_registry:
79 raise AlreadyRegistered('A plugin named \'%s\' is already registered' % plugin.name)
80 self.plugin_registry[plugin.name] = plugin(self)
82 def register_model(self, model_or_iterable, admin_class=GilbertModelAdmin, **admin_attrs):
83 if isinstance(model_or_iterable, ModelBase):
84 model_or_iterable = [model_or_iterable]
85 for model in model_or_iterable:
86 if model._meta.app_label not in self.model_registry:
87 self.model_registry[model._meta.app_label] = SortedDict()
88 if model._meta.object_name in self.model_registry[model._meta.app_label]:
89 raise AlreadyRegistered('The model %s.%s is already registered' % (model._meta.app_label, model.__name__))
91 admin_attrs['__module__'] = __name__
92 admin_class = type('%sAdmin' % model.__name__, (admin_class,), admin_attrs)
93 self.model_registry[model._meta.app_label][model._meta.object_name] = admin_class(self, model)
95 def has_permission(self, request):
96 return request.user.is_active and request.user.is_staff
100 urlpatterns = patterns('',
101 url(r'^$', self.index, name='index'),
102 url(r'^css$', self.css, name='css'),
103 url(r'^api$', self.api, name='api'),
104 url(r'^router/?$', self.router, name='router'),
105 url(r'^router/models/(?P<app_label>\w+)/?$', self.router, name='models'),
106 url(r'^login$', self.router, name='login'),
109 return (urlpatterns, self.app_name, self.namespace)
111 def request_context(self, request, extra_context=None):
112 from django.template import RequestContext
113 context = RequestContext(request, current_app=self.namespace)
114 context.update(extra_context or {})
115 context.update({'gilbert': self, 'user': request.user, 'logged_in': self.has_permission(request)})
119 def index(self, request, extra_context=None):
120 return render_to_response('gilbert/index.html', context_instance=self.request_context(request, extra_context))
122 def css(self, request, extra_context=None):
124 for plugin in self.plugin_registry.values():
125 icon_names.extend(plugin.fugue_icons)
127 icons = dict([(icon_name, staticmedia.url('gilbert/fugue-icons/icons/%s.png' % icon_name)) for icon_name in set(icon_names)])
129 context = extra_context or {}
130 context.update({'icons': icons})
132 return render_to_response('gilbert/styles.css', context_instance=self.request_context(request, context), mimetype='text/css')
135 def api(self, request, extra_context=None):
137 for app_label, models in self.model_registry.items():
139 'namespace': 'Gilbert.api.models.%s' % app_label,
140 'url': reverse('%s:models' % self.namespace, current_app=self.app_name, kwargs={'app_label': app_label}),
144 for model_name, admin in models.items():
146 for method in [admin.get_method(method_name) for method_name in admin.methods]:
147 if method.restricted and not self.has_permission(request):
149 model_methods.append({
154 model_actions[model_name] = model_methods
156 app_provider['actions'] = model_actions
157 providers.append(app_provider)
160 'namespace': 'Gilbert.api',
161 'url': reverse('%s:router' % self.namespace, current_app=self.app_name),
165 for plugin_name, plugin in self.plugin_registry.items():
167 for method in [plugin.get_method(method_name) for method_name in plugin.methods]:
168 if method.restricted and not self.has_permission(request):
170 plugin_methods.append({
175 plugin_actions[plugin_name] = plugin_methods
177 plugin_provider['actions'] = plugin_actions
178 providers.append(plugin_provider)
180 return HttpResponse(''.join(['Ext.Direct.addProvider('+json.dumps(provider, separators=(',', ':'))+');' for provider in providers]), mimetype='text/javascript')
182 def router(self, request, app_label=None, extra_context=None):
183 submitted_form = False
184 if request.META['CONTENT_TYPE'].startswith('application/x-www-form-urlencoded'):
185 submitted_form = True
188 post_dict = dict(request.POST)
190 'action': post_dict.pop('extAction'),
191 'method': post_dict.pop('extMethod'),
192 'type': post_dict.pop('extType'),
193 'tid': post_dict.pop('extTID'),
194 'upload': post_dict.pop('extUpload', False),
199 ext_request = json.loads(request.raw_post_data)
200 ext_request['upload'] = False
201 ext_request['kwdata'] = None
206 if app_label is not None:
208 plugin = self.model_registry[app_label][ext_request['action']]
210 raise NotImplementedError('A model named \'%s\' has not been registered' % ext_request['action'])
213 plugin = self.plugin_registry[ext_request['action']]
215 raise NotImplementedError('Gilbert does not provide a class named \'%s\'' % ext_request['action'])
217 method = plugin.get_method(ext_request['method'])
219 if method is None or (method.restricted and not self.has_permission(request)):
220 raise NotImplementedError('The method named \'%s\' is not available' % method.name)
222 response = {'type': 'rpc', 'tid': ext_request['tid'], 'action': ext_request['action'], 'method': ext_request['method'], 'result': method(request, *(ext_request['data'] or []), **(ext_request['kwdata'] or {}))}
224 exc_type, exc_value, exc_traceback = sys.exc_info()
225 response = {'type': 'exception', 'tid': ext_request['tid'], 'message': ('%s: %s' % (exc_type, exc_value)), 'where': format_tb(exc_traceback)[0]}
228 return HttpResponse(('<html><body><textarea>%s</textarea></body></html>' % json.dumps(response)))
229 return HttpResponse(json.dumps(response), content_type=('application/json; charset=%s' % settings.DEFAULT_CHARSET))