Cleanup of Gilbert plugins API and JavaScript.
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Mon, 23 Aug 2010 14:55:49 +0000 (10:55 -0400)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Tue, 24 Aug 2010 13:25:07 +0000 (09:25 -0400)
12 files changed:
contrib/gilbert/__init__.py
contrib/gilbert/media/gilbert/Gilbert.api.auth.js [new file with mode: 0644]
contrib/gilbert/media/gilbert/Gilbert.lib.js [new file with mode: 0644]
contrib/gilbert/options.py [deleted file]
contrib/gilbert/plugins.py [new file with mode: 0644]
contrib/gilbert/sites.py
contrib/gilbert/templates/gilbert/api.js [deleted file]
contrib/gilbert/templates/gilbert/base.html [deleted file]
contrib/gilbert/templates/gilbert/css.css [deleted file]
contrib/gilbert/templates/gilbert/index.html
contrib/gilbert/templates/gilbert/styles.css [new file with mode: 0644]
contrib/gilbert/utils.py [deleted file]

index d009571..c55f250 100644 (file)
@@ -1,3 +1,6 @@
+__version__ = 'alpha'
+
+
 from philo.contrib.gilbert.sites import GilbertSite, site
 
 
 from philo.contrib.gilbert.sites import GilbertSite, site
 
 
