package com.aperto.magnolia.vanity; /* * #%L * magnolia-vanity-url Magnolia Module * %% * Copyright (C) 2013 - 2014 Aperto AG * %% * 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/gpl-3.0.html>. * #L% */ import info.magnolia.context.MgnlContext; import info.magnolia.link.LinkUtil; import org.apache.jackrabbit.value.StringValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; import static com.aperto.magnolia.vanity.app.LinkConverter.isExternalLink; import static com.aperto.magnolia.vanity.app.VanityUrlSaveFormAction.IMAGE_EXTENSION; import static info.magnolia.cms.util.RequestDispatchUtil.*; import static info.magnolia.jcr.util.PropertyUtil.getString; import static info.magnolia.jcr.util.SessionUtil.getNodeByIdentifier; import static info.magnolia.link.LinkUtil.DEFAULT_EXTENSION; import static info.magnolia.repository.RepositoryConstants.WEBSITE; import static javax.jcr.query.Query.JCR_SQL2; import static org.apache.commons.lang.StringUtils.*; /** * Query service for vanity url nodes in vanity url workspace. * * @author frank.sommer * @since 28.05.14 */ public class VanityUrlService { private static final Logger LOGGER = LoggerFactory.getLogger(VanityUrlService.class); private static final String QUERY = "select * from [mgnl:vanityUrl] where vanityUrl = $vanityUrl and site = $site"; public static final String NN_IMAGE = "qrCode"; public static final String DEF_SITE = "default"; public static final String PN_SITE = "site"; public static final String PN_VANITY_URL = "vanityUrl"; public static final String PN_LINK = "link"; public static final String PN_SUFFIX = "linkSuffix"; public static final String PN_TYPE = "type"; @Inject @Named(value = "magnolia.contextpath") private String _contextPath = ""; private VanityUrlModule _vanityUrlModule; /** * Creates the redirect url for uri mapping. * Without context path, because of Magnolia's {@link info.magnolia.cms.util.RequestDispatchUtil}. * * @param node vanity url node * @return redirect url */ protected String createRedirectUrl(final Node node) { String result; String type = getString(node, PN_TYPE, EMPTY); String prefix; if ("forward".equals(type)) { result = createForwardLink(node); prefix = FORWARD_PREFIX; } else { result = createTargetLink(node); prefix = "301".equals(type) ? PERMANENT_PREFIX : REDIRECT_PREFIX; } if (isNotEmpty(result)) { result = prefix + result; } return result; } /** * Creates the public url for displaying as target link in app view. * * @param node vanity url node * @return public url */ public String createPublicUrl(final Node node) { PublicUrlService publicUrlService = _vanityUrlModule.getPublicUrlService(); return publicUrlService.createTargetUrl(node); } /** * Creates the vanity url for public instance, stored in qr code. * * @param node vanity url node * @return vanity url */ public String createVanityUrl(final Node node) { PublicUrlService publicUrlService = _vanityUrlModule.getPublicUrlService(); return publicUrlService.createVanityUrl(node); } /** * Creates the preview url for app preview. * Without contextPath, because of Magnolia's app framework. * * @param node vanity url node * @return preview url */ public String createPreviewUrl(final Node node) { return createTargetLink(node); } private String createForwardLink(final Node node) { // nearly the same functionality as in createTargetLink. the only difference // is the clearing of the url if an external url had been configured return createTargetLink(node, true); } private String createTargetLink(final Node node) { return createTargetLink(node, false); } private String createTargetLink(final Node node, boolean clearIfExternal) { String url = EMPTY; if (node != null) { url = getString(node, PN_LINK, EMPTY); if (isNotEmpty(url)) { if (isExternalLink(url)) { // we won't allow external links in a forward if (clearIfExternal) { url = EMPTY; } } else { String link = getLinkFromNode(getNodeByIdentifier(WEBSITE, url)); url = substringAfter(defaultString(link), _contextPath); } } if (isNotEmpty(url)) { url += getString(node, PN_SUFFIX, EMPTY); } } return url; } /** * Create the link to the qr image without context path. * * @param node vanity url node * @return link to qr image */ public String createImageLink(final Node node) { String link = EMPTY; try { if (node != null && node.hasNode(NN_IMAGE)) { link = getLinkFromNode(node.getNode(NN_IMAGE)); link = removeStart(defaultString(link), _contextPath); link = replace(link, "." + DEFAULT_EXTENSION, IMAGE_EXTENSION); } } catch (RepositoryException e) { LOGGER.error("Error creating link to image property.", e); } return link; } /** * Query for a vanity url node. * * @param vanityUrl vanity url from request * @param siteName site name from aggegation state * @return first vanity url node of result or null, if nothing found */ public Node queryForVanityUrlNode(final String vanityUrl, final String siteName) { Node node = null; try { Session jcrSession = MgnlContext.getJCRSession(VanityUrlModule.WORKSPACE); QueryManager queryManager = jcrSession.getWorkspace().getQueryManager(); Query query = queryManager.createQuery(QUERY, JCR_SQL2); query.bindValue(PN_VANITY_URL, new StringValue(vanityUrl)); query.bindValue(PN_SITE, new StringValue(siteName)); QueryResult queryResult = query.execute(); NodeIterator nodes = queryResult.getNodes(); if (nodes.hasNext()) { node = nodes.nextNode(); } } catch (RepositoryException e) { LOGGER.error("Error message.", e); } return node; } /** * Override for testing. */ protected String getLinkFromNode(final Node node) { return LinkUtil.createLink(node); } @Inject public void setVanityUrlModule(final VanityUrlModule vanityUrlModule) { _vanityUrlModule = vanityUrlModule; } }