/* * Copyright (c) 2009-2012 Lockheed Martin Corporation * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.eurekastreams.server.service.restlets; import java.net.URI; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eurekastreams.server.domain.GadgetDefinition; import org.eurekastreams.server.domain.GeneralGadgetDefinition; import org.eurekastreams.server.domain.gadgetspec.EnumValuePairDTO; import org.eurekastreams.server.domain.gadgetspec.GadgetMetaDataDTO; import org.eurekastreams.server.domain.gadgetspec.UserPrefDTO; import org.eurekastreams.server.domain.gadgetspec.UserPrefDTO.DataType; import org.eurekastreams.server.persistence.GadgetMapper; import org.eurekastreams.server.service.opensocial.gadgets.spec.GadgetMetaDataFetcher; import org.restlet.data.MediaType; import org.restlet.data.Request; import org.restlet.data.Status; import org.restlet.resource.Representation; import org.restlet.resource.ResourceException; import org.restlet.resource.StringRepresentation; import org.restlet.resource.Variant; /** * This class provides the Restlet implementation for creating user preferences form ui for an OpenSocial gadget. * */ public class UserPrefsFormUIResource extends SmpResource { /** * Local logger instance. */ private static Log logger = LogFactory.getLog(UserPrefsFormUIResource.class); /** * Key for retrieving the Gadget Id from the rest url. */ private static final String MODULE_ID_KEY = "moduleid"; /** * Key for retrieving the Gadget Definition Url from the rest url. */ private static final String GADGET_DEF_URL_KEY = "url"; /** * Key for retrieving the saved user preferences. */ private static final String SAVED_USER_PREFS = "saveduserprefs"; /** * Error message when user preferences cannot be retrieved. */ private static final String USER_PREFS_ERROR_MESSAGE = "Error occurred retrieving preferences for this gadget."; /** * Default format for encoding/decoding url's retrieved in parameters. */ private static final String DEFAULT_URL_ENCODING = "UTF-8"; /** * Local storage for the module Id of the calling gadget. */ private Long moduleId; /** * List of user preferences retrieved from the url passed in. */ private Map<String, String> savedUserPrefs; /** * Local instance of the gadget mapper. */ private GadgetMapper gadgetMapper; /** * Local instance of the gadget metadata fetcher. */ private GadgetMetaDataFetcher gadgetMetaFetcher; /** * URI for the gadget definition to retrieve user preferences for. */ private String gadgetDefUrl; /** * Initialize the parameters for the request. attributes handled: moduleId - refers to the gadget instance id (not * to be confused with the application id. url - gadget definition url target to generate preferences script for. * saveduserprefs - querystring formatted (and url encoded) collection of key value pairs representing the currently * saved user settings. This data is important for generating prepopulated inputs. * * @param request * - current request information. */ @Override protected void initParams(final Request request) { // Retrieve the module id and gadget def url for the // metadata request. if (request.getAttributes().containsKey(MODULE_ID_KEY) && request.getAttributes().containsKey(GADGET_DEF_URL_KEY)) { try { URI tempGadgetDefURI = new URI((String) request.getAttributes().get(GADGET_DEF_URL_KEY)); gadgetDefUrl = URLDecoder.decode(tempGadgetDefURI.toString(), DEFAULT_URL_ENCODING); String tempAppId = (String) request.getAttributes().get(MODULE_ID_KEY); logger.debug("Retreived moduleId from querystring: " + tempAppId); moduleId = Long.parseLong(tempAppId.trim()); } catch (Exception ex) { logger.error("Error occurred retrieving the module id from " + "the request, defaulting to 0 " + ex); moduleId = 0L; } } // Saved User Preferences are optional and thus pulled off the request seperately. savedUserPrefs = new HashMap<String, String>(); if (request.getAttributes().containsKey(SAVED_USER_PREFS)) { String urlUserPreferences = (String) request.getAttributes().get(SAVED_USER_PREFS); List<String> tempUserPrefsList = new ArrayList<String>(Arrays.asList(urlUserPreferences.split("&"))); for (String userPref : tempUserPrefsList) { if ((userPref.trim().length() > 3) && userPref.trim().contains("=")) { try { String[] userPrefBreakdown = userPref.split("="); savedUserPrefs.put(URLDecoder.decode(userPrefBreakdown[0], DEFAULT_URL_ENCODING), URLDecoder.decode(userPrefBreakdown[1], DEFAULT_URL_ENCODING)); } catch (Exception ex) { logger.error("Error occurred retrieving the user " + "preferences from the url string for: " + userPref); } } } } } /** * This method injects the gadget mapper. * * @param inGadgetMapper * mapper. */ public void setGadgetMapper(final GadgetMapper inGadgetMapper) { gadgetMapper = inGadgetMapper; } /** * This method injects the gadget metadata fetcher. * * @param inGadgetMetaFetcher * - instance of GadgetMetaFetcher to set locally. */ public void setGadgetMetaDataFetcher(final GadgetMetaDataFetcher inGadgetMetaFetcher) { gadgetMetaFetcher = inGadgetMetaFetcher; } /** * Provide the settings output for the gadget definitions supplied. * * @param variant * - context. * @return Representation of the response. * * @throws ResourceException * when an error is encountered with the representation code. */ @Override public Representation represent(final Variant variant) throws ResourceException { // retrieve gadget metadata with preferences from shindig. // assembly script based on types and number of preferences. if (moduleId != null && moduleId > 0 && gadgetDefUrl != null) { StringBuilder sb = new StringBuilder(); try { Map<String, GeneralGadgetDefinition> gadgetDefs = new HashMap<String, GeneralGadgetDefinition>(); GadgetDefinition targetGadgetDef = gadgetMapper.findById(moduleId).getGadgetDefinition(); gadgetDefs.put(gadgetDefUrl, targetGadgetDef); List<GadgetMetaDataDTO> gadgetMetaData = gadgetMetaFetcher.getGadgetsMetaData(gadgetDefs); for (GadgetMetaDataDTO currentGMeta : gadgetMetaData) { logger.debug("Retrieved Metadata for Gadget" + targetGadgetDef.getId() + " Author: " + currentGMeta.getAuthor() + " UserPrefsSize: " + currentGMeta.getUserPrefs().size()); buildUserPreferencesUI(currentGMeta, sb); } } catch (Exception ex) { logger.error("Error occurred retrieving User Preferences script", ex); sb.append("var htmlString_" + moduleId + "='';"); sb.append("htmlString_" + moduleId + " += '<div class=\"gadget-preferences\">" + USER_PREFS_ERROR_MESSAGE + "</div>';"); sb.append("gadget_callback_" + moduleId + "(htmlString_" + moduleId + ");"); } logger.debug("Returning the edit settings script: " + sb.toString()); Representation rep = new StringRepresentation(sb.toString(), MediaType.APPLICATION_JAVASCRIPT); rep.setExpirationDate(new Date()); return rep; } else { logger.error("Bad request: Invalid application id passed to endpoint"); throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Invalid application id passed to endpoint"); } } /** * This method constructs the user preferences for the gadget MetaData supplied. * * @param gadgetMetaData * GadgetMetaDataDTO object that contains the info for retrieving the User Preferences ui. * @param sb * String builder to which the string representation of the user preferences ui will be appended. */ private void buildUserPreferencesUI(final GadgetMetaDataDTO gadgetMetaData, final StringBuilder sb) { // initial wrappings sb.append("var htmlString_").append(moduleId).append("='"); int userPrefIndex = 0; String userPrefPrefix = "m_" + moduleId; sb.append("<div style=\"display:none\" class=\"gadget-preferences\">"); for (UserPrefDTO userPref : gadgetMetaData.getUserPrefs()) { // If the value has already been saved, use the saved value. if (savedUserPrefs.size() > 0 && savedUserPrefs.containsKey("up_" + userPref.getName())) { userPref.setDefaultValue(savedUserPrefs.get("up_" + userPref.getName())); } final DataType dataType = userPref.getDataType(); final String inputId = userPrefPrefix + "_" + userPrefIndex; final String inputName = userPrefPrefix + "_up_" + userPref.getName(); if (dataType == DataType.HIDDEN) { sb.append("<input type=\"hidden\" id=\"").append(inputId).append("\" name=\"").append(inputName) .append("\" value=\"").append(StringEscapeUtils.escapeHtml(userPref.getDefaultValue())) .append("\" />"); } else { sb.append("<div class=\"input-area\"><div class=\"label-area\"><label class=\"label\">"); sb.append(userPref.getDisplayName()); sb.append("</label></div><div class=\"input-box\">"); if (dataType == DataType.ENUM) { sb.append("<select class=\"drop-down\" id=\"").append(inputId).append("\" name=\"") .append(inputName).append("\">"); for (EnumValuePairDTO currentEnumValue : userPref.getOrderedEnumValues()) { sb.append("<option value=\"").append(currentEnumValue.getValue()).append("\" "); if (userPref.getDefaultValue().equals(currentEnumValue.getValue())) { sb.append("selected "); } sb.append(">").append(currentEnumValue.getDisplayValue()).append("</option>"); } sb.append("</select>"); } else if (dataType.name() == DataType.LIST.name()) // == LIST { // TODO: handle list here with a bunch of checkboxes. logger.info("Handle user preferences type List"); } else if (dataType.name() == DataType.BOOL.name()) // == BOOL { sb.append("<input type=\"checkbox\" class=\"checkbox\" id=\"").append(inputId).append("\" name=\"") .append(inputName).append("\" "); try { if (Boolean.parseBoolean(userPref.getDefaultValue()) || Integer.parseInt(userPref.getDefaultValue()) > 0) { sb.append("checked "); } } catch (NumberFormatException nex) { logger.info("Data passed in for boolean user preference" + "was not correct string or number format, assuming false."); } sb.append("value=\"true\" onClick=\"document.getElementById(\\'").append(inputId) .append("\\').value = (this.checked ? \\'1\\' : \\'0\\')\" />"); } else { sb.append("<input type=\"text\" class=\"text-box\" id=\"").append(inputId).append("\" name=\"") .append(inputName).append("\" value=\"") .append(StringEscapeUtils.escapeHtml(userPref.getDefaultValue().replace("'", "\\'"))) .append("\" />"); } if (userPref.getRequired()) { sb.append("<div>Required</div>"); } sb.append("</div></div>"); // closes input-area and input-box } userPrefIndex++; } // sb.append("</div>"); sb.append("<input type=\"hidden\" id=\"").append(userPrefPrefix).append("_numfields\" value=\"") .append(gadgetMetaData.getUserPrefs().size()).append("\"/>"); sb.append("</div>"); // final wrappings sb.append("';gadget_callback_").append(moduleId).append("(unescape(htmlString_").append(moduleId).append("));"); } }