Browse Source

Webui admin controller bugfix & factorisation

Implements a new exception class for webui : HttpErrors that uses a new template : errors.html to display multiples errors
Yann Weber 8 years ago
parent
commit
4df8bdea89

+ 20
- 0
lodel/plugins/webui/exceptions.py View File

@@ -47,5 +47,25 @@ class HttpException(Exception):
47 47
             return HttpException.STATUS_STR[status_fam][status_code]
48 48
 
49 49
 
50
+##@brief Render multiple errors
51
+class HttpErrors(HttpException):
52
+    
53
+    def __init__(self, errors, title = None, status_code = 400):
54
+        super().__init__(status_code = status_code, tpl = 'errors.html',
55
+            custom = title)
56
+        self.errors = errors
57
+
58
+    def render(self, request):
59
+        from .interface.template.loader import TemplateLoader
60
+        loader = TemplateLoader()
61
+        tpl_vars = {
62
+            'status_code': self.status_code,
63
+            'errors': self.errors,
64
+            'title': self.custom }
65
+        response = Response(
66
+            loader.render_to_response(self.tpl, template_vars = tpl_vars),
67
+            mimetype = 'text/html')
68
+        response.status_code = self.status_code
69
+        return response
50 70
 
51 71
         

+ 91
- 85
lodel/plugins/webui/interface/controllers/admin.py View File

@@ -6,7 +6,9 @@ from lodel.context import LodelContext
6 6
 LodelContext.expose_modules(globals(), {
7 7
     'lodel.leapi.exceptions': [],
8 8
     'lodel.logger': 'logger',
9
-    'lodel.leapi.datahandlers.base_classes': ['MultipleRef']})
9
+    'lodel.leapi.datahandlers.base_classes': ['MultipleRef'],
10
+    'lodel.leapi.exceptions': ['LeApiDataCheckErrors'],
11
+    'lodel.exceptions': ['LodelExceptions']})
10 12
 
11 13
 from ...client import WebUiClient
12 14
 import leapi_dyncode as dyncode
@@ -37,54 +39,20 @@ def admin_update(request):
37 39
     #    return get_response('users/signin.html')
38 40
     msg=''
39 41
     
40
-    # If the form has been submitted
41
-    if request.method == 'POST':
42
-        error = None
43
-        datas = list()
44
-        classname = request.form['classname']
45
-        logger.warning('Composed uids broken here')
46
-        uid = request.form['uid']
42
+    datas = process_form(request)
43
+    if not(datas is False):
44
+        if 'lodel_id' not in datas:
45
+            raise HttpException(400)
46
+        target_leo = dyncode.Object.name2class(datas['classname'])
47
+        leo = target_leo.get_from_uid(datas['lodel_id'])
47 48
         try:
48
-            target_leo = dyncode.Object.name2class(classname)
49
-        except LeApiError:
50
-            classname = None
51
-        if classname is None or target_leo.is_abstract():
52
-            raise HttpException(400, custom = "Bad classname given")
53
-
54
-        leo_to_update = target_leo.get_from_uid(uid)
55
-        
56
-        errors = dict()
57
-        for fieldname, value in request.form.items():
58
-            #We want to drop 2 input named 'classname' and 'uid'
59
-            if len(fieldname) > 12:
60
-                #Other input names are : field_input_FIELDNAME
61
-                #Extract the fieldname
62
-                fieldname = fieldname[12:]
63
-                try:
64
-                    dh = leo_to_update.data_handler(fieldname)
65
-                except NameError as e:
66
-                    errors[fieldname] = e
67
-                    continue
68
-                #Multiple ref list preparation
69
-                if issubclass(dh.__class__, MultipleRef):
70
-                    value=[spl for spl in [
71
-                           v.strip() for v in value.split(LIST_SEPARATOR)]
72
-                        if len(spl) > 0]
73
-                elif len(value.strip()) == 0:
74
-                    value = None
75
-                try:
76
-                    leo_to_update.set_data(fieldname, value)
77
-                except Exception as e:
78
-                    errors[fieldname] = e
79
-                    continue
80
-        if len(errors) > 0:
81
-            custom_msg = '<h1>Errors in datas</h1><ul>'
82
-            for fname, error in errors.items():
83
-                custom_msg += '<li>%s : %s</li>' % (
84
-                    fname, error)
85
-            custom_msg += '</ul>'
86
-            raise HttpException(400, custom = custom_msg)
87
-        leo_to_update.update()
49
+            leo.update(
50
+                { f:datas[f] for f in datas if f not in ('classname', 'lodel_id')})
51
+        except LeApiDataCheckErrors as e:
52
+            raise HttpErrors(
53
+                title='Form validation errors', errors = e._exceptions)
54
+            
55
+            
88 56
 
89 57
     # Display of the form with the object's values to be updated
90 58
     if 'classname' in request.GET:
@@ -134,45 +102,25 @@ def admin_create(request):
134 102
     # temporary, the acl will be more restrictive
135 103
     #if WebUiClient.is_anonymous():
136 104
     #    return get_response('users/signin.html')
137
-    classname = None
138
-     # If the form has been submitted
139
-    if request.method == 'POST':
140
-        error = None
141
-        datas = list()
142
-        classname = request.form['classname']
143
-        try:
144
-            target_leo = dyncode.Object.name2class(classname)
145
-        except LeApiError:
146
-            classname = None
147
-        if classname is None or target_leo.is_abstract():
148
-            raise HttpException(400)
149
-        fieldnames = target_leo.fieldnames()
150
-        fields = dict()
151 105
 
