VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py@ 65423

Last change on this file since 65423 was 65350, checked in by vboxsync, 8 years ago

TestManager/webui: Implemented simple column sorting direction toggle during the meeting. (Still testboxes only.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.8 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuibase.py 65350 2017-01-17 15:35:59Z vboxsync $
3
4"""
5Test Manager Web-UI - Base Classes.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2016 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.virtualbox.org. This file is free software;
14you can redistribute it and/or modify it under the terms of the GNU
15General Public License (GPL) as published by the Free Software
16Foundation, in version 2 as it comes in the "COPYING" file of the
17VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL) only, as it comes in the "COPYING.CDDL" file of the
23VirtualBox OSE distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28"""
29__version__ = "$Revision: 65350 $"
30
31
32# Standard python imports.
33import os;
34import sys;
35import string;
36
37# Validation Kit imports.
38from common import webutils, utils;
39from testmanager import config;
40from testmanager.core.base import ModelDataBase, ModelLogicBase, TMExceptionBase;
41from testmanager.core.db import TMDatabaseConnection;
42from testmanager.core.systemlog import SystemLogLogic, SystemLogData;
43from testmanager.core.useraccount import UserAccountLogic
44
45
46class WuiException(TMExceptionBase):
47 """
48 For exceptions raised by Web UI code.
49 """
50 pass;
51
52
53class WuiDispatcherBase(object):
54 """
55 Base class for the Web User Interface (WUI) dispatchers.
56
57 The dispatcher class defines the basics of the page (like base template,
58 menu items, action). It is also responsible for parsing requests and
59 dispatching them to action (POST) or/and content generators (GET+POST).
60 The content returned by the generator is merged into the template and sent
61 back to the webserver glue.
62 """
63
64 ## @todo possible that this should all go into presentation.
65
66 ## The action parameter.
67 ksParamAction = 'Action';
68 ## The name of the default action.
69 ksActionDefault = 'default';
70
71 ## The name of the current page number parameter used when displaying lists.
72 ksParamPageNo = 'PageNo';
73 ## The name of the page length (list items) parameter when displaying lists.
74 ksParamItemsPerPage = 'ItemsPerPage';
75
76 ## The name of the effective date (timestamp) parameter.
77 ksParamEffectiveDate = 'EffectiveDate';
78
79 ## The name of the redirect-to (test manager relative url) parameter.
80 ksParamRedirectTo = 'RedirectTo';
81
82 ## The name of the list-action parameter (WuiListContentWithActionBase).
83 ksParamListAction = 'ListAction';
84
85 ## One or more columns to sort by.
86 ksParamSortColumns = 'SortBy';
87
88 ## The name of the change log enabled/disabled parameter.
89 ksParamChangeLogEnabled = 'ChangeLogEnabled';
90 ## The name of the parmaeter indicating the change log page number.
91 ksParamChangeLogPageNo = 'ChangeLogPageNo';
92 ## The name of the parameter indicate number of change log entries per page.
93 ksParamChangeLogEntriesPerPage = 'ChangeLogEntriesPerPage';
94
95 ## @name Dispatcher debugging parameters.
96 ## {@
97 ksParamDbgSqlTrace = 'DbgSqlTrace';
98 ksParamDbgSqlExplain = 'DbgSqlExplain';
99 ## List of all debugging parameters.
100 kasDbgParams = (ksParamDbgSqlTrace, ksParamDbgSqlExplain,);
101 ## @}
102
103 ## Special action return code for skipping _generatePage. Useful for
104 # download pages and the like that messes with the HTTP header and more.
105 ksDispatchRcAllDone = 'Done - Page has been rendered already';
106
107
108 def __init__(self, oSrvGlue, sScriptName):
109 self._oSrvGlue = oSrvGlue;
110 self._oDb = TMDatabaseConnection(self.dprint if config.g_kfWebUiSqlDebug else None, oSrvGlue = oSrvGlue);
111 self._tsNow = None; # Set by getEffectiveDateParam.
112 self._asCheckedParams = [];
113 self._dParams = None; # Set by dispatchRequest.
114 self._sAction = None; # Set by dispatchRequest.
115 self._dDispatch = { self.ksActionDefault: self._actionDefault, };
116
117 # Template bits.
118 self._sTemplate = 'template-default.html';
119 self._sPageTitle = '$$TODO$$'; # The page title.
120 self._aaoMenus = []; # List of [sName, sLink, [ [sSideName, sLink], .. ] tuples.
121 self._sPageFilter = ''; # The filter controls (optional).
122 self._sPageBody = '$$TODO$$'; # The body text.
123 self._dSideMenuFormAttrs = {}; # key/value with attributes for the side menu <form> tag.
124 self._sRedirectTo = None;
125 self._sDebug = '';
126
127 # Debugger bits.
128 self._fDbgSqlTrace = False;
129 self._fDbgSqlExplain = False;
130 self._dDbgParams = dict();
131 for sKey, sValue in oSrvGlue.getParameters().iteritems():
132 if sKey in self.kasDbgParams:
133 self._dDbgParams[sKey] = sValue;
134 if len(self._dDbgParams) > 0:
135 from testmanager.webui.wuicontentbase import WuiTmLink;
136 WuiTmLink.kdDbgParams = self._dDbgParams;
137
138 # Determine currently logged in user credentials
139 self._oCurUser = UserAccountLogic(self._oDb).tryFetchAccountByLoginName(oSrvGlue.getLoginName());
140
141 # Calc a couple of URL base strings for this dispatcher.
142 self._sUrlBase = sScriptName + '?';
143 if len(self._dDbgParams) > 0:
144 self._sUrlBase += webutils.encodeUrlParams(self._dDbgParams) + '&';
145 self._sActionUrlBase = self._sUrlBase + self.ksParamAction + '=';
146
147
148 def _redirectPage(self):
149 """
150 Redirects the page to the URL given in self._sRedirectTo.
151 """
152 assert self._sRedirectTo is not None;
153 assert len(self._sRedirectTo) > 0;
154 assert self._sPageBody is None;
155 assert self._sPageTitle is None;
156
157 self._oSrvGlue.setRedirect(self._sRedirectTo);
158 return True;
159
160 def _isMenuMatch(self, sMenuUrl, sActionParam):
161 """ Overridable menu matcher. """
162 return sMenuUrl is not None and sMenuUrl.find(sActionParam) > 0;
163
164 def _isSideMenuMatch(self, sSideMenuUrl, sActionParam):
165 """ Overridable side menu matcher. """
166 return sSideMenuUrl is not None and sSideMenuUrl.find(sActionParam) > 0;
167
168 def _generateMenus(self):
169 """
170 Generates the two menus, returning them as (sTopMenuItems, sSideMenuItems).
171 """
172 #
173 # We use the action to locate the side menu.
174 #
175 aasSideMenu = None;
176 for cchAction in range(len(self._sAction), 1, -1):
177 sActionParam = '%s=%s' % (self.ksParamAction, self._sAction[:cchAction]);
178 for aoItem in self._aaoMenus:
179 if self._isMenuMatch(aoItem[1], sActionParam):
180 aasSideMenu = aoItem[2];
181 break;
182 for asSubItem in aoItem[2]:
183 if self._isMenuMatch(asSubItem[1], sActionParam):
184 aasSideMenu = aoItem[2];
185 break;
186 if aasSideMenu is not None:
187 break;
188
189 #
190 # Top menu first.
191 #
192 sTopMenuItems = '';
193 for aoItem in self._aaoMenus:
194 if aasSideMenu is aoItem[2]:
195 sTopMenuItems += '<li class="current_page_item">';
196 else:
197 sTopMenuItems += '<li>';
198 sTopMenuItems += '<a href="' + webutils.escapeAttr(aoItem[1]) + '">' \
199 + webutils.escapeElem(aoItem[0]) + '</a></li>\n';
200
201 #
202 # Side menu (if found).
203 #
204 sActionParam = '%s=%s' % (self.ksParamAction, self._sAction);
205 sSideMenuItems = '';
206 if aasSideMenu is not None:
207 for asSubItem in aasSideMenu:
208 if asSubItem[1] is not None:
209 if self._isSideMenuMatch(asSubItem[1], sActionParam):
210 sSideMenuItems += '<li class="current_page_item">';
211 else:
212 sSideMenuItems += '<li>';
213 sSideMenuItems += '<a href="' + webutils.escapeAttr(asSubItem[1]) + '">' \
214 + webutils.escapeElem(asSubItem[0]) + '</a></li>\n';
215 else:
216 sSideMenuItems += '<li class="subheader_item">' + webutils.escapeElem(asSubItem[0]) + '</li>';
217 return (sTopMenuItems, sSideMenuItems);
218
219 def _generatePage(self):
220 """
221 Generates the page using _sTemplate, _sPageTitle, _aaoMenus, and _sPageBody.
222 """
223 assert self._sRedirectTo is None;
224
225 #
226 # Build the replacement string dictionary.
227 #
228
229 # Provide basic auth log out for browsers that supports it.
230 sUserAgent = self._oSrvGlue.getUserAgent();
231 if (sUserAgent.startswith('Mozilla/') and sUserAgent.find('Firefox') > 0) \
232 or False:
233 # Log in as the logout user in the same realm, the browser forgets
234 # the old login and the job is done. (see apache sample conf)
235 sLogOut = ' (<a href="%s://logout:logout@%s%slogout.py">logout</a>)' \
236 % (self._oSrvGlue.getUrlScheme(), self._oSrvGlue.getUrlNetLoc(), self._oSrvGlue.getUrlBasePath());
237 elif (sUserAgent.startswith('Mozilla/') and sUserAgent.find('Safari') > 0) \
238 or False:
239 # For a 401, causing the browser to forget the old login. Works
240 # with safari as well as the two above. Since safari consider the
241 # above method a phishing attempt and displays a warning to that
242 # effect, which when taken seriously aborts the logout, this method
243 # is preferable, even if it throws logon boxes in the user's face
244 # till he/she/it hits escape, because it always works.
245 sLogOut = ' (<a href="logout2.py">logout</a>)'
246 elif (sUserAgent.startswith('Mozilla/') and sUserAgent.find('MSIE') > 0) \
247 or (sUserAgent.startswith('Mozilla/') and sUserAgent.find('Chrome') > 0) \
248 or False:
249 ## There doesn't seem to be any way to make IE really log out
250 # without using a cookie and systematically 401 accesses based on
251 # some logout state associated with it. Not sure how secure that
252 # can be made and we really want to avoid cookies. So, perhaps,
253 # just avoid IE for now. :-)
254 ## Chrome/21.0 doesn't want to log out either.
255 sLogOut = ''
256 else:
257 sLogOut = ''
258
259 # Prep Menus.
260 (sTopMenuItems, sSideMenuItems) = self._generateMenus();
261
262 # The dictionary (max variable length is 28 chars (see further down)).
263 dReplacements = {
264 '@@PAGE_TITLE@@': self._sPageTitle,
265 '@@LOG_OUT@@': sLogOut,
266 '@@TESTMANAGER_VERSION@@': config.g_ksVersion,
267 '@@TESTMANAGER_REVISION@@': config.g_ksRevision,
268 '@@BASE_URL@@': self._oSrvGlue.getBaseUrl(),
269 '@@TOP_MENU_ITEMS@@': sTopMenuItems,
270 '@@SIDE_MENU_ITEMS@@': sSideMenuItems,
271 '@@SIDE_FILTER_CONTROL@@': self._sPageFilter,
272 '@@SIDE_MENU_FORM_ATTRS@@': '',
273 '@@PAGE_BODY@@': self._sPageBody,
274 '@@DEBUG@@': '',
275 };
276
277 # Side menu form attributes.
278 if len(self._dSideMenuFormAttrs) > 0:
279 dReplacements['@@SIDE_MENU_FORM_ATTRS@@'] = ' '.join(['%s="%s"' % (sKey, webutils.escapeAttr(sValue))
280 for sKey, sValue in self._dSideMenuFormAttrs.iteritems()]);
281
282 # Special current user handling.
283 if self._oCurUser is not None:
284 dReplacements['@@USER_NAME@@'] = self._oCurUser.sUsername;
285 else:
286 dReplacements['@@USER_NAME@@'] = 'unauthorized user "' + self._oSrvGlue.getLoginName() + '"';
287
288 # Prep debug section.
289 if self._sDebug == '':
290 if config.g_kfWebUiSqlTrace or self._fDbgSqlTrace or self._fDbgSqlExplain:
291 self._sDebug = '<h3>Processed in %s ns.</h3>\n%s\n' \
292 % ( utils.formatNumber(utils.timestampNano() - self._oSrvGlue.tsStart,),
293 self._oDb.debugHtmlReport(self._oSrvGlue.tsStart));
294 elif config.g_kfWebUiProcessedIn:
295 self._sDebug = '<h3>Processed in %s ns.</h3>\n' \
296 % ( utils.formatNumber(utils.timestampNano() - self._oSrvGlue.tsStart,), );
297 if config.g_kfWebUiDebugPanel:
298 self._sDebug += self._debugRenderPanel();
299 if self._sDebug != '':
300 dReplacements['@@DEBUG@@'] = u'<div id="debug"><br><br><hr/>' \
301 + (unicode(self._sDebug, errors='ignore') if isinstance(self._sDebug, str)
302 else self._sDebug) \
303 + u'</div>\n';
304
305 #
306 # Load the template.
307 #
308 oFile = open(os.path.join(self._oSrvGlue.pathTmWebUI(), self._sTemplate));
309 sTmpl = oFile.read();
310 oFile.close();
311
312 #
313 # Process the template, outputting each part we process.
314 #
315 offStart = 0;
316 offCur = 0;
317 while offCur < len(sTmpl):
318 # Look for a replacement variable.
319 offAtAt = sTmpl.find('@@', offCur);
320 if offAtAt < 0:
321 break;
322 offCur = offAtAt + 2;
323 if sTmpl[offCur] not in string.ascii_uppercase:
324 continue;
325 offEnd = sTmpl.find('@@', offCur, offCur+28);
326 if offEnd <= 0:
327 continue;
328 offCur = offEnd;
329 sReplacement = sTmpl[offAtAt:offEnd+2];
330 if sReplacement in dReplacements:
331 # Got a match! Write out the previous chunk followed by the replacement text.
332 if offStart < offAtAt:
333 self._oSrvGlue.write(sTmpl[offStart:offAtAt]);
334 self._oSrvGlue.write(dReplacements[sReplacement]);
335 # Advance past the replacement point in the template.
336 offCur += 2;
337 offStart = offCur;
338 else:
339 assert False, 'Unknown replacement "%s" at offset %s in %s' % (sReplacement, offAtAt, self._sTemplate );
340
341 # The final chunk.
342 if offStart < len(sTmpl):
343 self._oSrvGlue.write(sTmpl[offStart:]);
344
345 return True;
346
347 #
348 # Interface for WuiContentBase classes.
349 #
350
351 def getParameters(self):
352 """
353 Returns a (shallow) copy of the request parameter dictionary.
354 """
355 return self._dParams.copy();
356
357 def getDb(self):
358 """
359 Returns the database connection.
360 """
361 return self._oDb;
362
363 def getNow(self):
364 """
365 Returns the effective date.
366 """
367 return self._tsNow;
368
369
370 #
371 # Parameter handling.
372 #
373
374 def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False):
375 """
376 Gets a string parameter.
377 Raises exception if not found and sDefault is None.
378 """
379 if sName in self._dParams:
380 if sName not in self._asCheckedParams:
381 self._asCheckedParams.append(sName);
382 sValue = self._dParams[sName];
383 if isinstance(sValue, list):
384 raise WuiException('%s parameter "%s" is given multiple times: "%s"' % (self._sAction, sName, sValue));
385 sValue = sValue.strip();
386 elif sDefault is None and fAllowNull is not True:
387 raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName,));
388 else:
389 sValue = sDefault;
390
391 if asValidValues is not None and sValue not in asValidValues:
392 raise WuiException('%s parameter %s value "%s" not in %s '
393 % (self._sAction, sName, sValue, asValidValues));
394 return sValue;
395
396 def getBoolParam(self, sName, fDefault = None):
397 """
398 Gets a boolean parameter.
399 Raises exception if not found and fDefault is None, or if not a valid boolean.
400 """
401 sValue = self.getStringParam(sName, [ 'True', 'true', '1', 'False', 'false', '0'],
402 '0' if fDefault is None else str(fDefault));
403 # HACK: Checkboxes doesn't return a value when unchecked, so we always
404 # provide a default when dealing with boolean parameters.
405 return sValue == 'True' or sValue == 'true' or sValue == '1';
406
407 def getIntParam(self, sName, iMin = None, iMax = None, iDefault = None):
408 """
409 Gets a integer parameter.
410 Raises exception if not found and iDefault is None, if not a valid int,
411 or if outside the range defined by iMin and iMax.
412 """
413 if iDefault is not None and sName not in self._dParams:
414 return iDefault;
415
416 sValue = self.getStringParam(sName, None, None if iDefault is None else str(iDefault));
417 try:
418 iValue = int(sValue);
419 except:
420 raise WuiException('%s parameter %s value "%s" cannot be convert to an integer'
421 % (self._sAction, sName, sValue));
422
423 if (iMin is not None and iValue < iMin) \
424 or (iMax is not None and iValue > iMax):
425 raise WuiException('%s parameter %s value %d is out of range [%s..%s]'
426 % (self._sAction, sName, iValue, iMin, iMax));
427 return iValue;
428
429 def getLongParam(self, sName, lMin = None, lMax = None, lDefault = None):
430 """
431 Gets a long integer parameter.
432 Raises exception if not found and lDefault is None, if not a valid long,
433 or if outside the range defined by lMin and lMax.
434 """
435 if lDefault is not None and sName not in self._dParams:
436 return lDefault;
437
438 sValue = self.getStringParam(sName, None, None if lDefault is None else str(lDefault));
439 try:
440 lValue = long(sValue);
441 except:
442 raise WuiException('%s parameter %s value "%s" cannot be convert to an integer'
443 % (self._sAction, sName, sValue));
444
445 if (lMin is not None and lValue < lMin) \
446 or (lMax is not None and lValue > lMax):
447 raise WuiException('%s parameter %s value %d is out of range [%s..%s]'
448 % (self._sAction, sName, lValue, lMin, lMax));
449 return lValue;
450
451 def getTsParam(self, sName, tsDefault = None, fRequired = True):
452 """
453 Gets a timestamp parameter.
454 Raises exception if not found and fRequired is True.
455 """
456 if fRequired is False and sName not in self._dParams:
457 return tsDefault;
458
459 sValue = self.getStringParam(sName, None, None if tsDefault is None else str(tsDefault));
460 (sValue, sError) = ModelDataBase.validateTs(sValue);
461 if sError is not None:
462 raise WuiException('%s parameter %s value "%s": %s'
463 % (self._sAction, sName, sValue, sError));
464 return sValue;
465
466 def getListOfIntParams(self, sName, iMin = None, iMax = None, aiDefaults = None):
467 """
468 Gets parameter list.
469 Raises exception if not found and aiDefaults is None, or if any of the
470 values are not valid integers or outside the range defined by iMin and iMax.
471 """
472 if sName in self._dParams:
473 if sName not in self._asCheckedParams:
474 self._asCheckedParams.append(sName);
475
476 if isinstance(self._dParams[sName], list):
477 asValues = self._dParams[sName];
478 else:
479 asValues = [self._dParams[sName],];
480 aiValues = [];
481 for sValue in asValues:
482 try:
483 iValue = int(sValue);
484 except:
485 raise WuiException('%s parameter %s value "%s" cannot be convert to an integer'
486 % (self._sAction, sName, sValue));
487
488 if (iMin is not None and iValue < iMin) \
489 or (iMax is not None and iValue > iMax):
490 raise WuiException('%s parameter %s value %d is out of range [%s..%s]'
491 % (self._sAction, sName, iValue, iMin, iMax));
492 aiValues.append(iValue);
493 else:
494 aiValues = aiDefaults;
495
496 return aiValues;
497
498 def getListOfStrParams(self, sName, asDefaults = None):
499 """
500 Gets parameter list.
501 Raises exception if not found and asDefaults is None.
502 """
503 if sName in self._dParams:
504 if sName not in self._asCheckedParams:
505 self._asCheckedParams.append(sName);
506
507 if isinstance(self._dParams[sName], list):
508 asValues = [str(s).strip() for s in self._dParams[sName]];
509 else:
510 asValues = [str(self._dParams[sName]).strip(), ];
511 elif asDefaults is None:
512 raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName,));
513 else:
514 asValues = asDefaults;
515
516 return asValues;
517
518 def getListOfTestCasesParam(self, sName, asDefaults = None): # too many local vars - pylint: disable=R0914
519 """Get list of test cases and their parameters"""
520 if sName in self._dParams:
521 if sName not in self._asCheckedParams:
522 self._asCheckedParams.append(sName)
523
524 aoListOfTestCases = []
525
526 aiSelectedTestCaseIds = self.getListOfIntParams('%s[asCheckedTestCases]' % sName, aiDefaults=[])
527 aiAllTestCases = self.getListOfIntParams('%s[asAllTestCases]' % sName, aiDefaults=[])
528
529 for idTestCase in aiAllTestCases:
530 aiCheckedTestCaseArgs = \
531 self.getListOfIntParams(
532 '%s[%d][asCheckedTestCaseArgs]' % (sName, idTestCase),
533 aiDefaults=[])
534
535 aiAllTestCaseArgs = \
536 self.getListOfIntParams(
537 '%s[%d][asAllTestCaseArgs]' % (sName, idTestCase),
538 aiDefaults=[])
539
540 oListEntryTestCaseArgs = []
541 for idTestCaseArgs in aiAllTestCaseArgs:
542 fArgsChecked = True if idTestCaseArgs in aiCheckedTestCaseArgs else False
543
544 # Dry run
545 sPrefix = '%s[%d][%d]' % (sName, idTestCase, idTestCaseArgs,);
546 self.getIntParam(sPrefix + '[idTestCaseArgs]', iDefault = -1,)
547
548 sArgs = self.getStringParam(sPrefix + '[sArgs]', sDefault = '')
549 cSecTimeout = self.getStringParam(sPrefix + '[cSecTimeout]', sDefault = '')
550 cGangMembers = self.getStringParam(sPrefix + '[cGangMembers]', sDefault = '')
551 cGangMembers = self.getStringParam(sPrefix + '[cGangMembers]', sDefault = '')
552
553 oListEntryTestCaseArgs.append((fArgsChecked, idTestCaseArgs, sArgs, cSecTimeout, cGangMembers))
554
555 sTestCaseName = self.getStringParam('%s[%d][sName]' % (sName, idTestCase), sDefault='')
556
557 oListEntryTestCase = \
558 (idTestCase,
559 True if idTestCase in aiSelectedTestCaseIds else False,
560 sTestCaseName,
561 oListEntryTestCaseArgs)
562
563 aoListOfTestCases.append(oListEntryTestCase)
564
565 if aoListOfTestCases == []:
566 if asDefaults is None:
567 raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName))
568 aoListOfTestCases = asDefaults
569
570 return aoListOfTestCases
571
572 def getEffectiveDateParam(self, sParamName = None):
573 """
574 Gets the effective date parameter.
575
576 Returns a timestamp suitable for database and url parameters.
577 Returns None if not found or empty.
578
579 The first call with sParamName set to None will set the internal _tsNow
580 value upon successfull return.
581 """
582
583 sName = sParamName if sParamName is not None else WuiDispatcherBase.ksParamEffectiveDate
584
585 if sName not in self._dParams:
586 return None;
587
588 if sName not in self._asCheckedParams:
589 self._asCheckedParams.append(sName);
590
591 sValue = self._dParams[sName];
592 if isinstance(sValue, list):
593 raise WuiException('%s parameter "%s" is given multiple times: %s' % (self._sAction, sName, sValue));
594 sValue = sValue.strip();
595 if sValue == '':
596 return None;
597
598 #
599 # Timestamp, just validate it and return.
600 #
601 if sValue[0] not in ['-', '+']:
602 (sValue, sError) = ModelDataBase.validateTs(sValue);
603 if sError is not None:
604 raise WuiException('%s parameter "%s" ("%s") is invalid: %s' % (self._sAction, sName, sValue, sError));
605 if sParamName is None and self._tsNow is None:
606 self._tsNow = sValue;
607 return sValue;
608
609 #
610 # Relative timestamp. Validate and convert it to a fixed timestamp.
611 #
612 chSign = sValue[0];
613 (sValue, sError) = ModelDataBase.validateTs(sValue[1:]);
614 if sError is not None:
615 raise WuiException('%s parameter "%s" ("%s") is invalid: %s' % (self._sAction, sName, sValue, sError));
616 if sValue[-6] in ['-', '+']:
617 raise WuiException('%s parameter "%s" ("%s") is a relative timestamp but incorrectly includes a time zone.'
618 % (self._sAction, sName, sValue));
619 offTime = 11;
620 if sValue[offTime - 1] != ' ':
621 raise WuiException('%s parameter "%s" ("%s") incorrect format.' % (self._sAction, sName, sValue));
622 sInterval = 'P' + sValue[:(offTime - 1)] + 'T' + sValue[offTime:];
623
624 self._oDb.execute('SELECT CURRENT_TIMESTAMP ' + chSign + ' \'' + sInterval + '\'::INTERVAL');
625 oDate = self._oDb.fetchOne()[0];
626
627 sValue = str(oDate);
628 if sParamName is None and self._tsNow is None:
629 self._tsNow = sValue;
630 return sValue;
631
632 def getRedirectToParameter(self, sDefault = None):
633 """
634 Gets the special redirect to parameter if it exists, will Return default
635 if not, with None being a valid default.
636
637 Makes sure the it doesn't got offsite.
638 Raises exception if invalid.
639 """
640 if sDefault is not None or self.ksParamRedirectTo in self._dParams:
641 sValue = self.getStringParam(self.ksParamRedirectTo, sDefault = sDefault);
642 cch = sValue.find("?");
643 if cch < 0:
644 cch = sValue.find("#");
645 if cch < 0:
646 cch = len(sValue);
647 for ch in (':', '/', '\\', '..'):
648 if sValue.find(ch, 0, cch) >= 0:
649 raise WuiException('Invalid character (%c) in redirect-to url: %s' % (ch, sValue,));
650 else:
651 sValue = None;
652 return sValue;
653
654
655 def _checkForUnknownParameters(self):
656 """
657 Check if we've handled all parameters, raises exception if anything
658 unknown was found.
659 """
660
661 if len(self._asCheckedParams) != len(self._dParams):
662 sUnknownParams = '';
663 for sKey in self._dParams:
664 if sKey not in self._asCheckedParams:
665 sUnknownParams += ' ' + sKey + '=' + str(self._dParams[sKey]);
666 raise WuiException('Unknown parameters: ' + sUnknownParams);
667
668 return True;
669
670 def _assertPostRequest(self):
671 """
672 Makes sure that the request we're dispatching is a POST request.
673 Raises an exception of not.
674 """
675 if self._oSrvGlue.getMethod() != 'POST':
676 raise WuiException('Expected "POST" request, got "%s"' % (self._oSrvGlue.getMethod(),))
677 return True;
678
679 #
680 # Client browser type.
681 #
682
683 ## @name Browser types.
684 ## @{
685 ksBrowserFamily_Unknown = 0;
686 ksBrowserFamily_Gecko = 1;
687 ksBrowserFamily_Webkit = 2;
688 ksBrowserFamily_Trident = 3;
689 ## @}
690
691 ## @name Browser types.
692 ## @{
693 ksBrowserType_FamilyMask = 0xff;
694 ksBrowserType_Unknown = 0;
695 ksBrowserType_Firefox = (1 << 8) | ksBrowserFamily_Gecko;
696 ksBrowserType_Chrome = (2 << 8) | ksBrowserFamily_Webkit;
697 ksBrowserType_Safari = (3 << 8) | ksBrowserFamily_Webkit;
698 ksBrowserType_IE = (4 << 8) | ksBrowserFamily_Trident;
699 ## @}
700
701 def getBrowserType(self):
702 """
703 Gets the browser type.
704 The browser family can be extracted from this using ksBrowserType_FamilyMask.
705 """
706 sAgent = self._oSrvGlue.getUserAgent();
707 if sAgent.find('AppleWebKit/') > 0:
708 if sAgent.find('Chrome/') > 0:
709 return self.ksBrowserType_Chrome;
710 if sAgent.find('Safari/') > 0:
711 return self.ksBrowserType_Safari;
712 return self.ksBrowserType_Unknown | self.ksBrowserFamily_Webkit;
713 if sAgent.find('Gecko/') > 0:
714 if sAgent.find('Firefox/') > 0:
715 return self.ksBrowserType_Firefox;
716 return self.ksBrowserType_Unknown | self.ksBrowserFamily_Gecko;
717 return self.ksBrowserType_Unknown | self.ksBrowserFamily_Unknown;
718
719 def isBrowserGecko(self, sMinVersion = None):
720 """ Returns true if it's a gecko based browser. """
721 if (self.getBrowserType() & self.ksBrowserType_FamilyMask) != self.ksBrowserFamily_Gecko:
722 return False;
723 if sMinVersion is not None:
724 sAgent = self._oSrvGlue.getUserAgent();
725 sVersion = sAgent[sAgent.find('Gecko/')+6:].split()[0];
726 if sVersion < sMinVersion:
727 return False;
728 return True;
729
730 #
731 # Debugging
732 #
733
734 def _debugProcessDispatch(self):
735 """
736 Processes any debugging parameters in the request and adds them to
737 _asCheckedParams so they won't cause trouble in the action handler.
738 """
739
740 self._fDbgSqlTrace = self.getBoolParam(self.ksParamDbgSqlTrace, False);
741 self._fDbgSqlExplain = self.getBoolParam(self.ksParamDbgSqlExplain, False);
742
743 if self._fDbgSqlExplain:
744 self._oDb.debugEnableExplain();
745
746 return True;
747
748 def _debugRenderPanel(self):
749 """
750 Renders a simple form for controlling WUI debugging.
751
752 Returns the HTML for it.
753 """
754
755 sHtml = '<div id="debug-panel">\n' \
756 ' <form id="debug-panel-form" type="get" action="#">\n';
757
758 for sKey, oValue in self._dParams.iteritems():
759 if sKey not in self.kasDbgParams:
760 if hasattr(oValue, 'startswith'):
761 sHtml += ' <input type="hidden" name="%s" value="%s"/>\n' \
762 % (webutils.escapeAttr(sKey), webutils.escapeAttrToStr(oValue),);
763 else:
764 for oSubValue in oValue:
765 sHtml += ' <input type="hidden" name="%s" value="%s"/>\n' \
766 % (webutils.escapeAttr(sKey), webutils.escapeAttrToStr(oSubValue),);
767
768 for aoCheckBox in (
769 [self.ksParamDbgSqlTrace, self._fDbgSqlTrace, 'SQL trace'],
770 [self.ksParamDbgSqlExplain, self._fDbgSqlExplain, 'SQL explain'], ):
771 sHtml += ' <input type="checkbox" name="%s" value="1"%s />%s\n' \
772 % (aoCheckBox[0], ' checked' if aoCheckBox[1] else '', aoCheckBox[2]);
773
774 sHtml += ' <button type="submit">Apply</button>\n';
775 sHtml += ' </form>\n' \
776 '</div>\n';
777 return sHtml;
778
779
780 def _debugGetParameters(self):
781 """
782 Gets a dictionary with the debug parameters.
783
784 For use when links are constructed from scratch instead of self._dParams.
785 """
786 return self._dDbgParams;
787
788 #
789 # Dispatching
790 #
791
792 def _actionDefault(self):
793 """The default action handler, always overridden. """
794 raise WuiException('The child class shall override WuiBase.actionDefault().')
795
796 def _actionGenericListing(self, oLogicType, oListContentType):
797 """
798 Generic listing action.
799
800 oLogicType implements fetchForListing.
801 oListContentType is a child of WuiListContentBase.
802 """
803 tsEffective = self.getEffectiveDateParam();
804 cItemsPerPage = self.getIntParam(self.ksParamItemsPerPage, iMin = 2, iMax = 9999, iDefault = 300);
805 iPage = self.getIntParam(self.ksParamPageNo, iMin = 0, iMax = 999999, iDefault = 0);
806 aiSortColumnsDup = self.getListOfIntParams(self.ksParamSortColumns,
807 iMin = -getattr(oLogicType, 'kcMaxSortColumns', 0) + 1,
808 iMax = getattr(oLogicType, 'kcMaxSortColumns', 0), aiDefaults = []);
809 aiSortColumns = [];
810 for iSortColumn in aiSortColumnsDup:
811 if iSortColumn not in aiSortColumns:
812 aiSortColumns.append(iSortColumn);
813 self._checkForUnknownParameters();
814
815 aoEntries = oLogicType(self._oDb).fetchForListing(iPage * cItemsPerPage, cItemsPerPage + 1, tsEffective, aiSortColumns);
816 oContent = oListContentType(aoEntries, iPage, cItemsPerPage, tsEffective,
817 fnDPrint = self._oSrvGlue.dprint, oDisp = self, aiSelectedSortColumns = aiSortColumns);
818 (self._sPageTitle, self._sPageBody) = oContent.show();
819 return True;
820
821 def _actionGenericFormAdd(self, oDataType, oFormType, sRedirectTo = None):
822 """
823 Generic add something form display request handler.
824
825 oDataType is a ModelDataBase child class.
826 oFormType is a WuiFormContentBase child class.
827 """
828 assert issubclass(oDataType, ModelDataBase);
829 from testmanager.webui.wuicontentbase import WuiFormContentBase;
830 assert issubclass(oFormType, WuiFormContentBase);
831
832 oData = oDataType().initFromParams(oDisp = self, fStrict = False);
833 sRedirectTo = self.getRedirectToParameter(sRedirectTo);
834 self._checkForUnknownParameters();
835
836 oForm = oFormType(oData, oFormType.ksMode_Add, oDisp = self);
837 oForm.setRedirectTo(sRedirectTo);
838 (self._sPageTitle, self._sPageBody) = oForm.showForm();
839 return True
840
841 def _actionGenericFormDetails(self, oDataType, oLogicType, oFormType, sIdAttr = None, sGenIdAttr = None): # pylint: disable=R0914
842 """
843 Generic handler for showing a details form/page.
844
845 oDataType is a ModelDataBase child class.
846 oLogicType may implement fetchForChangeLog.
847 oFormType is a WuiFormContentBase child class.
848 sIdParamName is the name of the ID parameter (not idGen!).
849 """
850 # Input.
851 assert issubclass(oDataType, ModelDataBase);
852 assert issubclass(oLogicType, ModelLogicBase);
853 from testmanager.webui.wuicontentbase import WuiFormContentBase;
854 assert issubclass(oFormType, WuiFormContentBase);
855
856 if sIdAttr is None:
857 sIdAttr = oDataType.ksIdAttr;
858 if sGenIdAttr is None:
859 sGenIdAttr = getattr(oDataType, 'ksGenIdAttr', None);
860
861 # Parameters.
862 idGenObject = -1;
863 if sGenIdAttr is not None:
864 idGenObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sGenIdAttr), 0, 0x7ffffffe, -1);
865 if idGenObject != -1:
866 idObject = tsNow = None;
867 else:
868 idObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sIdAttr), 0, 0x7ffffffe, -1);
869 tsNow = self.getEffectiveDateParam();
870 fChangeLog = self.getBoolParam(WuiDispatcherBase.ksParamChangeLogEnabled, True);
871 iChangeLogPageNo = self.getIntParam(WuiDispatcherBase.ksParamChangeLogPageNo, 0, 9999, 0);
872 cChangeLogEntriesPerPage = self.getIntParam(WuiDispatcherBase.ksParamChangeLogEntriesPerPage, 2, 9999, 4);
873 self._checkForUnknownParameters();
874
875 # Fetch item and display it.
876 if idGenObject == -1:
877 oData = oDataType().initFromDbWithId(self._oDb, idObject, tsNow);
878 else:
879 oData = oDataType().initFromDbWithGenId(self._oDb, idGenObject);
880
881 oContent = oFormType(oData, oFormType.ksMode_Show, oDisp = self);
882 (self._sPageTitle, self._sPageBody) = oContent.showForm();
883
884 # Add change log if supported.
885 if fChangeLog and hasattr(oLogicType, 'fetchForChangeLog'):
886 (aoEntries, fMore) = oLogicType(self._oDb).fetchForChangeLog(getattr(oData, sIdAttr),
887 iChangeLogPageNo * cChangeLogEntriesPerPage,
888 cChangeLogEntriesPerPage ,
889 tsNow);
890 self._sPageBody += oContent.showChangeLog(aoEntries, fMore, iChangeLogPageNo, cChangeLogEntriesPerPage, tsNow);
891 return True
892
893 def _actionGenericDoRemove(self, oLogicType, sParamId, sRedirAction):
894 """
895 Delete entry (using oLogicType.removeEntry).
896
897 oLogicType is a class that implements addEntry.
898
899 sParamId is the name (ksParam_...) of the HTTP variable hold the ID of
900 the database entry to delete.
901
902 sRedirAction is what action to redirect to on success.
903 """
904 import cgitb;
905
906 idEntry = self.getIntParam(sParamId, iMin = 1, iMax = 0x7ffffffe)
907 fCascade = self.getBoolParam('fCascadeDelete', False);
908 sRedirectTo = self.getRedirectToParameter(self._sActionUrlBase + sRedirAction);
909 self._checkForUnknownParameters()
910
911 try:
912 self._sPageTitle = None
913 self._sPageBody = None
914 self._sRedirectTo = sRedirectTo;
915 return oLogicType(self._oDb).removeEntry(self._oCurUser.uid, idEntry, fCascade = fCascade, fCommit = True);
916 except Exception as oXcpt:
917 self._oDb.rollback();
918 self._sPageTitle = 'Unable to delete entry';
919 self._sPageBody = str(oXcpt);
920 if config.g_kfDebugDbXcpt:
921 self._sPageBody += cgitb.html(sys.exc_info());
922 self._sRedirectTo = None;
923 return False;
924
925 def _actionGenericFormEdit(self, oDataType, oFormType, sIdParamName = None, sRedirectTo = None):
926 """
927 Generic edit something form display request handler.
928
929 oDataType is a ModelDataBase child class.
930 oFormType is a WuiFormContentBase child class.
931 sIdParamName is the name of the ID parameter (not idGen!).
932 """
933 assert issubclass(oDataType, ModelDataBase);
934 from testmanager.webui.wuicontentbase import WuiFormContentBase;
935 assert issubclass(oFormType, WuiFormContentBase);
936
937 if sIdParamName is None:
938 sIdParamName = getattr(oDataType, 'ksParam_' + oDataType.ksIdAttr);
939 assert len(sIdParamName) > 1;
940
941 tsNow = self.getEffectiveDateParam();
942 idObject = self.getIntParam(sIdParamName, 0, 0x7ffffffe);
943 sRedirectTo = self.getRedirectToParameter(sRedirectTo);
944 self._checkForUnknownParameters();
945 oData = oDataType().initFromDbWithId(self._oDb, idObject, tsNow = tsNow);
946
947 oContent = oFormType(oData, oFormType.ksMode_Edit, oDisp = self);
948 oContent.setRedirectTo(sRedirectTo);
949 (self._sPageTitle, self._sPageBody) = oContent.showForm();
950 return True
951
952 def _actionGenericFormEditL(self, oCoreObjectLogic, sCoreObjectIdFieldName, oWuiObjectLogic):
953 """
954 Generic modify something form display request handler.
955
956 @param oCoreObjectLogic A *Logic class
957
958 @param sCoreObjectIdFieldName Name of HTTP POST variable that
959 contains object ID information
960
961 @param oWuiObjectLogic Web interface renderer class
962 """
963
964 iCoreDataObjectId = self.getIntParam(sCoreObjectIdFieldName, 0, 0x7ffffffe, -1)
965 self._checkForUnknownParameters();
966
967 ## @todo r=bird: This will return a None object if the object wasn't found... Crash bang in the content generator
968 # code (that's not logic code btw.).
969 oData = oCoreObjectLogic(self._oDb).getById(iCoreDataObjectId)
970
971 # Instantiate and render the MODIFY dialog form
972 oContent = oWuiObjectLogic(oData, oWuiObjectLogic.ksMode_Edit, oDisp=self)
973
974 (self._sPageTitle, self._sPageBody) = oContent.showForm()
975
976 return True
977
978 def _actionGenericFormClone(self, oDataType, oFormType, sIdAttr, sGenIdAttr = None):
979 """
980 Generic clone something form display request handler.
981
982 oDataType is a ModelDataBase child class.
983 oFormType is a WuiFormContentBase child class.
984 sIdParamName is the name of the ID parameter.
985 sGenIdParamName is the name of the generation ID parameter, None if not applicable.
986 """
987 # Input.
988 assert issubclass(oDataType, ModelDataBase);
989 from testmanager.webui.wuicontentbase import WuiFormContentBase;
990 assert issubclass(oFormType, WuiFormContentBase);
991
992 # Parameters.
993 idGenObject = -1;
994 if sGenIdAttr is not None:
995 idGenObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sGenIdAttr), 0, 0x7ffffffe, -1);
996 if idGenObject != -1:
997 idObject = tsNow = None;
998 else:
999 idObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sIdAttr), 0, 0x7ffffffe, -1);
1000 tsNow = self.getEffectiveDateParam();
1001 self._checkForUnknownParameters();
1002
1003 # Fetch data and clear identifying attributes not relevant to the clone.
1004 if idGenObject != -1:
1005 oData = oDataType().initFromDbWithGenId(self._oDb, idGenObject);
1006 else:
1007 oData = oDataType().initFromDbWithId(self._oDb, idObject, tsNow);
1008
1009 setattr(oData, sIdAttr, None);
1010 if sGenIdAttr is not None:
1011 setattr(oData, sGenIdAttr, None);
1012 oData.tsEffective = None;
1013 oData.tsExpire = None;
1014
1015 # Display form.
1016 oContent = oFormType(oData, oFormType.ksMode_Add, oDisp = self);
1017 (self._sPageTitle, self._sPageBody) = oContent.showForm()
1018 return True
1019
1020
1021 def _actionGenericFormPost(self, sMode, fnLogicAction, oDataType, oFormType, sRedirectTo, fStrict = True):
1022 """
1023 Generic POST request handling from a WuiFormContentBase child.
1024
1025 oDataType is a ModelDataBase child class.
1026 oFormType is a WuiFormContentBase child class.
1027 fnLogicAction is a method taking a oDataType instance and uidAuthor as arguments.
1028 """
1029 assert issubclass(oDataType, ModelDataBase);
1030 from testmanager.webui.wuicontentbase import WuiFormContentBase;
1031 assert issubclass(oFormType, WuiFormContentBase);
1032
1033 #
1034 # Read and validate parameters.
1035 #
1036 oData = oDataType().initFromParams(oDisp = self, fStrict = fStrict);
1037 sRedirectTo = self.getRedirectToParameter(sRedirectTo);
1038 self._checkForUnknownParameters();
1039 self._assertPostRequest();
1040 if sMode == WuiFormContentBase.ksMode_Add and getattr(oData, 'kfIdAttrIsForForeign', False):
1041 enmValidateFor = oData.ksValidateFor_AddForeignId;
1042 elif sMode == WuiFormContentBase.ksMode_Add:
1043 enmValidateFor = oData.ksValidateFor_Add;
1044 else:
1045 enmValidateFor = oData.ksValidateFor_Edit;
1046 dErrors = oData.validateAndConvert(self._oDb, enmValidateFor);
1047 if len(dErrors) == 0:
1048 oData.convertFromParamNull();
1049
1050 #
1051 # Try do the job.
1052 #
1053 try:
1054 fnLogicAction(oData, self._oCurUser.uid, fCommit = True);
1055 except Exception as oXcpt:
1056 self._oDb.rollback();
1057 oForm = oFormType(oData, sMode, oDisp = self);
1058 oForm.setRedirectTo(sRedirectTo);
1059 sErrorMsg = str(oXcpt) if not config.g_kfDebugDbXcpt else '\n'.join(utils.getXcptInfo(4));
1060 (self._sPageTitle, self._sPageBody) = oForm.showForm(sErrorMsg = sErrorMsg);
1061 else:
1062 #
1063 # Worked, redirect to the specified page.
1064 #
1065 self._sPageTitle = None;
1066 self._sPageBody = None;
1067 self._sRedirectTo = sRedirectTo;
1068 else:
1069 oForm = oFormType(oData, sMode, oDisp = self);
1070 oForm.setRedirectTo(sRedirectTo);
1071 (self._sPageTitle, self._sPageBody) = oForm.showForm(dErrors = dErrors);
1072 return True;
1073
1074 def _actionGenericFormAddPost(self, oDataType, oLogicType, oFormType, sRedirAction, fStrict=True):
1075 """
1076 Generic add entry POST request handling from a WuiFormContentBase child.
1077
1078 oDataType is a ModelDataBase child class.
1079 oLogicType is a class that implements addEntry.
1080 oFormType is a WuiFormContentBase child class.
1081 sRedirAction is what action to redirect to on success.
1082 """
1083 assert issubclass(oDataType, ModelDataBase);
1084 assert issubclass(oLogicType, ModelLogicBase);
1085 from testmanager.webui.wuicontentbase import WuiFormContentBase;
1086 assert issubclass(oFormType, WuiFormContentBase);
1087
1088 oLogic = oLogicType(self._oDb);
1089 return self._actionGenericFormPost(WuiFormContentBase.ksMode_Add, oLogic.addEntry, oDataType, oFormType,
1090 '?' + webutils.encodeUrlParams({self.ksParamAction: sRedirAction}), fStrict=fStrict)
1091
1092 def _actionGenericFormEditPost(self, oDataType, oLogicType, oFormType, sRedirAction, fStrict = True):
1093 """
1094 Generic edit POST request handling from a WuiFormContentBase child.
1095
1096 oDataType is a ModelDataBase child class.
1097 oLogicType is a class that implements addEntry.
1098 oFormType is a WuiFormContentBase child class.
1099 sRedirAction is what action to redirect to on success.
1100 """
1101 assert issubclass(oDataType, ModelDataBase);
1102 assert issubclass(oLogicType, ModelLogicBase);
1103 from testmanager.webui.wuicontentbase import WuiFormContentBase;
1104 assert issubclass(oFormType, WuiFormContentBase);
1105
1106 oLogic = oLogicType(self._oDb);
1107 return self._actionGenericFormPost(WuiFormContentBase.ksMode_Edit, oLogic.editEntry, oDataType, oFormType,
1108 '?' + webutils.encodeUrlParams({self.ksParamAction: sRedirAction}),
1109 fStrict = fStrict);
1110
1111 def _unauthorizedUser(self):
1112 """
1113 Displays the unauthorized user message (corresponding record is not
1114 present in DB).
1115 """
1116
1117 sLoginName = self._oSrvGlue.getLoginName();
1118
1119 # Report to system log
1120 oSystemLogLogic = SystemLogLogic(self._oDb);
1121 oSystemLogLogic.addEntry(SystemLogData.ksEvent_UserAccountUnknown,
1122 'Unknown user (%s) attempts to access from %s'
1123 % (sLoginName, self._oSrvGlue.getClientAddr()),
1124 24, fCommit = True)
1125
1126 # Display message.
1127 self._sPageTitle = 'User not authorized'
1128 self._sPageBody = """
1129 <p>Access denied for user <b>%s</b>.
1130 Please contact an admin user to set up your access.</p>
1131 """ % (sLoginName,)
1132 return True;
1133
1134 def dispatchRequest(self):
1135 """
1136 Dispatches a request.
1137 """
1138
1139 #
1140 # Get the parameters and checks for duplicates.
1141 #
1142 try:
1143 dParams = self._oSrvGlue.getParameters();
1144 except Exception as oXcpt:
1145 raise WuiException('Error retriving parameters: %s' % (oXcpt,));
1146
1147 for sKey in dParams.keys():
1148
1149 # Take care about strings which may contain unicode characters: convert percent-encoded symbols back to unicode.
1150 for idxItem, _ in enumerate(dParams[sKey]):
1151 dParams[sKey][idxItem] = dParams[sKey][idxItem].decode('utf-8')
1152
1153 if not len(dParams[sKey]) > 1:
1154 dParams[sKey] = dParams[sKey][0];
1155 self._dParams = dParams;
1156
1157 #
1158 # Figure out the requested action and validate it.
1159 #
1160 if self.ksParamAction in self._dParams:
1161 self._sAction = self._dParams[self.ksParamAction];
1162 self._asCheckedParams.append(self.ksParamAction);
1163 else:
1164 self._sAction = self.ksActionDefault;
1165
1166 if isinstance(self._sAction, list) or self._sAction not in self._dDispatch:
1167 raise WuiException('Unknown action "%s" requested' % (self._sAction,));
1168
1169 #
1170 # Call action handler and generate the page (if necessary).
1171 #
1172 if self._oCurUser is not None:
1173 self._debugProcessDispatch();
1174 if self._dDispatch[self._sAction]() is self.ksDispatchRcAllDone:
1175 return True;
1176 else:
1177 self._unauthorizedUser();
1178
1179 if self._sRedirectTo is None:
1180 self._generatePage();
1181 else:
1182 self._redirectPage();
1183 return True;
1184
1185
1186 def dprint(self, sText):
1187 """ Debug printing. """
1188 if config.g_kfWebUiDebug and True:
1189 self._oSrvGlue.dprint(sText);
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette