package fr.openwide.core.wicket.more.notification.service;
import java.util.Locale;
import org.apache.wicket.Component;
import org.apache.wicket.Session;
import org.apache.wicket.util.lang.Args;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Node;
import org.jsoup.select.NodeVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import fr.openwide.core.commons.util.context.IExecutionContext;
import fr.openwide.core.jpa.exception.ServiceException;
import fr.openwide.core.spring.notification.exception.NotificationContentRenderingException;
import fr.openwide.core.spring.notification.model.AbstractExecutionContextNotificationContentDescriptorWrapper;
import fr.openwide.core.spring.notification.model.INotificationContentDescriptor;
import fr.openwide.core.spring.notification.model.INotificationRecipient;
import fr.openwide.core.spring.property.SpringPropertyIds;
import fr.openwide.core.spring.property.service.IPropertyService;
import fr.openwide.core.spring.util.StringUtils;
import fr.openwide.core.wicket.more.notification.model.IWicketNotificationDescriptor;
import fr.openwide.core.wicket.more.notification.service.IHtmlNotificationCssService.IHtmlNotificationCssRegistry;
public abstract class AbstractNotificationContentDescriptorFactory extends AbstractWicketRendererServiceImpl {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNotificationContentDescriptorFactory.class);
private static final String STYLE_ATTRIBUTE = "style";
private static final String STYLE_ATTRIBUTE_SEPARATOR = "; ";
private static final String LINK_TAG = "a";
private static final String LINK_TARGET_ATTRIBUTE = "target";
private static final String LINK_TARGET_ATTRIBUTE_BLANK_VALUE = "_blank";
@Autowired
private IHtmlNotificationCssService cssService;
@Autowired
private IPropertyService propertyService;
public AbstractNotificationContentDescriptorFactory(IWicketContextProvider wicketContextProvider) {
super(wicketContextProvider);
}
@SuppressWarnings("deprecation")
@Override
protected IWicketContextProvider getWicketContextProvider() {
return super.getWicketContextProvider();
}
private Locale getDefaultLocale() {
if (Session.exists()) {
return Session.get().getLocale();
} else {
return propertyService.get(SpringPropertyIds.DEFAULT_LOCALE);
}
}
protected abstract class AbstractWicketNotificationDescriptor implements IWicketNotificationDescriptor {
@Override
public final String renderSubject() {
return renderSubject(getDefaultLocale());
}
protected abstract String renderSubject(Locale locale);
@Override
public final String renderTextBody() throws NotificationContentRenderingException {
return null;
}
public String getVariation() {
return IWicketNotificationDescriptor.DEFAULT_NOTIFICATION_VARIATION;
}
@Override
public final String renderHtmlBody() {
return renderHtmlBody(getDefaultLocale());
}
private String renderHtmlBody(Locale locale) {
return AbstractNotificationContentDescriptorFactory.this.renderComponent(
new Supplier<Component>() {
@Override
public Component get() {
return createComponent("htmlComponent");
}
},
locale,
getVariation()
);
}
@Override
public abstract Component createComponent(String wicketId);
@Override
public INotificationContentDescriptor withContext(INotificationRecipient recipient) {
return new Wrapper(getExecutionContext(recipient));
}
private class Wrapper extends AbstractExecutionContextNotificationContentDescriptorWrapper
implements INotificationContentDescriptor {
public Wrapper(IExecutionContext excecutionContext) {
super(AbstractWicketNotificationDescriptor.this, excecutionContext);
}
@Override
public INotificationContentDescriptor withContext(INotificationRecipient recipient) {
return AbstractWicketNotificationDescriptor.this.withContext(recipient);
}
}
}
protected abstract class AbstractSimpleWicketNotificationDescriptor extends AbstractWicketNotificationDescriptor {
private final String messageKeyRoot;
public AbstractSimpleWicketNotificationDescriptor(String messageKeyRoot) {
super();
Args.notEmpty(messageKeyRoot, "messageKeyRoot");
this.messageKeyRoot = messageKeyRoot;
}
public String getMessageKeyRoot() {
return messageKeyRoot;
}
@Override
public final String renderSubject(Locale locale) {
return AbstractNotificationContentDescriptorFactory.this.renderString(
getSubjectMessageKey(), locale,
getSubjectParameter(locale),
(Object[]) Iterables.toArray(getSubjectPositionalParameters(locale), Object.class)
);
}
protected Object getSubjectParameter() {
return null;
}
protected Object getSubjectParameter(Locale locale) {
return getSubjectParameter();
}
protected Iterable<?> getSubjectPositionalParameters() {
return ImmutableList.of();
}
protected Iterable<?> getSubjectPositionalParameters(Locale locale) {
return getSubjectPositionalParameters();
}
protected String getSubjectMessageKey() {
return getMessageKeyRoot() + ".subject";
}
}
/**
* Replace CSS classes by the corresponding style and add a target="_blank" attribute to links.
*/
@Override
@SuppressWarnings("deprecation")
protected String postProcessHtml(Component component, Locale locale, String variation, String htmlBodyToProcess) {
htmlBodyToProcess = super.postProcessHtml(component, locale, variation, htmlBodyToProcess);
IHtmlNotificationCssRegistry cssRegistry = null;
if (cssService.hasRegistry(variation)) {
try {
cssRegistry = cssService.getRegistry(variation);
} catch (ServiceException e) {
LOGGER.error("Unable to load the CSS file", e);
}
}
Document doc = Jsoup.parse(htmlBodyToProcess);
doc.traverse(new AddStyleAndTargetBlankAttributesNodeVisitor(cssRegistry));
return doc.html();
}
public IExecutionContext getExecutionContext(INotificationRecipient recipient) {
return getWicketContextProvider().context(recipient.getLocale());
}
private class AddStyleAndTargetBlankAttributesNodeVisitor implements NodeVisitor {
private final IHtmlNotificationCssRegistry cssRegistry;
private AddStyleAndTargetBlankAttributesNodeVisitor(IHtmlNotificationCssRegistry cssRegistry) {
this.cssRegistry = cssRegistry;
}
@Override
public void head(Node node, int depth) {
if (cssRegistry != null) {
String style = cleanAttribute(cssRegistry.getStyle(node));
if (StringUtils.hasText(style)) {
String existingStyleAttribute = cleanAttribute(node.attr(STYLE_ATTRIBUTE));
StringBuilder styleAttributeSb = new StringBuilder();
if (StringUtils.hasText(existingStyleAttribute)) {
styleAttributeSb.append(existingStyleAttribute);
styleAttributeSb.append(STYLE_ATTRIBUTE_SEPARATOR);
}
styleAttributeSb.append(style);
node.attr(STYLE_ATTRIBUTE, styleAttributeSb.toString());
}
}
if (LINK_TAG.equals(node.nodeName())) {
node.attr(LINK_TARGET_ATTRIBUTE, LINK_TARGET_ATTRIBUTE_BLANK_VALUE);
}
}
@Override
public void tail(Node node, int depth) {
}
private String cleanAttribute(String attribute) {
return StringUtils.trimTrailingCharacter(
StringUtils.trimLeadingCharacter(
StringUtils.trimWhitespace(attribute),
';'
),
';'
);
}
}
}