diff --git a/contrib/gilbert/media/gilbert/Gilbert.api.auth.js b/contrib/gilbert/media/gilbert/Gilbert.api.auth.js
new file mode 100644 (file)
index 0000000..877f4f5
--- /dev/null
@@ -0,0 +1,146 @@
+GILBERT_PLUGINS.push(new (function() {
+       return {
+               init: function(application) {
+                       if (GILBERT_LOGGED_IN) {
+                               application.on('ready', this.addUserMenu, this, {
+                                       single: true,
+                               });
+                       } else {
+                               application.on('ready', this.showLoginWindow, this, {
+                                       single: true,
+                               });
+                       }
+               },
+               addUserMenu: function(application) {
+                       Gilbert.api.auth.whoami(function(result) {
+                               application.mainmenu.add({
+                                       xtype: 'tbfill',
+                               },{
+                                       xtype: 'tbseparator',
+                               },{
+                                       xtype: 'button',
+                                       iconCls: 'user-silhouette',
+                                       text: '<span style="font-weight: bolder;">' + result + '</span>',
+                                       menu: [{
+                                               text: 'Change password',
+                                               iconCls: 'key--pencil',
+                                               handler: function(button, event) {
+                                                       var change_password_window = application.createWindow({
+                                                               layout: 'fit',
+                                                               resizable: false,
+                                                               title: 'Change password',
+                                                               iconCls: 'key--pencil',
+                                                               width: 266,
+                                                               height: 170,
+                                                               items: change_password_form = new Ext.FormPanel({
+                                                                       frame: true,
+                                                                       bodyStyle: 'padding: 5px 5px 0',
+                                                                       items: [{
+                                                                               fieldLabel: 'Current password',
+                                                                               name: 'current_password',
+                                                                               xtype: 'textfield',
+                                                                               inputType: 'password',
+                                                                       },{
+                                                                               fieldLabel: 'New password',
+                                                                               name: 'new_password',
+                                                                               xtype: 'textfield',
+                                                                               inputType: 'password',
+                                                                       },{
+                                                                               fieldLabel: 'New password (confirm)',
+                                                                               name: 'new_password_confirm',
+                                                                               xtype: 'textfield',
+                                                                               inputType: 'password',
+                                                                       }],
+                                                                       buttons: [{
+                                                                               text: 'Change password',
+                                                                               iconCls: 'key--pencil',
+                                                                               handler: function(button, event) {
+                                                                                       var the_form = change_password_form.getForm().el.dom;
+                                                                                       var current_password = the_form[0].value;
+                                                                                       var new_password = the_form[1].value;
+                                                                                       var new_password_confirm = the_form[2].value;
+                                                                                       Gilbert.api.auth.passwd(current_password, new_password, new_password_confirm, function(result) {
+                                                                                               if (result) {
+                                                                                                       Ext.MessageBox.alert('Password changed', 'Your password has been changed.');
+                                                                                               } else {
+                                                                                                       Ext.MessageBox.alert('Password unchanged', 'Unable to change your password.', function() {
+                                                                                                               change_password_form.getForm().reset();
+                                                                                                       });
+                                                                                               }
+                                                                                       });
+                                                                               },
+                                                                       }],
+                                                               })
+                                                       });
+                                                       change_password_window.show();
+                                               },
+                                       },{
+                                               text: 'Log out',
+                                               iconCls: 'door-open-out',
+                                               handler: function(button, event) {
+                                                       Gilbert.api.auth.logout(function(result) {
+                                                               if (result) {
+                                                                       document.location.reload();
+                                                               } else {
+                                                                       Ext.MessageBox.alert('Log out failed', 'You have <strong>not</strong> been logged out. This could mean that your connection with the server has been severed. Please try again.');
+                                                               }
+                                                       })
+                                               }
+                                       }],
+                               });
+                               application.doLayout();
+                       });
+               },
+               showLoginWindow: function(application) {
+                       application.mainmenu.hide();
+                       application.doLayout();
+                       var login_window = application.createWindow({
+                               header: false,
+                               closable: false,
+                               resizable: false,
+                               draggable: false,
+                               width: 266,
+                               height: 130,
+                               layout: 'fit',
+                               items: login_form = new Ext.FormPanel({
+                                       frame: true,
+                                       bodyStyle: 'padding: 5px 5px 0',
+                                       items: [
+                                               {
+                                                       fieldLabel: 'Username',
+                                                       name: 'username',
+                                                       xtype: 'textfield',
+                                               },
+                                               {
+                                                       fieldLabel: 'Password',
+                                                       name: 'password',
+                                                       xtype: 'textfield',
+                                                       inputType: 'password',
+                                               }
+                                       ],
+                                       buttons: [
+                                               {
+                                                       text: 'Log in',
+                                                       iconCls: 'door-open-in',
+                                                       handler: function(button, event) {
+                                                               var the_form = login_form.getForm().el.dom;
+                                                               var username = the_form[0].value;
+                                                               var password = the_form[1].value;
+                                                               Gilbert.api.auth.login(username, password, function(result) {
+                                                                       if (result) {
+                                                                               document.location.reload();
+                                                                       } else {
+                                                                               Ext.MessageBox.alert('Log in failed', 'Unable to authenticate using the credentials provided. Please try again.', function() {
+                                                                                       login_form.getForm().reset();
+                                                                               });
+                                                                       }
+                                                               });
+                                                       }
+                                               }
+                                       ],
+                               }),
+                       });
+                       login_window.show();
+               },
+       }
+})());
\ No newline at end of file
diff --git a/contrib/gilbert/media/gilbert/Gilbert.lib.js b/contrib/gilbert/media/gilbert/Gilbert.lib.js
new file mode 100644 (file)
index 0000000..02bdb7b
--- /dev/null
@@ -0,0 +1,83 @@
+Ext.ns('Gilbert.lib');
+
+Gilbert.lib.Desktop = Ext.extend(Ext.Panel, {
+       constructor: function(config) {
+               Gilbert.lib.Desktop.superclass.constructor.call(this, Ext.applyIf(config||{}, {
+                       region: 'center',
+                       border: false,
+                       padding: '5',
+                       bodyStyle: 'background: none;',
+               }));
+       },
+});
+
+Gilbert.lib.MainMenu = Ext.extend(Ext.Toolbar, {
+       constructor: function(application) {
+               var application = this.application = application;
+               Gilbert.lib.MainMenu.superclass.constructor.call(this, {
+                       region: 'north',
+                       autoHeight: true,
+               });
+       },
+});
+
+Gilbert.lib.Application = Ext.extend(Ext.util.Observable, {
+       constructor: function(config) {
+               Ext.apply(this, config, {
+                       renderTo: Ext.getBody(),
+                       plugins: [],
+                       
+               });
+               Gilbert.lib.Application.superclass.constructor.call(this);
+               this.addEvents({
+                       'ready': true,
+               });
+               this.init();
+       },
+       init: function() {
+               Ext.QuickTips.init();
+               
+               var desktop = this.desktop = new Gilbert.lib.Desktop();
+               var mainmenu = this.mainmenu = new Gilbert.lib.MainMenu(this);
+               var viewport = this.viewport = new Ext.Viewport({
+                       renderTo: this.renderTo,
+                       layout: 'border',
+                       items: [
+                               this.mainmenu,
+                               this.desktop,
+                       ],
+               });
+               var windows = this.windows = new Ext.WindowGroup();
+               
+               if (this.plugins) {
+                       if (Ext.isArray(this.plugins)) {
+                               for (var i = 0; i < this.plugins.length; i++) {
+                                       this.plugins[i] = this.initPlugin(this.plugins[i]);
+                               }
+                       } else {
+                               this.plugins = this.initPlugin(this.plugins);
+                       }
+               }
+               
+               this.doLayout();
+               
+               this.fireEvent('ready', this);
+       },
+       initPlugin: function(plugin) {
+               plugin.init(this);
+               return plugin;
+       },
+       createWindow: function(config, cls) {
+               var win = new(cls||Ext.Window)(Ext.applyIf(config||{},{
+                       renderTo: this.desktop.el,
+                       manager: this.windows,
+                       constrainHeader: true,
+               }));
+               win.render(this.desktop.el);
+               return win;
+       },
+       doLayout: function() {
+               this.mainmenu.doLayout();
+               this.viewport.doLayout();
+       }
+});
\ No newline at end of file
diff --git a/contrib/gilbert/options.py b/contrib/gilbert/options.py
deleted file mode 100644 (file)
index fbdd4e3..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-from philo.contrib.gilbert.utils import gilbert_method, is_gilbert_method, is_gilbert_class
-
-
-class GilbertClassBase(type):
-       def __new__(cls, name, bases, attrs):
-               if 'gilbert_class' not in attrs:
-                       attrs['gilbert_class'] = True
-               if 'gilbert_class_name' not in attrs:
-                       attrs['gilbert_class_name'] = name
-               if 'gilbert_class_methods' not in attrs:
-                       gilbert_class_methods = {}
-                       for attr in attrs.values():
-                               if is_gilbert_method(attr):
-                                       gilbert_class_methods[attr.gilbert_method_name] = attr
-                       attrs['gilbert_class_methods'] = gilbert_class_methods
-               return super(GilbertClassBase, cls).__new__(cls, name, bases, attrs)
-
-
-class GilbertClass(object):
-       __metaclass__ = GilbertClassBase
-
-
-class GilbertPluginBase(type):
-       def __new__(cls, name, bases, attrs):
-               if 'gilbert_plugin' not in attrs:
-                       attrs['gilbert_plugin'] = True
-               if 'gilbert_plugin_name' not in attrs:
-                       attrs['gilbert_plugin_name'] = name
-               if 'gilbert_plugin_classes' not in attrs:
-                       gilbert_plugin_classes = {}
-                       for attr_name, attr in attrs.items():
-                               if is_gilbert_class(attr):
-                                       gilbert_plugin_classes[attr_name] = attr
-                       attrs['gilbert_plugin_classes'] = gilbert_plugin_classes
-               return super(GilbertPluginBase, cls).__new__(cls, name, bases, attrs)
-
-
-class GilbertPlugin(object):
-       __metaclass__ = GilbertPluginBase
-       
-       def __init__(self, site):
-               self.site = site
-
-
-class GilbertModelAdmin(GilbertClass):
-       def __init__(self, site, model):
-               self.site = site
-               self.model = model
-               self.gilbert_class_name = model._meta.object_name
-       
-       @gilbert_method
-       def all(self):
-               return list(self.model._default_manager.all().values())
-       
-       @gilbert_method
-       def get(self, constraint):
-               return self.model._default_manager.all().values().get(**constraint)
\ No newline at end of file
diff --git a/contrib/gilbert/plugins.py b/contrib/gilbert/plugins.py
new file mode 100644 (file)
index 0000000..5760c9a
--- /dev/null
@@ -0,0 +1,84 @@
+from inspect import isclass, getargspec
+
+
+def is_gilbert_method(function):
+       return getattr(function, 'gilbert_method', False)
+
+
+def gilbert_method(function=None, name=None, argc=None, restricted=True):
+       def wrapper(function):
+               setattr(function, 'gilbert_method', True)
+               setattr(function, 'name', name or function.__name__)
+               setattr(function, 'restricted', restricted)
+               new_argc = argc
+               if new_argc is None:
+                       args = getargspec(function)[0]
+                       new_argc = len(args)
+                       if new_argc > 0:
+                               if args[0] == 'self':
+                                       args = args[1:]
+                                       new_argc = new_argc - 1
+                               if new_argc > 0:
+                                       if args[0] == 'request':
+                                               args = args[1:]
+                                               new_argc = new_argc - 1
+               setattr(function, 'argc', new_argc)
+               return function
+       if function is not None:
+               return wrapper(function)
+       return wrapper
+
+
+class GilbertPluginBase(type):
+       def __new__(cls, name, bases, attrs):
+               if 'methods' not in attrs:
+                       methods = []
+                       for attr in attrs.values():
+                               if is_gilbert_method(attr):
+                                       methods.append(attr.name)
+                       attrs['methods'] = methods
+               return super(GilbertPluginBase, cls).__new__(cls, name, bases, attrs)
+
+
+class GilbertPlugin(object):
+       __metaclass__ = GilbertPluginBase
+       
+       def __init__(self, site):
+               self.site = site
+       
+       def get_method(self, method_name):
+               method = getattr(self, method_name, None)
+               if not is_gilbert_method(method):
+                       return None
+               return method
+       
+       @property
+       def urls(self):
+               return []
+       
+       @property
+       def js(self):
+               return []
+       
+       @property
+       def css(self):
+               return []
+       
+       @property
+       def fugue_icons(self):
+               return []
+
+
+class GilbertModelAdmin(GilbertPlugin):
+       def __init__(self, site, model):
+               self.model = model
+               self.name = model._meta.object_name
+               super(GilbertModelAdmin, self).__init__(site)
+       
+       @gilbert_method
+       def all(self):
+               return list(self.model._default_manager.all().values())
+       
+       @gilbert_method
+       def get(self, constraint):
+               return self.model._default_manager.all().values().get(**constraint)
\ No newline at end of file
index 76c1d9a..b3f0df2 100644 (file)
@@ -1,6 +1,6 @@
 from django.contrib.admin.sites import AdminSite
 from django.contrib.auth import authenticate, login, logout
 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.core.urlresolvers import reverse
 from django.shortcuts import render_to_response
 from django.conf import settings
