/**
* <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>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.admin.layout;
import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.ArrayUtils;
import org.olat.admin.SystemAdminMainController;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.FileElement;
import org.olat.core.gui.components.form.flexible.elements.FormLink;
import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
import org.olat.core.gui.components.form.flexible.elements.TextElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.helpers.GUISettings;
import org.olat.core.helpers.Settings;
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.coordinate.CoordinatorManager;
import org.springframework.beans.factory.annotation.Autowired;
/**
* <h3>Description:</h3>
* Admin workflow to configure the application layout
* <p>
* Initial Date: 31.03.2008 <br>
*
* @author Florian Gnaegi, frentix GmbH, http://www.frentix.com
*/
public class LayoutAdminController extends FormBasicController {
private static final Set<String> imageMimeTypes = new HashSet<String>();
static {
imageMimeTypes.add("image/gif");
imageMimeTypes.add("image/jpg");
imageMimeTypes.add("image/jpeg");
imageMimeTypes.add("image/png");
}
private FormLink deleteLogo;
private TextElement logoAltEl, logoUrlEl;
private SingleSelection logoLinkTypeEl;
private TextElement footerLine, footerUrl;
private SingleSelection themeSelection;
private FileElement logoUpload;
private static final String[] logoUrlTypeKeys = new String[]{ LogoURLType.landingpage.name(), LogoURLType.custom.name() };
@Autowired
private GUISettings guiSettings;
@Autowired
private LayoutModule layoutModule;
@Autowired
private CoordinatorManager coordinatorManager;
public LayoutAdminController(UserRequest ureq, WindowControl wControl) {
// use admin package fallback translator to display warn message about not
// saving the data (see comment in formInnerEvent method)
super(ureq, wControl, LAYOUT_BAREBONE);
setTranslator(Util.createPackageTranslator(SystemAdminMainController.class, getLocale(), getTranslator()));
initForm(ureq);
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#initForm(org.olat.core.gui.components.form.flexible.FormItemContainer, org.olat.core.gui.control.Controller, org.olat.core.gui.UserRequest)
*/
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
//themes
FormLayoutContainer themeCont = FormLayoutContainer.createDefaultFormLayout("themeAdminFormContainer", getTranslator());
formLayout.add(themeCont);
themeCont.setFormTitle(translate("layout.title"));
themeCont.setFormDescription(translate("layout.intro"));
String[] keys = getThemes();
String enabledTheme = guiSettings.getGuiThemeIdentifyer();
themeSelection = uifactory.addDropdownSingleselect("themeSelection", "form.theme", themeCont, keys, keys, null);
// select current theme if available but don't break on unavailable theme
for (String theme : keys) {
if (theme.equals(enabledTheme)) {
themeSelection.select(enabledTheme, true);
break;
}
}
themeSelection.addActionListener(FormEvent.ONCHANGE);
//logo
FormLayoutContainer logoCont = FormLayoutContainer.createDefaultFormLayout("logo", getTranslator());
formLayout.add(logoCont);
logoCont.setFormTitle(translate("customizing.logo"));
File logo = layoutModule.getLogo();
boolean hasLogo = logo != null && logo.exists();
deleteLogo = uifactory.addFormLink("deleteimg", "delete", null, logoCont, Link.BUTTON);
deleteLogo.setVisible(hasLogo);
logoUpload = uifactory.addFileElement(getWindowControl(), "customizing.logo", "customizing.logo", logoCont);
logoUpload.setMaxUploadSizeKB(1024, null, null);
logoUpload.setPreview(ureq.getUserSession(), true);
logoUpload.addActionListener(FormEvent.ONCHANGE);
if(hasLogo) {
logoUpload.setPreview(ureq.getUserSession(), true);
logoUpload.setInitialFile(logo);
}
logoUpload.limitToMimeType(imageMimeTypes, null, null);
String[] logoUrlTypeValues = new String[]{
translate("customizing.logo.link.landingpage"),
translate("customizing.logo.link.custom")
};
logoLinkTypeEl = uifactory.addDropdownSingleselect("logo.url.type", "customizing.logo.link.type", logoCont,
logoUrlTypeKeys, logoUrlTypeValues, null);
logoLinkTypeEl.addActionListener(FormEvent.ONCHANGE);
String linkType = layoutModule.getLogoLinkType();
if(StringHelper.containsNonWhitespace(linkType)) {
for(String key:logoUrlTypeKeys) {
if(key.equals(linkType)) {
logoLinkTypeEl.select(key, true);
}
}
}
String customUrl = layoutModule.getLogoLinkUri();
if(StringHelper.containsNonWhitespace(customUrl) && !StringHelper.containsNonWhitespace(linkType)) {
logoLinkTypeEl.select(LogoURLType.custom.name(), true);
}
logoUrlEl = uifactory.addTextElement("linkUrl", "linkUrl.description", 256, customUrl, logoCont);
logoUrlEl.setPlaceholderKey("linkUrl.default", null);
boolean custom = logoLinkTypeEl.isOneSelected() && "custom".equals(logoLinkTypeEl.getSelectedKey());
logoUrlEl.setVisible(custom);
String oldLogoAlt = layoutModule.getLogoAlt();
logoAltEl = uifactory.addTextElement("logoAlt", "logoAlt.description", 256, oldLogoAlt, logoCont);
logoAltEl.setPlaceholderKey("logoAlt.default", null);
//footer
FormLayoutContainer footerCont = FormLayoutContainer.createDefaultFormLayout("customizing", getTranslator());
formLayout.add(footerCont);
footerCont.setFormTitle(translate("customizing.settings"));
footerCont.setFormDescription(translate("customizing.settings.desc"));
String oldFooterUrl = layoutModule.getFooterLinkUri();
footerUrl = uifactory.addTextElement("footerUrl", "footerUrl.description", 256, oldFooterUrl, footerCont);
footerUrl.setPlaceholderKey("linkUrl.default", null);
String oldFooterLine = layoutModule.getFooterLine();
footerLine = uifactory.addTextAreaElement("footerLine", "footerLine.description", -1, 3, 50, true, oldFooterLine, footerCont);
footerLine.setPlaceholderKey("footerLine.default", null);
FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
footerCont.add(buttonsCont);
uifactory.addFormSubmitButton("save", "submit.save", buttonsCont);
}
@Override
protected void doDispose() {
// nothing to clean up
}
@Override
protected boolean validateFormLogic(UserRequest ureq) {
boolean allOk = true;
allOk &= validateUrl(logoUrlEl);
allOk &= validateUrl(footerUrl);
return allOk & super.validateFormLogic(ureq);
}
private boolean validateUrl(TextElement el) {
boolean allOk = true;
String value = el.getValue();
if (StringHelper.containsNonWhitespace(value)) {
try {
URL url = new URL(value);
allOk &= StringHelper.containsNonWhitespace(url.getHost());
} catch (MalformedURLException e) {
el.setErrorKey("linkUrl.invalid", null);
showError("linkUrl.invalid");
allOk &= false;
}
}
return allOk;
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(logoUpload == source) {
if (logoUpload.isUploadSuccess()) {
layoutModule.removeLogo();
File destinationDir = layoutModule.getLogoDirectory();
File newLogo = logoUpload.moveUploadFileTo(destinationDir);
layoutModule.setLogoFilename(newLogo.getName());
logoUpload.setInitialFile(newLogo);
deleteLogo.setVisible(true);
getWindowControl().getWindowBackOffice().getChiefController().wishReload(ureq, true);
}
} else if(logoLinkTypeEl == source) {
boolean custom = logoLinkTypeEl.isOneSelected() && "custom".equals(logoLinkTypeEl.getSelectedKey());
logoUrlEl.setVisible(custom);
} else if(deleteLogo == source) {
layoutModule.removeLogo();
logoUpload.reset();
deleteLogo.setVisible(false);
logoUpload.setInitialFile(null);
getWindowControl().getWindowBackOffice().getChiefController().wishReload(ureq, true);
} else if(themeSelection == source) {
// set new theme in Settings
String newThemeIdentifyer = themeSelection.getSelectedKey();
guiSettings.setGuiThemeIdentifyer(newThemeIdentifyer);
// use new theme in current window
getWindowControl().getWindowBackOffice().getWindow().getGuiTheme().init(newThemeIdentifyer);
getWindowControl().getWindowBackOffice().getWindow().setDirty(true);
logAudit("GUI theme changed", newThemeIdentifyer);
fireEvent(ureq, Event.CHANGED_EVENT);
}
}
@Override
protected void formOK(UserRequest ureq) {
if(logoLinkTypeEl.isOneSelected()) {
layoutModule.setLogoLinkType(logoLinkTypeEl.getSelectedKey());
}
//Logo-Link URI
if(logoUrlEl.isVisible()) {
layoutModule.setLogoLinkUri(logoUrlEl.getValue());
} else {
layoutModule.setLogoLinkUri("");
}
//Logo Alternative Text
layoutModule.setLogoAlt(logoAltEl.getValue());
//FooterLine (large property -> text)
layoutModule.setFooterLinkUri(footerUrl.getValue());
layoutModule.setFooterLine(footerLine.getValue());
//reload window for changes to take effect, fire event to footer/header
getWindowControl().getWindowBackOffice().getWindow().setDirty(true);
coordinatorManager.getCoordinator().getEventBus().fireEventToListenersOf(new LayoutChangedEvent(LayoutChangedEvent.LAYOUTSETTINGSCHANGED),
LayoutModule.layoutCustomizingOResourceable);
showInfo("settings.saved");
}
private String[] getThemes(){
// get all themes from disc
String staticAbsPath = WebappHelper.getContextRealPath("/static/themes");
File themesDir = new File(staticAbsPath);
if(!themesDir.exists()){
logWarn("Themes dir not found: "+staticAbsPath, null);
return new String[0];
}
File[] themes = themesDir.listFiles(new ThemesFileNameFilter());
String[] themesStr = new String[themes.length];
for (int i = 0; i < themes.length; i++) {
File theme = themes[i];
themesStr[i] = theme.getName();
}
// add custom themes from configuration if available
File customThemesDir = Settings.getGuiCustomThemePath();
if (customThemesDir != null) {
File[] customThemes = customThemesDir.listFiles(new ThemesFileNameFilter());
String[] customThemesStr = new String[customThemes.length];
for (int i = 0; i < customThemes.length; i++) {
File theme = customThemes[i];
customThemesStr[i] = theme.getName();
}
themesStr = (String[]) ArrayUtils.addAll(themesStr, customThemesStr);
Arrays.sort(themesStr);
}
return themesStr;
}
/**
* just a simple fileNameFilter that skips OS X .DS_Store , CVS and .sass-cache directories
*
* @author strentini
*/
private static class ThemesFileNameFilter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
// remove files - only accept dirs
if (!new File(dir, name).isDirectory()) {
return false;
}
// remove unwanted meta-dirs
if (FileUtils.isMetaFilename(name)) {
return false;
}
return true;
}
}
}