/* * RHQ Management Platform * Copyright (C) 2005-2014 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.coregui.client.inventory.common.detail.operation.history; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import com.google.gwt.user.client.History; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.data.Criteria; import com.smartgwt.client.data.DSRequest; import com.smartgwt.client.data.Record; import com.smartgwt.client.data.SortSpecifier; import com.smartgwt.client.types.Alignment; import com.smartgwt.client.types.Overflow; import com.smartgwt.client.types.SortDirection; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.HTMLPane; import com.smartgwt.client.widgets.Window; import com.smartgwt.client.widgets.events.CloseClickEvent; import com.smartgwt.client.widgets.events.CloseClickHandler; import com.smartgwt.client.widgets.grid.CellFormatter; import com.smartgwt.client.widgets.grid.HoverCustomizer; import com.smartgwt.client.widgets.grid.ListGridField; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.grid.events.RecordClickEvent; import com.smartgwt.client.widgets.grid.events.RecordClickHandler; import org.rhq.core.domain.operation.OperationRequestStatus; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.ImageManager; import org.rhq.coregui.client.LinkManager; import org.rhq.coregui.client.components.table.Table.TableActionInfo.TableActionInfoBuilder; import org.rhq.coregui.client.components.table.TableAction; import org.rhq.coregui.client.components.table.TableSection; import org.rhq.coregui.client.components.table.TimestampCellFormatter; import org.rhq.coregui.client.gwt.GWTServiceLookup; import org.rhq.coregui.client.gwt.OperationGWTServiceAsync; import org.rhq.coregui.client.inventory.resource.AncestryUtil; import org.rhq.coregui.client.operation.OperationHistoryDataSource; import org.rhq.coregui.client.util.enhanced.EnhancedIButton.ButtonColor; import org.rhq.coregui.client.util.message.Message; import org.rhq.coregui.client.util.message.Message.Option; import org.rhq.coregui.client.util.message.Message.Severity; /** * @author Greg Hinkle * @author John Mazzitelli * @author Ian Springer */ public abstract class AbstractOperationHistoryListView<T extends AbstractOperationHistoryDataSource<?, ?>> extends TableSection<T> { private static final String HEADER_ICON = "subsystems/control/Operation_24.png"; public AbstractOperationHistoryListView(T dataSource, String title) { super(title); setDataSource(dataSource); setTitleIcon(HEADER_ICON); } public AbstractOperationHistoryListView(T dataSource, String title, Criteria criteria) { super(title, criteria); setDataSource(dataSource); } protected abstract boolean hasControlPermission(); protected abstract boolean showNewScheduleButton(); @Override protected void configureTable() { List<ListGridField> fields = createFields(); setListGridFields(fields.toArray(new ListGridField[fields.size()])); // explicitly sort on started time so the user can see the last operation at the top and is sorted descendingly SortSpecifier sortSpec = new SortSpecifier(AbstractOperationHistoryDataSource.Field.STARTED_TIME, SortDirection.DESCENDING); getListGrid().setSort(new SortSpecifier[] { sortSpec }); addTableAction(MSG.common_button_delete(), getDeleteConfirmMessage(), ButtonColor.RED, new TableAction() { public boolean isEnabled(ListGridRecord[] selection) { int count = selection.length; return (count >= 1 && hasControlPermission()); } public void executeAction(ListGridRecord[] selection, Object actionValue) { deleteSelectedRecords(); } }); addTableAction(MSG.view_operationHistoryList_button_forceDelete(), getDeleteConfirmMessage(), ButtonColor.RED, new TableAction() { public boolean isEnabled(ListGridRecord[] selection) { int count = selection.length; return (count >= 1 && hasControlPermission()); } public void executeAction(ListGridRecord[] selection, Object actionValue) { DSRequest requestProperties = new DSRequest(); requestProperties.setAttribute("force", true); deleteSelectedRecords(requestProperties); } }); // the below addTableAction and the enclosing TableAction anon class code is taken from // OperationHistoryView. I don't know why we have an abstract operation history list hierarchy separate // from OperationHistoryView. Perhaps independently developed and the developer of one didn't know the other // existed. In any case, this code is almost identical as the table action in OperationHistoryView with the // exception that this code uses AbstractOperationHistoryDataSource.Field constants. addTableAction(MSG.common_button_cancel(), MSG.view_operationHistoryList_cancelConfirm(), new TableAction() { public boolean isEnabled(ListGridRecord[] selection) { int count = selection.length; for (ListGridRecord item : selection) { if (!OperationRequestStatus.INPROGRESS.name().equals( item.getAttribute(AbstractOperationHistoryDataSource.Field.STATUS))) { count--; // one selected item was not in-progress, it doesn't count } } return (count >= 1 && hasControlPermission()); } public void executeAction(ListGridRecord[] selection, Object actionValue) { int numCancelRequestsSubmitted = 0; OperationGWTServiceAsync opService = GWTServiceLookup.getOperationService(); for (ListGridRecord toBeCanceled : selection) { // only cancel those selected operations that are currently in progress if (OperationRequestStatus.INPROGRESS.name().equals( toBeCanceled.getAttribute(AbstractOperationHistoryDataSource.Field.STATUS))) { numCancelRequestsSubmitted++; final int historyId = toBeCanceled.getAttributeAsInt(OperationHistoryDataSource.Field.ID); opService.cancelOperationHistory(historyId, false, new AsyncCallback<Void>() { public void onSuccess(Void result) { Message msg = new Message(MSG.view_operationHistoryList_cancelSuccess(String .valueOf(historyId)), Severity.Info, EnumSet.of(Option.BackgroundJobResult)); CoreGUI.getMessageCenter().notify(msg); }; public void onFailure(Throwable caught) { Message msg = new Message(MSG.view_operationHistoryList_cancelFailure(String .valueOf(historyId)), caught, Severity.Error, EnumSet .of(Option.BackgroundJobResult)); CoreGUI.getMessageCenter().notify(msg); }; }); } } CoreGUI.getMessageCenter().notify( new Message(MSG.view_operationHistoryList_cancelSubmitted(String .valueOf(numCancelRequestsSubmitted)), Severity.Info)); refreshTableInfo(); } }); if (showNewScheduleButton()) { TableActionInfo rescheduleAction = new TableActionInfoBuilder(MSG.common_button_reschedule(), new RescheduleTableAction()).setTooltip(MSG.common_button_reschedule_tooltip()).createTableActionInfo(); addTableAction(rescheduleAction); addTableAction(MSG.common_button_new() + " " + MSG.common_button_schedule(), ButtonColor.BLUE, new NewScheduleTableAction()); } super.configureTable(); } protected List<ListGridField> createFields() { List<ListGridField> fields = new ArrayList<ListGridField>(); ListGridField idField = new ListGridField(AbstractOperationHistoryDataSource.Field.ID); idField.setWidth(38); fields.add(idField); ListGridField opNameField = new ListGridField(AbstractOperationHistoryDataSource.Field.OPERATION_NAME); opNameField.setWidth("34%"); fields.add(opNameField); ListGridField subjectField = new ListGridField(AbstractOperationHistoryDataSource.Field.SUBJECT); subjectField.setWidth("33%"); fields.add(subjectField); ListGridField statusField = createStatusField(); fields.add(statusField); ListGridField startedTimeField = createStartedTimeField(); startedTimeField.setWidth("33%"); fields.add(startedTimeField); return fields; } protected ListGridField createStartedTimeField() { ListGridField startedTimeField = new ListGridField(AbstractOperationHistoryDataSource.Field.STARTED_TIME); startedTimeField.setAlign(Alignment.LEFT); startedTimeField.setCellAlign(Alignment.LEFT); startedTimeField.setCellFormatter(new TimestampCellFormatter() { public String format(Object value, ListGridRecord record, int rowNum, int colNum) { if (value != null) { return super.format(value, record, rowNum, colNum); } else { return "<i>" + MSG.view_operationHistoryList_notYetStarted() + "</i>"; } } }); startedTimeField.setShowHover(true); startedTimeField.setHoverCustomizer(TimestampCellFormatter .getHoverCustomizer(AbstractOperationHistoryDataSource.Field.STARTED_TIME)); return startedTimeField; } protected ListGridField createStatusField() { ListGridField statusField = new ListGridField(AbstractOperationHistoryDataSource.Field.STATUS); statusField.setAlign(Alignment.CENTER); statusField.setCellAlign(Alignment.CENTER); statusField.setShowHover(true); statusField.setHoverCustomizer(new HoverCustomizer() { @Override public String hoverHTML(Object value, ListGridRecord record, int rowNum, int colNum) { String statusStr = record.getAttribute(AbstractOperationHistoryDataSource.Field.STATUS); OperationRequestStatus status = OperationRequestStatus.valueOf(statusStr); switch (status) { case SUCCESS: { return MSG.common_status_success(); } case FAILURE: { return MSG.common_status_failed(); } case INPROGRESS: { return MSG.common_status_inprogress(); } case CANCELED: { return MSG.common_status_canceled(); } } // should never get here return MSG.common_status_unknown(); } }); statusField.setCellFormatter(new CellFormatter() { public String format(Object o, ListGridRecord listGridRecord, int i, int i1) { OperationRequestStatus status = OperationRequestStatus.valueOf((String) o); String icon = ImageManager.getOperationResultsIcon(status); return Canvas.imgHTML(icon, 16, 16); } }); statusField.addRecordClickHandler(new RecordClickHandler() { @Override public void onRecordClick(RecordClickEvent event) { Record record = event.getRecord(); String statusStr = record.getAttribute(AbstractOperationHistoryDataSource.Field.STATUS); OperationRequestStatus status = OperationRequestStatus.valueOf(statusStr); if (status == OperationRequestStatus.FAILURE) { final Window winModal = new Window(); winModal.setTitle(MSG.common_title_details()); winModal.setOverflow(Overflow.VISIBLE); winModal.setShowMinimizeButton(false); winModal.setShowMaximizeButton(true); winModal.setIsModal(true); winModal.setShowModalMask(true); winModal.setAutoSize(true); winModal.setAutoCenter(true); winModal.setShowResizer(true); winModal.setCanDragResize(true); winModal.centerInPage(); winModal.addCloseClickHandler(new CloseClickHandler() { @Override public void onCloseClick(CloseClickEvent event) { winModal.markForDestroy(); } }); HTMLPane htmlPane = new HTMLPane(); htmlPane.setMargin(10); htmlPane.setDefaultWidth(500); htmlPane.setDefaultHeight(400); String errorMsg = record.getAttribute(AbstractOperationHistoryDataSource.Field.ERROR_MESSAGE); if (errorMsg == null) { errorMsg = MSG.common_status_failed(); } htmlPane.setContents("<pre>" + errorMsg + "</pre>"); winModal.addItem(htmlPane); winModal.show(); } } }); statusField.setWidth(44); return statusField; } protected ListGridField createResourceField() { ListGridField resourceField = new ListGridField(AncestryUtil.RESOURCE_NAME, MSG.common_title_resource()); resourceField.setAlign(Alignment.LEFT); resourceField.setCellAlign(Alignment.LEFT); resourceField.setCellFormatter(new CellFormatter() { public String format(Object o, ListGridRecord listGridRecord, int i, int i1) { String url = LinkManager.getResourceLink(listGridRecord.getAttributeAsInt(AncestryUtil.RESOURCE_ID)); return LinkManager.getHref(url, o.toString()); } }); resourceField.setShowHover(true); resourceField.setHoverCustomizer(new HoverCustomizer() { public String hoverHTML(Object value, ListGridRecord listGridRecord, int rowNum, int colNum) { return AncestryUtil.getResourceHoverHTML(listGridRecord, 0); } }); return resourceField; } protected ListGridField createAncestryField() { ListGridField ancestryField = AncestryUtil.setupAncestryListGridField(); return ancestryField; } @Override protected void deleteSelectedRecords(DSRequest requestProperties) { disableAllFooterControls(); // wait for this to complete before we allow more... final ListGridRecord[] recordsToBeDeleted = getListGrid().getSelectedRecords(); final int numberOfRecordsToBeDeleted = recordsToBeDeleted.length; final Boolean forceValue = (requestProperties != null && requestProperties.getAttributeAsBoolean("force")); final boolean force = ((forceValue != null) && forceValue); final int[] idsToBeDeleted = new int[numberOfRecordsToBeDeleted]; int i = 0; for (ListGridRecord record : recordsToBeDeleted) { idsToBeDeleted[i++] = record.getAttributeAsInt(OperationHistoryDataSource.Field.ID); } GWTServiceLookup.getOperationService().deleteOperationHistories(idsToBeDeleted, force, new AsyncCallback<Void>() { public void onSuccess(Void result) { CoreGUI.getMessageCenter().notify( new Message(MSG.view_operationHistoryList_deleteSuccess(String .valueOf(numberOfRecordsToBeDeleted)))); refresh(); refreshTableInfo(); // enable proper buttons } public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_operationHistoryList_deleteFailure(), caught); refresh(); refreshTableInfo(); // enable proper buttons } }); } @Override protected String getDetailsLinkColumnName() { return AbstractOperationHistoryDataSource.Field.OPERATION_NAME; } private class NewScheduleTableAction implements TableAction { public boolean isEnabled(ListGridRecord[] selection) { return hasControlPermission(); } public void executeAction(ListGridRecord[] selection, Object actionValue) { String url = getViewUrl(selection); if (url != null) { CoreGUI.goToView(url); } } protected String getViewUrl(ListGridRecord[] selection) { String oldurl = History.getToken(); return oldurl.substring(0, oldurl.lastIndexOf("/")) + "/Schedules/0"; } } private class RescheduleTableAction extends NewScheduleTableAction { @Override public boolean isEnabled(ListGridRecord[] selection) { return super.isEnabled(selection) && selection.length == 1; } @Override protected String getViewUrl(ListGridRecord[] selection) { String viewUrl = super.getViewUrl(selection); if (viewUrl != null) { ListGridRecord selectedRecord = selection[0]; Integer operationHistoryId = selectedRecord.getAttributeAsInt(OperationHistoryDataSource.Field.ID); viewUrl += "/example=" + operationHistoryId; } return viewUrl; } } }