@@ -9,60 +9,75 @@ from django.utils.datastructures import SortedDict
 from django.http import HttpResponse
 from django.db.models.base import ModelBase
 from philo.utils import fattr
 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.plugins import GilbertModelAdmin, GilbertPlugin, is_gilbert_method, gilbert_method
 from philo.contrib.gilbert.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.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 philo.contrib.gilbert import __version__ as gilbert_version
+import staticmedia
+import os
 
 __all__ = ('GilbertSite', 'site')
 
 
 
 __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)
+class GilbertAuthPlugin(GilbertPlugin):
+       name = 'auth'
+       
+       @property
+       def js(self):
+               return [staticmedia.url('gilbert/Gilbert.api.auth.js')]
+       
+       @property
+       def fugue_icons(self):
+               return ['user-silhouette', 'key--pencil', 'door-open-out', 'door-open-in']
+       
+       @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
                        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
+               else:
                        return False
                        return False
+       
+       @gilbert_method
+       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
+       
+       @gilbert_method
+       def whoami(self, request):
+               user = request.user
+               return user.get_full_name() or user.username
 
 
 class GilbertSite(object):
 
 
 class GilbertSite(object):
+       version = gilbert_version
+       
        def __init__(self, namespace='gilbert', app_name='gilbert', title='Gilbert'):
                self.namespace = namespace
                self.app_name = app_name
                self.title = title
        def __init__(self, namespace='gilbert', app_name='gilbert', title='Gilbert'):
                self.namespace = namespace
                self.app_name = app_name
                self.title = title
