Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow dynamic skin files to be stored in S3. (updated for uPortal 5) #735

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions pom.xml
Expand Up @@ -146,6 +146,7 @@
<ant.version>1.8.4</ant.version>
<aspectjrt.version>1.7.4</aspectjrt.version>
<aspectjweaver.version>1.7.4</aspectjweaver.version>
<aws.version>1.10.65</aws.version>
<casclient.version>3.4.1</casclient.version>
<ccpp.version>1.0</ccpp.version>
<cernunnos.version>1.2.2</cernunnos.version>
Expand Down Expand Up @@ -179,6 +180,7 @@
<hibernate-jpamodelgen.version>1.2.0.Final</hibernate-jpamodelgen.version>
<hibernate-jpa-api.version>1.0.1.Final</hibernate-jpa-api.version>
<hsqldb.version>2.3.2</hsqldb.version>
<httpclient.version>4.5.2</httpclient.version> <!-- aws skd does not like 4.2.3 -->
<icu4j.version>53.1</icu4j.version>
<jackson2.version>2.3.3</jackson2.version>
<jansi.version>1.11</jansi.version>
Expand Down Expand Up @@ -264,6 +266,28 @@
<artifactId>aopalliance</artifactId>
<version>${aopalliance.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>${aws.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
Expand Down
9 changes: 9 additions & 0 deletions uportal-war/pom.xml
Expand Up @@ -94,6 +94,15 @@
<artifactId>aopalliance</artifactId>
</dependency>

<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>

<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
Expand Down
Expand Up @@ -54,7 +54,12 @@
<portlet-preference>
<name>PREFdynamicSkinName</name>
<readOnly>false</readOnly>
<value></value>
<value>myskin.css</value>
</portlet-preference>
<portlet-preference>
<name>dynamicSkinCssFileName</name>
<readOnly>true</readOnly>
<value>myskin.css</value>
</portlet-preference>
-->
</portlet-definition>
@@ -0,0 +1,59 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you 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 the following location:
*
* 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.apereo.portal.portlets.dynamicskin;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;

import javax.portlet.PortletPreferences;

/**
* {@link DynamicSkinUniqueTokenGenerator} implementation that generates a token by computing a hashcode using the
* values for the Dynamic Skin configurable portlet preferences. To ensure that when using the same
* {@link PortletPreferences} that multiple calls to generate the token will return identical result, the portlet
* preferences are ordered by name prior to hashcode calculation.
*/
public class ConfigurablePreferencesBasedTokenGenerator implements DynamicSkinUniqueTokenGenerator {

/**
* Returns a String hashcode of the values for the portlet preferences that are configurable by the Dynamic Skin
* portlet. The hashcode is generated in a repeatable fashion by calculating it based on sorted portlet preference
* names. Though hashcode does not guarantee uniqueness, from a practical perspective we'll have so few different
* values we can reasonably assume preference value combinations will be unique.
*
* @see DynamicSkinUniqueTokenGenerator#generateToken(DynamicSkinInstanceData)
*/
public String generateToken(final DynamicSkinInstanceData data) {
final PortletPreferences preferences = data.getPortletRequest().getPreferences();
int hash = 0;
// Add the list of preference names to an ordered list so we can get reliable hashcode calculations.
final Map<String, String[]> prefs = preferences.getMap();
final TreeSet<String> orderedNames = new TreeSet<String>(prefs.keySet());
final Iterator<String> iterator = orderedNames.iterator();
while (iterator.hasNext()) {
final String preferenceName = iterator.next();
if (preferenceName.startsWith(DynamicRespondrSkinConstants.CONFIGURABLE_PREFIX)) {
hash = hash * 31 + preferences.getValue(preferenceName, "").trim().hashCode();
}
}
return Integer.toString(hash);
}

}
@@ -0,0 +1,108 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you 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 the following location:
*
* 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.apereo.portal.portlets.dynamicskin;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.portlet.PortletContext;
import javax.portlet.PortletPreferences;
import javax.portlet.PortletRequest;

import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
* Default {@link DynamicSkinInstanceData} implementation that pulls required data from the provided
* {@link PortletRequest}.
*/
public class DefaultDynamicSkinInstanceDataImpl implements DynamicSkinInstanceData {

private String portletAbsolutePathRoot;
private String skinName;
private PortletRequest portletRequest;
private Map<String, String> variableNameToValueMap;

public DefaultDynamicSkinInstanceDataImpl(final PortletRequest request) {
this.pullDataFromPortletPreferences(request.getPreferences());
this.pullDataFromPortletContext(request.getPortletSession().getPortletContext());
this.portletRequest = request;
}

/**
* @see DynamicSkinInstanceData#getPortletAbsolutePathRoot()
*/
@Override
public String getPortletAbsolutePathRoot() {
return this.portletAbsolutePathRoot;
}

/**
* @see DynamicSkinInstanceData#getSkinName()
*/
@Override
public String getSkinName() {
return this.skinName;
}

@Override
public PortletRequest getPortletRequest() {
return this.portletRequest;
}

/**
* @see DynamicSkinInstanceData#getVariablesValuesMap()
*/
@Override
public Map<String, String> getVariableNameToValueMap() {
return this.variableNameToValueMap;
}

@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}

private void pullDataFromPortletPreferences(final PortletPreferences prefs) {
this.skinName = prefs.getValue(
DynamicRespondrSkinConstants.PREF_SKIN_NAME, DynamicRespondrSkinConstants.DEFAULT_SKIN_NAME);

this.variableNameToValueMap = new HashMap<String, String>();
final Enumeration<String> prefNames = prefs.getNames();
while (prefNames.hasMoreElements()) {
final String prefName = prefNames.nextElement();
if (prefName.startsWith(DynamicRespondrSkinConstants.CONFIGURABLE_PREFIX)) {
final String nameWithoutPrefix = prefName.substring(DynamicRespondrSkinConstants.CONFIGURABLE_PREFIX.length());
final String value = prefs.getValue(prefName, "");
this.variableNameToValueMap.put(nameWithoutPrefix, value);
}
}
}

private void pullDataFromPortletContext(final PortletContext ctx) {
this.portletAbsolutePathRoot = ctx.getRealPath("/");
}

}
Expand Up @@ -17,6 +17,7 @@
import java.io.IOException;
import java.util.Enumeration;
import java.util.SortedSet;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletMode;
Expand All @@ -27,6 +28,8 @@
import javax.portlet.ValidatorException;
import javax.portlet.WindowState;
import javax.portlet.WindowStateException;

import org.apereo.portal.portlets.dynamicskin.storage.DynamicSkinService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -68,7 +71,7 @@ public void updateSkinConfiguration(
} else {
log.warn("Skin name {} is not recognized", formValue);
}
} else if (name.startsWith(DynamicSkinService.CONFIGURABLE_PREFIX)) {
} else if (name.startsWith(DynamicRespondrSkinConstants.CONFIGURABLE_PREFIX)) {
String formValue = request.getParameter(name);
prefs.setValue(name, formValue != null ? formValue : "");
}
Expand Down Expand Up @@ -106,11 +109,11 @@ public String showConfigPage(
Enumeration<String> preferenceNames = preferences.getNames();
while (preferenceNames.hasMoreElements()) {
String name = preferenceNames.nextElement();
if (name.startsWith(DynamicSkinService.CONFIGURABLE_PREFIX)) {
if (name.startsWith(DynamicRespondrSkinConstants.CONFIGURABLE_PREFIX)) {
model.addAttribute(name, preferences.getValue(name, ""));
}
}

return "jsp/DynamicRespondrSkin/skinConfig";
}
}
}
@@ -0,0 +1,67 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you 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 the following location:
*
* 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.apereo.portal.portlets.dynamicskin;

