package org.rhq.coregui.client.dashboard.portlets.recent.operations; /* * RHQ Management Platform * Copyright (C) 2010 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ import com.google.gwt.user.client.Timer; import com.smartgwt.client.types.Alignment; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.HTMLFlow; import com.smartgwt.client.widgets.form.DynamicForm; import com.smartgwt.client.widgets.form.events.SubmitValuesEvent; import com.smartgwt.client.widgets.form.events.SubmitValuesHandler; import com.smartgwt.client.widgets.form.fields.CheckboxItem; import com.smartgwt.client.widgets.form.fields.SelectItem; import com.smartgwt.client.widgets.form.fields.events.ChangeEvent; import com.smartgwt.client.widgets.form.fields.events.ChangeHandler; import com.smartgwt.client.widgets.grid.CellFormatter; import com.smartgwt.client.widgets.grid.HoverCustomizer; import com.smartgwt.client.widgets.grid.ListGrid; import com.smartgwt.client.widgets.grid.ListGridField; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.layout.VStack; import org.rhq.core.domain.common.EntityContext; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.configuration.definition.ConfigurationDefinition; import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple; import org.rhq.core.domain.configuration.definition.PropertySimpleType; import org.rhq.core.domain.dashboard.DashboardPortlet; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.LinkManager; import org.rhq.coregui.client.components.table.TimestampCellFormatter; import org.rhq.coregui.client.dashboard.AutoRefreshPortlet; import org.rhq.coregui.client.dashboard.AutoRefreshUtil; import org.rhq.coregui.client.dashboard.CustomSettingsPortlet; import org.rhq.coregui.client.dashboard.Portlet; import org.rhq.coregui.client.dashboard.PortletViewFactory; import org.rhq.coregui.client.dashboard.PortletWindow; import org.rhq.coregui.client.inventory.resource.AncestryUtil; import org.rhq.coregui.client.operation.ScheduledOperationsDataSource; import org.rhq.coregui.client.util.enhanced.EnhancedHLayout; import org.rhq.coregui.client.util.enhanced.EnhancedVLayout; /** * A view that displays a live table of completed Operations and scheduled operations. * * @author Simeon Pinder */ public class OperationSchedulePortlet extends EnhancedVLayout implements CustomSettingsPortlet, AutoRefreshPortlet { // A non-displayed, persisted identifier for the portlet public static final String KEY = "OperationSchedule"; // A default displayed, persisted name for the portlet public static final String NAME = MSG.common_title_scheduled_operations(); public static final String OPERATIONS_RANGE_SCHEDULED_ENABLED = "operations-scheduled-enabled"; public static final String OPERATIONS_RANGE_SCHEDULED = "operations-range-scheduled"; public static final String RANGE_DISABLED_MESSAGE = MSG.view_portlet_operations_disabled(); //TODO: change this to use the Smart GWT default value. public static final String RANGE_DISABLED_MESSAGE_DEFAULT = MSG.common_msg_noItemsToShow(); private static final int WIDTH_RECENT_TIME = 150; private static final int WIDTH_RECENT_STATUS = 50; private static final int WIDTH_SCHEDULED_TIME = WIDTH_RECENT_TIME + WIDTH_RECENT_STATUS; // set on initial configuration, the window for this portlet view. private PortletWindow portletWindow; //ListGrids for operations private ListGrid scheduledOperationsGrid = null; private ScheduledOperationsDataSource dataSourceScheduled; public static final String unlimited = "-1"; public static final String unlimitedString = MSG.common_label_unlimited(); public static final String defaultValue = "5"; public static final boolean defaultEnabled = true; private Timer refreshTimer; public OperationSchedulePortlet() { super(); this.dataSourceScheduled = new ScheduledOperationsDataSource(this); } @Override protected void onInit() { super.onInit(); this.scheduledOperationsGrid = new ListGrid(); scheduledOperationsGrid.setDataSource(getDataSourceScheduled()); scheduledOperationsGrid.setAutoFetchData(true); scheduledOperationsGrid.setWidth100(); scheduledOperationsGrid.setWrapCells(true); addMember(scheduledOperationsGrid); } @Override protected void onDraw() { super.onDraw(); ListGridField timeNext = new ListGridField(ScheduledOperationsDataSource.Field.TIME.propertyName(), ScheduledOperationsDataSource.Field.TIME.title(), WIDTH_SCHEDULED_TIME); timeNext.setCellFormatter(new TimestampCellFormatter() { public String format(Object value, ListGridRecord record, int rowNum, int colNum) { if (value != null) { String timestamp = super.format(value, record, rowNum, colNum); Integer id = record.getAttributeAsInt(AncestryUtil.RESOURCE_ID); String url = null; Integer opScheduleId = record.getAttributeAsInt("id"); if (id == null) { id = record.getAttributeAsInt(ScheduledOperationsDataSource.Field.GROUP_ID.propertyName()); if (record.getAttributeAsBoolean(ScheduledOperationsDataSource.Field.IS_AUTO_GROUP .propertyName())) { url = LinkManager.getAutoGroupOperationScheduleLink(id, opScheduleId); } else { url = LinkManager.getSubsystemGroupOperationScheduleLink(id, opScheduleId); } } else { url = LinkManager.getSubsystemResourceOperationScheduleLink(id, opScheduleId); } return LinkManager.getHref(url, timestamp); } else { return "<i>" + MSG.common_label_none() + "</i>"; } } }); timeNext.setShowHover(true); timeNext.setHoverCustomizer(TimestampCellFormatter.getHoverCustomizer(ScheduledOperationsDataSource.Field.TIME .propertyName())); ListGridField operationNext = new ListGridField(ScheduledOperationsDataSource.Field.OPERATION.propertyName(), ScheduledOperationsDataSource.Field.OPERATION.title()); ListGridField resourceNext = new ListGridField( ScheduledOperationsDataSource.Field.RESOURCE_OR_GROUP.propertyName(), ScheduledOperationsDataSource.Field.RESOURCE_OR_GROUP.title()); resourceNext.setCellFormatter(new CellFormatter() { public String format(Object o, ListGridRecord listGridRecord, int i, int i1) { Integer id = listGridRecord.getAttributeAsInt(AncestryUtil.RESOURCE_ID); String url = null; if (id == null) { id = listGridRecord.getAttributeAsInt(ScheduledOperationsDataSource.Field.GROUP_ID.propertyName()); if (listGridRecord.getAttributeAsBoolean(ScheduledOperationsDataSource.Field.IS_AUTO_GROUP .propertyName())) { url = LinkManager.getAutoGroupLink(id); } else { url = LinkManager.getResourceGroupLink(id); } } else { url = LinkManager.getResourceLink(id); } return LinkManager.getHref(url, o.toString()); } }); resourceNext.setShowHover(true); resourceNext.setHoverCustomizer(new HoverCustomizer() { public String hoverHTML(Object value, ListGridRecord listGridRecord, int rowNum, int colNum) { Integer id = listGridRecord.getAttributeAsInt(AncestryUtil.RESOURCE_ID); if (id != null) { return MSG.common_title_resource() + ": " + AncestryUtil.getResourceHoverHTML(listGridRecord, 0); } else { String type = listGridRecord.getAttributeAsString(ScheduledOperationsDataSource.Field.GROUP_TYPE .propertyName()); String name = listGridRecord .getAttributeAsString(ScheduledOperationsDataSource.Field.RESOURCE_OR_GROUP.propertyName()); return "<p>" + MSG.common_title_group() + " (" + type + "):<br/><br/>" + name + "</p>"; } } }); ListGridField ancestryNext = new ListGridField(AncestryUtil.RESOURCE_ANCESTRY, CoreGUI.getMessages() .common_title_ancestry()); ancestryNext.setAlign(Alignment.LEFT); ancestryNext.setCellAlign(Alignment.LEFT); AncestryUtil.setupAncestryListGridFieldCellFormatter(ancestryNext); ancestryNext.setShowHover(true); ancestryNext.setHoverCustomizer(new HoverCustomizer() { public String hoverHTML(Object value, ListGridRecord listGridRecord, int rowNum, int colNum) { Integer id = listGridRecord.getAttributeAsInt(AncestryUtil.RESOURCE_ID); if (id != null) { return AncestryUtil.getAncestryHoverHTML(listGridRecord, 0); } else { return MSG.common_title_group(); } } }); scheduledOperationsGrid.setFields(timeNext, operationNext, resourceNext, ancestryNext); } @Override public void configure(PortletWindow portletWindow, DashboardPortlet storedPortlet) { if (null == this.portletWindow && null != portletWindow) { this.portletWindow = portletWindow; } if ((null == storedPortlet) || (null == storedPortlet.getConfiguration())) { return; } String retrieved = defaultValue; PropertySimple property = storedPortlet.getConfiguration().getSimple(OPERATIONS_RANGE_SCHEDULED); if ((property != null) && (property.getStringValue() != null)) { retrieved = property.getStringValue(); // protect against legacy issue with non-numeric values try { Integer.parseInt(retrieved); } catch (NumberFormatException e) { retrieved = unlimited; } } else { storedPortlet.getConfiguration().put(new PropertySimple(OPERATIONS_RANGE_SCHEDULED, defaultValue)); } getDataSourceScheduled().setOperationsRangeScheduled(Integer.parseInt(retrieved)); //Checkbox settings property property = storedPortlet.getConfiguration().getSimple(OPERATIONS_RANGE_SCHEDULED_ENABLED); if ((property != null) && (property.getBooleanValue() != null)) { getDataSourceScheduled().setOperationsRangeScheduleEnabled(property.getBooleanValue().booleanValue()); } else { storedPortlet.getConfiguration() .put(new PropertySimple(OPERATIONS_RANGE_SCHEDULED_ENABLED, defaultEnabled)); getDataSourceScheduled().setOperationsRangeScheduleEnabled(defaultEnabled); } } @Override public Canvas getHelpCanvas() { return new HTMLFlow(MSG.view_portlet_help_scheduledOperations()); } /** Constructs the dynamic form instance using 1 column and multiple row layouts. */ public DynamicForm getCustomSettingsForm() { //root dynamic form instance final DynamicForm form = new DynamicForm(); final DashboardPortlet storedPortlet = portletWindow.getStoredPortlet(); //vertical layout VStack column = new VStack(); //horizontal layout EnhancedHLayout sheduledOperationsLayout = new EnhancedHLayout(); final CheckboxItem enableScheduledOperationsGrouping = new CheckboxItem(); enableScheduledOperationsGrouping.setName(OPERATIONS_RANGE_SCHEDULED_ENABLED); enableScheduledOperationsGrouping.setTitle(" " + MSG.view_portlet_operations_config_show_next() + " "); enableScheduledOperationsGrouping.addChangeHandler(new ChangeHandler() { public void onChange(ChangeEvent event) { String selectedItem = "" + event.getValue(); //stuff into the master form for retrieval form.setValue(OPERATIONS_RANGE_SCHEDULED_ENABLED, selectedItem); } }); //wrap field item in dynamicform for addition DynamicForm fieldWrapper = new DynamicForm(); fieldWrapper.setFields(enableScheduledOperationsGrouping); sheduledOperationsLayout.addMember(fieldWrapper); //retrieve previous value otherwise initialize to true(live unlimited list) PropertySimple property = storedPortlet.getConfiguration().getSimple(OPERATIONS_RANGE_SCHEDULED_ENABLED); if (property != null) { enableScheduledOperationsGrouping.setValue(property.getBooleanValue()); } else { enableScheduledOperationsGrouping.setValue(true); } //------------- Build second combobox for timeframe for problem resources search. final SelectItem maximumScheduledOperationsComboBox = new SelectItem(OPERATIONS_RANGE_SCHEDULED); maximumScheduledOperationsComboBox.setTitle(""); maximumScheduledOperationsComboBox.setHint("<nobr><b> " + MSG.common_label_scheduled_operations() + ".</b></nobr>"); maximumScheduledOperationsComboBox.setType("selection"); //define acceptable values for display amount String[] acceptableDisplayValues = { "1", "5", "10", "15", unlimitedString }; maximumScheduledOperationsComboBox.setValueMap(acceptableDisplayValues); maximumScheduledOperationsComboBox.setWidth(100); maximumScheduledOperationsComboBox.addChangeHandler(new ChangeHandler() { public void onChange(ChangeEvent event) { String selectedItem = "" + event.getValue(); //stuff into the master form for retrieval form.setValue(OPERATIONS_RANGE_SCHEDULED, selectedItem); } }); String retrieved = defaultValue; if ((property = storedPortlet.getConfiguration().getSimple(OPERATIONS_RANGE_SCHEDULED)) != null) { retrieved = property.getStringValue(); // protect against legacy issue with non-numeric values try { Integer.parseInt(retrieved); } catch (NumberFormatException e) { retrieved = unlimited; } } //prepopulate the combobox with the previously stored selection String selectedValue = retrieved.equals(unlimited) ? unlimitedString : retrieved; //prepopulate the combobox with the previously stored selection maximumScheduledOperationsComboBox.setDefaultValue(selectedValue); DynamicForm fieldWrapper2 = new DynamicForm(); fieldWrapper2.setFields(maximumScheduledOperationsComboBox); sheduledOperationsLayout.addMember(fieldWrapper2); column.addMember(sheduledOperationsLayout); form.addChild(column); //submit handler form.addSubmitValuesHandler(new SubmitValuesHandler() { @Override public void onSubmitValues(SubmitValuesEvent event) { //no need to insert validation here as user not allowed to enter values String value = (String) form.getValue(OPERATIONS_RANGE_SCHEDULED); if (value != null) { // convert display string to stored integer if necessary value = unlimitedString.equals(value) ? unlimited : value; storedPortlet.getConfiguration().put(new PropertySimple(OPERATIONS_RANGE_SCHEDULED, value)); } if (form.getValue(OPERATIONS_RANGE_SCHEDULED_ENABLED) != null) {//if new value supplied storedPortlet.getConfiguration().put( new PropertySimple(OPERATIONS_RANGE_SCHEDULED_ENABLED, form .getValue(OPERATIONS_RANGE_SCHEDULED_ENABLED))); } storedPortlet.setConfiguration(storedPortlet.getConfiguration()); configure(portletWindow, storedPortlet); refresh(); } }); return form; } public static final class Factory implements PortletViewFactory { public static final PortletViewFactory INSTANCE = new Factory(); public final Portlet getInstance(EntityContext context) { return new OperationSchedulePortlet(); } } public ConfigurationDefinition getConfigurationDefinition() { ConfigurationDefinition definition = new ConfigurationDefinition(MSG.view_portlet_configure_definitionTitle(), MSG.view_portlet_configure_definitionDesc()); definition.put(new PropertyDefinitionSimple(OPERATIONS_RANGE_SCHEDULED, MSG .view_portlet_operations_config_scheduled_maximum(), true, PropertySimpleType.STRING)); definition.put(new PropertyDefinitionSimple(OPERATIONS_RANGE_SCHEDULED_ENABLED, MSG .view_portlet_operations_config_scheduled_enable(), true, PropertySimpleType.BOOLEAN)); return definition; } public ScheduledOperationsDataSource getDataSourceScheduled() { return this.dataSourceScheduled; } public ListGrid getScheduledOperationsGrid() { return this.scheduledOperationsGrid; } public void startRefreshCycle() { refreshTimer = AutoRefreshUtil.startRefreshCycleWithPageRefreshInterval(this, this, refreshTimer); } @Override protected void onDestroy() { AutoRefreshUtil.onDestroy(refreshTimer); super.onDestroy(); } public boolean isRefreshing() { return false; } // Custom refresh operation as we cannot directly extend Table because it only // contains one ListGrid while the OperationsPortlet displays two tables. @Override public void refresh() { if (!isRefreshing()) { if (null != this.scheduledOperationsGrid) { this.scheduledOperationsGrid.invalidateCache(); } markForRedraw(); } } }