new object pluginset
authorThierry Parmentelat <thierry.parmentelat@inria.fr>
Thu, 7 Mar 2013 17:00:50 +0000 (18:00 +0100)
committerThierry Parmentelat <thierry.parmentelat@inria.fr>
Thu, 7 Mar 2013 17:00:50 +0000 (18:00 +0100)
a bit like Plugins in php
acts as a context for a given page
has the Prelude object for the side effects
must be passed to Plugin creation

12 files changed:
Makefile
auth/backend.py
auth/manifoldbackend.py
engine/manifoldapi.py
engine/plugin.py
engine/pluginset.py [new file with mode: 0644]
engine/prelude.py
engine/static/js/onavail.js [new file with mode: 0644]
engine/static/js/plugin.js
myslice/dashboard.py
plugins/simplelist.py
templates/layout-myslice.html

index c4074fa..a29b5b3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ force:
 #################### compute emacs tags
 # list files under git but exclude third-party stuff like bootstrap and jquery
 myfiles: force
-       @git ls-files | egrep -v 'insert(_|-)above|/bootstrap.*[0-9]|/jquery/|datatables/'
+       @git ls-files | egrep -v 'insert(_|-)above|static/bootstrap|/jquery/|datatables/'
 
 # in general it's right to rely on the contents as reported by git
 tags: force
index 0eb1f2d..4c93676 100644 (file)
@@ -1,10 +1,6 @@
 # import the User object
 from django.contrib.auth.models import User
 
-# import the IMAP library
-#from imaplib import IMAP4
-
-# import time - this is used to create Django's internal username
 import time
 
 # Name my backend 'MyCustomBackend'
index 54b16e7..dbaf863 100644 (file)
@@ -1,10 +1,8 @@
-# import the User object
-from django.contrib.auth.models import User
-from engine.manifoldapi import ManifoldAPI
+import time
 
+from django.contrib.auth.models import User
 
-# import time - this is used to create Django's internal username
-import time
+from engine.manifoldapi import ManifoldAPI
 
 # Name my backend 'ManifoldBackend'
 class ManifoldBackend:
index b125a59..b900855 100644 (file)
@@ -1,5 +1,6 @@
 # Manifold API Python interface
 import xmlrpclib
+
 from myslice.config import Config
 
 debug=True
index cad2706..00b37b7 100644 (file)
@@ -6,6 +6,7 @@ import json
 
 from django.template.loader import render_to_string
 
+from engine.pluginset import PluginSet
 from engine.prelude import Prelude
 
 #################### 
@@ -15,7 +16,14 @@ from engine.prelude import Prelude
 # . True : to debug all plugin
 
 DEBUG= False
-DEBUG= [ 'SimpleList' ]
+#DEBUG= [ 'SimpleList' ]
+
+# decorator to deflect calls on Plugin to its PluginSet
+def to_prelude (method):
+    def actual (self, *args, **kwds):
+        prelude_method=Prelude.__dict__[method.__name__]
+        return prelude_method(self.pluginset.prelude,*args, **kwds)
+    return actual
 
 class Plugin:
 
@@ -31,6 +39,7 @@ class Plugin:
     ########## 
     # Constructor
     #### mandatory
+    # . pluginset: the context of the request being served
     # . title: is used visually for displaying the widget
     #### optional
     # . togglable: whether it can be turned on and off (like PleKitToggle)
@@ -46,8 +55,9 @@ class Plugin:
     # p=Plugin(foo='bar')
     # which will result in 'foo' being accessible to the template engine
     # 