/**
* Defines constants used with the Dynamic Respondr Skin Portlet.
*/
public final class DynamicRespondrSkinConstants {

/**
* String that is prepended to preferences that are configurable, and also are passed into the LESS file as
* variables (minus the prefix). This insures someone can add a non-skin preference value in later as long
* as it doesn't have this prefix and the preference will not impact the skin.
*/
static final String CONFIGURABLE_PREFIX = "PREF";

/**
* Name of default skin.
*/
public static final String DEFAULT_SKIN_NAME = "defaultSkin";

/**
* Name of the preference that indicates the name of the skin to use.
*/
public static final String PREF_SKIN_NAME = CONFIGURABLE_PREFIX + "dynamicSkinName";

/**
* Name of preference used to determine whether or not dynamic skins is enabled.
*/
public static final String PREF_DYNAMIC = CONFIGURABLE_PREFIX + "dynamicSkinEnabled";

/**
* Default relative root folder for skin files.
*/
public static final String DEFAULT_RELATIVE_ROOT_FOLDER = "/media/skins/respondr";

/**
* Model name for boolean indicating whether or not the user can access the skin config.
*/
public static final String CAN_ACCESS_SKIN_CONFIG_MODEL_NAME = "canAccessSkinConfig";

/**
* Model attribute name for skin CSS URL.
*/
public static final String SKIN_CSS_URL_MODEL_ATTRIBUTE_NAME = "skinCssUrl";

private DynamicRespondrSkinConstants() {
// prevent instantiation
}

}