Changeset 65914 in vbox for trunk/src/VBox/ValidationKit/testmanager
- Timestamp:
- Mar 1, 2017 4:09:45 PM (8 years ago)
- svn:sync-xref-src-repo-rev:
- 113709
- Location:
- trunk/src/VBox/ValidationKit/testmanager
- Files:
-
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/testmanager/core/useraccount.pgsql
r56295 r65914 108 108 -- 109 109 CREATE OR REPLACE FUNCTION UserAccountLogic_addEntry(a_uidAuthor INTEGER, a_sUsername TEXT, a_sEmail TEXT, a_sFullName TEXT, 110 a_sLoginName TEXT )110 a_sLoginName TEXT, a_fReadOnly BOOLEAN) 111 111 RETURNS VOID AS $$ 112 112 DECLARE … … 120 120 121 121 CREATE OR REPLACE FUNCTION UserAccountLogic_editEntry(a_uidAuthor INTEGER, a_uid INTEGER, a_sUsername TEXT, a_sEmail TEXT, 122 a_sFullName TEXT, a_sLoginName TEXT )122 a_sFullName TEXT, a_sLoginName TEXT, a_fReadOnly BOOLEAN) 123 123 RETURNS VOID AS $$ 124 124 BEGIN … … 127 127 128 128 PERFORM UserAccountLogic_historizeEntry(a_uid, CURRENT_TIMESTAMP); 129 INSERT INTO Users (uid, uidAuthor, sUsername, sEmail, sFullName, sLoginName )130 VALUES (a_uid, a_uidAuthor, a_sUsername, a_sEmail, a_sFullName, a_sLoginName );129 INSERT INTO Users (uid, uidAuthor, sUsername, sEmail, sFullName, sLoginName, fReadOnly) 130 VALUES (a_uid, a_uidAuthor, a_sUsername, a_sEmail, a_sFullName, a_sLoginName, a_fReadOnly); 131 131 END; 132 132 $$ LANGUAGE plpgsql; -
trunk/src/VBox/ValidationKit/testmanager/core/useraccount.py
r65226 r65914 53 53 ksParam_sEmail = 'UserAccount_sEmail' 54 54 ksParam_sFullName = 'UserAccount_sFullName' 55 ksParam_fReadOnly = 'UserAccount_fReadOnly' 55 56 56 57 kasAllowNullAttributes = ['uid', 'tsEffective', 'tsExpire', 'uidAuthor']; … … 68 69 self.sFullName = None; 69 70 self.sLoginName = None; 71 self.fReadOnly = None; 70 72 71 73 def initFromDbRow(self, aoRow): … … 85 87 self.sFullName = aoRow[6]; 86 88 self.sLoginName = aoRow[7]; 89 self.fReadOnly = aoRow[8]; 87 90 return self; 88 91 … … 157 160 """ 158 161 self._oDb.callProc('UserAccountLogic_addEntry', 159 (uidAuthor, oData.sUsername, oData.sEmail, oData.sFullName, oData.sLoginName, ));162 (uidAuthor, oData.sUsername, oData.sEmail, oData.sFullName, oData.sLoginName, oData.fReadOnly)); 160 163 self._oDb.maybeCommit(fCommit); 161 164 return True; … … 166 169 """ 167 170 self._oDb.callProc('UserAccountLogic_editEntry', 168 (uidAuthor, oData.uid, oData.sUsername, oData.sEmail, oData.sFullName, oData.sLoginName,)); 171 ( uidAuthor, oData.uid, oData.sUsername, oData.sEmail, 172 oData.sFullName, oData.sLoginName, oData.fReadOnly)); 169 173 self._oDb.maybeCommit(fCommit); 170 174 return True; -
trunk/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseComments.pgsql
r64948 r65914 55 55 COMMENT ON COLUMN Users.sLoginName IS 56 56 'The login name used by apache.'; 57 58 59 COMMENT ON COLUMN Users.fReadOnly IS 60 'Read access only.'; 57 61 58 62 -
trunk/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql
r61507 r65914 125 125 --- The login name used by apache. 126 126 sLoginName text NOT NULL, 127 --- Read access only. 128 fReadOnly BOOLEAN NOT NULL DEFAULT FALSE, 127 129 128 130 PRIMARY KEY (uid, tsExpire) -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmin.py
r65040 r65914 58 58 ksActionUserList = 'UserList' 59 59 ksActionUserAdd = 'UserAdd' 60 ksActionUserAddPost = 'UserAddPost' 60 61 ksActionUserEdit = 'UserEdit' 61 ksActionUserAddPost = 'UserAddPost'62 62 ksActionUserEditPost = 'UserEditPost' 63 63 ksActionUserDelPost = 'UserDelPost' 64 ksActionUserDetails = 'UserDetails' 64 65 65 66 ksActionTestBoxList = 'TestBoxList' … … 178 179 self._dDispatch[self.ksActionUserAddPost] = self._actionUserAddPost; 179 180 self._dDispatch[self.ksActionUserEditPost] = self._actionUserEditPost; 181 self._dDispatch[self.ksActionUserDetails] = self._actionUserDetails; 180 182 self._dDispatch[self.ksActionUserDelPost] = self._actionUserDelPost; 181 183 … … 457 459 return self._actionGenericFormAdd(UserAccountData, WuiUserAccount) 458 460 461 def _actionUserDetails(self): 462 """ Action wrapper. """ 463 from testmanager.core.useraccount import UserAccountData, UserAccountLogic; 464 from testmanager.webui.wuiadminuseraccount import WuiUserAccount; 465 return self._actionGenericFormDetails(UserAccountData, UserAccountLogic, WuiUserAccount, 'uid'); 466 459 467 def _actionUserEdit(self): 460 468 """ Action wrapper. """ … … 643 651 644 652 self._sPageTitle = 'Regenerate All Scheduling Queues'; 645 self._sPageBody = ''; 646 aoGroups = SchedGroupLogic(self._oDb).getAll(); 647 for oGroup in aoGroups: 648 self._sPageBody += '<h3>%s (ID %#d)</h3>' % (webutils.escapeElem(oGroup.sName), oGroup.idSchedGroup); 649 try: 650 (aoErrors, asMessages) = SchedulerBase.recreateQueue(self._oDb, self._oCurUser.uid, oGroup.idSchedGroup, 2); 651 except Exception as oXcpt: 652 self._oDb.rollback(); 653 self._sPageBody += '<p>SchedulerBase.recreateQueue threw an exception: %s</p>' \ 654 % (webutils.escapeElem(str(oXcpt)),); 655 self._sPageBody += cgitb.html(sys.exc_info()); 656 else: 657 if len(aoErrors) == 0: 658 self._sPageBody += '<p>Successfully regenerated.</p>'; 653 if not self.isReadOnlyUser(): 654 self._sPageBody = ''; 655 aoGroups = SchedGroupLogic(self._oDb).getAll(); 656 for oGroup in aoGroups: 657 self._sPageBody += '<h3>%s (ID %#d)</h3>' % (webutils.escapeElem(oGroup.sName), oGroup.idSchedGroup); 658 try: 659 (aoErrors, asMessages) = SchedulerBase.recreateQueue(self._oDb, self._oCurUser.uid, oGroup.idSchedGroup, 2); 660 except Exception as oXcpt: 661 self._oDb.rollback(); 662 self._sPageBody += '<p>SchedulerBase.recreateQueue threw an exception: %s</p>' \ 663 % (webutils.escapeElem(str(oXcpt)),); 664 self._sPageBody += cgitb.html(sys.exc_info()); 659 665 else: 660 for oError in aoErrors: 661 if oError[1] is None: 662 self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),); 663 ## @todo links. 664 #elif isinstance(oError[1], TestGroupData): 665 # self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),); 666 #elif isinstance(oError[1], TestGroupCase): 667 # self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),); 668 else: 669 self._sPageBody += '<p>%s. [Cannot link to %s]</p>' \ 670 % (webutils.escapeElem(oError[0]), webutils.escapeElem(str(oError[1]))); 671 for sMsg in asMessages: 672 self._sPageBody += '<p>%s<p>\n' % (webutils.escapeElem(sMsg),); 666 if len(aoErrors) == 0: 667 self._sPageBody += '<p>Successfully regenerated.</p>'; 668 else: 669 for oError in aoErrors: 670 if oError[1] is None: 671 self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),); 672 ## @todo links. 673 #elif isinstance(oError[1], TestGroupData): 674 # self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),); 675 #elif isinstance(oError[1], TestGroupCase): 676 # self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),); 677 else: 678 self._sPageBody += '<p>%s. [Cannot link to %s]</p>' \ 679 % (webutils.escapeElem(oError[0]), webutils.escapeElem(str(oError[1]))); 680 for sMsg in asMessages: 681 self._sPageBody += '<p>%s<p>\n' % (webutils.escapeElem(sMsg),); 682 else: 683 self._sPageBody = webutils.escapeElem('%s is a read only user and may not regenerate the scheduling queues!' 684 % (self._oCurUser.sUsername,)); 673 685 return True; 674 686 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminbuild.py
r65350 r65914 105 105 BuildBlacklistData.ksParam_iFirstRevision: oEntry.iRevision, 106 106 BuildBlacklistData.ksParam_iLastRevision: oEntry.iRevision } 107 aoActions += [ 108 WuiTmLink('Blacklist', WuiAdmin.ksScriptName, dParams), 109 WuiTmLink('Details', WuiAdmin.ksScriptName, 110 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDetails, 111 BuildData.ksParam_idBuild: oEntry.idBuild, 112 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }), 113 WuiTmLink('Clone', WuiAdmin.ksScriptName, 114 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildClone, 115 BuildData.ksParam_idBuild: oEntry.idBuild, 116 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }), 117 ]; 118 if isDbTimestampInfinity(oEntry.tsExpire): 107 108 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 119 109 aoActions += [ 120 WuiTmLink('Modify', WuiAdmin.ksScriptName, 121 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildEdit, 122 BuildData.ksParam_idBuild: oEntry.idBuild }), 123 WuiTmLink('Remove', WuiAdmin.ksScriptName, 124 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDoRemove, 125 BuildData.ksParam_idBuild: oEntry.idBuild }, 126 sConfirm = 'Are you sure you want to remove build #%d?' % (oEntry.idBuild,) ), 110 WuiTmLink('Blacklist', WuiAdmin.ksScriptName, dParams), 111 WuiTmLink('Details', WuiAdmin.ksScriptName, 112 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDetails, 113 BuildData.ksParam_idBuild: oEntry.idBuild, 114 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }), 115 WuiTmLink('Clone', WuiAdmin.ksScriptName, 116 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildClone, 117 BuildData.ksParam_idBuild: oEntry.idBuild, 118 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }), 127 119 ]; 120 if isDbTimestampInfinity(oEntry.tsExpire): 121 aoActions += [ 122 WuiTmLink('Modify', WuiAdmin.ksScriptName, 123 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildEdit, 124 BuildData.ksParam_idBuild: oEntry.idBuild }), 125 WuiTmLink('Remove', WuiAdmin.ksScriptName, 126 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDoRemove, 127 BuildData.ksParam_idBuild: oEntry.idBuild }, 128 sConfirm = 'Are you sure you want to remove build #%d?' % (oEntry.idBuild,) ), 129 ]; 128 130 129 131 return [ oEntry.idBuild, -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildblacklist.py
r65350 r65914 121 121 oEntry = self._aoEntries[iEntry] 122 122 123 sShortFailReason = \ 124 FailureReasonLogic(TMDatabaseConnection()).getById(oEntry.idFailureReason).sShort 123 sShortFailReason = FailureReasonLogic(TMDatabaseConnection()).getById(oEntry.idFailureReason).sShort 124 125 aoActions = [ 126 WuiTmLink('Details', WuiAdmin.ksScriptName, 127 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistDetails, 128 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }), 129 ]; 130 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 131 aoActions += [ 132 WuiTmLink('Edit', WuiAdmin.ksScriptName, 133 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistEdit, 134 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }), 135 WuiTmLink('Clone', WuiAdmin.ksScriptName, 136 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistClone, 137 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting, 138 WuiAdmin.ksParamEffectiveDate: oEntry.tsEffective, }), 139 WuiTmLink('Remove', WuiAdmin.ksScriptName, 140 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistDoRemove, 141 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }, 142 sConfirm = 'Are you sure you want to remove black list entry #%d?' % (oEntry.idBlacklisting,)), 143 ]; 125 144 126 145 return [ oEntry.idBlacklisting, … … 132 151 oEntry.iFirstRevision, 133 152 oEntry.iLastRevision, 134 [ WuiTmLink('Details', WuiAdmin.ksScriptName, 135 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistDetails, 136 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }), 137 WuiTmLink('Edit', WuiAdmin.ksScriptName, 138 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistEdit, 139 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }), 140 WuiTmLink('Clone', WuiAdmin.ksScriptName, 141 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistClone, 142 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting, 143 WuiAdmin.ksParamEffectiveDate: oEntry.tsEffective, }), 144 WuiTmLink('Remove', WuiAdmin.ksScriptName, 145 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistDoRemove, 146 BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }, 147 sConfirm = 'Are you sure you want to remove black list entry #%d?' % (oEntry.idBlacklisting,)), 148 ] 153 aoActions 149 154 ]; -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildcategory.py
r65350 r65914 57 57 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryDetails, 58 58 BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }), 59 WuiTmLink('Clone', WuiAdmin.ksScriptName,60 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryClone,61 BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }),62 WuiTmLink('Try Remove', WuiAdmin.ksScriptName,63 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryDoRemove,64 BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }),65 59 ]; 60 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 61 aoActions += [ 62 WuiTmLink('Clone', WuiAdmin.ksScriptName, 63 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryClone, 64 BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }), 65 WuiTmLink('Try Remove', WuiAdmin.ksScriptName, 66 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryDoRemove, 67 BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }), 68 ]; 66 69 67 70 sHtml = '<ul class="tmshowall">\n'; -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildsource.py
r65350 r65914 118 118 BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc, 119 119 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }), 120 WuiTmLink('Clone', WuiAdmin.ksScriptName,121 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcClone,122 BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc,123 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }),124 120 ]; 125 if isDbTimestampInfinity(oEntry.tsExpire):121 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 126 122 aoActions += [ 127 WuiTmLink('Modify', WuiAdmin.ksScriptName, 128 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcEdit, 129 BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc } ), 130 WuiTmLink('Remove', WuiAdmin.ksScriptName, 131 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcDoRemove, 132 BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc }, 133 sConfirm = 'Are you sure you want to remove build source #%d?' % (oEntry.idBuildSrc,) ) 123 WuiTmLink('Clone', WuiAdmin.ksScriptName, 124 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcClone, 125 BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc, 126 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }), 134 127 ]; 128 if isDbTimestampInfinity(oEntry.tsExpire): 129 aoActions += [ 130 WuiTmLink('Modify', WuiAdmin.ksScriptName, 131 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcEdit, 132 BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc } ), 133 WuiTmLink('Remove', WuiAdmin.ksScriptName, 134 { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcDoRemove, 135 BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc }, 136 sConfirm = 'Are you sure you want to remove build source #%d?' % (oEntry.idBuildSrc,) ) 137 ]; 135 138 136 139 return [ oEntry.idBuildSrc, -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurecategory.py
r65350 r65914 121 121 oEntry = self._aoEntries[iEntry] 122 122 123 aoActions = [ 124 WuiTmLink('Details', WuiAdmin.ksScriptName, 125 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryDetails, 126 FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }), 127 ]; 128 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 129 aoActions += [ 130 WuiTmLink('Modify', WuiAdmin.ksScriptName, 131 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryEdit, 132 FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }), 133 WuiTmLink('Remove', WuiAdmin.ksScriptName, 134 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryDoRemove, 135 FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }, 136 sConfirm = 'Do you really want to remove failure cateogry #%d?' % (oEntry.idFailureCategory,)), 137 ] 138 123 139 return [ oEntry.idFailureCategory, 124 140 oEntry.sShort, 125 141 oEntry.sFull, 126 [ WuiTmLink('Details', WuiAdmin.ksScriptName, 127 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryDetails, 128 FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }), 129 WuiTmLink('Modify', WuiAdmin.ksScriptName, 130 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryEdit, 131 FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }), 132 WuiTmLink('Remove', WuiAdmin.ksScriptName, 133 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryDoRemove, 134 FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }, 135 sConfirm = 'Do you really want to remove failure cateogry #%d?' % (oEntry.idFailureCategory,)), 136 ] ]; 142 aoActions, 143 ]; 144 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurereason.py
r65350 r65914 140 140 oEntry = self._aoEntries[iEntry] 141 141 142 aoActions = [ 143 WuiTmLink('Details', WuiAdmin.ksScriptName, 144 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDetails, 145 FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason } ), 146 ]; 147 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 148 aoActions += [ 149 WuiTmLink('Modify', WuiAdmin.ksScriptName, 150 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonEdit, 151 FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason } ), 152 WuiTmLink('Remove', WuiAdmin.ksScriptName, 153 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDoRemove, 154 FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason }, 155 sConfirm = 'Are you sure you want to remove failure reason #%d?' % (oEntry.idFailureReason,)), 156 ]; 157 142 158 return [ oEntry.idFailureReason, 143 159 WuiFailureReasonCategoryLink(oEntry.idFailureCategory, sName = oEntry.oCategory.sShort, fBracketed = False), … … 146 162 oEntry.iTicket, 147 163 oEntry.asUrls, 148 [ WuiTmLink('Details', WuiAdmin.ksScriptName, 149 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDetails, 150 FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason } ), 151 WuiTmLink('Modify', WuiAdmin.ksScriptName, 152 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonEdit, 153 FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason } ), 154 WuiTmLink('Remove', WuiAdmin.ksScriptName, 155 { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDoRemove, 156 FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason }, 157 sConfirm = 'Are you sure you want to remove failure reason #%d?' % (oEntry.idFailureReason,)), 158 ] ] 164 aoActions, 165 ] -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminglobalrsrc.py
r65350 r65914 101 101 oEntry = self._aoEntries[iEntry] 102 102 103 aoActions = [ ]; 104 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 105 aoActions += [ 106 WuiTmLink('Modify', WuiAdmin.ksScriptName, 107 { WuiAdmin.ksParamAction: WuiAdmin.ksActionGlobalRsrcShowEdit, 108 GlobalResourceData.ksParam_idGlobalRsrc: oEntry.idGlobalRsrc }), 109 WuiTmLink('Remove', WuiAdmin.ksScriptName, 110 { WuiAdmin.ksParamAction: WuiAdmin.ksActionGlobalRsrcDel, 111 GlobalResourceData.ksParam_idGlobalRsrc: oEntry.idGlobalRsrc }, 112 sConfirm = 'Are you sure you want to remove global resource #%d?' % (oEntry.idGlobalRsrc,)), 113 ]; 114 103 115 return [ oEntry.idGlobalRsrc, 104 116 oEntry.sName, 105 117 oEntry.sDescription, 106 118 oEntry.fEnabled, 107 [ WuiTmLink('Modify', WuiAdmin.ksScriptName, 108 { WuiAdmin.ksParamAction: WuiAdmin.ksActionGlobalRsrcShowEdit, 109 GlobalResourceData.ksParam_idGlobalRsrc: oEntry.idGlobalRsrc }), 110 WuiTmLink('Remove', WuiAdmin.ksScriptName, 111 { WuiAdmin.ksParamAction: WuiAdmin.ksActionGlobalRsrcDel, 112 GlobalResourceData.ksParam_idGlobalRsrc: oEntry.idGlobalRsrc }, 113 sConfirm = 'Are you sure you want to remove global resource #%d?' % (oEntry.idGlobalRsrc,)), 114 ] ] 119 aoActions, ]; 115 120 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminschedgroup.py
r65350 r65914 158 158 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup, 159 159 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } ),]; 160 if isDbTimestampInfinity(oEntry.tsExpire): 161 aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName, 162 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit, 163 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup } )); 164 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 165 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupClone, 166 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup, 167 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } )); 168 if isDbTimestampInfinity(oEntry.tsExpire): 169 aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName, 170 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupDoRemove, 171 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup }, 172 sConfirm = 'Are you sure you want to remove scheduling group #%d?' 173 % (oEntry.idSchedGroup,))); 160 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 161 162 if isDbTimestampInfinity(oEntry.tsExpire): 163 aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName, 164 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit, 165 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup } )); 166 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 167 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupClone, 168 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup, 169 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } )); 170 if isDbTimestampInfinity(oEntry.tsExpire): 171 aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName, 172 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupDoRemove, 173 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup }, 174 sConfirm = 'Are you sure you want to remove scheduling group #%d?' 175 % (oEntry.idSchedGroup,))); 174 176 175 177 return [ … … 185 187 aoActions, 186 188 ]; 189 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemlog.py
r65350 r65914 52 52 oEntry = self._aoEntries[iEntry]; 53 53 54 if oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown \ 55 and oEntry.sLogText.find('addr=') >= 0 \ 56 and oEntry.sLogText.find('uuid=') >= 0: 57 sUuid = (oEntry.sLogText[(oEntry.sLogText.find('uuid=') + 5):])[:36]; 58 sAddr = (oEntry.sLogText[(oEntry.sLogText.find('addr=') + 5):]).split(' ')[0]; 59 oAction = WuiTmLink('Add TestBox', WuiAdmin.ksScriptName, 60 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxAdd, 61 TestBoxData.ksParam_uuidSystem: sUuid, 62 TestBoxData.ksParam_ip: sAddr }); 54 oAction = ''; # pylint: disable=R0204 55 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 56 if oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown \ 57 and oEntry.sLogText.find('addr=') >= 0 \ 58 and oEntry.sLogText.find('uuid=') >= 0: 59 sUuid = (oEntry.sLogText[(oEntry.sLogText.find('uuid=') + 5):])[:36]; 60 sAddr = (oEntry.sLogText[(oEntry.sLogText.find('addr=') + 5):]).split(' ')[0]; 61 oAction = WuiTmLink('Add TestBox', WuiAdmin.ksScriptName, 62 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxAdd, 63 TestBoxData.ksParam_uuidSystem: sUuid, 64 TestBoxData.ksParam_ip: sAddr }); 63 65 64 elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown: 65 sUserName = oEntry.sLogText[oEntry.sLogText.find('(') + 1: 66 oEntry.sLogText.find(')')] 67 oAction = WuiTmLink('Add User', WuiAdmin.ksScriptName, 68 { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserAdd, 69 UserAccountData.ksParam_sLoginName: sUserName }); 70 else: 71 oAction = ''; # pylint: disable=R0204 66 elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown: 67 sUserName = oEntry.sLogText[oEntry.sLogText.find('(') + 1: 68 oEntry.sLogText.find(')')] 69 oAction = WuiTmLink('Add User', WuiAdmin.ksScriptName, 70 { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserAdd, 71 UserAccountData.ksParam_sLoginName: sUserName }); 72 72 73 73 return [oEntry.tsCreated, oEntry.sEvent, oEntry.sLogText, oAction]; -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py
r65423 r65914 357 357 ] 358 358 359 if isDbTimestampInfinity(oEntry.tsExpire): 360 aoActions += [ 361 WuiTmLink('Edit', WuiAdmin.ksScriptName, 362 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxEdit, 363 TestBoxData.ksParam_idTestBox: oEntry.idTestBox, } ), 364 WuiTmLink('Remove', WuiAdmin.ksScriptName, 365 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxRemovePost, 366 TestBoxData.ksParam_idTestBox: oEntry.idTestBox }, 367 sConfirm = 'Are you sure that you want to remove %s (%s)?' % (oEntry.sName, oEntry.ip) ), 368 ] 369 370 if oEntry.sOs not in [ 'win', 'os2', ] and oEntry.ip is not None: 371 aoActions.append(WuiLinkBase('ssh', 'ssh://vbox@%s' % (oEntry.ip,),)); 359 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 360 if isDbTimestampInfinity(oEntry.tsExpire): 361 aoActions += [ 362 WuiTmLink('Edit', WuiAdmin.ksScriptName, 363 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxEdit, 364 TestBoxData.ksParam_idTestBox: oEntry.idTestBox, } ), 365 WuiTmLink('Remove', WuiAdmin.ksScriptName, 366 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxRemovePost, 367 TestBoxData.ksParam_idTestBox: oEntry.idTestBox }, 368 sConfirm = 'Are you sure that you want to remove %s (%s)?' % (oEntry.sName, oEntry.ip) ), 369 ] 370 371 if oEntry.sOs not in [ 'win', 'os2', ] and oEntry.ip is not None: 372 aoActions.append(WuiLinkBase('ssh', 'ssh://vbox@%s' % (oEntry.ip,),)); 372 373 373 374 return [ self._getCheckBoxColumn(iEntry, oEntry.idTestBox), -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestcase.py
r65350 r65914 161 161 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDetails, 162 162 TestCaseData.ksParam_idGenTestCase: oEntry.idGenTestCase }), ]; 163 if isDbTimestampInfinity(oEntry.tsExpire): 164 aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName, 165 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseEdit, 166 TestCaseData.ksParam_idTestCase: oEntry.idTestCase })); 167 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 168 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseClone, 169 TestCaseData.ksParam_idGenTestCase: oEntry.idGenTestCase })); 170 if isDbTimestampInfinity(oEntry.tsExpire): 171 aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName, 172 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDoRemove, 173 TestCaseData.ksParam_idTestCase: oEntry.idTestCase }, 174 sConfirm = 'Are you sure you want to remove test case #%d?' % (oEntry.idTestCase,))); 163 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 164 if isDbTimestampInfinity(oEntry.tsExpire): 165 aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName, 166 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseEdit, 167 TestCaseData.ksParam_idTestCase: oEntry.idTestCase })); 168 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 169 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseClone, 170 TestCaseData.ksParam_idGenTestCase: oEntry.idGenTestCase })); 171 if isDbTimestampInfinity(oEntry.tsExpire): 172 aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName, 173 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDoRemove, 174 TestCaseData.ksParam_idTestCase: oEntry.idTestCase }, 175 sConfirm = 'Are you sure you want to remove test case #%d?' % (oEntry.idTestCase,))); 175 176 aoRet.append(aoActions); 176 177 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestgroup.py
r65350 r65914 113 113 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseEdit, 114 114 TestCaseData.ksParam_idTestCase: oMember.oTestCase.idTestCase, } ).toHtml() 115 if isDbTimestampInfinity(oMember.oTestCase.tsExpire) else '', 115 if isDbTimestampInfinity(oMember.oTestCase.tsExpire) 116 and self._oDisp is not None 117 and not self._oDisp.isReadOnlyUser() else '', 116 118 ); 117 119 … … 155 157 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup, 156 158 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }) ]; 157 if isDbTimestampInfinity(oEntry.tsExpire): 158 aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName, 159 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupEdit, 160 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup })); 161 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 162 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupClone, 163 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup, 164 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, })); 165 aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName, 166 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupDoRemove, 167 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup }, 168 sConfirm = 'Do you really want to remove test group #%d?' % (oEntry.idTestGroup,))); 169 else: 170 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 171 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupClone, 172 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup, 173 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, })); 159 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 160 161 if isDbTimestampInfinity(oEntry.tsExpire): 162 aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName, 163 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupEdit, 164 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup })); 165 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 166 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupClone, 167 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup, 168 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, })); 169 aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName, 170 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupDoRemove, 171 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup }, 172 sConfirm = 'Do you really want to remove test group #%d?' % (oEntry.idTestGroup,))); 173 else: 174 aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName, 175 { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupClone, 176 TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup, 177 WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, })); 174 178 175 179 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminuseraccount.py
r65350 r65914 57 57 oForm.addText( UserAccountData.ksParam_sFullName, oData.sFullName, 'Full name') 58 58 oForm.addText( UserAccountData.ksParam_sEmail, oData.sEmail, 'E-mail') 59 oForm.addCheckBox( UserAccountData.ksParam_fReadOnly, oData.fReadOnly, 'Only read access') 59 60 if self._sMode != WuiFormContentBase.ksMode_Show: 60 61 oForm.addSubmit('Add User' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change User'); … … 71 72 sTitle = 'Registered User Accounts', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp, 72 73 aiSelectedSortColumns = aiSelectedSortColumns); 73 self._asColumnHeaders = ['User ID', 'Name', 'E-mail', 'Full Name', 'Login Name', 'Ac tions'];74 self._asColumnHeaders = ['User ID', 'Name', 'E-mail', 'Full Name', 'Login Name', 'Access', 'Actions']; 74 75 self._asColumnAttribs = ['align="center"', 'align="center"', 'align="center"', 'align="center"', 'align="center"', 75 'align="center"' ];76 'align="center"', 'align="center"', ]; 76 77 77 78 def _formatListEntry(self, iEntry): 78 79 from testmanager.webui.wuiadmin import WuiAdmin; 79 80 oEntry = self._aoEntries[iEntry]; 81 aoActions = [ 82 WuiTmLink('Details', WuiAdmin.ksScriptName, 83 { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserDetails, 84 UserAccountData.ksParam_uid: oEntry.uid } ), 85 ]; 86 if self._oDisp is None or not self._oDisp.isReadOnlyUser(): 87 aoActions += [ 88 WuiTmLink('Modify', WuiAdmin.ksScriptName, 89 { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserEdit, 90 UserAccountData.ksParam_uid: oEntry.uid } ), 91 WuiTmLink('Remove', WuiAdmin.ksScriptName, 92 { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserDelPost, 93 UserAccountData.ksParam_uid: oEntry.uid }, 94 sConfirm = 'Are you sure you want to remove user #%d?' % (oEntry.uid,)), 95 ]; 96 80 97 return [ oEntry.uid, oEntry.sUsername, oEntry.sEmail, oEntry.sFullName, oEntry.sLoginName, 81 [ WuiTmLink('Modify', WuiAdmin.ksScriptName, 82 { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserEdit, 83 UserAccountData.ksParam_uid: oEntry.uid } ), 84 WuiTmLink('Remove', WuiAdmin.ksScriptName, 85 { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserDelPost, 86 UserAccountData.ksParam_uid: oEntry.uid }, 87 sConfirm = 'Are you sure you want to remove user #%d?' % (oEntry.uid,)), 88 ] ]; 98 'read only' if oEntry.fReadOnly else 'full', 99 aoActions, ]; 89 100 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py
r65350 r65914 729 729 730 730 # 731 # User related stuff. 732 # 733 734 def isReadOnlyUser(self): 735 """ Returns true if the logged in user is read-only or if no user is logged in. """ 736 return self._oCurUser is None or self._oCurUser.fReadOnly; 737 738 739 # 731 740 # Debugging 732 741 # … … 910 919 911 920 try: 921 if self.isReadOnlyUser(): 922 raise Exception('"%s" is a read only user!' % (self._oCurUser.sUsername,)); 912 923 self._sPageTitle = None 913 924 self._sPageBody = None … … 1045 1056 enmValidateFor = oData.ksValidateFor_Edit; 1046 1057 dErrors = oData.validateAndConvert(self._oDb, enmValidateFor); 1047 if len(dErrors) == 0: 1058 1059 # Check that the user can do this. 1060 sErrorMsg = None; 1061 assert self._oCurUser is not None; 1062 if self.isReadOnlyUser(): 1063 sErrorMsg = 'User %s is not allowed to modify anything!' % (self._oCurUser.sUsername,) 1064 1065 if len(dErrors) == 0 and sErrorMsg is None: 1048 1066 oData.convertFromParamNull(); 1049 1067 … … 1069 1087 oForm = oFormType(oData, sMode, oDisp = self); 1070 1088 oForm.setRedirectTo(sRedirectTo); 1071 (self._sPageTitle, self._sPageBody) = oForm.showForm(dErrors = dErrors );1089 (self._sPageTitle, self._sPageBody) = oForm.showForm(dErrors = dErrors, sErrorMsg = sErrorMsg); 1072 1090 return True; 1073 1091 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py
r65422 r65914 415 415 self._sTitle = sTitle; 416 416 self._sId = sId if sId is not None else (type(oData).__name__.lower() + 'form'); 417 self._fEditable = fEditable ;417 self._fEditable = fEditable and (oDisp is None or not oDisp.isReadOnlyUser()) 418 418 self._sSubmitAction = sSubmitAction; 419 419 if sSubmitAction is None and sMode != self.ksMode_Show: -
trunk/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py
r65350 r65914 175 175 # Format bits for adding or editing the failure reason. Level 0 is handled at the top of the page. 176 176 sChangeReason = ''; 177 if oTestResult.cErrors > 0 and iDepth > 0 :177 if oTestResult.cErrors > 0 and iDepth > 0 and self._oDisp is not None and not self._oDisp.isReadOnlyUser(): 178 178 dTmp = { 179 179 self._oDisp.ksParamAction: self._oDisp.ksActionTestResultFailureAdd if oTestResult.oReason is None else … … 406 406 WuiMain.ksActionTestResultFailureAddPost if oData is None else 407 407 WuiMain.ksActionTestResultFailureEditPost ) 408 fReadOnly = not self._oDisp or self._oDisp.isReadOnlyUser(); 408 409 oForm = WuiHlpForm('failure-reason', sFormActionUrl, 409 sOnSubmit = WuiHlpForm.ksOnSubmit_AddReturnToFieldWithCurrentUrl );410 sOnSubmit = WuiHlpForm.ksOnSubmit_AddReturnToFieldWithCurrentUrl, fReadOnly = fReadOnly); 410 411 oForm.addTextHidden(TestResultFailureData.ksParam_idTestResult, oTestResultTree.idTestResult); 411 412 oForm.addTextHidden(TestResultFailureData.ksParam_idTestSet, oTestSet.idTestSet); … … 414 415 aoFailureReasons, 415 416 sPostHtml = u' ' + WuiFailureReasonDetailsLink(oData.idFailureReason).toHtml() 416 + u' ' + WuiFailureReasonAddLink('New', fBracketed = False).toHtml()); 417 + (u' ' + WuiFailureReasonAddLink('New', fBracketed = False).toHtml() 418 if not fReadOnly else u'')); 417 419 oForm.addMultilineText(TestResultFailureData.ksParam_sComment, oData.sComment, 'Comment') 418 420 … … 429 431 else: 430 432 oForm.addComboBox(TestResultFailureData.ksParam_idFailureReason, -1, 'Reason', aoFailureReasons, 431 sPostHtml = ' ' + WuiFailureReasonAddLink('New').toHtml() );433 sPostHtml = ' ' + WuiFailureReasonAddLink('New').toHtml() if not fReadOnly else ''); 432 434 oForm.addMultilineText(TestResultFailureData.ksParam_sComment, '', 'Comment'); 433 435 oForm.addTextHidden(TestResultFailureData.ksParam_tsEffective, '');
Note:
See TracChangeset
for help on using the changeset viewer.