-    def __init__ (self, title, domid=None,
+    def __init__ (self, pluginset, title, domid=None,
                   visible=True, togglable=True, toggled=True, **settings):
+        self.pluginset = pluginset
         self.title=title
         if not domid: domid=Plugin.newdomid()
         self.domid=domid
@@ -64,6 +74,8 @@ class Plugin:
             print "%s init dbg .... BEG"%self.classname
             for (k,v) in self.__dict__.items(): print "dbg %s:%s"%(k,v)
             print "%s init dbg .... END"%self.classname
+        # do this only once the structure is fine
+        self.pluginset.record_plugin(self)
 
     def _classname (self): 
         try:    return self.__class__.__name__
@@ -102,8 +114,6 @@ class Plugin:
     # returns the html code for that plugin
     # in essence, wraps the results of self.render_content ()
     def render (self, request):
-        # initialize prelude placeholder if needed
-        self._init_prelude (request)
         # call render_content
         plugin_content = self.render_content (request)
         # shove this into plugin.html
@@ -118,7 +128,7 @@ class Plugin:
             env ['settings_json' ] = self.settings_json()
             # compute plugin-specific initialization
             js_init = render_to_string ( 'plugin-setenv.js', env )
-            self.add_js_chunks (request, js_init)
+            self.add_js_chunks (js_init)
         
         # interpret the result of requirements ()
         self.handle_requirements (request)
@@ -144,26 +154,6 @@ class Plugin:
             print "%s.render_content: END --------------------"%self.classname
         return result
 
-    #################### requirements/prelude management
-    def _init_prelude (self, request):
-        if not hasattr (request, 'plugin_prelude'): 
-            # include css/plugins.css
-            request.plugin_prelude=Prelude(css_files='css/plugin.css')
-
-    # can be used directly in render_content()
-    def add_js_files (self, request, files):
-        self._init_prelude (request)
-        request.plugin_prelude.add_js_files (files)
-    def add_css_files (self, request, files):
-        self._init_prelude (request)
-        request.plugin_prelude.add_css_files (files)
-    def add_js_chunks (self, request, chunks):
-        self._init_prelude (request)
-        request.plugin_prelude.add_js_chunks (chunks)
-    def add_css_chunks (self, request, chunks):
-        self._init_prelude (request)
-        request.plugin_prelude.add_css_chunks (chunks)
-
     # or from the result of self.requirements()
     def handle_requirements (self, request):
         try:
@@ -172,8 +162,8 @@ class Plugin:
                 if self.need_debug():
                     print "%s: handling requirement %s"%(self.classname,v)
                 method_name='add_'+k
-                method=Plugin.__dict__[method_name]
-                method(self,request,v)
+                method=PluginSet.__dict__[method_name]
+                method(self.pluginset,v)
         except AttributeError: 
             # most likely the object does not have that method defined, which is fine
             pass
@@ -182,6 +172,17 @@ class Plugin:
             traceback.print_exc()
             pass
 
+    #################### requirements/prelude management
+    # just forward to self.pluginset - see decorator above
+    @to_prelude
+    def add_js_files (self):pass
+    @to_prelude
+    def add_css_files (self):pass
+    @to_prelude
+    def add_js_chunks (self):pass
+    @to_prelude
+    def add_css_chunks (self):pass
+
     ######################################## abstract interface
     # your plugin is expected to implement either 
     # (*) def render_content(self, request) -> html fragment
diff --git a/engine/pluginset.py b/engine/pluginset.py
new file mode 100644 (file)
index 0000000..633874c
--- /dev/null
@@ -0,0 +1,66 @@
+# the supervisor for Plugins
+# keeps a handle on all present plugins for managing their queries in a consistent way
+# it is expected to exist one such object for a given page
+
+from engine.prelude import Prelude
+
+# decorator to deflect calls on this PluginSet to its prelude
+def to_prelude (method):
+    def actual (self, *args, **kwds):
+        prelude_method=Prelude.__dict__[method.__name__]
+        return prelude_method(self.prelude,*args, **kwds)
+    return actual
+
+class PluginSet:
+
+    def __init__ (self):
+        self._plugins = {}
+        # queue of queries
+        self._queue=[]
+        self.prelude=Prelude(css_files='css/plugin.css')
+        # no queries yet, needed ?
+
+    # record known plugins hashed on their domid
+    def record_plugin (self, plugin):
+        self._plugins[plugin.domid]=plugin
+
+    def get_plugin (self, domid):
+        return self._plugins.get(domid,None)
+    
+    def reset_queue (self):
+        self._queue = []
+
+    # the js async methods (see manifold_async_success)
+    # offer the option to deliver the result to a specific DOM elt
+    # otherwise it goes through the pubsub using query's uuid
+    def query_enqueue (self, query, domid=None):
+        self._queue.append ( (query,domid,) )
+
+    # return the javascript that triggers all the queries
+    def exec_queue_asynchroneously (self):
+        js = ""
+        js += "var manifold_query_array = new Array();"
+        for (query,domid) in self._queue:
+            qjson=query.to_json()
+            id="'%s'"%domid if domid else undefined
+            js += "manifold_query_array.push({'query':'%(qjson)s', 'id':%(id)s});"%locals()
+        js += "onFunctionAvailable('manifold_async_exec', function() {manifold_async_exec(manifold_query_array);}, this, true);"
+        self.reset_queue()
+        # run only once the document is ready
+        js = "jQuery(function(){%(js)s})"%locals()
+        self.prelude.inspect('before add_js_chunks in async')
+        self.add_js_chunks (js)
+        self.prelude.inspect('after add_js_chunks in async')
+
+    #################### requirements/prelude management
+    # just forward to self.pluginset - see decorator above
+    @to_prelude
+    def add_js_files (self):pass
+    @to_prelude
+    def add_css_files (self):pass
+    @to_prelude
+    def add_js_chunks (self):pass
+    @to_prelude
+    def add_css_chunks (self):pass
+    @to_prelude
+    def template_env (self):pass
index 2975cca..2905355 100644 (file)
@@ -2,12 +2,14 @@ from types import StringTypes, ListType
 
 from django.template.loader import render_to_string
 
+debug=True
+
 class Prelude:
 
     """A class for collecting dependencies on js/css files or fragments"""
 
     keys=[ 'js_files','css_files','js_chunks', 'css_chunks' ]
-    def __init__ (self, js_files=[], css_files=[], js_chunks=[], css_chunks=[]):
+    def __init__ (self, js_files=None, css_files=None, js_chunks=None, css_chunks=None):
         # it's tempting to use sets but sets are not ordered..
         self.js_files  = Prelude._normalize(js_files)
         self.css_files = Prelude._normalize(css_files)
@@ -16,7 +18,8 @@ class Prelude:
 
     @staticmethod
     def _normalize (input):
-        if   isinstance (input, ListType):      return input
+        if not input:                           return []
+        elif isinstance (input, ListType):      return input
         elif isinstance (input, StringTypes):   return [ input ]
         else:                                   return list (input)
 
@@ -27,10 +30,19 @@ class Prelude:
         for i in Prelude._normalize (x):
             if i not in self.css_files: self.css_files.append(i)
     def add_js_chunks (self, x):
+        print 'add_js_chunks BEFORE',len(self.js_chunks)
         self.js_chunks += Prelude._normalize (x)
+        print 'add_js_chunks AFTER',len(self.js_chunks)
     def add_css_chunks (self, x):
         self.css_chunks += Prelude._normalize (x)
 
+    def inspect_string (self,msg):
+        result =  'Prelude.inspect %s (%s) with '%(msg,self)
+        result += ",".join( [ "%s->%s"%(k,len(getattr(self,k))) for k in ['js_files','js_chunks','css_files','css_chunks'] ] )
+        return result
+    def inspect (self,msg):
+        print self.inspect_string(msg)
+
     # first attempt was to use a simple dict like this
     #    env={}
     #    env['js_files']=  self.js_files
@@ -46,6 +58,8 @@ class Prelude:
     # 
     # so a much simpler and safer approach is for use to compute the html header directly
     def template_env (self): 
+        inspect = self.inspect ('template_env')
+        print inspect
         env={}
         env['js_files']=  self.js_files
         env['css_files']= self.css_files
diff --git a/engine/static/js/onavail.js b/engine/static/js/onavail.js
new file mode 100644 (file)
index 0000000..57ef552
--- /dev/null
@@ -0,0 +1,18 @@
+onavail_debug=true;
+
+function onFunctionAvailable(sMethod, oCallback, oObject, bScope) {
+    if (eval('typeof ' + sMethod) == 'function') {
+       if (onavail_debug) console.log("onFunctionAvailable, running");
+        bScope ? oCallback.call(oObject) : oCallback(oObject);
+    } else { 
+       if (onavail_debug) console.log("onFunctionAvailable, delaying for 50 ms");
+        setTimeout(function () {onFunctionAvailable(sMethod, oCallback, oObject, bScope);}, 50);
+    }
+}       
+function onObjectAvailable(sMethod, oCallback, oObject, bScope) {
+    if (eval('typeof ' + sMethod) == 'object') {
+        bScope ? oCallback.call(oObject) : oCallback(oObject);
+    } else {
+        setTimeout(function () {onObjectAvailable(sMethod, oCallback, oObject, bScope);}, 50);
+    }
+}
index 0a8bd44..709421e 100644 (file)
@@ -1,7 +1,3 @@
-/*
- * This file is included in includes/js_libraries.php
- */
-
 function getMetadata(){
     return all_headers;
 }
index 142d33b..3fa6d20 100644 (file)
@@ -7,14 +7,11 @@ from django.shortcuts import render_to_response
 
 from django.contrib.auth.decorators import login_required
 
+from engine.pluginset import PluginSet
 from engine.manifoldquery import ManifoldQuery
 
-#from plugins.verticallayout import VerticalLayout
-#from plugins.tabs import Tabs
 from plugins.simplelist import SimpleList
-# from plugins.slicelist import SliceList
-# from plugins.quickfilter import QuickFilter
-# from plugins.raw import Raw
+
 # 
 from myslice.viewutils import topmenu_items, the_user
 # from myslice.viewutils import hard_wired_slice_names, hard_wired_list, lorem_p, lorem, quickfilter_criterias
@@ -22,6 +19,8 @@ from myslice.viewutils import topmenu_items, the_user
 @login_required
 def dashboard_view (request):
     
+    pluginset = PluginSet()
+
     slices_query = ManifoldQuery (action='get',
                                   method='slice',
                                   timestamp='latest',
@@ -31,10 +30,8 @@ def dashboard_view (request):
                                   # in addition this currently returns all slices anyways
                                   sort='slice_hrn',)
 
-    # variables that will get passed to this template
-    template_env = {}
-    
     main_plugin = SimpleList ( # setting visible attributes first
+        pluginset=pluginset,
         title='SimpleList and dataTables',
         header='slices list', 
         with_datatables=True,
@@ -43,12 +40,15 @@ def dashboard_view (request):
         query=slices_query,
         key='slice_hrn',
         value='slice_hrn',
-)
+        )
 
