From: Thierry Parmentelat Date: Sat, 14 Dec 2013 09:39:44 +0000 (+0100) Subject: A first stab at the ‘validatebutton’ plugin X-Git-Tag: myslice-0.3-0~87 X-Git-Url: http://git.onelab.eu/?p=myslice.git;a=commitdiff_plain;h=69482802ee8c2bef1323ba7b95ba5e629d11fa91 A first stab at the ‘validatebutton’ plugin This is not yet integrated in the sliceview and others, but works in a standalone test view like this http://localhost:8080/trash/simplevalidatebutton/ple.inria.thierry_parmentelat — in a nutshell, we have topmenu display a disabled link ‘validation’ and send a query in background if anything gets returned by this query - meaning the user is PI at some authority - we then re-enable the menu button — There is one catch at this point, which is that the DOM element attached to the plugin - the menu button - needs to have the ‘plugin’ class because this is what is used to trigger query events So in this version we add this plugin class to the topmenu buttons, which is awkward — The reason why this plugin is special is, it does not come with its own DOM element but piggybacks on an existing one - which had no reason to have this plugin class attached The plan to get this right is to have the plugin.js code manage an extra class (for example named pubsub) independantly from the DOM building code and conventions --- diff --git a/manifold/static/js/manifold.js b/manifold/static/js/manifold.js index 1bb5b975..58f28485 100644 --- a/manifold/static/js/manifold.js +++ b/manifold/static/js/manifold.js @@ -317,7 +317,7 @@ var manifold = { // reasonably low-noise, shows manifold requests coming in and out asynchroneous_debug : true, // print our more details on result publication and related callbacks - publish_result_debug : false, + publish_result_debug : true, /** * \brief We use js function closure to be able to pass the query (array) @@ -417,19 +417,21 @@ var manifold = { count += 1; }); if (manifold.publish_result_debug) - messages.debug(".. publish_result NEW API (2) count=" + count); + messages.debug(".. publish_result (2) has used NEW API on " + count + " records"); manifold.raise_record_event(query.query_uuid, DONE); + if (manifold.publish_result_debug) + messages.debug(".. publish_result (3) has used NEW API to say DONE"); // OLD PLUGIN API BELOW /* Publish an update announce */ var channel="/results/" + query.query_uuid + "/changed"; if (manifold.publish_result_debug) - messages.debug(".. publish_result OLD API (3) " + channel); + messages.debug(".. publish_result (4) OLD API on channel" + channel); $.publish(channel, [result, query]); if (manifold.publish_result_debug) - messages.debug(".. publish_result - END (4) q=" + query.__repr()); + messages.debug(".. publish_result (5) END q=" + query.__repr()); }, /*! @@ -736,7 +738,7 @@ var manifold = { } if (manifold.asynchroneous_debug) - messages.debug ("========== asynchroneous_success " + query.object + " -- before process_query_records"); + messages.debug ("========== asynchroneous_success " + query.object + " -- before process_query_records [" + query.query_uuid +"]"); // once everything is checked we can use the 'value' part of the manifoldresult var result=data.value; @@ -760,10 +762,12 @@ var manifold = { **************************************************************************/ raise_event_handler: function(type, query_uuid, event_type, value) { + if (manifold.publish_result_debug) + messages.debug("raise_event_handler, quuid="+query_uuid+" type="+type+" event_type="+event_type); if ((type != 'query') && (type != 'record')) throw 'Incorrect type for manifold.raise_event()'; // xxx we observe quite a lot of incoming calls with an undefined query_uuid - // this should be fixed upstream + // this should be fixed upstream in manifold I expect if (query_uuid === undefined) { messages.warning("undefined query in raise_event_handler"); return; @@ -775,8 +779,10 @@ var manifold = { $.each(channels, function(i, channel) { if (value === undefined) { + if (manifold.publish_result_debug) messages.debug("triggering [no value] on channel="+channel+" and event_type="+event_type); $('.plugin').trigger(channel, [event_type]); } else { + if (manifold.publish_result_debug) messages.debug("triggering [value="+value+"] on channel="+channel+" and event_type="+event_type); $('.plugin').trigger(channel, [event_type, value]); } }); diff --git a/myslice/settings.py b/myslice/settings.py index ff355e40..33588d40 100644 --- a/myslice/settings.py +++ b/myslice/settings.py @@ -195,7 +195,7 @@ INSTALLED_APPS = ( # 'django.contrib.admindocs', 'portal', # temporary - not packaged - # 'trash', + 'trash', 'sample', 'sandbox' # DEPRECATED # 'django.contrib.formtools', diff --git a/plugins/validatebutton/__init__.py b/plugins/validatebutton/__init__.py new file mode 100644 index 00000000..ab143fc4 --- /dev/null +++ b/plugins/validatebutton/__init__.py @@ -0,0 +1,33 @@ +from unfold.plugin import Plugin + +class ValidateButton (Plugin): + + """This plugin is designed to work together with topmenu. + +It will check to see if user has PI rights at least on one authority, +and if so will enable corresponding button in topmenu. + +A realistic example would have incoming query as + +Query.get('ple:user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') + +""" + + def __init__ (self, query=None, button_domid=None, **settings): + Plugin.__init__ (self, **settings) + # set defaults + if query is None: + query = Query.get('ple:user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') + if button_domid is None: button_domid="topmenu-validate" + self.query=query + self.button_domid=button_domid + + # this does not have any materialization + def render_content (self, request): + return "" + + def requirements (self): + return { 'js_files': [ 'js/validatebutton.js', 'js/manifold-query.js', ], } + + def json_settings_list (self): + return [ 'query_uuid', 'button_domid', ] diff --git a/plugins/validatebutton/static/js/validatebutton.js b/plugins/validatebutton/static/js/validatebutton.js new file mode 100644 index 00000000..dd987cde --- /dev/null +++ b/plugins/validatebutton/static/js/validatebutton.js @@ -0,0 +1,33 @@ +// first application is for the 'validation' button in the topmenu +// if the subject query is non empty, then we turn on the subject button +// that is provided through button_domid + +(function($){ + + var debug=false; + debug=true + + var ValidateButton = Plugin.extend({ + + init: function(options, element) { + this._super(options, element); + this.listen_query(options.query_uuid); + this.triggered=false; + }, + + // we have received at least one answer: we'll do something + on_new_record: function (record) { + // we only need to act on the first record + if (this.triggered) return; + if (debug) messages.debug("validatebutton.on_query_done - turning on "+this.options.button_domid); + $('#'+this.options.button_domid).removeClass('disabled'); + this.triggered=true; + }, + // for reference only, since there is nothing we need to do at this point + on_query_done: function() { + }, + }); + + $.plugin('ValidateButton', ValidateButton); + +})(jQuery); diff --git a/trash/simplevalidatebutton.py b/trash/simplevalidatebutton.py new file mode 100644 index 00000000..ff77ee2f --- /dev/null +++ b/trash/simplevalidatebutton.py @@ -0,0 +1,65 @@ +# just one instance of validator +from django.views.generic.base import TemplateView +from django.template import RequestContext +from django.shortcuts import render_to_response + +from manifold.core.query import Query, AnalyzedQuery + +from unfold.page import Page + +from ui.topmenu import topmenu_items, the_user + +from plugins.validatebutton import ValidateButton + +class SimpleValidateButtonView (TemplateView): + + # mention a user name in the URL as .../trash/simplevalidatebutton/ple.inria.thierry_parmentelat + def get (self, request, username='ple.inria.thierry_parmentelat'): + + page=Page(request) + page.expose_js_metadata() + query_pi_auths = Query.get('ple:user').filter_by('user_hrn', '==', username ).select('pi_authorities') + page.enqueue_query(query_pi_auths) + + # even though this plugin does not have any html materialization, the corresponding domid + # must exist because it is searched at init-time to create the JS plugin + # so we simply piggy-back the target button here + validatebutton = ValidateButton (page=page, + # see above + domid='topmenu-validation', + query=query_pi_auths, + # this one is the target for a $.show() when the query comes back + button_domid="topmenu-validation") + + # variables that will get passed to the view-unfold1.html template + template_env = {} + + # there is a need to call render() for exposing the query and creating the js plugin + # even though this returns an empty string + rendered=validatebutton.render(request) + + # write something of our own instead + template_env ['unfold_main'] = '

