updated portal
authorJordan Augé <jordan.auge@lip6.fr>
Tue, 11 Jun 2013 13:52:45 +0000 (15:52 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Tue, 11 Jun 2013 13:52:45 +0000 (15:52 +0200)
manifold/core/query.py
manifold/js/manifold.js
manifold/manifoldproxy.py
plugins/form/__init__.py
plugins/form/form.html
plugins/form/form.js
plugins/wizard/wizard.js
portal/templates/user-registration-step2.html [moved from portal/user-registration-step2.html with 100% similarity]
portal/views.py

index e3118e0..bedf04b 100644 (file)
@@ -148,21 +148,26 @@ class Query(object):
         self.filters = Filter([])
         self.params  = {}
         self.fields  = set([])
-        self.timestamp      = "now" 
+        self.timestamp  = "now" 
         self.timestamp  = 'now' # ignored for now
 
     @returns(StringTypes)
     def __str__(self):
+        return "SELECT %(select)s%(from)s%(where)s%(at)s" % {
+            "select": ", ".join(self.get_select())          if self.get_select()    else "*",
+            "from"  : "\n  FROM  %s" % self.get_from(),
+            "where" : "\n  WHERE %s" % self.get_where()     if self.get_where()     else "",
+            "at"    : "\n  AT    %s" % self.get_timestamp() if self.get_timestamp() else ""
+        }
+
+    @returns(StringTypes)
+    def __repr__(self):
         return "SELECT %s FROM %s WHERE %s" % (
             ", ".join(self.get_select()) if self.get_select() else '*',
             self.get_from(),
             self.get_where()
         )
 
-    @returns(StringTypes)
-    def __repr__(self):
-        return self.__str__()
-
     def __key(self):
         return (self.action, self.object, self.filters, frozendict(self.params), frozenset(self.fields))
 
@@ -284,6 +289,10 @@ class Query(object):
     @classmethod
     def execute(self, object): return self.action('execute', object)
 
+    def at(self, timestamp):
+        self.timestamp = timestamp
+        return self
+
     def filter_by(self, *args):
         if len(args) == 1:
             filters = args[0]
@@ -298,7 +307,11 @@ class Query(object):
             raise Exception, 'Invalid expression for filter'
         return self
             
-    def select(self, fields):
+    def select(self, fields=None):
+        if not fields:
+            # Delete all fields
+            self.fields = set()
+            return self
         if not isinstance(fields, (set, list, tuple)):
             fields = [fields]
         for field in fields:
@@ -313,8 +326,9 @@ class AnalyzedQuery(Query):
 
     # XXX we might need to propagate special parameters sur as DEBUG, etc.
 
-    def __init__(self, query=None):
+    def __init__(self, query=None, metadata=None):
         self.clear()
+        self.metadata = metadata
         if query:
             self.query_uuid = query.query_uuid
             self.analyze(query)
@@ -324,8 +338,10 @@ class AnalyzedQuery(Query):
     @returns(StringTypes)
     def __str__(self):
         out = []
+        fields = self.get_select()
+        fields = ", ".join(fields) if fields else '*'
         out.append("SELECT %s FROM %s WHERE %s" % (
-            ", ".join(self.get_select()),
+            fields,
             self.get_from(),
             self.get_where()
         ))
@@ -343,12 +359,25 @@ class AnalyzedQuery(Query):
     def subquery(self, method):
         # Allows for the construction of a subquery
         if not method in self._subqueries:
-            analyzed_query = AnalyzedQuery()
+            analyzed_query = AnalyzedQuery(metadata=self.metadata)
             analyzed_query.action = self.action
-            analyzed_query.object = method
+            try:
+                type = self.metadata.get_field_type(self.object, method)
+            except ValueError ,e: # backwards 1..N
+                type = method
+            analyzed_query.object = type
             self._subqueries[method] = analyzed_query
         return self._subqueries[method]
 
+    def get_subquery(self, method):
+        return self._subqueries.get(method, None)
+
+    def remove_subquery(self, method):
+        del self._subqueries[method]
+
+    def get_subquery_names(self):
+        return set(self._subqueries.keys())
+
     def subqueries(self):
         for method, subquery in self._subqueries.iteritems():
             yield (method, subquery)
@@ -360,6 +389,8 @@ class AnalyzedQuery(Query):
         for predicate in filters:
             if '.' in predicate.key:
                 method, subkey = pred.key.split('.', 1)
+                # Method contains the name of the subquery, we need the type
+                # XXX type = self.metadata.get_field_type(self.object, method)
                 sub_pred = Predicate(subkey, pred.op, pred.value)
                 self.subquery(method).filter_by(sub_pred)
             else:
@@ -372,6 +403,8 @@ class AnalyzedQuery(Query):
         for field in fields:
             if '.' in field:
                 method, subfield = field.split('.', 1)
+                # Method contains the name of the subquery, we need the type
+                # XXX type = self.metadata.get_field_type(self.object, method)
                 self.subquery(method).select(subfield)
             else:
                 super(AnalyzedQuery, self).select(field)
@@ -381,6 +414,8 @@ class AnalyzedQuery(Query):
         for param, value in self.params.items():
             if '.' in param:
                 method, subparam = param.split('.', 1)
+                # Method contains the name of the subquery, we need the type
+                # XXX type = self.metadata.get_field_type(self.object, method)
                 self.subquery(method).set({subparam: value})
             else:
                 super(AnalyzedQuery, self).set({param: value})
index d24211c..927924f 100644 (file)
@@ -77,10 +77,10 @@ var manifold = {
      * \brief We use js function closure to be able to pass the query (array)
      * to the callback function used when data is received
      */
-    success_closure: function(query, publish_uuid, domid)
+    success_closure: function(query, publish_uuid, callback /*domid*/)
     {
         return function(data, textStatus) {
-            manifold.asynchroneous_success(data, query, publish_uuid, domid);
+            manifold.asynchroneous_success(data, query, publish_uuid, callback /*domid*/);
         }
     },
 
@@ -109,18 +109,20 @@ var manifold = {
             }
             // not quite sure what happens if we send a string directly, as POST data is named..
             // this gets reconstructed on the proxy side with ManifoldQuery.fill_from_POST
-                jQuery.post(manifold.proxy_url, {'json':query_json} , manifold.success_closure(query, publish_uuid, tuple.domid));
+                jQuery.post(manifold.proxy_url, {'json':query_json} , manifold.success_closure(query, publish_uuid, tuple.callback /*domid*/));
         })
     },
 
     /**
      * \brief Forward a query to the manifold backend
      * \param query (dict) the query to be executed asynchronously
+     * \param callback (function) the function to be called when the query terminates
+     * Deprecated:
      * \param domid (string) the domid to be notified about the results (null for using the pub/sub system
      */
