VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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