/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Bugzilla Testopia System. * * The Initial Developer of the Original Code is Greg Hendricks. * Portions created by Greg Hendricks are Copyright (C) 2006 * Novell. All Rights Reserved. * * Contributor(s): Greg Hendricks * Ryan Hamilton * Daniel Parker */ Testopia.Util.displayStatusIcon = function(name){ return '' + name + ''; }; Testopia.Util.makeLink = function(id, m, r, ri, ci, s, type){ if (type == 'bug') { if (s.isTreport === true) return '' + id + ''; return '' + id + ''; } if (s.isTreport === true) return '' + id + ''; return '' + id + ''; }; Testopia.Util.CascadeProductSelection = function(){ addOption = function(selectElement, newOption){ try { selectElement.add(newOption, null); } catch (e) { selectElement.add(newOption, selectElement.length); } }; lsearch = function(val, arr){ if (typeof arr != 'object') { if (arr == val) return true; return false; } for (var i in arr) { if (arr[i] == val) return true; } return false; }; this.addOption = addOption; var fillSelects = function(data, prods){ var s = Testopia.Util.urlQueryToJSON(window.location.search); if (prods) { s.product = prods; } for (var i in data.selectTypes) { if (typeof data.selectTypes[i] != 'function') { try { document.getElementById(data.selectTypes[i]).options.length = 0; for (var j in data[data.selectTypes[i]]) { if (typeof data[data.selectTypes[i]][j] != 'function') { var newOption = new Option(data[data.selectTypes[i]][j], data[data.selectTypes[i]][j], false, lsearch(data[data.selectTypes[i]][j], s[data.selectTypes[i]])); addOption(document.getElementById(data.selectTypes[i]), newOption); } } document.getElementById(data.selectTypes[i]).disabled = false; document.getElementById(data.selectTypes[i]) } catch (err) { } } } }; this.fillSelects = fillSelects; this.onProductSelection = function(prod){ var ids = []; for (var i = 0; i < prod.options.length; i++) { if (prod.options[i].selected === true) { ids.push(prod.options[i].value); } } var form = new Ext.form.BasicForm('testopia_helper_frm', {}); var type = prod.id == 'classification' ? 'classification' : 'product'; form.submit({ url: "tr_query.cgi", params: { value: ids.join(","), action: "getversions", type: type }, success: function(f, a){ fillSelects(a.result.objects); }, failure: Testopia.Util.error }); }; return this; }; /* * Testopia.User.Lookup - This generates a typeahead lookup for usernames. * It can be used anywhere in Testopia. Extends Ext ComboBox */ Testopia.User.Lookup = function(cfg){ Testopia.User.Lookup.superclass.constructor.call(this, { id: cfg.id || 'user_lookup', store: new Ext.data.JsonStore({ url: 'tr_quicksearch.cgi', listeners: { 'exception': Testopia.Util.loadError }, baseParams: { action: 'getuser' }, root: 'users', totalProperty: 'total', id: 'login', fields: [{ name: 'login', mapping: 'id' }, { name: 'name', mapping: 'name' }] }), listeners: { 'valid': function(f){ f.value = f.getRawValue(); }, 'beforequery': function(o){ if (cfg.multistring) { var term = o.query.match(/(^.*),(.*)/); if (term) { o.combo.multivalue = term[1]; o.query = term[2]; } } }, 'select': function(c, r, i){ if (cfg.multistring) { var v = c.multivalue || ''; v = v ? v + ', ' + r.get('login') : r.get('login'); c.setValue(v); } } }, queryParam: 'search', loadingText: 'Looking up users...', displayField: 'login', valueField: 'login', typeAhead: true, hideTrigger: true, minListWidth: 300, forceSelection: false, emptyText: 'Type a username...', pageSize: 20, tpl: '
{name}
{login}
' }); Ext.apply(this, cfg); }; Ext.extend(Testopia.User.Lookup, Ext.form.ComboBox); //TODO: Implement this (see bug 340461) DocCompareToolbar = function(object, id){ var store = new Ext.data.JsonStore({ url: 'tr_history.cgi', listeners: { 'exception': Testopia.Util.loadError }, baseParams: { action: 'getdocversions', object: object, object_id: id }, root: 'list', fields: [{ name: 'id', mapping: 'id' }, { name: 'name', mapping: 'name' }] }); this.toolbar = new Ext.Toolbar({ id: 'doc_compare_tbar', items: [ // new Ext.form.ComboBox({ // id: 'doc_compare_v1', // store: store, // displayField: 'name', // valueField: 'id', // width: 50, // mode: 'local', // triggerAction: 'all' // }), // new Ext.form.ComboBox({ // id: 'doc_compare_v2', // store: store, // displayField: 'name', // valueField: 'id', // width: 50, // mode: 'local', // triggerAction: 'all' // }),{ // xtype: 'button', // id: 'doc_compare_btn', // text: 'Compare', // handler: function(){ // // } // }, // new Ext.Toolbar.Spacer(), // new Ext.Toolbar.Separator(), new Ext.Toolbar.Fill(), new Ext.form.ComboBox({ id: 'doc_view', store: store, displayField: 'name', valueField: 'id', width: 50, triggerAction: 'all' }), { xtype: 'button', id: 'doc_view_btn', text: 'View Version', handler: function(){ var tab = Ext.getCmp('object_panel').add({ title: 'Version ' + Ext.getCmp('doc_view').getValue(), closable: true, autoScroll: true }); tab.show(); tab.load({ url: 'tr_history.cgi', params: { action: 'showdoc', object: object, object_id: id, version: Ext.getCmp('doc_view').getValue() }, failure: Testopia.Util.error }); } }] }); return this.toolbar; }; /* * Testopia.Util.HistoryList - */ Testopia.Util.HistoryList = function(object, id){ this.store = new Ext.data.JsonStore({ url: 'tr_history.cgi', listeners: { 'exception': Testopia.Util.loadError }, baseParams: { action: 'show', object: object, object_id: id }, root: 'list', fields: [{ name: "what", mapping: "what" }, { name: "who", mapping: "who" }, { name: "oldvalue", mapping: "oldvalue" }, { name: "newvalue", mapping: "newvalue" }, { name: "when", mapping: "changed" }] }); this.columns = [{ header: "What", width: 150, dataIndex: 'what', sortable: true }, { header: "Who", width: 180, sortable: true, dataIndex: 'who' }, { header: "When", width: 150, sortable: true, dataIndex: 'when' }, { header: "Old", width: 180, sortable: true, dataIndex: 'oldvalue' }, { id: 'new', header: "New", width: 180, sortable: true, dataIndex: 'newvalue' }]; Testopia.Util.HistoryList.superclass.constructor.call(this, { title: 'Change History', id: 'history-grid', layout: 'fit', loadMask: { msg: 'Loading History...' }, autoExpandColumn: "new", autoScroll: true, sm: new Ext.grid.RowSelectionModel({ singleSelect: false }) }); this.on('rowcontextmenu', this.onContextClick, this); this.on('activate', this.onActivate, this); }; Ext.extend(Testopia.Util.HistoryList, Ext.grid.GridPanel, { onActivate: function(){ if (!this.store.getCount()) { this.store.load(); } }, onContextClick: function(grid, index, e){ if (!this.menu) { // create context menu on first right click this.menu = new Ext.menu.Menu({ id: 'history-ctx-menu', enableScrolling: false, items: [{ text: 'Refresh', icon: 'extensions/Testopia/img/refresh.png', iconCls: 'img_button_16x', handler: function(){ grid.store.reload(); } }] }); } e.stopEvent(); this.menu.showAt(e.getXY()); } }); Testopia.Util.PagingBar = function(type, store){ this.type = type; var baseParams = clone(store.baseParams); function doUpdate(){ this.updateInfo(); } function clone(orig){ var clone = new Object(); for (var i in orig) { clone[i] = orig[i]; } return clone; } function viewallUpdate(){ this.cursor = 0; this.afterTextEl.el.innerHTML = String.format(this.afterPageText, 1); this.field.dom.value = 1; this.updateInfo(); } var sizer = new Ext.form.ComboBox({ store: new Ext.data.SimpleStore({ fields: ['value', 'name'], id: 0, data: [[25, 25], [50, 50], [100, 100], [500, 500]], autoLoad: true }), id: type + '_page_sizer', mode: 'local', displayField: 'name', valueField: 'value', triggerAction: 'all', editable: false, width: 50 }); sizer.on('select', function(c, r, i){ this.pageSize = r.get('value'); Ext.state.Manager.set('TESTOPIA_DEFAULT_PAGE_SIZE', r.get('value')); store.baseParams.limit = r.get('value'); store.load({ params: { start: 0 }, callback: doUpdate.createDelegate(this) }); }, this); this.sizer = sizer; var viewall = new Ext.Button({ text: 'View All', enableToggle: true }); viewall.on('toggle', function(b, p){ if (p) { this.pageSize = 0; store.load({ params: { viewall: 1 }, callback: viewallUpdate.createDelegate(this) }); } else { this.pageSize = sizer.getValue(); store.load({ params: { start: 0, limit: sizer.getValue() } }); } }, this); var filter = new Ext.form.TextField({ allowBlank: true, id: type + '_paging_filter', selectOnFocus: true }); filter.on('specialkey', function(f, e){ var key = e.getKey(); if (key == e.ENTER) { var params = { start: 0, limit: sizer.getValue() }; if (this.getValue().length === 0) { store.baseParams = clone(baseParams); store.load({ params: params }); Ext.getCmp(type + '_filtered_txt').hide(); return; } var params = { start: 0, limit: sizer.getValue() }; var s = this.getValue(); var term = s.match(/(^.*?):/); if (term) { term = term[1]; var q = Testopia.Util.trim(s.substr(s.indexOf(':') + 1, s.length)); if (term.match(/^start/i)) { term = 'start_date'; } if (term.match(/^stop/i)) { term = 'stop_date'; } if (term.match(/^manager/i)) { term = 'manager'; } switch (term) { case 'status': if (type == 'case') { term = 'case_status'; } else if (type == 'caserun') { term = 'case_run_status'; } else { term = 'run_status'; if (q.match(/running/i)) { q = 0; } else { q = 1; } } break; case 'tester': term = 'default_tester'; break; case 'plan': term = 'plan_id'; break; case 'case': term = 'case_id'; break; case 'run': term = 'run_id'; break; case 'product_version': term = 'default_product_version'; break; } store.baseParams[term] = q; store.baseParams[term + '_type'] = 'substring'; } else { if (type == 'case' || type == 'run') { store.baseParams.summary = this.getValue(); store.baseParams.summary_type = 'allwordssubst'; } else if (type == 'caserun') { store.baseParams.case_summary = this.getValue(); store.baseParams.case_summary_type = 'allwordssubst'; } else { store.baseParams.name = this.getValue(); store.baseParams.name_type = 'allwordssubst'; } } store.load({ params: params }); Ext.getCmp(type + '_filtered_txt').show(); } if ((key == e.BACKSPACE || key == e.DELETE) && this.getValue().length === 0) { store.baseParams = baseParams; store.load({ params: { start: 0, limit: sizer.getValue() } }); Ext.getCmp(type + '_filtered_txt').hide(); } }); sizer.on('render', function(){ var tt = new Ext.ToolTip({ target: type + '_paging_filter', title: 'Quick Search Filter', hideDelay: '500', html: "Enter column and search term separated by ':'
Example: priority: P3
Blank field and ENTER to clear" }); }); Testopia.Util.PagingBar.superclass.constructor.call(this, { id: type + '_pager', pageSize: Ext.state.Manager.get('TESTOPIA_DEFAULT_PAGE_SIZE', 25), displayInfo: true, displayMsg: 'Displaying test ' + type + 's {0} - {1} of {2}', emptyMsg: 'No test ' + type + 's were found', store: store, items: ['Filter: ', filter, ' ', '-', 'View ', ' ', sizer, ' ', viewall, ' ', { type: 'tbtext', text: '(FILTERED)', hidden: true, id: type + '_filtered_txt', style: 'font-weight:bold;color:red' }] }); this.on('render', this.setPager, this); this.cursor = 0; }; Ext.extend(Testopia.Util.PagingBar, Ext.PagingToolbar, { setPager: function(){ Ext.getCmp(this.type + '_page_sizer').setValue(Ext.state.Manager.get('TESTOPIA_DEFAULT_PAGE_SIZE', 25)); } }); Testopia.Util.updateFromList = function(type, params, grid){ var form = new Ext.form.BasicForm('testopia_helper_frm', {}); params.ctype = 'json'; params.action = 'update'; form.submit({ url: 'tr_list_' + type + 's.cgi', params: params, success: function(f, a){ if (type == 'caserun') { Ext.getCmp('run_progress').updateProgress(a.result.passed, a.result.failed, a.result.blocked, a.result.complete); } Testopia.Util.notify.msg('Test ' + type + 's updated', 'The selected {0}s were updated successfully', type); if (grid.selectedRows) { grid.store.baseParams.addcases = grid.selectedRows.join(','); Ext.getCmp(type + '_filtered_txt').show(); } try { Ext.getCmp('case_details_panel').store.reload(); } catch (err) { } grid.store.reload({ callback: function(){ if (grid.selectedRows) { var sm = grid.getSelectionModel(); var sel = []; for (var i = 0; i < grid.selectedRows.length; i++) { var index = grid.store.find('case_id', grid.selectedRows[i]); if (index >= 0) sel.push(index); } sm.selectRows(sel); if (sm.getCount() < 1) { Ext.getCmp('case_details_panel').disable(); } } } }); }, failure: function(f, a){ Testopia.Util.error(f, a); grid.store.reload({ callback: function(){ if (grid.selectedRows) { grid.getSelectionModel().selectRows(grid.selectedRows); } } }); } }); }; Testopia.Util.ComboRenderer = function(v, md, r, ri, ci, s){ f = this.getColumnModel().getCellEditor(ci, ri).field; record = f.store.getById(v); if (record) { return record.data[f.displayField]; } else { return v; } }; /* * Testopia.Util.error - global public function for displaying Bugzilla error messages * when ERROR_MODE_AJAX is set. All failure branches of Ext.basicForm submit calls * should point here. */ Testopia.Util.error = function(f, a){ f.el.unmask(); var message; if (a.response.status && a.response.status != 200) { message = { title: 'System Error!', msg: a.response.responseText, buttons: Ext.Msg.OK, icon: Ext.MessageBox.ERROR, minWidth: 450 }; } else { message = { title: 'An Error Has Occurred', msg: a.result.message, buttons: Ext.Msg.OK, icon: Ext.MessageBox.ERROR, prompt: true, value: a.result.message, multiline: true, minWidth: 450 }; } Ext.Msg.show(message); }; Testopia.Util.loadError = function(dp, errtype, a,o,r,ar,args){ var message = 'There was an error loading the data: '; if (errtype == 'response'){ message += r.responseText; } Ext.Msg.show({ title: 'An Error Has Occurred', width: 450, msg: message, prompt: true, multiline: true, value: message, buttons: Ext.Msg.OK, icon: Ext.MessageBox.ERROR }); }; Testopia.Util.getSelectedObjects = function(grid, field){ var selections = grid.getSelectionModel().getSelections(); var arIDs = []; var ids; for (var i = 0; i < selections.length; i++) { arIDs.push(selections[i].get(field)); } ids = arIDs.join(','); return ids; }; Testopia.Util.editFirstSelection = function(grid){ if (grid.store.getCount() === 0) { return; } var row = grid.store.indexOf(grid.getSelectionModel().getSelected()); if (row == -1){ row = 0; } grid.plugins[0].startEditing(row); }; Testopia.Util.urlQueryToJSON = function(url){ url = url.replace(/.*\//, ''); var params = {}; var loc = url.split('?', 2); var file = loc[0]; var search = loc[1] ? loc[1] : file; var pairs = search.split('&'); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split('='); if (params[pair[0]]) { if (typeof params[pair[0]] == 'object') { params[pair[0]].push(unescape(pair[1])); } else { params[pair[0]] = new Array(params[pair[0]]); params[pair[0]].push(unescape(pair[1])); } } else { params[pair[0]] = unescape(pair[1]); } } return params; }; Testopia.Util.JSONToURLQuery = function(params, searchStr, drops){ searchStr = searchStr || ''; for (var key in params) { if (drops.indexOf(key) != -1) { continue; } if (typeof params[key] == 'object') { for (i = 0; i < params[key].length; i++) { searchStr = searchStr + key + '=' + encodeURIComponent(params[key][i]) + '&'; } } else { searchStr = searchStr + key + '=' + encodeURIComponent(params[key]) + '&'; } } if (searchStr.lastIndexOf('&') == searchStr.length - 1) { searchStr = searchStr.substr(0, searchStr.length - 1); } return searchStr; }; /* * Testopia.notify - Displays a floating notification area. * Taken from ext/examples/examples.js */ Testopia.Util.notify = function(){ var msgCt; function createBox(t, s){ return ['
', '
', '

', t, '

', s, '
', '
', '
'].join(''); } return { msg: function(title, format){ if (!msgCt) { msgCt = Ext.DomHelper.insertFirst(document.getElementById('bugzilla-body'), { id: 'msg-div' }, true); } msgCt.alignTo(document, 't-t'); var s = String.format.apply(String, Array.prototype.slice.call(arguments, 1)); var m = Ext.DomHelper.append(msgCt, { html: createBox(title, s) }, true); m.slideIn('t').pause(1).ghost("t", { remove: true }); }, init: function(){ return; } }; }(); Testopia.Util.trim = function(input){ input = input.replace(/^\s+/g, ''); input = input.replace(/\s+$/g, ''); return input; }; Testopia.Util.PlanSelector = function(product_id, cfg){ var single = cfg.action.match('case') ? false : true; var pg = new Testopia.TestPlan.Grid({ product_id: product_id }, { id: 'plan_selector_grid', height: 300, single: single }); var pchooser = new Testopia.Product.Combo({ mode: 'local', value: product_id }); pchooser.on('select', function(c, r, i){ pg.store.baseParams = { ctype: 'json', product_id: r.get('id') }; pg.store.load(); }); Testopia.Util.PlanSelector.superclass.constructor.call(this, { items: [pg], buttons: [{ text: 'Use Selected', handler: function(){ var loc = cfg.action + '?plan_id=' + Testopia.Util.getSelectedObjects(pg, 'plan_id'); if (cfg.bug_id) { loc = loc + '&bug=' + cfg.bug_id; } window.location = loc; } }] }); pg.on('render', function(){ var items = pg.getTopToolbar().items.items; for (var i = 0; i < items.length; i++) { items[i].destroy(); } pg.getTopToolbar().add('Product: ', pchooser); pg.getSelectionModel().un('rowselect', pg.getSelectionModel().events['rowselect'].listeners[0].fn); pg.getSelectionModel().un('rowdeselect', pg.getSelectionModel().events['rowdeselect'].listeners[0].fn); pg.store.load(); }); } Ext.extend(Testopia.Util.PlanSelector, Ext.Panel); Testopia.Util.DisableTools = function(tbar, ex){ if (typeof ex != 'object'){ ex = []; } for (var i in tbar.items.items) { if (ex.indexOf(tbar.items.items[i].id) != -1) { continue; } try { tbar.items.items[i].disable(); } catch (e) { } } }