-    forward: function(query, domid) {
+    forward: function(query, callback /*domid*/) {
         var query_json = JSON.stringify(query);
-        $.post(manifold.proxy_url, {'json': query_json} , manifold.success_closure(query, query.query_uuid, domid));
+        $.post(manifold.proxy_url, {'json': query_json} , manifold.success_closure(query, query.query_uuid, callback/*domid*/));
     },
 
     /*!
@@ -177,8 +179,12 @@ var manifold = {
     // most of the time publish_uuid will be query.query_uuid
     // however in some cases we wish to publish the result under a different uuid
     // e.g. an updater wants to publish its result as if from the original (get) query
-    asynchroneous_success : function (data, query, publish_uuid, domid) {
+    asynchroneous_success : function (data, query, publish_uuid, callback /*domid*/) {
         // xxx should have a nicer declaration of that enum in sync with the python code somehow
+
+        /* If a callback has been specified, we redirect results to it */
+        if (!!callback) { callback(data); return; }
+
         if (data.code == 2) { // ERROR
             alert("Your session has expired, please log in again");
             window.location="/logout/";
@@ -192,18 +198,20 @@ var manifold = {
         // once everything is checked we can use the 'value' part of the manifoldresult
         var result=data.value;
         if (result) {
-            if (!!domid) {
-                /* Directly inform the requestor */
-                if (manifold.asynchroneous_debug) messages.debug("directing result to " + domid);
-                jQuery('#' + domid).trigger('results', [result]);
-            } else {
+            //if (!!callback /* domid */) {
+            //    /* Directly inform the requestor */
+            //    if (manifold.asynchroneous_debug) messages.debug("directing result to callback");
+            //    callback(result);
+            //    //if (manifold.asynchroneous_debug) messages.debug("directing result to " + domid);
+            //    //jQuery('#' + domid).trigger('results', [result]);
+            //} else {
                 /* XXX Jordan XXX I don't need publish_uuid here... What is it used for ? */
                 /* query is the query we sent to the backend; we need to find the
                  * corresponding analyezd_query in manifold.all_queries
                  */
                 tmp_query = manifold.find_query(query.query_uuid);
                 manifold.publish_result_rec(tmp_query.analyzed_query, result);
-            }
+            //}
 
         }
     },
index 99de784..fd4c5ad 100644 (file)
@@ -53,7 +53,14 @@ with the query passed using POST"""
         manifold_query.fill_from_POST(request.POST)
         offline_filename="offline-%s-%s.json"%(manifold_query.action,manifold_query.object)
         # retrieve session for request
-        manifold_api_session_auth = request.session['manifold']['auth']
+
+        # We allow some requests to use the ADMIN user account
+        if manifold_query.get_from() == 'local:user' and manifold_query.get_action() == 'create':
+            print "W: Used hardcoded demo account for admin queries"
+            manifold_api_session_auth = {'AuthMethod': 'password', 'Username': 'demo', 'AuthString': 'demo'}
+        else:
+            manifold_api_session_auth = request.session['manifold']['auth']
+
         if debug_empty and manifold_query.action.lower()=='get':
             json_answer=json.dumps({'code':0,'value':[]})
             print "By-passing : debug_empty & 'get' request : returning a fake empty list"
index cbbac20..ff569d5 100644 (file)
@@ -7,16 +7,13 @@ class CreateForm (Plugin):
         Plugin.__init__(self, **settings)
         print "SETTINGS", settings
         assert 'page'   in settings, "You should specify page"
-        assert 'object' in settings or 'fields' in settings, "You should specify object or field list"
+        assert 'object' in settings, "You should specify object"
 
         if 'object' in settings:
-            # Retrieve object fields from metadata
-            metadata = settings['page'].get_metadata()
-            md_o = metadata.details_by_object(settings['object'])
-            self.columns = md_o['column']
+            self.object = settings['object']
 
-        elif 'fields' in settings:
-            self.columns = []
+        if 'fields' in settings:
+            self.fields = []
             for field in settings['fields']:
                 c = {
                     'name'          : field.get('name', ''),
@@ -27,7 +24,12 @@ class CreateForm (Plugin):
                     'validate_err'  : field.get('validate_err', ''),
                     'old_value'     : 'POST',
                 }
-                self.columns.append(c)
+                self.fields.append(c)
+        else:
+            # Attempt to retrieve object fields from metadata
+            metadata = settings['page'].get_metadata()
+            md_o = metadata.details_by_object(settings['object'])
+            self.fields = md_o['column']
     
     def requirements (self):
         # Some should be included by default by manifold
@@ -42,32 +44,9 @@ class CreateForm (Plugin):
     def template_env (self, request):
         env={}
         env.update(self.__dict__)
-        #env['columns']=self.columns
         return env
 
     def template_file (self):
         return "form.html"
 
-    def json_settings_list (self): return ['plugin_uuid', 'columns']
-
-    def get_validation_js(self):
-        # XXX We need to avoid sending the same query twice !"
-        # somehow store something into the dom, to perform an update afterwards
-        # XXX This should be moved to a template
-        # XXX We also need some storage into the wizard to be displayed later
-        return """
-            // Useless since this is now a parameter
-            //frm = document.forms['form_%(domid)s'];
-
-            // Loop on the fields and test regexp if present
-            err = false;
-            $.each(options.columns, function(column) {
-                if (!frm.elements[column['field']].match(column['validate_rx'])) {
-                    $('err_%(domid)s_' + column['field']).html(column['validation_err']);
-                    err = true;
-                }
-            });
-            if (!err) {
-                // Issue json query
-            }
-        """ % self.__dict__
+    def json_settings_list (self): return ['plugin_uuid', 'object', 'fields']
index cc2226f..9fd413c 100644 (file)
@@ -2,12 +2,12 @@
   <form class="cmxform" id="form_{{domid}}" method="post" action="#" enctype="multipart/form-data" onsubmit="return validate_form_{{domid}}(this)">
     <fieldset>
 
-      {% for c in columns %}
+      {% for f in fields %}
       <div class="field">
-        <label for="lname">{{c.name}}</label>
-        <input type="{{c.type}}" id="{{domid}}-{{c.field}}" name="{{c.field}}" size="25" class="required" minlength="2" value="{{c.old_value}}"/>
-        <p class="hint">{{c.description}}</p>
-        <span id='err-{{domid}}-{{c.field}}'><font color='red'>{{c.validation_err}}</font></span>
+        <label for="lname">{{f.name}}</label>
+        <input type="{{f.type}}" id="{{domid}}-{{f.field}}" name="{{f.field}}" size="25" class="required" minlength="2" value="{{f.old_value}}"/>
+        <p class="hint">{{f.description}}</p>
+        <span id='err-{{domid}}-{{f.field}}'><font color='red'>{{f.validation_err}}</font></span>
       </div>
       {% endfor %}
 
index 8cec174..b9d30d3 100644 (file)
 
         /* methods */
 
-        this.on_result = function(data) {
-
-        }
-        
         /**
          * \brief Validate the form
          * \param validate_callback (function) a callback to be triggered when validation is done
             // Loop on the fields and test regexp if present
             var err = false;
             var params = {}
-            $.each(options.columns, function(i, column) {
-                var value = frm.elements[column['field']].value;
-                var rx    = column['validate_rx'];
+            $.each(options.fields, function(i, field) {
+                var value = frm.elements[field['field']].value;
+                var rx    = field['validate_rx'];
                 var str   = '';
                 if (rx && !value.match(rx)) {
-                    str = column['validate_err'];
+                    str = field['validate_err'];
                     err = true;
                 }
-                params[column['field']] = value;
-                $('#err-' + options.plugin_uuid + '-' + column['field']).html(str);
+                params[field['field']] = value;
+                $('#err-' + options.plugin_uuid + '-' + field['field']).html(str);
             });
 
             /* If the form correctly validates, we issue a create query */
             if (!err) {
                 var query = {
                     'action': 'create',
-                    'object': 'local:user',
+                    'object': options.object,
                     'params': params,
                 };
 
                 /* Inform user about ongoing query: spinner */
-                this.disable();
+                this.enable(false);
                 manifold.spin($obj);
 
                 /* Issue json query and wait for callback */
-                manifold.forward(query, this.onresult);
+                manifold.forward(query, function(data) {
+                    manifold.spin($obj, false);
+                    if (data.code != 0) { // ERROR OR WARNING, which we don't expect
+                        alert("ERROR IN CALLING THE API");
+                        validate_callback(false);
+                        return;
+                    }
+                    validate_callback(true);
+                });
             }
 
             /* Note, if the create has already been done (or fails, or ... ?)
         /**
          * \brief Disable the form entirely, during a create query for example
          */
-        this.disable = function() {
+        this.enable = function(is_enabled) {
 
         }
 
index f974259..c95826f 100644 (file)
             }
             
             /* Otherwise, proceed to next step */
-            this.GoToNextStep();
+            $this.GoToNextStep();
         }
     
         this.doForwardProgress = function()
index ba7f30f..869bd2d 100644 (file)
@@ -44,9 +44,10 @@ def index(request):
         sons.append(Raw(page=p, title=STEP1_TITLE, togglable=False, html=STEP0))
         start_step += 1
     else:
-        # XXX This should become local:user
         # We could pass a list of fields also, instead of retrieving them from metadata
         # Otherwise we need some heuristics to display nice forms
+        # XXX Could we log the user in after the form is validated ?
+        # XXX Explain the password is for XXX
         field_list = [{
             'name'        : 'First name',
             'field'       : 'firstname',
@@ -77,7 +78,7 @@ def index(request):
             'type'        : 'password',
             'description' : 'Enter your password again',
         }]
-        sons.append(CreateForm(page = p, title = STEP1_TITLE, togglable = False, fields = field_list))
+        sons.append(CreateForm(page = p, title = STEP1_TITLE, togglable = False, object = 'local:user', fields = field_list))
 
     # STEP 2
     # If the user already exists (is logged), let's display a summary of its institution