VirtualBox

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

Last change on this file since 86149 was 84498, checked in by vboxsync, 5 years ago

TestManager/*base.py: Tweaked the month validation for relative timestamps from the xxxx-ago combo box.

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