/**
* (C) Copyright 2013 Jabylon (http://www.jabylon.org) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
/**
*
*/
package org.jabylon.rest.ui.wicket.config;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.apache.wicket.RestartResponseAtInterceptPageException;
import org.apache.wicket.authorization.UnauthorizedInstantiationException;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.string.StringValue;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.CDOState;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.util.CommitException;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.jabylon.common.resolver.URIResolver;
import org.jabylon.common.util.AttachablePreferences;
import org.jabylon.common.util.DelegatingPreferences;
import org.jabylon.common.util.PreferencesUtil;
import org.jabylon.common.util.config.DynamicConfigUtil;
import org.jabylon.properties.PropertiesPackage;
import org.jabylon.properties.Resolvable;
import org.jabylon.rest.ui.model.AttachableModel;
import org.jabylon.rest.ui.model.AttachableWritableModel;
import org.jabylon.rest.ui.security.CDOAuthenticatedSession;
import org.jabylon.rest.ui.security.LoginPage;
import org.jabylon.rest.ui.util.WicketUtil;
import org.jabylon.rest.ui.wicket.components.ClientSideTabbedPanel;
import org.jabylon.security.CommonPermissions;
import org.jabylon.users.User;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ArrayListMultimap;
/**
* @author jutzig.dev@googlemail.com
*
*/
public class SettingsPanel<T extends CDOObject> extends GenericPanel<T> {
public static final String QUERY_PARAM_NAMESPACE = "namespace";
private static final long serialVersionUID = 1L;
public static final String QUERY_PARAM_CREATE = "create";
private static final Logger logger = LoggerFactory.getLogger(SettingsPage.class);
private List<ConfigSection<?>> allSections;
@Inject
private URIResolver resolver;
@SuppressWarnings("unchecked")
public SettingsPanel(String id, IModel<T> model, PageParameters pageParameters) {
super(id, model);
EClass eclass = getEClassToCreate(pageParameters);
if (eclass != null) {
setModel(new AttachableWritableModel<T>(eclass, getModel()));
}
T modelObject = getModelObject();
boolean isNew = modelObject.cdoState() == CDOState.NEW || modelObject.cdoState() == CDOState.TRANSIENT;
final Preferences preferences = isNew ? new AttachablePreferences() : new DelegatingPreferences(PreferencesUtil.scopeFor(modelObject));
final List<ITab> extensions = loadTabExtensions(preferences);
// submit section
@SuppressWarnings({ "rawtypes" })
Form form = new Form("form", getModel()) {
private static final long serialVersionUID = 1L;
String oldName;
@Override
protected void beforeUpdateFormComponentModels() {
super.beforeUpdateFormComponentModels();
IModel model = getModel();
if(model!=null && model.getObject() instanceof Resolvable)
{
//store the original project name in case it gets changed
Resolvable resolvable = (Resolvable) model.getObject();
oldName = resolvable.getName();
}
}
@Override
protected void onSubmit() {
Preferences prefs = preferences;
IModel<T> model = SettingsPanel.this.getModel();
CDOObject object = model.getObject();
CDOView cdoView;
if (model instanceof AttachableModel) {
// it's a new object that needs attaching
AttachableModel<CDOObject> attachable = (AttachableModel) model;
attachable.attach();
CDOObject parent = (CDOObject) attachable.getObject().eContainer();
cdoView = parent.cdoView();
} else
cdoView = object.cdoView();
if (cdoView instanceof CDOTransaction) {
CDOTransaction transaction = (CDOTransaction) cdoView;
if (prefs instanceof AttachablePreferences) {
// the prefs are not in the tree yet
Preferences targetPrefs = PreferencesUtil.scopeFor(object);
try {
PreferencesUtil.cloneNode(prefs, targetPrefs);
prefs = targetPrefs;
} catch (BackingStoreException e) {
error("Some settings could not be saved: " + e.getMessage());
logger.error("Failed to attach preferences to target path", e);
}
}
if(oldName!=null && !oldName.equals(model.getObject().eGet(PropertiesPackage.Literals.RESOLVABLE__NAME)))
{
renameResolvable(oldName,model);
prefs = PreferencesUtil.renamePreferenceNode(preferences, (String) model.getObject().eGet(PropertiesPackage.Literals.RESOLVABLE__NAME));
}
commit(prefs, object, transaction);
// model.detach();
} else
throw new IllegalStateException("not a transaction");
super.onSubmit();
}
protected void commit(final Preferences preferences, CDOObject object, CDOTransaction transaction) {
for (ConfigSection<?> section : allSections) {
section.commit(getModel(), preferences);
}
try {
transaction.commit();
URI uri = resolver.getURI(object);
setResponsePage(SettingsPage.class, WicketUtil.buildPageParametersFor(uri));
preferences.flush();
getSession().success(getString("save.success.feedback.message"));
} catch (CommitException e) {
getSession().error(e.getMessage());
logger.error("failed to commit configuration for " + object, e);
} catch (BackingStoreException e) {
getSession().error(e.getMessage());
logger.error("failed to commit configuration for " + object, e);
} finally {
// transaction.close();
}
}
};
ClientSideTabbedPanel<ITab> tabContainer = new ClientSideTabbedPanel<ITab>("tabs", extensions, false, "settings/"
+ model.getObject().getClass().getSimpleName()) {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
boolean visible = super.isVisible();
List<ITab> tabContents = extensions;
for (ITab component : tabContents) {
if (component.isVisible())
return visible;
}
CDOAuthenticatedSession session = (CDOAuthenticatedSession) CDOAuthenticatedSession.get();
User user = session.getUser();
if (user == null || CommonPermissions.USER_ANONYMOUS.equals(user.getName()))
// user is not logged in, give him the chance
throw new RestartResponseAtInterceptPageException(LoginPage.class);
// if no tab is visible, the user has no permission to be here
throw new UnauthorizedInstantiationException(SettingsPanel.class);
}
};
form.add(tabContainer);
// form.add(new CustomFeedbackPanel("feedback"));
Button submitButton = new Button("submit", new StringResourceModel("submit.button.label", this, null));
form.add(submitButton);
// Button cancelButton = new Button("cancel-button",
// Model.of("Cancel"));
// form.add(cancelButton);
add(form);
}
/**
* called when the name of a resolvable changes
* @param oldName
* @param model
*/
@SuppressWarnings("rawtypes")
protected void renameResolvable(String oldName, IModel<T> model) {
T object = model.getObject();
if (object instanceof Resolvable) {
Resolvable r = (Resolvable) object;
logger.info("Renaming {} {} to {}",r.getClass().getSimpleName(), oldName, r.getName());
URI absoluteFilePath = r.absoluteFilePath();
String fileString = absoluteFilePath.toFileString();
if(!fileString.startsWith("/") || fileString.startsWith("\\"))
{
fileString = "/"+fileString;
}
File targetFile = new File(fileString);
File sourceFile = new File(targetFile.getParentFile(),oldName);
sourceFile.renameTo(targetFile);
}
}
private EClass getEClassToCreate(PageParameters pageParameters) {
StringValue value = pageParameters.get(QUERY_PARAM_CREATE);
if (value != null && !value.isEmpty()) {
String namespace = pageParameters.get(QUERY_PARAM_NAMESPACE).toString(PropertiesPackage.eNS_URI);
EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(namespace);
if (ePackage != null) {
EClassifier eClassifier = ePackage.getEClassifier(value.toString());
if (eClassifier instanceof EClass) {
EClass eclass = (EClass) eClassifier;
return eclass;
}
}
}
return null;
}
@SuppressWarnings({ "unused", "unchecked", "rawtypes" })
private List<ITab> loadTabExtensions(Preferences preferences) {
List<IConfigurationElement> configurationElements = DynamicConfigUtil.getConfigTabs();
ArrayListMultimap<String, ConfigSection<?>> sections = ArrayListMultimap.create(configurationElements.size(), 5);
allSections = new ArrayList<ConfigSection<?>>();
List<IConfigurationElement> elements = DynamicConfigUtil.getApplicableElements(getModelObject());
for (IConfigurationElement element : elements) {
String id = element.getAttribute("tab");
ConfigSection<?> extension;
try {
extension = (ConfigSection<?>) element.createExecutableExtension("section");
sections.put(id, extension);
} catch (CoreException e) {
logger.error("Failed to create executable extension: " + element, e);
}
}
List<ITab> extensions = new ArrayList<ITab>();
T modelObject = getModelObject();
for (IConfigurationElement element : configurationElements) {
String name = element.getAttribute("name");
String id = element.getAttribute("tabID");
List<ConfigSection<?>> tabSections = sections.removeAll(id);
ConfigTab tab = new ConfigTab(name, tabSections, getModel(), preferences);
allSections.addAll(tabSections);
if (tab.isVisible())
extensions.add(tab);
}
if (!sections.isEmpty()) {
logger.warn("Unmapped config sections left {}", sections);
}
return extensions;
}
}