-               self.core_api = GilbertSitePlugin(self)
                self.model_registry = SortedDict()
                self.plugin_registry = SortedDict()
                self.model_registry = SortedDict()
                self.plugin_registry = SortedDict()
+               self.register_plugin(GilbertAuthPlugin)
        
        def register_plugin(self, plugin):
        
        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')
+               if plugin.name in self.plugin_registry:
+                       raise AlreadyRegistered('A plugin named \'%s\' is already registered' % plugin.name)
+               self.plugin_registry[plugin.name] = plugin(self)
        
        def register_model(self, model_or_iterable, admin_class=GilbertModelAdmin, **admin_attrs):
                if isinstance(model_or_iterable, ModelBase):
        
        def register_model(self, model_or_iterable, admin_class=GilbertModelAdmin, **admin_attrs):
                if isinstance(model_or_iterable, ModelBase):
@@ -71,7 +86,7 @@ class GilbertSite(object):
                        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]:
                        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__)
+                               raise AlreadyRegistered('The model %s.%s is already registered' % (model._meta.app_label, model.__name__))
                        if admin_attrs:
                                admin_attrs['__module__'] = __name__
                                admin_class = type('%sAdmin' % model.__name__, (admin_class,), admin_attrs)
                        if admin_attrs:
                                admin_attrs['__module__'] = __name__
                                admin_class = type('%sAdmin' % model.__name__, (admin_class,), admin_attrs)
@@ -82,15 +97,16 @@ class GilbertSite(object):
        
        @property
        def urls(self):
        
        @property
        def urls(self):
-               return (patterns('',
+               urlpatterns = patterns('',
                        url(r'^$', self.index, name='index'),
                        url(r'^$', self.index, name='index'),
-                       url(r'^css.css$', self.css, name='css'),
-                       url(r'^api.js$', self.api, name='api'),
+                       url(r'^css$', self.css, name='css'),
+                       url(r'^api$', self.api, name='api'),
                        url(r'^router/?$', self.router, name='router'),
                        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'^router/models/(?P<app_label>\w+)/?$', self.router, name='models'),
                        url(r'^login$', self.router, name='login'),
                        url(r'^login$', self.router, name='login'),
-               ), self.app_name, self.namespace)
+               )
+               
+               return (urlpatterns, self.app_name, self.namespace)
        
        def request_context(self, request, extra_context=None):
                from django.template import RequestContext
        
        def request_context(self, request, extra_context=None):
                from django.template import RequestContext
@@ -99,16 +115,71 @@ class GilbertSite(object):
                context.update({'gilbert': self, 'user': request.user, 'logged_in': self.has_permission(request)})
                return context
        
                context.update({'gilbert': self, 'user': request.user, 'logged_in': self.has_permission(request)})
                return context
        
+       @never_cache
        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):
        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')
+               icon_names = []
+               for plugin in self.plugin_registry.values():
+                       icon_names.extend(plugin.fugue_icons)
+               
+               icons = dict([(icon_name, staticmedia.url('gilbert/fugue-icons/icons/%s.png' % icon_name)) for icon_name in set(icon_names)])
+               
+               context = extra_context or {}
+               context.update({'icons': icons})
+               
+               return render_to_response('gilbert/styles.css', context_instance=self.request_context(request, context), mimetype='text/css')
        
        
+       @never_cache
        def api(self, request, extra_context=None):
        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')
+               providers = []
+               for app_label, models in self.model_registry.items():
+                       app_provider = {
+                               'namespace': 'Gilbert.api.models.%s' % app_label,
+                               'url': reverse('%s:models' % self.namespace, current_app=self.app_name, kwargs={'app_label': app_label}),
+                               'type': 'remoting',
+                       }
+                       model_actions = {}
+                       for model_name, admin in models.items():
+                               model_methods = []
+                               for method in [admin.get_method(method_name) for method_name in admin.methods]:
+                                       if method.restricted and not self.has_permission(request):
+                                               continue
+                                       model_methods.append({
+                                               'name': method.name,
+                                               'len': method.argc,
+                                       })
+                               if model_methods:
+                                       model_actions[model_name] = model_methods
+                       if model_actions:
+                               app_provider['actions'] = model_actions
+                               providers.append(app_provider)
+               
+               plugin_provider = {
+                       'namespace': 'Gilbert.api',
+                       'url': reverse('%s:router' % self.namespace, current_app=self.app_name),
+                       'type': 'remoting',
+               }
+               plugin_actions = {}
+               for plugin_name, plugin in self.plugin_registry.items():
+                       plugin_methods = []
+                       for method in [plugin.get_method(method_name) for method_name in plugin.methods]:
+                               if method.restricted and not self.has_permission(request):
+                                       continue
+                               plugin_methods.append({
+                                       'name': method.name,
+                                       'len': method.argc,
+                               })
+                       if plugin_methods:
+                               plugin_actions[plugin_name] = plugin_methods
+               if plugin_actions:
+                       plugin_provider['actions'] = plugin_actions
+                       providers.append(plugin_provider)
+               
+               return HttpResponse(''.join(['Ext.Direct.addProvider('+json.dumps(provider, separators=(',', ':'))+');' for provider in providers]), mimetype='text/javascript')
        
        
-       def router(self, request, app_label=None, plugin_name=None, extra_context=None):
+       def router(self, request, app_label=None, extra_context=None):
                submitted_form = False
                if request.META['CONTENT_TYPE'].startswith('application/x-www-form-urlencoded'):
                        submitted_form = True
                submitted_form = False
                if request.META['CONTENT_TYPE'].startswith('application/x-www-form-urlencoded'):
                        submitted_form = True
