/*
documentr - Edit, maintain, and present software documentation on the web.
Copyright (C) 2012-2013 Maik Schreiber
This program 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 3 of the License, or
(at your option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.blizzy.documentr.system;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.PersonIdent;
import org.gitective.core.BlobUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.Lifecycle;
import org.springframework.stereotype.Component;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.google.common.eventbus.EventBus;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import de.blizzy.documentr.access.User;
import de.blizzy.documentr.access.UserStore;
import de.blizzy.documentr.repository.IGlobalRepositoryManager;
import de.blizzy.documentr.repository.ILockedRepository;
import de.blizzy.documentr.repository.RepositoryNotFoundException;
import de.blizzy.documentr.repository.RepositoryUtil;
import de.blizzy.documentr.util.Util;
/** Manages storage of system settings. */
@Component
@Slf4j
public class SystemSettingsStore implements Lifecycle {
public static final String DOCUMENTR_HOST = "documentrHost"; //$NON-NLS-1$
public static final String SITE_NOTICE = "siteNotice"; //$NON-NLS-1$
public static final String MAIL_HOST_NAME = "mail.host.name"; //$NON-NLS-1$
public static final String MAIL_HOST_PORT = "mail.host.port"; //$NON-NLS-1$
public static final String MAIL_SENDER_EMAIL = "mail.sender.email"; //$NON-NLS-1$
public static final String MAIL_SENDER_NAME = "mail.sender.name"; //$NON-NLS-1$
public static final String MAIL_SUBJECT_PREFIX = "mail.subject.prefix"; //$NON-NLS-1$
public static final String MAIL_DEFAULT_LANGUAGE = "mail.defaultLanguage"; //$NON-NLS-1$
public static final String BCRYPT_ROUNDS = "bcrypt.rounds"; //$NON-NLS-1$
public static final String PAGE_FOOTER_HTML = "page.footerHtml"; //$NON-NLS-1$
public static final String UPDATE_CHECK_INTERVAL = "updateCheck.interval"; //$NON-NLS-1$
public static final String UPDATE_CHECK_INTERVAL_NEVER = "never"; //$NON-NLS-1$
public static final String UPDATE_CHECK_INTERVAL_DAILY = "daily"; //$NON-NLS-1$
public static final String UPDATE_CHECK_INTERVAL_WEEKLY = "weekly"; //$NON-NLS-1$
private static final String REPOSITORY_NAME = "_system"; //$NON-NLS-1$
private static final String SETTINGS_FILE_NAME = "settings"; //$NON-NLS-1$
private static final String MACRO_KEY_PREFIX = "macro."; //$NON-NLS-1$
@Autowired
private IGlobalRepositoryManager globalRepositoryManager;
@Autowired
private UserStore userStore;
@Autowired
private EventBus eventBus;
private Map<String, String> settings;
private boolean running;
@PostConstruct
public void init() throws IOException {
settings = loadSettings();
setDefaultSettings();
}
@Override
public void start() {
running = true;
try {
User adminUser = userStore.getUser("admin"); //$NON-NLS-1$
ILockedRepository repo = globalRepositoryManager.createProjectCentralRepository(REPOSITORY_NAME, false, adminUser);
Util.closeQuietly(repo);
// repository has just been created, store settings for the first time
Map<String, String> settingsToStore;
synchronized (settings) {
settingsToStore = Maps.newHashMap(settings);
}
storeSettings(settingsToStore, adminUser);
} catch (IllegalStateException e) {
// okay
} catch (IOException e) {
throw new RuntimeException(e);
} catch (GitAPIException e) {
throw new RuntimeException(e);
}
}
@Override
public void stop() {
running = false;
}
@Override
public boolean isRunning() {
return running;
}
private void setDefaultSettings() {
Map<String, String> defaultSettings = Maps.newHashMap();
defaultSettings.put(DOCUMENTR_HOST, "http://localhost:8080"); //$NON-NLS-1$
defaultSettings.put(MAIL_HOST_PORT, "25"); //$NON-NLS-1$
defaultSettings.put(MAIL_SENDER_NAME, "documentr"); //$NON-NLS-1$
defaultSettings.put(MAIL_SUBJECT_PREFIX, "[documentr]"); //$NON-NLS-1$
defaultSettings.put(MAIL_DEFAULT_LANGUAGE, Locale.ENGLISH.getLanguage());
defaultSettings.put(BCRYPT_ROUNDS, "12"); //$NON-NLS-1$
defaultSettings.put(UPDATE_CHECK_INTERVAL, UPDATE_CHECK_INTERVAL_WEEKLY);
for (Map.Entry<String, String> entry : defaultSettings.entrySet()) {
if (!settings.containsKey(entry.getKey())) {
settings.put(entry.getKey(), entry.getValue());
}
}
}
public Map<String, String> getSettings() {
synchronized (settings) {
return Collections.unmodifiableMap(Maps.newHashMap(settings));
}
}
public String getSetting(String key) {
synchronized (settings) {
return settings.get(key);
}
}
public void saveSettings(Map<String, String> newSettings, User currentUser) throws IOException {
Map<String,String> settingsToStore;
synchronized (settings) {
settings.putAll(newSettings);
settingsToStore = Maps.newHashMap(settings);
}
storeSettings(settingsToStore, currentUser);
eventBus.post(new SystemSettingsChangedEvent());
}
private void storeSettings(Map<String, String> settings, User currentUser) throws IOException {
log.info("storing system settings"); //$NON-NLS-1$
ILockedRepository repo = null;
try {
repo = getOrCreateRepository(currentUser);
File workingDir = RepositoryUtil.getWorkingDir(repo.r());
File file = new File(workingDir, SETTINGS_FILE_NAME);
Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
String json = gson.toJson(settings);
FileUtils.writeStringToFile(file, json, Charsets.UTF_8);
Git git = Git.wrap(repo.r());
git.add()
.addFilepattern(SETTINGS_FILE_NAME)
.call();
PersonIdent ident = new PersonIdent(currentUser.getLoginName(), currentUser.getEmail());
git.commit()
.setAuthor(ident)
.setCommitter(ident)
.setMessage(SETTINGS_FILE_NAME)
.call();
} catch (GitAPIException e) {
throw new IOException(e);
} finally {
Util.closeQuietly(repo);
}
}
private ILockedRepository getOrCreateRepository(User user) throws IOException, GitAPIException {
try {
return globalRepositoryManager.getProjectCentralRepository(REPOSITORY_NAME, false);
} catch (RepositoryNotFoundException e) {
return globalRepositoryManager.createProjectCentralRepository(REPOSITORY_NAME, false, user);
}
}
private Map<String, String> loadSettings() throws IOException {
log.info("loading system settings"); //$NON-NLS-1$
ILockedRepository repo = null;
Map<String, String> settingsMap = Maps.newHashMap();
try {
repo = globalRepositoryManager.getProjectCentralRepository(REPOSITORY_NAME, false);
String json = BlobUtils.getHeadContent(repo.r(), SETTINGS_FILE_NAME);
Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
settingsMap = gson.fromJson(json, new TypeToken<Map<String, String>>(){}.getType());
} catch (RepositoryNotFoundException e) {
// okay
} finally {
Util.closeQuietly(repo);
}
return settingsMap;
}
public void setMacroSetting(String macroName, Map<String, String> newSettings, User currentUser) throws IOException {
Map<String, String> settings = Maps.newHashMap();
for (Map.Entry<String, String> entry : newSettings.entrySet()) {
settings.put(MACRO_KEY_PREFIX + macroName + "." + entry.getKey(), entry.getValue()); //$NON-NLS-1$
}
saveSettings(settings, currentUser);
}
public String getMacroSetting(String macroName, String key) {
return getSetting(MACRO_KEY_PREFIX + macroName + "." + key); //$NON-NLS-1$
}
}