wuicontentbase.py revision cf22150eaeeb72431bf1cf65c309a431454fb22b
# -*- coding: utf-8 -*-
# $Id$
"""
Test Manager Web-UI - Content Base Classes.
"""
__copyright__ = \
"""
Copyright (C) 2012-2014 Oracle Corporation
This file is part of VirtualBox Open Source Edition (OSE), as
available from http://www.virtualbox.org. This file is free software;
General Public License (GPL) as published by the Free Software
Foundation, in version 2 as it comes in the "COPYING" file of the
VirtualBox OSE distribution. VirtualBox OSE is distributed in the
hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
The contents of this file may alternatively be used under the terms
of the Common Development and Distribution License Version 1.0
(CDDL) only, as it comes in the "COPYING.CDDL" file of the
VirtualBox OSE distribution, in which case the provisions of the
CDDL are applicable instead of those of the GPL.
You may elect to license modified versions of this file under the
terms and conditions of either the GPL or the CDDL or both.
"""
__version__ = "$Revision$"
# Standard python imports.
import copy;
# Validation Kit imports.
from testmanager import config;
"""
Base class for HTML objects.
"""
"""Dummy init to shut up pylint."""
pass;
"""
Must be overridden by sub-classes.
"""
assert False;
return '';
""" String representation is HTML, simplifying formatting and such. """
"""
For passing links from WuiListContentBase._formatListEntry.
"""
# Do some massaging of None arguments.
if sFragmentId is not None:
"""Changes the bracketing style."""
return True;
"""
Returns a simple HTML anchor element.
"""
sExtraAttrs += ' ';
sFmt = '[<a %shref="%s">%s</a>]';
if not self.fBracketed:
sFmt = '<a %shref="%s">%s</a>';
""" Local link to the test manager. """
kdDbgParams = None;
# Add debug parameters if necessary.
else:
"""
For linking to a SVN revision.
"""
if sName is None:
"""
For linking to a SVN revision with changelog tooltip.
"""
sExtraAttrs = ' onmouseover="return svnHistoryTooltipShow(event,\'%s\',%s);" onmouseout="return tooltipHide();"' \
% ( sRepository, iRevision, );
WuiSvnLink.__init__(self, iRevision, sName = sName, fBracketed = fBracketed, sExtraAttrs = sExtraAttrs);
class WuiBuildLogLink(WuiLinkBase):
"""
For linking to a build log.
"""
if sName is None:
sName = 'Build log';
else:
"""
For passing raw html from WuiListContentBase._formatListEntry.
"""
"""
Outputs the given text within a span of the given CSS class.
"""
u'<span class="%s">%s</span>'
"""
Base for the content classes.
"""
ksShortEditLink = u'\u270D'
ksShortDetailsLink = u'\u2318'
""" Debug printing. """
"""
Formats a timestamp (db rep) into a short form.
"""
"""
Generic page walker generator.
sHrefFmt has three %s sequences:
1. The first is the page number link parameter (0-based).
2. The title text, iBase-based number or text.
3. The link text, iBase-based number or text.
"""
# Calc display range.
sHtml = u'';
# Previous page (using << >> because « and » are too tiny).
if iCurItem > 0:
else:
sHtml += '<< ';
# 1 2 3 4...
if iStart > 0:
sHtml += ' \n'.join(sHrefFmt % (i, '%s %d' % (sItemName, i + iBase), i + iBase) if i != iCurItem
sHtml += ' ... %s\n' % (sHrefFmt % (cItems - 1, 'last %s' % (sItemName,), cItems - 1 + iBase));
# Next page.
else:
sHtml += ' >>';
return sHtml;
"""
Base for the content classes working on a single data object (oData).
"""
"""
Base class for simple input form content classes (single data object).
"""
## @name Form mode.
## @{
ksMode_Add = 'add';
ksMode_Edit = 'edit';
ksMode_Show = 'show';
## @}
## Default action mappings.
ksMode_Add: 'AddPost',
ksMode_Edit: 'EditPost',
};
def __init__(self, oData, sMode, sCoreName, oDisp, sTitle, sId = None, fEditable = True, sSubmitAction = None):
"""
Populates the form. oData has parameter NULL values.
This must be reimplemented by the child.
"""
raise Exception('Reimplement me!');
"""
Returns an array of links to go with the change log entry.
"""
## @todo detect deletion and recreation.
## @todo view details link.
## @todo restore link (need new action)
## @todo clone link.
return [];
"""
Guesses the action + author that caused the change log entry.
Returns descriptive string.
"""
# Figure the author of the change.
else:
sAuthor = None;
# Figure the action.
if sAuthor is None:
return 'Created by batch job.';
return 'Created by %s.' % (sAuthor,);
if sAuthor is None:
return 'Automatically updated.'
return 'Modified by %s.' % (sAuthor,);
"""
Formats one change log entry into one or more HTML table rows.
Note! The parameters are given as array + index in case someone wishes
to access adjacent entries later in order to generate better
change descriptions.
"""
# The primary row.
sContent = ' <tr class="%s">\n' \
' <td rowspan="%d">%s</td>\n' \
' <td rowspan="%d">%s</td>\n' \
' <td colspan="3">%s%s</td>\n' \
' </tr>\n' \
% ( sRowClass,
# Additional rows for each changed attribute.
j = 0;
sContent += ' <tr class="%s%s"><td>%s</td><td>%s</td><td>%s</td></tr>\n' \
j += 1;
return sContent;
"""
Returns the HTML for the change log navigator.
Note! See also _generateNavigation.
"""
sNavigation += ' <table class="tmlistnavtab">\n' \
' <tr>\n';
if tsNow is not None:
# Prev and combo box in one cell. Both inside the form for formatting reasons.
sNavigation += ' <td align="left">\n' \
' <form name="ChangeLogEntriesPerPageForm" method="GET">\n'
# Prev
if iPageNo > 0:
sNavigation += '<a href="?%s#tmchangelog">Previous</a>\n' \
else:
sNavigation += 'Previous\n';
# Entries per page selector.
sNavigation += ' \n' \
' <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
'this.options[this.selectedIndex].value + \'#tmchangelog\';" ' \
'title="Max change log entries per page">\n' \
for iEntriesPerPage in [2, 4, 8, 16, 32, 64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 8192]:
sNavigation += ' <option value="%d" %s>%d entries per page</option>\n' \
% ( iEntriesPerPage,
sNavigation += ' </select>\n';
# End of cell (and form).
sNavigation += ' </form>\n' \
' </td>\n';
# Next
if fMoreEntries:
sNavigation += ' <td align="right"><a href="?%s#tmchangelog">Next</a></td>\n' \
else:
sNavigation += ' <td align="right">Next</td>\n';
sNavigation += ' </tr>\n' \
' </table>\n' \
'</div>\n';
return sNavigation;
def showChangeLog(self, aoEntries, fMoreEntries, iPageNo, cEntriesPerPage, tsNow, fShowNavigation = True):
"""
Render the change log, returning raw HTML.
aoEntries is an array of ChangeLogEntry.
"""
sContent = '\n' \
'<hr>\n' \
'<div id="tmchangelog">\n' \
' <h3>Change Log </h3>\n';
if fShowNavigation:
sContent += ' <table class="tmtable tmchangelog">\n' \
' <thead class="tmheader">' \
' <tr>' \
' <th rowspan="2">When</th>\n' \
' <th rowspan="2">Expire (excl)</th>\n' \
' <th colspan="3">Changes</th>\n' \
' </tr>\n' \
' <tr>\n' \
' <th>Attribute</th>\n' \
' <th>Old value</th>\n' \
' <th>New value</th>\n' \
' </tr>\n' \
' </thead>\n' \
' <tbody>\n';
sContent += ' <tbody>\n' \
' </table>\n';
sContent += '</div>\n\n';
return sContent;
"""
Render the form.
"""
# If form cannot be constructed due to some reason we
# need to show this reason
try:
except WuiException, oXcpt:
else:
# Add action to the top.
aoActions = [];
# Remove _idGen and effective date since we're always editing the current data,
# and make sure the primary ID is present.
if sIdGenParam in dParams:
del dParams[sIdGenParam];
dParams[getattr(self._oData, 'ksParam_' + self._oData.ksIdAttr)] = getattr(self._oData, self._oData.ksIdAttr);
# Add clone operation if available. This uses the same data selection as for showing details.
# Details views the details at a given time, so we need either idGen or an effecive date + regular id.
dParams = {};
dParams[getattr(self._oData, 'ksParam_' + self._oData.ksIdAttr)] = getattr(self._oData, self._oData.ksIdAttr);
# Add error info to the top.
if sErrorMsg is not None:
"""
Format generic list which should be used by HTML form
"""
aoRet = []
for sListItem in asListItems:
return aoRet
class WuiListContentBase(WuiContentBase):
"""
Base for the list content classes.
"""
def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffectiveDate, sTitle, sId = None, fnDPrint = None, oDisp = None):
self._aoEntries = aoEntries; ## @todo should replace this with a Logic object and define methods for querying.
if sId is None:
self._asColumnHeaders = [];
self._asColumnAttribs = [];
"""
Formats the specified list entry as a list of column values.
Returns HTML for a table row.
The child class really need to override this!
"""
# ASSUMES ModelDataBase children.
asRet = [];
return asRet;
"""
Formats the specified list entry as HTML.
Returns HTML for a table row.
The child class can override this to
"""
sRow = u' <tr class="tmodd">\n';
else:
sRow = u' <tr class="tmeven">\n';
assert len(aoValues) == len(self._asColumnHeaders), '%s vs %s' % (len(aoValues), len(self._asColumnHeaders));
else:
sRow += u' <td>';
else:
sRow += ' ';
elif aoValues[i] is not None:
sRow += u'</td>\n';
return sRow + u' </tr>\n';
"""
Returns HTML for time navigation.
Note! Views without a need for a timescale just stubs this method.
"""
_ = sWhere;
sNavigation = '';
return sNavigation;
"""
Return HTML for navigation.
"""
#
# ASSUMES the dispatcher/controller code fetches one entry more than
# needed to fill the page to indicate further records.
#
sNavigation += ' <table class="tmlistnavtab">\n' \
' <tr>\n';
if self._tsEffectiveDate is not None:
# Prev
sNavigation += ' <td align="left"><a href="?%s">Previous</a></td>\n' % (webutils.encodeUrlParams(dParams),);
else:
sNavigation += ' <td></td>\n';
# Time scale.
sNavigation += '<td align="center" class="tmtimenav">';
sNavigation += '</td>';
# Next
sNavigation += ' <td align="right"><a href="?%s">Next</a></td>\n' % (webutils.encodeUrlParams(dParams),);
else:
sNavigation += ' <td></td>\n';
sNavigation += ' </tr>\n' \
' </table>\n' \
'</div>\n';
return sNavigation;
def _generateTable(self):
"""
show worker that just generates the table.
"""
#
# Create a table.
# If no colum headers are provided, fall back on database field
# names, ASSUMING that the entries are ModelDataBase children.
# Note! the cellspacing is for IE8.
#
sPageBody += ' <thead class="tmheader"><tr>';
else:
sPageBody += '</tr><thead>\n';
#
# Format the body and close the table.
#
sPageBody += ' <tbody>\n';
sPageBody += ' </tbody>\n' \
'</table>\n';
return sPageBody;
def _composeTitle(self):
"""Composes the title string (return value)."""
if self._tsEffectiveDate is not None:
return sTitle;
"""
Displays the list.
Returns (Title, HTML) on success, raises exception on error.
"""
sPageBody = ''
if fShowNavigation:
if fShowNavigation:
else:
sPageBody += '<p>No entries.</p>'
"""
Base for the list content with action classes.
"""
def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffectiveDate, sTitle, sId = None, fnDPrint = None, oDisp = None):
"""
Used by _formatListEntry implementations, returns a WuiRawHtmlBase object.
"""
_ = iEntry;
return WuiRawHtml('<input type="checkbox" name="%s" value="%s">'
"""
Displays the list.
Returns (Title, HTML) on success, raises exception on error.
"""
assert self._aoActions is not None;
sPageBody = '<script language="JavaScript">\n' \
'function toggle%s(oSource) {\n' \
' aoCheckboxes = document.getElementsByName(\'%s\');\n' \
' for(var i in aoCheckboxes)\n' \
' aoCheckboxes[i].checked = oSource.checked;\n' \
'}\n' \
'</script>\n' \
if fShowNavigation:
sPageBody += '<form action="?%s" method="post" class="tmlistactionform">\n' \
sPageBody += ' <label>Actions</label>\n' \
' <select name="%s" id="%s-action-combo" class="tmlistactionform-combo">\n' \
sPageBody += ' <option value="%s">%s</option>\n' \
sPageBody += ' </select>\n';
sPageBody += ' <input type="submit"></input>\n';
sPageBody += '</form>\n';
if fShowNavigation:
else:
sPageBody += '<p>No entries.</p>'