/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* BPS Bildungsportal Sachsen GmbH, http://www.bps-system.de
* <p>
*/
package de.bps.olat.portal.links;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.generic.portal.AbstractPortlet;
import org.olat.core.gui.control.generic.portal.Portlet;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.WebappHelper;
import org.olat.core.util.xml.XStreamHelper;
import com.thoughtworks.xstream.XStream;
/**
*
* @author skoeber
*/
public class LinksPortlet extends AbstractPortlet {
private String cssWrapperClass = "o_portlet_links";
protected static final String LANG_ALL = "*";
protected static final String LANG_DE = "de";
protected static final String LANG_EN = "en";
protected static final String ACCESS_GUEST = "-";
protected static final String ACCESS_REG = "+";
protected static final String ACCESS_ALL = "*";
//configuration file
private static final String CONFIG_FILE = "/WEB-INF/olat_portals_links.xml";
//configuration file xml elements
private static final String ELEM_INSTITUTION = "University";
private static final String ATTR_INSTITUTION_NAME = "name";
private static final String ELEM_LINK = "Link";
private static final String ELEM_LINK_TITLE = "Title";
private static final String ELEM_LINK_URL = "URL";
private static final String ELEM_LINK_DESC = "Description";
private static final String ELEM_LINK_IDENT = "Identifier";
private static final String ELEM_LINK_TARGET = "Target";
private static final String ELEM_LINK_LANG = "Language";
private static HashMap<String, PortletInstitution> content;
private static File fxConfXStreamFile;
private Controller runCtr;
/**
* @see org.olat.gui.control.generic.portal.AbstractPortlet#createInstance(org.olat.gui.control.WindowControl, org.olat.gui.UserRequest, java.util.Map)
*/
public Portlet createInstance(WindowControl wControl, UserRequest ureq, Map<String,String> configuration) {
if(content == null) init();
LinksPortlet p = new LinksPortlet();
p.setName(this.getName());
p.setConfiguration(configuration);
p.setTranslator(Util.createPackageTranslator(LinksPortlet.class, ureq.getLocale()));
// override css class if configured
String cssClass = configuration.get("cssWrapperClass");
if (cssClass != null) p.setCssWrapperClass(cssClass);
return p;
}
private static void init() {
OLog logger = Tracing.createLoggerFor(LinksPortlet.class);
if(logger.isDebug()) logger.debug("START: Loading remote portlets content.");
File configurationFile = new File(WebappHelper.getContextRealPath(CONFIG_FILE));
// fxdiff: have file outside of war/olatapp
File fxConfFolder = new File(WebappHelper.getUserDataRoot() + "/customizing/linksPortlet");
if (!fxConfFolder.exists()) {
fxConfFolder.mkdir();
}
File fxConfFile = new File(fxConfFolder + "/olat_portals_links.xml");
fxConfXStreamFile = new File(fxConfFolder + "/olat_portals_xstream.xml");
if (!fxConfFile.exists() && !fxConfXStreamFile.exists()) {
try {
fxConfFile.createNewFile();
FileUtils.copyFileToFile(configurationFile, fxConfFile, false);
logger.info("portal links portlet: copied initial config from " + CONFIG_FILE);
} catch (IOException e) {
new AssertException("could not copy an initial portal links config to olatdata", e);
}
}
// this map contains the whole data
HashMap<String, PortletInstitution> portletMap = new HashMap<String, PortletInstitution>();
if (!fxConfXStreamFile.exists()){
SAXReader reader = new SAXReader();
try {
// fxdiff: read from fx-config-file in olatdata
Document doc = reader.read(fxConfFile);
Element rootElement = doc.getRootElement();
List<Element> lstInst = rootElement.elements(ELEM_INSTITUTION);
for( Element instElem : lstInst ) {
String inst = instElem.attributeValue(ATTR_INSTITUTION_NAME);
List<Element> lstTmpLinks = instElem.elements(ELEM_LINK);
List<PortletLink> lstLinks = new ArrayList<PortletLink>(lstTmpLinks.size());
for( Element linkElem: lstTmpLinks ) {
String title = linkElem.elementText(ELEM_LINK_TITLE);
String url = linkElem.elementText(ELEM_LINK_URL);
String target = linkElem.elementText(ELEM_LINK_TARGET);
String lang = linkElem.elementText(ELEM_LINK_LANG);
String desc = linkElem.elementText(ELEM_LINK_DESC);
String identifier = linkElem.elementText(ELEM_LINK_IDENT);
lstLinks.add(new PortletLink(title, url, target, lang, desc, identifier));
}
portletMap.put(inst, new PortletInstitution(inst, lstLinks));
}
} catch (Exception e) {
logger.error("Error reading configuration file", e);
} finally {
content = portletMap;
}
// lazy migrate to new format
saveLinkList(content);
FileUtils.copyFileToFile(fxConfFile, new File(fxConfFile + ".bak"), true);
} else {
XStream xstream = XStreamHelper.createXStreamInstance();
xstream.alias("LinksPortlet", Map.class);
xstream.alias(ELEM_LINK, PortletLink.class);
xstream.alias(ELEM_INSTITUTION, PortletInstitution.class);
xstream.aliasAttribute(PortletInstitution.class, ATTR_INSTITUTION_NAME, ATTR_INSTITUTION_NAME);
content = (HashMap<String, PortletInstitution>) XStreamHelper.readObject(xstream, fxConfXStreamFile);
}
}
public static boolean saveLinkList(HashMap<String, PortletInstitution> portletMap){
XStream xstream = XStreamHelper.createXStreamInstance();
xstream.alias("LinksPortlet", Map.class);
xstream.alias(ELEM_LINK, PortletLink.class);
xstream.alias(ELEM_INSTITUTION, PortletInstitution.class);
xstream.aliasAttribute(PortletInstitution.class, ATTR_INSTITUTION_NAME, ATTR_INSTITUTION_NAME);
String output = xstream.toXML(portletMap);
XStreamHelper.writeObject(xstream, fxConfXStreamFile, portletMap);
return (output.length() != 0);
}
public static PortletLink getLinkByIdentifier(String identifier){
for (Iterator<String> iterator = content.keySet().iterator(); iterator.hasNext();) {
String inst = iterator.next();
PortletInstitution portletsForInst = content.get(inst);
List<PortletLink> instLinks = portletsForInst.getLinks();
for (PortletLink portletLink : instLinks) {
if (portletLink.getIdentifier().equals(identifier)) return portletLink;
}
}
return null;
}
public static void removeLink(PortletLink link){
if (link == null) return;
for (Iterator<String> iterator = content.keySet().iterator(); iterator.hasNext();) {
String inst = iterator.next();
PortletInstitution portletsForInst = content.get(inst);
List<PortletLink> instLinks = portletsForInst.getLinks();
for (PortletLink portletLink : instLinks) {
if (portletLink.getIdentifier().equals(link.getIdentifier())) {
instLinks.remove(link);
break;
}
}
}
saveLinkList(content);
}
public static void updateLink(PortletLink link){
if (link == null) return;
for (Iterator<String> iterator = content.keySet().iterator(); iterator.hasNext();) {
String inst = iterator.next();
PortletInstitution portletsForInst = content.get(inst);
List<PortletLink> instLinks = portletsForInst.getLinks();
boolean existingLink = false;
for (PortletLink portletLink : instLinks) {
if (portletLink.getIdentifier().equals(link.getIdentifier())) {
portletLink = link;
existingLink = true;
break;
}
}
if (!existingLink && portletsForInst == link.getInstitution()) {
portletsForInst.addLink(link);
break;
}
}
saveLinkList(content);
}
/**
* @see org.olat.gui.control.generic.portal.Portlet#getTitle()
*/
public String getTitle() {
return getTranslator().translate("portlet.title");
}
public static void reInit(UserRequest ureq){
content = null;
init();
}
/**
* @see org.olat.gui.control.generic.portal.Portlet#getDescription()
*/
public String getDescription() {
return getTranslator().translate("portlet.description");
}
/**
* @see org.olat.gui.control.generic.portal.Portlet#getInitialRunComponent(org.olat.gui.control.WindowControl, org.olat.gui.UserRequest)
*/
public Component getInitialRunComponent(WindowControl wControl, UserRequest ureq) {
if(this.runCtr != null) runCtr.dispose();
this.runCtr = new LinksPortletRunController(ureq, wControl);
return runCtr.getInitialComponent();
}
/**
* @see org.olat.gui.control.Disposable#dispose(boolean)
*/
public void dispose() {
disposeRunComponent();
}
/**
* @see org.olat.gui.control.generic.portal.Portlet#getCssClass()
*/
public String getCssClass() {
return cssWrapperClass;
}
/**
* Helper used to overwrite the default css class with the configured class
* @param cssWrapperClass
*/
void setCssWrapperClass(String cssWrapperClass) {
this.cssWrapperClass = cssWrapperClass;
}
/**
* @see org.olat.gui.control.generic.portal.Portlet#disposeRunComponent(boolean)
*/
public void disposeRunComponent() {
if (runCtr != null) {
runCtr.dispose();
runCtr = null;
}
}
/**
* @return Returns the content map.
*/
public static Map<String, PortletInstitution> getContent() {
return content;
}
}
/**
* @author skoeber
*
*/
class PortletInstitution {
private String name;
private List<PortletLink> links;
public PortletInstitution(String name) {
this.name = name;
this.links = new ArrayList<PortletLink>();
}
public PortletInstitution(String name, List<PortletLink> links) {
this.name = name;
this.links = links;
}
public void addLink(PortletLink link) {
links.add(link);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<PortletLink> getLinks() {
return links;
}
public void setLinks(List<PortletLink> links) {
this.links = links;
}
}
/**
* @author skoeber
*
*/
class PortletLink {
private String title, url, target, language, description;
private String identifier;
private transient PortletInstitution institution;
public PortletLink(String title, String url, String target, String language, String description, String identifier) {
setTitle(title);
setUrl(url);
setTarget(target);
setLanguage(language);
setDescription(description);
setIdentifier(identifier);
}
public PortletInstitution getInstitution() {
return institution;
}
public void setInstitution(PortletInstitution institution) {
this.institution = institution;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void setIdentifier(String identifier){
if (identifier == null) {
this.identifier = UUID.randomUUID().toString().replace("-", "");
} else {
this.identifier = identifier;
}
}
public String getIdentifier(){
if (!StringHelper.containsNonWhitespace(identifier)){
setIdentifier(null);
}
return identifier;
}
}