Some title

' + + # more general variables expected in the template + template_env [ 'title' ] = 'simple validatebutton %(username)s'%locals() + # the menu items on the top + template_env [ 'topmenu_items' ] = topmenu_items('Slice', request) + # so we can see who is logged + template_env [ 'username' ] = the_user (request) + + # don't forget to run the requests + page.expose_queries () + + # the prelude object in page contains a summary of the requirements() for all plugins + # define {js,css}_{files,chunks} + prelude_env = page.prelude_env() + +# print prelude_env.keys() +# for k in [ 'js_files' ] : +# print 'prelude_env',prelude_env,k,prelude_env[k] + + template_env.update(prelude_env) + result=render_to_response ('view-unfold1.html',template_env, + context_instance=RequestContext(request)) + return result diff --git a/trash/urls.py b/trash/urls.py index 5e14d977..32e3ad4b 100644 --- a/trash/urls.py +++ b/trash/urls.py @@ -2,6 +2,7 @@ from django.conf.urls import patterns, include, url import trash.simpletableview import trash.simplegridview +import trash.simplevalidatebutton urlpatterns = patterns( '', @@ -11,4 +12,5 @@ urlpatterns = patterns( url(r'^dashboard/?$', 'trash.dashboard.dashboard_view'), url(r'^simpletable/(?P[\w\.]+)/?$', trash.simpletableview.SimpleTableView.as_view()), url(r'^simplegrid/(?P[\w\.]+)/?$', trash.simplegridview.SimpleGridView.as_view()), + url(r'^simplevalidatebutton/(?P[\w\._]+)/?$', trash.simplevalidatebutton.SimpleValidateButtonView.as_view()), ) diff --git a/ui/templates/widget-topmenu.html b/ui/templates/widget-topmenu.html index dbcbe1fc..79f1bf83 100644 --- a/ui/templates/widget-topmenu.html +++ b/ui/templates/widget-topmenu.html @@ -28,7 +28,8 @@ {% else %} - {% if d.is_active %}
  • {% else %}
  • {% endif %} +
  • {{ d.label }}
  • {% endif %} {% endfor %} diff --git a/ui/topmenu.py b/ui/topmenu.py index 6fbede50..d0238788 100644 --- a/ui/topmenu.py +++ b/ui/topmenu.py @@ -7,11 +7,14 @@ from manifold.core.query import Query # dropdowns are kind of ad hoc for now, and limited to one level # [ # ### a regular first-level button -# {'label':...,'href':...}, +# {'label':...,'href':..., ['domid':.., 'disabled':...]}, # ### a dropdown # { 'label': ..., 'href'=..., 'dropdown':True, 'contents': [ { 'label':.., 'href'} ] } # , ..] +# see also templates/widget-topmenu.html for how these items are put together +# and plugins/validatebutton for how this hident button is turned on when necessary + # current: the beginning of the label in the menu that you want to outline def topmenu_items (current,request=None): has_user=request.user.is_authenticated() @@ -20,41 +23,44 @@ def topmenu_items (current,request=None): if has_user: result.append({'label':'Dashboard', 'href': '/portal/dashboard/'}) result.append({'label':'Request a slice', 'href': '/portal/slice_request/'}) - # ** Where am I a PI ** - # For this we need to ask SFA (of all authorities) = PI function - user_query = Query().get('local:user').select('config','email') - user_details = execute_query(request, user_query) - - # Required: the user must have an authority in its user.config - # XXX Temporary solution - # not always found in user_details... - config={} -# Deactivated until fixed -# if user_details is not None: -# for user_detail in user_details: -# #email = user_detail['email'] -# if user_detail['config']: -# config = json.loads(user_detail['config']) -# user_detail['authority'] = config.get('authority',"Unknown Authority") -# print "topmenu: %s", (user_detail['authority']) -# if user_detail['authority'] is not None: -# sub_authority = user_detail['authority'].split('.') -# root_authority = sub_authority[0] -# pi_authorities_query = Query.get(root_authority+':user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') -# else: -# pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') -# try: -# pi_authorities_tmp = execute_query(request, pi_authorities_query) -# except: -# pi_authorities_tmp = set() -# pi_authorities = set() -# for pa in pi_authorities_tmp: -# if 'pi_authorities' in pa: -# pi_authorities |= set(pa['pi_authorities']) -# print "pi_authorities =", pi_authorities -# if len(pi_authorities) > 0: -# result.append({'label':'Validation', 'href': '/portal/validate/'}) - result.append({'label':'Validation', 'href': '/portal/validate/'}) +### # ** Where am I a PI ** +### # For this we need to ask SFA (of all authorities) = PI function +### user_query = Query().get('local:user').select('config','email') +### user_details = execute_query(request, user_query) +### +### # Required: the user must have an authority in its user.config +### # XXX Temporary solution +### # not always found in user_details... +### config={} +#### Deactivated until fixed +#### if user_details is not None: +#### for user_detail in user_details: +#### #email = user_detail['email'] +#### if user_detail['config']: +#### config = json.loads(user_detail['config']) +#### user_detail['authority'] = config.get('authority',"Unknown Authority") +#### print "topmenu: %s", (user_detail['authority']) +#### if user_detail['authority'] is not None: +#### sub_authority = user_detail['authority'].split('.') +#### root_authority = sub_authority[0] +#### pi_authorities_query = Query.get(root_authority+':user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') +#### else: +#### pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') +#### try: +#### pi_authorities_tmp = execute_query(request, pi_authorities_query) +#### except: +#### pi_authorities_tmp = set() +#### pi_authorities = set() +#### for pa in pi_authorities_tmp: +#### if 'pi_authorities' in pa: +#### pi_authorities |= set(pa['pi_authorities']) +#### print "pi_authorities =", pi_authorities +#### if len(pi_authorities) > 0: +#### result.append({'label':'Validation', 'href': '/portal/validate/'}) +### result.append({'label':'Validation', 'href': '/portal/validate/'}) + # always create a disabled button for validation, and let the + # validatebutton plugin handle that asynchroneously, based on this domid + result.append({'label':'Validation', 'href': '/portal/validate/', 'domid':'topmenu-validation', 'disabled':True}) dropdown = [] dropdown.append({'label':'Platforms', 'href': '/portal/platforms/'}) dropdown.append({'label':'My Account', 'href': '/portal/account/'}) diff --git a/unfold/page.py b/unfold/page.py index 0d3a81e6..07ce237a 100644 --- a/unfold/page.py +++ b/unfold/page.py @@ -120,7 +120,7 @@ class Page: def expose_js_metadata (self): # expose global MANIFOLD_METADATA as a js variable # xxx this is fetched synchroneously.. - self.add_js_init_chunks("var MANIFOLD_METADATA =" + self.get_metadata().to_json() + ";") + self.add_js_init_chunks("var MANIFOLD_METADATA =" + self.get_metadata().to_json() + ";\n") def expose_js_manifold_config (self): config=Config()