/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Cyclos 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 General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.services.settings;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nl.strohalm.cyclos.dao.settings.SettingDAO;
import nl.strohalm.cyclos.entities.customization.fields.MemberCustomField;
import nl.strohalm.cyclos.entities.settings.AccessSettings;
import nl.strohalm.cyclos.entities.settings.AlertSettings;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.entities.settings.LogSettings;
import nl.strohalm.cyclos.entities.settings.MailSettings;
import nl.strohalm.cyclos.entities.settings.MailTranslation;
import nl.strohalm.cyclos.entities.settings.MessageSettings;
import nl.strohalm.cyclos.entities.settings.Setting;
import nl.strohalm.cyclos.entities.settings.Setting.Type;
import nl.strohalm.cyclos.entities.settings.events.AccessSettingsChangeListener;
import nl.strohalm.cyclos.entities.settings.events.AlertSettingsChangeListener;
import nl.strohalm.cyclos.entities.settings.events.LocalSettingsChangeListener;
import nl.strohalm.cyclos.entities.settings.events.LogSettingsChangeListener;
import nl.strohalm.cyclos.entities.settings.events.MailSettingsChangeListener;
import nl.strohalm.cyclos.entities.settings.events.MailTranslationChangeListener;
import nl.strohalm.cyclos.entities.settings.events.MessageSettingsChangeListener;
import nl.strohalm.cyclos.entities.settings.events.SettingsChangeListener;
import nl.strohalm.cyclos.services.InitializingService;
import nl.strohalm.cyclos.services.fetch.FetchServiceLocal;
import nl.strohalm.cyclos.services.settings.exceptions.SelectedSettingTypeNotInFileException;
import nl.strohalm.cyclos.utils.EntityHelper;
import nl.strohalm.cyclos.utils.XmlHelper;
import nl.strohalm.cyclos.utils.conversion.CoercionHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Implementation class for settings service
* @author luis
* @author Jefferson Magno
*/
public class SettingsServiceImpl implements SettingsServiceLocal, InitializingService, InitializingBean {
private static final String ROOT_ELEMENT = "cyclos-settings";
private static final String SETTINGS_ELEMENT = "settings";
private static final String SETTING_ELEMENT = "setting";
private static final List<String> IGNORED_SETTINGS = Arrays.asList("applicationName", "language");
private FetchServiceLocal fetchService;
private SettingDAO settingDao;
// Handlers for each settings type
private Map<Setting.Type, SettingsHandler<?, ?>> handlersMap;
private SettingsHandler<AccessSettings, AccessSettingsChangeListener> accessSettingsHandler;
private SettingsHandler<AlertSettings, AlertSettingsChangeListener> alertSettingsHandler;
private SettingsHandler<LocalSettings, LocalSettingsChangeListener> localSettingsHandler;
private SettingsHandler<LogSettings, LogSettingsChangeListener> logSettingsHandler;
private SettingsHandler<MailSettings, MailSettingsChangeListener> mailSettingsHandler;
private SettingsHandler<MailTranslation, MailTranslationChangeListener> mailTranslationHandler;
private SettingsHandler<MessageSettings, MessageSettingsChangeListener> messageSettingsHandler;
private Set<SettingsChangeListener> listenersPendingToAdd = new HashSet<SettingsChangeListener>();
@Override
public void addListener(final SettingsChangeListener listener) {
// Before completing initialization, other components might want to register listeners, but handlers are not set yet.
// So, we'll just add them after the initialization is complete
if (listenersPendingToAdd != null) {
listenersPendingToAdd.add(listener);
return;
}
if (listener instanceof AccessSettingsChangeListener) {
accessSettingsHandler.addListener((AccessSettingsChangeListener) listener);
}
if (listener instanceof AlertSettingsChangeListener) {
alertSettingsHandler.addListener((AlertSettingsChangeListener) listener);
}
if (listener instanceof LocalSettingsChangeListener) {
localSettingsHandler.addListener((LocalSettingsChangeListener) listener);
}
if (listener instanceof LogSettingsChangeListener) {
logSettingsHandler.addListener((LogSettingsChangeListener) listener);
}
if (listener instanceof MailSettingsChangeListener) {
mailSettingsHandler.addListener((MailSettingsChangeListener) listener);
}
if (listener instanceof MailTranslationChangeListener) {
mailTranslationHandler.addListener((MailTranslationChangeListener) listener);
}
if (listener instanceof MessageSettingsChangeListener) {
messageSettingsHandler.addListener((MessageSettingsChangeListener) listener);
}
}
@Override
public void afterPropertiesSet() throws Exception {
// Create a map with all handlers by type
handlersMap = new HashMap<Setting.Type, SettingsHandler<?, ?>>();
handlersMap.put(Setting.Type.ACCESS, accessSettingsHandler);
handlersMap.put(Setting.Type.ALERT, alertSettingsHandler);
handlersMap.put(Setting.Type.LOCAL, localSettingsHandler);
handlersMap.put(Setting.Type.LOG, logSettingsHandler);
handlersMap.put(Setting.Type.MAIL, mailSettingsHandler);
handlersMap.put(Setting.Type.MAIL_TRANSLATION, mailTranslationHandler);
handlersMap.put(Setting.Type.MESSAGE, messageSettingsHandler);
// Add any listeners which were registered before the initialization was completed
Set<SettingsChangeListener> listeners = listenersPendingToAdd;
listenersPendingToAdd = null;
for (SettingsChangeListener listener : listeners) {
addListener(listener);
}
}
@Override
public String exportToXml(final Collection<Type> types) {
final LocalSettings localSettings = getLocalSettings();
final StringBuilder xml = new StringBuilder();
xml.append("<?xml version=\"1.0\" encoding=\"").append(localSettings.getCharset()).append("\"?>\n");
xml.append('<').append(ROOT_ELEMENT).append(">\n");
for (final Type type : types) {
appendSettingType(xml, type);
}
xml.append("</").append(ROOT_ELEMENT).append(">\n");
return xml.toString();
}
@Override
public AccessSettings getAccessSettings() {
return accessSettingsHandler.get();
}
@Override
public AlertSettings getAlertSettings() {
return alertSettingsHandler.get();
}
@Override
public LocalSettings getLocalSettings() {
return localSettingsHandler.get();
}
@Override
public LogSettings getLogSettings() {
return logSettingsHandler.get();
}
@Override
public MailSettings getMailSettings() {
return mailSettingsHandler.get();
}
@Override
public MailTranslation getMailTranslation() {
return mailTranslationHandler.get();
}
@Override
public MessageSettings getMessageSettings() {
return messageSettingsHandler.get();
}
@Override
public MemberCustomField getSmsCustomField() {
final Long id = getLocalSettings().getSmsCustomFieldId();
if (id == null) {
return null;
}
final MemberCustomField reference = EntityHelper.reference(MemberCustomField.class, id);
return fetchService.fetch(reference);
}
@Override
public List<?> importFromXml(final String xml, final Collection<Setting.Type> types) {
final Document doc = XmlHelper.readDocument(xml);
final Element root = doc.getDocumentElement();
final List<Element> settingTypesNodes = XmlHelper.getChilden(root, SETTINGS_ELEMENT);
final Set<Setting.Type> typesImported = new HashSet<Setting.Type>();
final List<Object> settings = new ArrayList<Object>();
for (final Element settingTypeNode : settingTypesNodes) {
final String settingTypeName = settingTypeNode.getAttribute("type");
final Setting.Type type = CoercionHelper.coerce(Setting.Type.class, settingTypeName);
if (!types.contains(type)) {
continue;
}
final Map<String, String> values = new HashMap<String, String>();
final List<Element> settingsNodes = XmlHelper.getChilden(settingTypeNode, SETTING_ELEMENT);
for (final Element settingNode : settingsNodes) {
final String settingName = settingNode.getAttribute("name");
// Setting in ignore list, don't import it
if (type == Setting.Type.LOCAL && IGNORED_SETTINGS.contains(settingName)) {
continue;
}
String settingValue;
try {
settingValue = StringUtils.trimToNull(settingNode.getChildNodes().item(0).getNodeValue());
} catch (final Exception e) {
settingValue = null;
}
values.put(settingName, settingValue);
}
final SettingsHandler<?, ?> settingsHandler = handlersMap.get(type);
final Object setting = settingsHandler.importFrom(values);
typesImported.add(type);
settings.add(setting);
}
final List<Setting.Type> notImportedTypes = new ArrayList<Setting.Type>();
for (final Setting.Type type : types) {
if (!typesImported.contains(type)) {
notImportedTypes.add(type);
}
}
if (CollectionUtils.isNotEmpty(notImportedTypes)) {
throw new SelectedSettingTypeNotInFileException(notImportedTypes);
}
return settings;
}
@Override
public void initializeService() {
importNew();
}
@Override
public void reloadTranslation() {
// Delete all mail and message translations
settingDao.deleteByType(Setting.Type.MAIL_TRANSLATION, Setting.Type.MESSAGE);
// Delete some local settings which are also 'translatable'
for (final Setting setting : settingDao.listByType(Setting.Type.LOCAL)) {
final String name = setting.getName();
if ("applicationUsername".equals(name) || "chargebackDescription".equals(name)) {
settingDao.delete(setting.getId());
}
}
// Now import them all again, with the current language
importNew();
// Then, refresh the handler's state
refreshTranslationRelatedHandlers();
}
@Override
public void removeListener(final SettingsChangeListener listener) {
if (listener instanceof AccessSettingsChangeListener) {
accessSettingsHandler.removeListener((AccessSettingsChangeListener) listener);
}
if (listener instanceof AlertSettingsChangeListener) {
alertSettingsHandler.removeListener((AlertSettingsChangeListener) listener);
}
if (listener instanceof LocalSettingsChangeListener) {
localSettingsHandler.removeListener((LocalSettingsChangeListener) listener);
}
if (listener instanceof LogSettingsChangeListener) {
logSettingsHandler.removeListener((LogSettingsChangeListener) listener);
}
if (listener instanceof MailSettingsChangeListener) {
mailSettingsHandler.removeListener((MailSettingsChangeListener) listener);
}
if (listener instanceof MailTranslationChangeListener) {
mailTranslationHandler.removeListener((MailTranslationChangeListener) listener);
}
if (listener instanceof MessageSettingsChangeListener) {
messageSettingsHandler.removeListener((MessageSettingsChangeListener) listener);
}
}
@Override
public AccessSettings save(final AccessSettings settings) {
return accessSettingsHandler.update(settings);
}
@Override
public AlertSettings save(final AlertSettings settings) {
return alertSettingsHandler.update(settings);
}
@Override
public LocalSettings save(final LocalSettings settings) {
return localSettingsHandler.update(settings);
}
@Override
public LogSettings save(final LogSettings settings) {
return logSettingsHandler.update(settings);
}
@Override
public MailSettings save(final MailSettings settings) {
return mailSettingsHandler.update(settings);
}
@Override
public MailTranslation save(final MailTranslation settings) {
return mailTranslationHandler.update(settings);
}
@Override
public MessageSettings save(final MessageSettings settings) {
return messageSettingsHandler.update(settings);
}
public void setAccessSettingsHandler(final SettingsHandler<AccessSettings, AccessSettingsChangeListener> accessSettingsHandler) {
this.accessSettingsHandler = accessSettingsHandler;
}
public void setAlertSettingsHandler(final SettingsHandler<AlertSettings, AlertSettingsChangeListener> alertSettingsHandler) {
this.alertSettingsHandler = alertSettingsHandler;
}
public void setFetchServiceLocal(final FetchServiceLocal fetchService) {
this.fetchService = fetchService;
}
public void setLocalSettingsHandler(final SettingsHandler<LocalSettings, LocalSettingsChangeListener> localSettingsHandler) {
this.localSettingsHandler = localSettingsHandler;
}
public void setLogSettingsHandler(final SettingsHandler<LogSettings, LogSettingsChangeListener> logSettingsHandler) {
this.logSettingsHandler = logSettingsHandler;
}
public void setMailSettingsHandler(final SettingsHandler<MailSettings, MailSettingsChangeListener> mailSettingsHandler) {
this.mailSettingsHandler = mailSettingsHandler;
}
public void setMailTranslationHandler(final SettingsHandler<MailTranslation, MailTranslationChangeListener> mailTranslationHandler) {
this.mailTranslationHandler = mailTranslationHandler;
}
public void setMessageSettingsHandler(final SettingsHandler<MessageSettings, MessageSettingsChangeListener> messageSettingsHandler) {
this.messageSettingsHandler = messageSettingsHandler;
}
public void setSettingDao(final SettingDAO settingDao) {
this.settingDao = settingDao;
}
@Override
public void validate(final AccessSettings settings) {
accessSettingsHandler.validate(settings);
}
@Override
public void validate(final AlertSettings settings) {
alertSettingsHandler.validate(settings);
}
@Override
public void validate(final LocalSettings settings) {
localSettingsHandler.validate(settings);
}
@Override
public void validate(final LogSettings settings) {
logSettingsHandler.validate(settings);
}
@Override
public void validate(final MailSettings settings) {
mailSettingsHandler.validate(settings);
}
@Override
public void validate(final MailTranslation settings) {
mailTranslationHandler.validate(settings);
}
@Override
public void validate(final MessageSettings settings) {
messageSettingsHandler.validate(settings);
}
private void appendSetting(final StringBuilder xml, final Setting setting) {
final String indent2Levels = StringUtils.repeat(" ", 2);
final String indent3Levels = StringUtils.repeat(" ", 3);
xml.append(indent2Levels).append(String.format("<setting name=\"%s\" >\n", setting.getName()));
xml.append(indent3Levels).append(StringEscapeUtils.escapeXml(setting.getValue()));
xml.append(indent2Levels).append("</setting>\n");
}
private void appendSettingType(final StringBuilder xml, final Setting.Type type) {
final String indent = StringUtils.repeat(" ", 1);
xml.append(String.format("%s<settings type=\"%s\" >\n", indent, type.getValue()));
final SettingsHandler<?, ?> handler = handlersMap.get(type);
final List<Setting> settings = handler.listSettings();
for (final Setting setting : settings) {
// Setting in ignore list, don't export it
if (type == Setting.Type.LOCAL && IGNORED_SETTINGS.contains(setting.getName())) {
continue;
}
appendSetting(xml, setting);
}
xml.append(indent).append("</settings>\n");
}
private void importNew() {
settingDao.importNew(getLocalSettings().getLocale());
refreshTranslationRelatedHandlers();
}
private void refreshTranslationRelatedHandlers() {
localSettingsHandler.refresh();
messageSettingsHandler.refresh();
mailTranslationHandler.refresh();
}
}