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 get_passwd_form(self, request):
53 from django.contrib.auth.forms import PasswordChangeForm
54 return PasswordChangeForm(request.user).as_ext()
56 @gilbert_method(form_handler=True)
57 def submit_passwd_form(self, request):
58 from django.contrib.auth.forms import PasswordChangeForm
59 form = PasswordChangeForm(request.user, data=request.POST)
62 return {'success': True}
64 return {'success': False, 'errors': form.errors}
67 def whoami(self, request):
69 return user.get_full_name() or user.username
72 class GilbertSite(object):
73 version = gilbert_version
75 def __init__(self, namespace='gilbert', app_name='gilbert', title='Gilbert'):
76 self.namespace = namespace
77 self.app_name = app_name
79 self.model_registry = SortedDict()
80 self.plugin_registry = SortedDict()
81 self.register_plugin(GilbertAuthPlugin)
83 def register_plugin(self, plugin):
84 if plugin.name in self.plugin_registry:
85 raise AlreadyRegistered('A plugin named \'%s\' is already registered' % plugin.name)
86 self.plugin_registry[plugin.name] = plugin(self)
88 def register_model(self, model_or_iterable, admin_class=GilbertModelAdmin, **admin_attrs):
89 if isinstance(model_or_iterable, ModelBase):
90 model_or_iterable = [model_or_iterable]
91 for model in model_or_iterable:
92 if model._meta.app_label not in self.model_registry:
93 self.model_registry[model._meta.app_label] = SortedDict()
94 if model._meta.object_name in self.model_registry[model._meta.app_label]:
95 raise AlreadyRegistered('The model %s.%s is already registered' % (model._meta.app_label, model.__name__))
97 admin_attrs['__module__'] = __name__
98 admin_class = type('%sAdmin' % model.__name__, (admin_class,), admin_attrs)
99 self.model_registry[model._meta.app_label][model._meta.object_name] = admin_class(self, model)
101 def has_permission(self, request):
102 return request.user.is_active and request.user.is_staff
106 urlpatterns = patterns('',
107 url(r'^$', self.index, name='index'),
108 url(r'^css$', self.css, name='css'),
109 url(r'^api$', self.api, name='api'),
110 url(r'^router/?$', self.router, name='router'),
111 url(r'^router/models/(?P<app_label>\w+)/?$', self.router, name='models'),
112 url(r'^login$', self.router, name='login'),
115 return (urlpatterns, self.app_name, self.namespace)
117 def request_context(self, request, extra_context=None):
118 from django.template import RequestContext
119 context = RequestContext(request, current_app=self.namespace)
120 context.update(extra_context or {})
121 context.update({'gilbert': self, 'user': request.user, 'logged_in': self.has_permission(request)})
125 def index(self, request, extra_context=None):
126 return render_to_response('gilbert/index.html', context_instance=self.request_context(request, extra_context))
128 def css(self, request, extra_context=None):
130 for plugin in self.plugin_registry.values():
131 icon_names.extend(plugin.fugue_icons)
133 icons = dict([(icon_name, staticmedia.url('gilbert/fugue-icons/icons/%s.png' % icon_name)) for icon_name in set(icon_names)])
135 context = extra_context or {}
136 context.update({'icons': icons})
138 return render_to_response('gilbert/styles.css', context_instance=self.request_context(request, context), mimetype='text/css')
141 def api(self, request, extra_context=None):
143 for app_label, models in self.model_registry.items():
145 'namespace': 'Gilbert.api.models.%s' % app_label,
146 'url': reverse('%s:models' % self.namespace, current_app=self.app_name, kwargs={'app_label': app_label}),
150 for model_name, admin in models.items():
152 for method in [admin.get_method(method_name) for method_name in admin.methods]:
153 if method.restricted and not self.has_permission(request):
155 model_methods.append({
158 'formHandler': method.form_handler,
161 model_actions[model_name] = model_methods
163 app_provider['actions'] = model_actions
164 providers.append(app_provider)
167 'namespace': 'Gilbert.api',
168 'url': reverse('%s:router' % self.namespace, current_app=self.app_name),
172 for plugin_name, plugin in self.plugin_registry.items():
174 for method in [plugin.get_method(method_name) for method_name in plugin.methods]:
175 if method.restricted and not self.has_permission(request):
177 plugin_methods.append({
180 'formHandler': method.form_handler,
183 plugin_actions[plugin_name] = plugin_methods
185 plugin_provider['actions'] = plugin_actions
186 providers.append(plugin_provider)
188 return HttpResponse(''.join(['Ext.Direct.addProvider('+json.dumps(provider, separators=(',', ':'))+');' for provider in providers]), mimetype='text/javascript')
190 def router(self, request, app_label=None, extra_context=None):
191 submitted_form = False
192 if request.META['CONTENT_TYPE'].startswith('application/x-www-form-urlencoded'):
193 submitted_form = True
197 'action': request.POST.get('extAction'),
198 'method': request.POST.get('extMethod'),
199 'type': request.POST.get('extType'),
200 'tid': request.POST.get('extTID'),
201 'upload': request.POST.get('extUpload', False),
204 response = self.handle_ext_request(request, ext_request, app_label)
206 ext_requests = json.loads(request.raw_post_data)
207 if type(ext_requests) is dict:
208 ext_requests['upload'] = False
209 response = self.handle_ext_request(request, ext_requests, app_label)
212 for ext_request in ext_requests:
213 ext_request['upload'] = False
214 responses.append(self.handle_ext_request(request, ext_request, app_label))
218 if ext_request['upload'] is True:
219 return HttpResponse(('<html><body><textarea>%s</textarea></body></html>' % json.dumps(response)))
220 return HttpResponse(json.dumps(response), content_type=('application/json; charset=%s' % settings.DEFAULT_CHARSET))
222 def handle_ext_request(self, request, ext_request, app_label=None):
226 if app_label is not None:
228 plugin = self.model_registry[app_label][ext_request['action']]
230 raise NotImplementedError('A model named \'%s\' has not been registered' % ext_request['action'])
233 plugin = self.plugin_registry[ext_request['action']]
235 raise NotImplementedError('Gilbert does not provide a class named \'%s\'' % ext_request['action'])
237 method = plugin.get_method(ext_request['method'])
239 if method is None or (method.restricted and not self.has_permission(request)):
240 raise NotImplementedError('The method named \'%s\' is not available' % method.name)
242 return {'type': 'rpc', 'tid': ext_request['tid'], 'action': ext_request['action'], 'method': ext_request['method'], 'result': method(request, *(ext_request['data'] or []))}
244 exc_type, exc_value, exc_traceback = sys.exc_info()
245 return {'type': 'exception', 'tid': ext_request['tid'], 'message': ('%s: %s' % (exc_type, exc_value)), 'where': format_tb(exc_traceback)[0]}