@@ -120,45 +191,35 @@ class GilbertSite(object):
                                'method': post_dict.pop('extMethod'),
                                'type': post_dict.pop('extType'),
                                'tid': post_dict.pop('extTID'),
                                'method': post_dict.pop('extMethod'),
                                'type': post_dict.pop('extType'),
                                'tid': post_dict.pop('extTID'),
+                               'upload': post_dict.pop('extUpload', False),
                                'data': None,
                                'data': None,
-                               'kwdata': post_dict,
+                               'kwdata': post_dict
                        }
                        }
-                       if 'extUpload' in request.POST:
-                               ext_request['upload'] = request.POST['extUpload']
                else:
                        ext_request = json.loads(request.raw_post_data)
                else:
                        ext_request = json.loads(request.raw_post_data)
+                       ext_request['upload'] = False
                        ext_request['kwdata'] = None
                
                try:
                        ext_request['kwdata'] = None
                
                try:
-                       gilbert_class = None
+                       plugin = None
                        
                        if app_label is not None:
                                try:
                        
                        if app_label is not None:
                                try:
-                                       gilbert_class = self.model_registry[app_label][ext_request['action']]
+                                       plugin = self.model_registry[app_label][ext_request['action']]
                                except KeyError:
                                        raise NotImplementedError('A model named \'%s\' has not been registered' % 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']))
                        else:
                                try:
                        else:
                                try:
-                                       gilbert_class = self.core_api.gilbert_plugin_classes[ext_request['action']]
+                                       plugin = self.plugin_registry[ext_request['action']]
                                except KeyError:
                                        raise NotImplementedError('Gilbert does not provide a class named \'%s\'' % 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 {}))}
+                       method = plugin.get_method(ext_request['method'])
+                       
+                       if method is None or (method.restricted and not self.has_permission(request)):
+                               raise NotImplementedError('The method named \'%s\' is not available' % method.name)
+                       
+                       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 {}))}
                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]}
                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]}