+    # variables that will get passed to the view-plugin.html template
+    template_env = {}
+    
     # define 'content_main' to the template engine
     template_env [ 'content_main' ] = main_plugin.render(request)
 
-#    ##########
+#    ########## add another plugin with the same request, on the RHS pane
 #    # lacks a/href to /slice/%s
 #    related_plugin = SliceList (title='SliceList plugin',domid='slicelist1',
 #                                with_datatables='yes', 
@@ -58,15 +58,18 @@ def dashboard_view (request):
 #    template_env [ 'content_related' ] = related_plugin.render (request)
 
     # more general variables expected in the template
-    template_env [ 'title' ] = 'Test Plugin View' 
+    template_env [ 'title' ] = 'SimpleList Test View'
+    # the menu items on the top 
     template_env [ 'topmenu_items' ] = topmenu_items('dashboard', request) 
+    # so we can sho who is logged
     template_env [ 'username' ] = the_user (request) 
 
+    pluginset.exec_queue_asynchroneously ()
+
     # request.plugin_prelude holds a summary of the requirements() for all plugins
     # define {js,css}_{files,chunks}
-    prelude_env = request.plugin_prelude.template_env()
+    prelude_env = pluginset.template_env()
     template_env.update(prelude_env)
-
     return render_to_response ('view-plugin.html',template_env,
                                context_instance=RequestContext(request))
                                
index 815170e..19a7769 100644 (file)
@@ -14,7 +14,7 @@ class SimpleList (Plugin) :
     def template_file (self): return "simplelist.html"
 
     def requirements (self):
-        reqs = { 'js_files' : [ "js/simplelist.js", "js/plugin.js", "js/query.js",
+        reqs = { 'js_files' : [ "js/simplelist.js", "js/plugin.js", "js/query.js", "js/onavail.js",
                                 "js/manifold-pubsub.js", "js/manifold-async.js", ] ,
                  'css_files': [ "css/simplelist.css" ],
                  }
index 22309ba..98c6f78 100644 (file)
@@ -9,7 +9,7 @@
 {{ header_prelude }}
 </head>{# let's add these ones no matter what #}
 {% insert_str prelude "jquery/js/jquery.js" %}
-{% insert prelude_js %} "jQuery.noConflict();" {% endinsert %}
+{# {% insert prelude_js %} jQuery.noConflict(); {% endinsert %} #}
 {% insert_str prelude "js/plugin-init.js" %}
 {% insert_str prelude "css/myslice.css" %}
 <body>