152
-        for in_put, in_value in request.form.items():
153
-            # The classname is handled by the datasource, we are not allowed to modify it
154
-            # both are hidden in the form, to identify the object here
155
-            if in_put != 'classname' and in_value != '':
156
-                dhl = target_leo.data_handler(in_put[12:])
157
-                if dhl.is_reference() and in_value != '' and not dhl.is_singlereference():
158
-                    logger.info(in_value)
159
-                    in_value.replace(" ","")
160
-                    in_value=in_value.split(',')
161
-                    in_value=list(in_value)
162
-                fields[in_put[12:]] = in_value
163
-            if in_value == '':
164
-                fields[in_put[12:]] = None             
165
-
166
-        # Insertion in the database of the values corresponding to a new object
167
-        new_uid = target_leo.insert(fields)
168
-        
169
-        # reurn to the form with a confirmation or error message
170
-        if not new_uid is None:
171
-            msg = 'Successfull creation';
106
+    datas = process_form(request)
107
+    if not(datas is False):
108
+        target_leo = dyncode.Object.name2class(datas['classname'])
109
+        if 'lodel_id' in datas:
110
+            raise HttpException(400)
111
+        try:
112
+            new_uid = target_leo.insert(
113
+                { f:datas[f] for f in datas if f != 'classname'})
114
+        except LeApiDataCheckErrors as e:
115
+            raise HttpErrors(
116
+                title='Form validation errors', errors = e._exceptions)
117
+        if new_uid is None:
118
+            raise HttpException(400, "Creation fails")
172 119
         else:
173
-            msg = 'Oops something wrong happened...object not saved'
174
-        return get_response('admin/admin_create.html', target=target_leo, msg = msg)
175
-    
120
+            return get_response(
121
+                'admin/admin_create.html', target=target_leo,
122
+                msg = "Created with uid %s" % new_uid)
123
+
176 124
     # Display of an empty form
177 125
     if 'classname' in request.GET:
178 126
         # We need the class to create an object in
@@ -320,4 +268,62 @@ def search_object(request):
320 268
         # TODO The get method must be implemented here
321 269
     return get_response('admin/admin_search.html', my_classes = dyncode.dynclasses)
322 270
 
271
+##@brief Process a form POST and return the posted datas
272
+#@param request : the request object
273
+#@return a dict with datas as value and fieldname as key
274
+def process_form(request):
275
+    if request.method != 'POST':
276
+        return False
277
+    res = dict()
278
+    errors = dict()
279
+    #Fetch concerned LeObject
280
+    if 'classname' not in request.form:
281
+        logger.error("Received a form without classname !")
282
+        raise HttpException(400)
283
+    res['classname'] = classname = request.form['classname']
284
+    try:
285
+        target_leo = dyncode.Object.name2class(classname)
286
+    except LeApiError:
287
+        logger.error(
288
+            "Received a form with an invalid leo name : '%s'" % classname)
289
+        raise HttpException(400, "No leobject named '%s'" % classname)
290
+    if target_leo.is_abstract():
291
+        logger.error(
292
+            "Received a form with an abstract leo : '%s'" % classname)
293
+        raise HttpException(400, '%s is abstract' % classname)
294
+    #Process input fields
295
+    for fieldname, value in request.form.items():
296
+        if fieldname == 'classname':
297
+            continue
298
+        elif fieldname == 'uid':
299
+            fieldname = 'lodel_id' #wow
300
+        elif fieldname.startswith('field_input_'):
301
+            fieldname = fieldname[12:]
302
+        try:
303
+            dh = target_leo.data_handler(fieldname)
304
+        except NameError as e:
305
+            errors[fieldname] = e
306
+            continue
307
+        if dh.is_reference() and not dh.is_singlereference():
308
+            #Converting multiple references fields
309
+            value = value.strip()
310
+            if len(value) == 0:
311
+                #handling default value for empty string
312
+                if hasattr(dh, 'default'):
313
+                    value = dh.default
314
+                else:
315
+                    #if not explicit default value, enforcing default as 
316
+                    #an empty list
317
+                    value = []
318
+            else:
319
+                value = [ v.strip() for v in value.split(LIST_SEPARATOR) ]
320
+        else:
321
+            #Handling default value for empty string
322
+            if len(value.strip()) == 0 and hasattr(dh, 'default'):
323
+                value = dh.default
324
+        res[fieldname] = value
325
+    if len(errors) > 0:
326
+        del(res)
327
+        raise HttpErrors(errors, title="Form validation error")
328
+    return res
323 329
 

+ 18
- 0
lodel/plugins/webui/templates/errors.html View File

@@ -0,0 +1,18 @@
1
+<!DOCTYPE html>
2
+<html>
3
+	<head>
4
+		<title>{{status_code}} {{status_str}}</title>
5
+	</head>
6
+	<body>
7
+                {% if title is not none %}
8
+                <h1>{{title}}</h1>
9
+                {% else %}
10
+		<h1>{{status_code}} {{status_str}}</h1>
11
+                {% endif %}
12
+                <ul>
13
+                {% for k, v in errors.items() %}
14
+                    <li><strong>{{k}}</strong> : {{v}}</li>
15
+                {% endfor %}
16
+                </ul>
17
+	</body>
18
+</html>

Loading…
Cancel
Save