diff --git a/contrib/gilbert/templates/gilbert/api.js b/contrib/gilbert/templates/gilbert/api.js
deleted file mode 100644 (file)
index 71aaab8..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-{% load staticmedia %}
-
-Ext.Direct.addProvider({
-       'namespace': 'Gilbert.api',
-       'url': '{% url gilbert:router %}',
-       'type': 'remoting',
-       'actions': {{% for gilbert_class in gilbert.core_api.gilbert_plugin_classes.values %}
-               '{{ gilbert_class.gilbert_class_name }}': [{% for method in gilbert_class.gilbert_class_methods.values %}{
-                       'name': '{{ method.gilbert_method_name }}',
-                       'len': {{ method.gilbert_method_argc }}
-               },{% endfor %}],{% endfor %}
-       }
-});
-
-{% if not logged_in %}
-
-Ext.onReady(function() {
-       var login_form = new Ext.FormPanel({
-               frame: true,
-               bodyStyle: 'padding: 5px 5px 0',
-               items: [
-                       {
-                               fieldLabel: 'Username',
-                               name: 'username',
-                               xtype: 'textfield',
-                       },
-                       {
-                               fieldLabel: 'Password',
-                               name: 'password',
-                               xtype: 'textfield',
-                               inputType: 'password',
-                       }
-               ],
-               buttons: [
-                       {
-                               text: 'Login',
-                               handler: function(sender) {
-                                       // document.location.reload();
-                                       var the_form = login_form.getForm().el.dom;
-                                       var username = the_form[0].value;
-                                       var password = the_form[1].value;
-                                       Gilbert.api.auth.login(username, password, function(result) {
-                                               if (result) {
-                                                       document.location.reload();
-                                               } else {
-                                                       Ext.MessageBox.alert('Login failed', 'Unable to authenticate.', function() {
-                                                               login_form.getForm().reset();
-                                                       });
-                                               }
-                                       });
-                               }
-                       }
-               ],
-       });
-       var login_window = new Ext.Window({
-               title: 'Login',
-               closable: false,
-               width: 266,
-               height: 130,
-               layout: 'fit',
-               items: login_form,
-       });
-       login_window.show();
-});
-
-
-{% else %}
-
-Ext.ns('Gilbert', 'Gilbert.ui', 'Gilbert.models', 'Gilbert.plugins');
-
-{% for app_label, models in gilbert.model_registry.items %}Ext.Direct.addProvider({
-       'namespace': 'Gilbert.models.{{ app_label }}',
-       'url': '{% url gilbert:models app_label %}',
-       'type': 'remoting',
-       'actions': {{% for model_name, admin in models.items %}
-               '{{ model_name }}': [{% for method in admin.gilbert_class_methods.values %}{
-                               'name': '{{ method.gilbert_method_name }}',
-                               'len': {{ method.gilbert_method_argc }}
-                       },{% endfor %}],{% endfor %}
-       }
-});{% endfor %}
-{% for plugin in gilbert.plugin_registry.values %}Ext.Direct.addProvider({
-       'namespace': 'Gilbert.plugins.{{ plugin.gilbert_plugin_name }}',
-       'url': '{% url gilbert:plugins plugin.gilbert_plugin_name %}',
-       'type': 'remoting',
-       'actions': {{% for gilbert_class in plugin.gilbert_plugin_classes %}
-               '{{ gilbert_class.gilbert_class_name }}': [{% for method in gilbert_class.gilbert_class_methods.values %}{}
-                       'name': '{{ method.gilbert_method_name }}',
-                       'len': {{ method.gilbert_method_argc }}
-               },{% endfor %}],{% endfor %}
-       }
-});{% endfor %}
-
-Gilbert.ui.Application = function(cfg) {
-       Ext.apply(this, cfg, {
-               title: '{{ gilbert.title }}',
-       });
-       this.addEvents({
-               'ready': true,
-               'beforeunload': true,
-       });
-       Ext.onReady(this.initApplication, this);
-};
-
-Ext.extend(Gilbert.ui.Application, Ext.util.Observable, {
-       initApplication: function() {
-               
-               Ext.QuickTips.init();
-               
-               this.desktop = new Ext.Panel({
-                       region: 'center',
-                       border: false,
-                       padding: '5',
-                       bodyStyle: 'background: none;',
-               });
-               var desktop = this.desktop;
-               
-               this.toolbar = new Ext.Toolbar({
-                       region: 'north',
-                       autoHeight: true,
-                       items: [
-                       {
-                               xtype: 'tbtext',
-                               text: this.title,
-                               style: 'font-weight: bolder; font-size: larger; text-transform: uppercase;',
-                       },
-                       {
-                               xtype: 'tbseparator',
-                       }
-                       ]
-               });
-               var toolbar = this.toolbar;
-               
-               this.viewport = new Ext.Viewport({
-                       renderTo: Ext.getBody(),
-                       layout: 'border',
-                       items: [
-                       toolbar,
-                       desktop,
-                       ],
-               });
-               var viewport = this.viewport;
-               
-               var windows = new Ext.WindowGroup();
-               
-               this.createWindow = function(config, cls) {
-                       var win = new(cls || Ext.Window)(Ext.applyIf(config || {},
-                       {
-                               renderTo: desktop.el,
-                               manager: windows,
-                               constrainHeader: true,
-                               maximizable: true,
-                       }));
-                       win.render(desktop.el);
-                       return win;
-               };
-               var createWindow = this.createWindow;
-               
-               if (this.plugins) {
-                       for (var pluginNum = 0; pluginNum < this.plugins.length; pluginNum++) {
-                               this.plugins[pluginNum].initWithApp(this);
-                       };
-               };
-               
-               if (this.user) {
-                       var user = this.user;
-                       toolbar.add({ xtype: 'tbfill' });
-                       toolbar.add({ xtype: 'tbseparator' });
-                       toolbar.add({
-                               xtype: 'button',
-                               text: '<b>' + user + '</b>',
-                               style: 'font-weight: bolder !important; font-size: smaller !important; text-transform: uppercase !important;',
-                               menu: [
-                               {
-                                       text: 'Change password',
-                                       handler: function(button, event) {
-                                               var edit_window = createWindow({
-                                                       layout: 'fit',
-                                                       title: 'Change password',
-                                                       width: 266,
-                                                       height: 170,
-                                                       layout: 'fit',
-                                                       items: _change_password_form = new Ext.FormPanel({
-                                                               frame: true,
-                                                               bodyStyle: 'padding: 5px 5px 0',
-                                                               items: [
-                                                                       {
-                                                                               fieldLabel: 'Current password',
-                                                                               name: 'current_password',
-                                                                               xtype: 'textfield',
-                                                                               inputType: 'password',
-                                                                       },
-                                                                       {
-                                                                               fieldLabel: 'New password',
-                                                                               name: 'new_password',
-                                                                               xtype: 'textfield',
-                                                                               inputType: 'password',
-                                                                       },
-                                                                       {
-                                                                               fieldLabel: 'New password (confirm)',
-                                                                               name: 'new_password_confirm',
-                                                                               xtype: 'textfield',
-                                                                               inputType: 'password',
-                                                                       }
-                                                               ],
-                                                               buttons: [
-                                                                       {
-                                                                               text: 'Change password',
-                                                                               handler: function(sender) {
-                                                                                       // document.location.reload();
-                                                                                       var the_form = _change_password_form.getForm().el.dom;
-                                                                                       var current_password = the_form[0].value;
-                                                                                       var new_password = the_form[1].value;
-                                                                                       var new_password_confirm = the_form[2].value;
-                                                                                       Gilbert.api.auth.passwd(current_password, new_password, new_password_confirm, function(result) {
-                                                                                               if (result) {
-                                                                                                       Ext.MessageBox.alert('Password changed', 'Your password has been changed.');
-                                                                                               } else {
-                                                                                                       Ext.MessageBox.alert('Password unchanged', 'Unable to change your password.', function() {
-                                                                                                               _change_password_form.getForm().reset();
-                                                                                                       });
-                                                                                               }
-                                                                                       });
-                                                                               }
-                                                                       }
-                                                               ],
-                                                       }),
-                                               });
-                                               edit_window.show(this);
-                                       },
-                               },
-                               {
-                                       text: 'Log out',
-                                       handler: function(button, event) {
-                                               Gilbert.api.auth.logout(function(result) {
-                                                       if (result) {
-                                                               Ext.MessageBox.alert('Logout successful', 'You have been logged out.', function() {
-                                                                       document.location.reload();
-                                                               });
-                                                       } else {
-                                                               Ext.MessageBox.alert('Logout failed', 'A bit odd, you might say.');
-                                                       }
-                                               });
-                                       },
-                               },
-                               ],
-                       });
-               };
-               
-               toolbar.doLayout();
-               viewport.doLayout();
-       },
-});
-
-Ext.BLANK_IMAGE_URL = '{% mediaurl "gilbert/extjs/resources/images/default/s.gif" %}';
-
-Ext.onReady(function(){
-       Gilbert.Application = new Gilbert.ui.Application({
-               user: '{% filter force_escape %}{% firstof user.get_full_name user.username %}{% endfilter %}',
-               plugins: [{% for plugin in gilbert.plugin_registry.values %}{% if plugin.gilbert_plugin_javascript %}
-                       {{ plugin.gilbert_plugin_javascript|safe }},
-               {% endif %}{% endfor %}],
-       });
-});
-{% endif %}
\ No newline at end of file
diff --git a/contrib/gilbert/templates/gilbert/base.html b/contrib/gilbert/templates/gilbert/base.html
deleted file mode 100644 (file)
index 61a1c65..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{% load staticmedia %}<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-<head>{% block head %}
-       <title>{% block title %}{{ gilbert.title }}{% endblock %}</title>
-       
-       {% block css %}
-       <link rel="stylesheet" type="text/css" href="{% mediaurl 'gilbert/extjs/resources/css/ext-all.css' %}" />
-       <link rel="stylesheet" type="text/css" href="{% mediaurl 'gilbert/extjs/resources/css/xtheme-gray.css' %}" />
-       <link rel="stylesheet" type="text/css" href="{% url gilbert:css %}" />
-       {% endblock %}
-       
-       {% block js %}
-       <script type="text/javascript" src="{% mediaurl 'gilbert/extjs/adapter/ext/ext-base.js' %}"></script>
-       <script type="text/javascript" src="{% mediaurl 'gilbert/extjs/ext-all.js' %}"></script>
-       <script type="text/javascript" src="{% url gilbert:api %}" id="api.js"></script>
-       {% endblock %}
-       
-{% endblock %}</head>
-<body style="background-image: url({% mediaurl 'gilbert/wallpaper.jpg' %});">{% block body %}{% endblock %}</body>
-</html>
diff --git a/contrib/gilbert/templates/gilbert/css.css b/contrib/gilbert/templates/gilbert/css.css
deleted file mode 100644 (file)
index e69de29..0000000
index a4d9290..23a7e87 100644 (file)
@@ -1 +1,86 @@
-{% extends 'gilbert/base.html' %}
\ No newline at end of file
+{% load staticmedia %}<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>{% block head %}
+       <title>{% block title %}{{ gilbert.title }}{% endblock %}</title>
+       
+       {% block css %}
+       <link rel="stylesheet" type="text/css" href="{% mediaurl 'gilbert/extjs/resources/css/ext-all.css' %}" />
+       <link rel="stylesheet" type="text/css" href="{% mediaurl 'gilbert/extjs/resources/css/xtheme-gray.css' %}" />
+       <link rel="stylesheet" type="text/css" href="{% url gilbert:css %}" />
+       {% for plugin in gilbert.plugin_registry.values %}{% for css in plugin.css %}
+       <link rel="stylesheet" type="text/css" href="{{ css }}" />
+       {% endfor %}{% endfor %}
+       {% endblock %}
+       
+       {% block js %}
+       <script type="text/javascript" src="{% mediaurl 'gilbert/extjs/adapter/ext/ext-base.js' %}"></script>
+       <script type="text/javascript" src="{% mediaurl 'gilbert/extjs/ext-all.js' %}"></script>
+       <script type="text/javascript">
+               Ext.BLANK_IMAGE_URL = '{% mediaurl "gilbert/extjs/resources/images/default/s.gif" %}';
+       </script>
+       <script type="text/javascript" src="{% mediaurl 'gilbert/Gilbert.lib.js' %}"></script>
+       <script type="text/javascript" src="{% url gilbert:api %}"></script>
+       <script type="text/javascript">
+               GILBERT_LOGGED_IN = {% if logged_in %}true{% else %}false{% endif %};
+               GILBERT_PLUGINS = [];
+       </script>
+       {% for plugin in gilbert.plugin_registry.values %}{% for js in plugin.js %}
+       <script type="text/javascript" src="{{ js }}"></script>
+       {% endfor %}{% endfor %}
+       <script type="text/javascript">
+               Ext.onReady(function() {
+                       GILBERT_APPLICATION = new Gilbert.lib.Application({
+                               user: '{% filter force_escape %}{% firstof user.get_full_name user.username %}{% endfilter %}',
+                               plugins: [new (function() {
+                                       return {
+                                               init: function(application) {
+                                                       var plugin = this;
+                                                       application.mainmenu.insert(0, {
+                                                               xtype: 'button',
+                                                               text: '<span style="font-weight: bolder; text-transform: uppercase;">Gilbert</span>',
+                                                               handler: function(button, event) {
+                                                                       plugin.showAbout(button);
+                                                               },
+                                                       });
+                                                       application.mainmenu.insert(1, {
+                                                               xtype: 'tbseparator',
+                                                       });
+                                               },
+                                               showAbout: function(sender) {
+                                                       if (!this.about_window) {
+                                                               var about_window = this.about_window = new Ext.Window({
+                                                                       height: 176,
+                                                                       width: 284,
+                                                                       header: false,
+                                                                       html: '<h1>{{ gilbert.title }}</h1><h2>Version {{ gilbert.version }}</h2>',
+                                                                       bodyStyle: 'background: none; font-size: larger; line-height: 1.4em; text-align: center;',
+                                                                       modal: true,
+                                                                       closeAction: 'hide',
+                                                                       renderTo: Ext.getBody(),
+                                                                       closable: false,
+                                                                       resizable: false,
+                                                                       draggable: false,
+                                                                       fbar: [{
+                                                                               text: 'OK',
+                                                                               handler: function(button, event) {
+                                                                                       about_window.hide();
+                                                                               }
+                                                                       }],
+                                                                       defaultButton: 0,
+                                                               });
+                                                       }
+                                                       this.about_window.render(Ext.getBody());
+                                                       this.about_window.center();
+                                                       this.about_window.show();
+                                               },
+                                       }
+                               })()].concat(GILBERT_PLUGINS),
+                       });
+               });
+       </script>
+       {% endblock %}
+       
+{% endblock %}</head>
+<body style="background-image: url({% mediaurl 'gilbert/wallpaper.jpg' %});">{% block body %}{% endblock %}</body>
+</html>
diff --git a/contrib/gilbert/templates/gilbert/styles.css b/contrib/gilbert/templates/gilbert/styles.css
new file mode 100644 (file)
index 0000000..d6d6eba
--- /dev/null
@@ -0,0 +1 @@
+{% for icon, url in icons.items %}.{{icon}}{background:url({{ url }}) no-repeat !important;}{% endfor %}
\ No newline at end of file
diff --git a/contrib/gilbert/utils.py b/contrib/gilbert/utils.py
deleted file mode 100644 (file)
index 0e7b071..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-from inspect import isclass, getargspec
-
-
-def is_gilbert_plugin(class_or_instance):
-       from philo.contrib.gilbert.options import GilbertPluginBase, GilbertPlugin
-       return (isclass(class_or_instance) and issubclass(class_or_instance, GilbertPlugin)) or isinstance(class_or_instance, GilbertPlugin) or (getattr(class_or_instance, '__metaclass__', None) is GilbertPluginBase) or (getattr(class_or_instance, 'gilbert_plugin', False) and (getattr(class_or_instance, 'gilbert_plugin_name', None) is not None) and (getattr(class_or_instance, 'gilbert_plugin_classes', None) is not None))
-
-
-def is_gilbert_class(class_or_instance):
-       from philo.contrib.gilbert.options import GilbertClassBase, GilbertClass
-       return (isclass(class_or_instance) and issubclass(class_or_instance, GilbertClass)) or isinstance(class_or_instance, GilbertClass) or (getattr(class_or_instance, '__metaclass__', None) is GilbertClassBase) or (getattr(class_or_instance, 'gilbert_class', False) and (getattr(class_or_instance, 'gilbert_class_name', None) is not None) and (getattr(class_or_instance, 'gilbert_class_methods', None) is not None))
-
-
-def is_gilbert_method(function):
-       return getattr(function, 'gilbert_method', False)
-
-
-def gilbert_method(function=None, name=None, argc=None, restricted=True):
-       def wrapper(function):
-               setattr(function, 'gilbert_method', True)
-               setattr(function, 'gilbert_method_name', name or function.__name__)
-               setattr(function, 'gilbert_method_restricted', restricted)
-               new_argc = argc
-               if new_argc is None:
-                       args = getargspec(function)[0]
-                       new_argc = len(args)
-                       if new_argc > 0:
-                               if args[0] == 'self':
-                                       args = args[1:]
-                                       new_argc = new_argc - 1
-                               if new_argc > 0:
-                                       if args[0] == 'request':
-                                               args = args[1:]
-                                               new_argc = new_argc - 1
-               setattr(function, 'gilbert_method_argc', new_argc)
-               return function
-       if function is not None:
-               return wrapper(function)
-       return wrapper
-
-
-def call_gilbert_method(method, cls, request, *args, **kwargs):
-       arg_list = getargspec(method)[0]
-       if len(arg_list) > 0:
-               if arg_list[0] == 'self':
-                       if len(arg_list) > 1 and arg_list[1] == 'request':
-                               return method(cls, request, *args, **kwargs)
-                       return method(cls, *args, **kwargs)
-               elif arg_list[0] == 'request':
-                       return method(request, *args, **kwargs)
-       else:
-               return method(*args, **kwargs)
\ No newline at end of file