VirtualBox

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

Last change on this file since 57761 was 56295, checked in by vboxsync, 10 years ago

ValidationKit: Updated (C) year.

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