VirtualBox

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

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

test manager: Hide menu requiring write access from read-only users.

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