VirtualBox

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

Last change on this file since 82972 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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

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