/*==========================================================================*\ | $Id: PluginManagerPage.java,v 1.6 2012/01/05 19:51:44 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2012 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT 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 Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.grader; import com.webobjects.appserver.*; import com.webobjects.foundation.*; import java.util.*; import er.extensions.appserver.ERXDisplayGroup; import er.extensions.foundation.ERXValueUtilities; import net.sf.webcat.*; import org.apache.log4j.Logger; import org.webcat.core.*; // ------------------------------------------------------------------------- /** * The main "control panel" page for plugins in the Plug-ins * tab. * * @author Stephen Edwards * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.6 $, $Date: 2012/01/05 19:51:44 $ */ public class PluginManagerPage extends WCComponent { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Creates a new page object. * * @param context The context to use */ public PluginManagerPage( WOContext context ) { super( context ); } //~ KVC Attributes (must be public) ....................................... public int index; public GradingPlugin plugin; public ERXDisplayGroup<GradingPlugin> publishedPluginGroup; public ERXDisplayGroup<GradingPlugin> unpublishedPluginGroup; public ERXDisplayGroup<GradingPlugin> personalPluginGroup; public NSArray<FeatureDescriptor> newPlugins; public NSData uploadedData; public String uploadedName; public FeatureDescriptor feature; public String providerURL; public static final String TERSE_DESCRIPTIONS_KEY = "tersePluginDescriptions"; //~ Methods ............................................................... // ---------------------------------------------------------- public void appendToResponse( WOResponse response, WOContext context ) { terse = null; publishedPluginGroup.fetch(); if ( user().hasAdminPrivileges() ) { unpublishedPluginGroup.fetch(); } else { personalPluginGroup.queryBindings().setObjectForKey( user(), "user" ); personalPluginGroup.fetch(); } if ( newPlugins == null ) { for (FeatureProvider fp : FeatureProvider.providers()) { fp.refresh(); } newPlugins = newPlugins(); } super.appendToResponse( response, context ); } // ---------------------------------------------------------- /** * Get the current servlet adaptor, if one is available. * @return the servlet adaptor, or null when none is available */ public net.sf.webcat.WCServletAdaptor adaptor() { return net.sf.webcat.WCServletAdaptor.getInstance(); } // ---------------------------------------------------------- /** * Determine if there is a download site. * @return true if a download url is defined */ public boolean canDownload() { return plugin.descriptor().getProperty( "provider.url" ) != null; } // ---------------------------------------------------------- /** * Download the latest version of the current subsystem for updating * on restart. * @return null to refresh the current page */ public WOComponent download() { String msg = plugin.installUpdate(); possibleErrorMessage( msg ); if ( msg == null ) { if (applyLocalChanges()) { confirmationMessage( "The plug-in '" + plugin.name() + "' has been downloaded from its provider and " + "re-installed." ); } } else { cancelLocalChanges(); } return null; } // ---------------------------------------------------------- /** * Download a new subsystem for installation on restart. * @return null to refresh the current page */ public WOComponent downloadNew() { String msg = GradingPlugin.installOrUpdate( user(), feature, false ); possibleErrorMessage( msg ); if ( msg == null ) { if (applyLocalChanges()) { confirmationMessage( "New plug-in '" + feature.name() + "' has been downloaded and installed." ); } } else { cancelLocalChanges(); } newPlugins = null; return null; } // ---------------------------------------------------------- /** * Scan the specified provider URL. * @return null to refresh the current page */ public WOComponent scanNow() { if ( providerURL == null || providerURL.equals( "" ) ) { error( "Please specify a provider URL first." ); } else { try { // TODO: fix this to correctly re-load ... FeatureProvider.getProvider(providerURL); } catch (java.io.IOException e) { error("Cannot read feature provider information from " + " specified URL: '" + providerURL + "'."); } } // Erase cache of new subsystems so it will be recalculated now newPlugins = null; // refresh page return null; } // ---------------------------------------------------------- /** * Determine whether this user has permissions to edit the current plug-in. * @return true if the current plug-in can be edited by the current user */ public boolean canEditPlugin() { User user = user(); return user.hasAdminPrivileges() || user == plugin.author(); } // ---------------------------------------------------------- /** * Edit the selected plug-in's configuration settings. * @return the subsystem's edit page */ public WOComponent edit() { EditPluginGlobalsPage newPage = pageWithName(EditPluginGlobalsPage.class); newPage.nextPage = this; newPage.plugin = plugin; return newPage; } // ---------------------------------------------------------- /** * Browse or edit the selected plug-in's files. Administrators can * edit all plug-ins. Otherwise, users can only edit plug-ins they * have authored, and can only browse others. * @return the subsystem's edit page */ public WOComponent editFiles() { EditScriptFilesPage newPage = pageWithName(EditScriptFilesPage.class); newPage.nextPage = this; newPage.gradingPlugin = plugin; newPage.hideNextAndBack( true ); newPage.isEditable = user().hasAdminPrivileges() || user().equals( plugin.author() ); return newPage; } // ---------------------------------------------------------- /** * Toggle the * {@link net.sf.webcat.WCServletAdaptor#willUpdateAutomatically()} * attribute. * @return null to refresh the current page */ public WOComponent toggleAutoUpdates() { boolean option = Application.configurationProperties() .booleanForKey( GradingPlugin.NO_AUTO_UPDATE_KEY ); Application.configurationProperties().put( GradingPlugin.NO_AUTO_UPDATE_KEY, option ? "false" : "true" ); Application.configurationProperties().attemptToSave(); return null; } // ---------------------------------------------------------- /** * Toggle the * {@link net.sf.webcat.WCServletAdaptor#willUpdateAutomatically()} * attribute. * @return null to refresh the current page */ public WOComponent toggleAutoInstalls() { boolean option = Application.configurationProperties() .booleanForKey( GradingPlugin.NO_AUTO_INSTALL_KEY ); Application.configurationProperties().put( GradingPlugin.NO_AUTO_INSTALL_KEY, option ? "false" : "true" ); Application.configurationProperties().attemptToSave(); return null; } // ---------------------------------------------------------- /** * Upload a new plug-in. * @return null to refresh the page */ public WOComponent upload() { if ( uploadedName == null || uploadedData == null ) { error( "Please select a file to upload." ); return null; } GradingPlugin.createNewGradingPlugin( localContext(), user(), uploadedName, uploadedData, false, true, messages() ); applyLocalChanges(); uploadedName = null; uploadedData = null; newPlugins = null; return null; } // ---------------------------------------------------------- /** * Publish/unpublish a plug-in by togglinh its isPublished attribute. * @return null to refresh the page */ public WOComponent togglePublished() { plugin.setIsPublished( !plugin.isPublished() ); applyLocalChanges(); return null; } // ---------------------------------------------------------- /** * Force a fresh reload of the script's config.plist file to pick up * any changes (i.e., new attributes, new default values, etc.). * @return null, to force this page to reload in the browser when the * action completes */ public WOComponent reloadScriptDefinition() { String errMsg = plugin.initializeConfigAttributes(); if ( errMsg != null ) { cancelLocalChanges(); error( errMsg ); } else { if (applyLocalChanges()) { confirmationMessage( "Configuration definition for plug-in '" + plugin.name() + "' has been reloaded." ); } } return null; } // ---------------------------------------------------------- /** * Toggle whether or not the user wants verbose descriptions of subsystems * to be shown or hidden. The setting is stored in the user's preferences * under the key specified by the VERBOSE_DESCRIPTIONS_KEY, and will be * permanently saved the next time the session's local changes are saved. */ public void toggleVerboseDescriptions() { boolean verboseDescriptions = ERXValueUtilities.booleanValue( user().preferences().objectForKey( TERSE_DESCRIPTIONS_KEY ) ); verboseDescriptions = !verboseDescriptions; user().preferences().setObjectForKey( Boolean.valueOf( verboseDescriptions ), TERSE_DESCRIPTIONS_KEY ); user().savePreferences(); } // ---------------------------------------------------------- /** * Look up the user's preferences and determine whether or not to show * verbose subsystem descriptions in this component. * @return true if verbose descriptions should be hidden, or false if * they should be shown */ public Boolean terse() { if ( terse == null ) { terse = ERXValueUtilities.booleanValue( user().preferences().objectForKey( TERSE_DESCRIPTIONS_KEY ) ) ? Boolean.TRUE : Boolean.FALSE; } return terse; } // ---------------------------------------------------------- /** * Retrieve the history URL for the current installed plug-in. * @return The history URL, or null if none is defined */ public String pluginHistoryUrl() { String result = plugin.descriptor().getProperty( "history.url" ); log.debug( "pluginHistoryUrl() = " + result ); return result; } // ---------------------------------------------------------- /** * Retrieve the information URL for the current installed plug-in. * @return The information URL, or null if none is defined */ public String pluginInfoUrl() { String result = plugin.descriptor().getProperty( "info.url" ); log.debug( "pluginInfoUrl() = " + result ); return result; } // ---------------------------------------------------------- /** * Retrieve the history URL for the current uninstalled plug-in. * @return The history URL, or null if none is defined */ public String featureHistoryUrl() { return feature.getProperty( "history.url" ); } // ---------------------------------------------------------- /** * Retrieve the information URL for the current uninstalled plug-in. * @return The information URL, or null if none is defined */ public String featureInfoUrl() { return feature.getProperty( "info.url" ); } // ---------------------------------------------------------- /** * Retrieve the information URL for the current uninstalled plug-in. * @return The information URL, or null if none is defined */ public String featureDisplayableName() { return feature.getProperty( "displayableName" ); } // ---------------------------------------------------------- /** * Calculate the current set of plug-ins that are available from * all registered providers, but that are not yet installed. * @return an array of feature descriptors for available uninstalled * grader plug-ins */ public NSArray<FeatureDescriptor> newPlugins() { Collection<FeatureDescriptor> availablePlugins = new HashSet<FeatureDescriptor>(); for (FeatureProvider provider : FeatureProvider.providers()) { if ( provider != null ) { for (FeatureDescriptor aPlugin : provider.plugins()) { // Screen out batch plug-ins if (aPlugin.getProperty("batchEntity") == null) { availablePlugins.add(aPlugin); } } } } NSArray<GradingPlugin> exclude = publishedPluginGroup.displayedObjects(); if (exclude != null) { for (GradingPlugin s : exclude) { FeatureDescriptor fd = s.descriptor().providerVersion(); if (fd != null) { availablePlugins.remove(fd); } } } exclude = unpublishedPluginGroup.displayedObjects(); if (exclude != null) { for (GradingPlugin s : exclude) { FeatureDescriptor fd = s.descriptor().providerVersion(); if (fd != null) { availablePlugins.remove(fd); } } } exclude = personalPluginGroup.displayedObjects(); if (exclude != null) { for (GradingPlugin s : exclude) { FeatureDescriptor fd = s.descriptor().providerVersion(); if (fd != null) { availablePlugins.remove(fd); } } } FeatureDescriptor[] descriptors = new FeatureDescriptor[availablePlugins.size()]; return new NSArray<FeatureDescriptor>( availablePlugins.toArray(descriptors)); } //~ Instance/static variables ............................................. private Boolean terse; static Logger log = Logger.getLogger( PluginManagerPage.class ); }