InternalCrossContextLinkType.java

  1. /*
  2.  * #%L
  3.  * wcm.io
  4.  * %%
  5.  * Copyright (C) 2019 wcm.io
  6.  * %%
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *      http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  * #L%
  19.  */
  20. package io.wcm.handler.link.type;

  21. import java.util.HashMap;
  22. import java.util.Map;

  23. import org.apache.commons.lang3.StringUtils;
  24. import org.apache.sling.api.SlingHttpServletRequest;
  25. import org.apache.sling.api.resource.Resource;
  26. import org.apache.sling.api.resource.ResourceResolver;
  27. import org.apache.sling.models.annotations.Model;
  28. import org.apache.sling.models.annotations.injectorspecific.Self;
  29. import org.jetbrains.annotations.NotNull;
  30. import org.jetbrains.annotations.Nullable;
  31. import org.osgi.annotation.versioning.ProviderType;

  32. import io.wcm.handler.link.Link;
  33. import io.wcm.handler.link.LinkNameConstants;
  34. import io.wcm.handler.link.LinkRequest;
  35. import io.wcm.handler.link.SyntheticLinkResource;
  36. import io.wcm.handler.link.spi.LinkType;
  37. import io.wcm.handler.link.type.helpers.InternalLinkResolver;
  38. import io.wcm.handler.link.type.helpers.InternalLinkResolverOptions;

  39. /**
  40.  * Implementation of {@link io.wcm.handler.link.spi.LinkType} for internal links with supports
  41.  * links between different sites or configuration context paths.
  42.  * Internal links are links to content pages inside the CMS.
  43.  *
  44.  * <p>
  45.  * This link type ensures that links that are referenced from other sites/configuration contexts are resolved
  46.  * using the URL handler configuration of the target context, e.g. with the Site URL from the other site.
  47.  * </p>
  48.  */
  49. @Model(adaptables = {
  50.     SlingHttpServletRequest.class, Resource.class
  51. })
  52. @ProviderType
  53. public final class InternalCrossContextLinkType extends LinkType {

  54.   /**
  55.    * Link type ID
  56.    */
  57.   public static final @NotNull String ID = "internalCrossContext";

  58.   private final @NotNull InternalLinkResolverOptions resolverOptions = new InternalLinkResolverOptions()
  59.       .primaryLinkRefProperty(getPrimaryLinkRefProperty())
  60.       .rewritePathToContext(false)
  61.       .useTargetContext(true);

  62.   @Self
  63.   private InternalLinkResolver internalLinkResolver;

  64.   /**
  65.    * @return Link type ID (is stored as identifier in repository)
  66.    */
  67.   @Override
  68.   public @NotNull String getId() {
  69.     return ID;
  70.   }

  71.   @Override
  72.   public @NotNull String getLabel() {
  73.     return "Internal (other site)";
  74.   }

  75.   @Override
  76.   public String getPrimaryLinkRefProperty() {
  77.     return LinkNameConstants.PN_LINK_CROSSCONTEXT_CONTENT_REF;
  78.   }

  79.   @Override
  80.   public @Nullable String getEditComponentResourceType() {
  81.     return "wcm-io/handler/link/components/granite/form/linktype/internalCrossContext";
  82.   }

  83.   @Override
  84.   public boolean hasRichTextPlugin() {
  85.     return true;
  86.   }

  87.   @Override
  88.   public boolean accepts(@NotNull String linkRef) {
  89.     // accept as internal link if the ref starts with "/content/"
  90.     return StringUtils.startsWith(linkRef, "/content/")
  91.         && !MediaLinkType.isDefaultMediaContentPath(linkRef);
  92.   }

  93.   @Override
  94.   public boolean accepts(@NotNull LinkRequest linkRequest) {
  95.     if (internalLinkResolver.acceptPage(linkRequest.getPage(), resolverOptions)) {
  96.       // support direct links to pages
  97.       return true;
  98.     }
  99.     // check for matching link type ID in link resource
  100.     return super.accepts(linkRequest);
  101.   }

  102.   @Override
  103.   public @NotNull Link resolveLink(@NotNull Link link) {
  104.     return internalLinkResolver.resolveLink(link, resolverOptions);
  105.   }

  106.   /**
  107.    * Get synthetic link resource for this link type.
  108.    * @param resourceResolver Resource resolver
  109.    * @param path Resource path. Can be a non-existing path, but the path should be located somewhere within the
  110.    *          applications content paths to make sure the handler configuration looked up via context-aware services
  111.    *          is the expected one.
  112.    * @param pageRef Path to target page
  113.    * @return Synthetic link resource
  114.    */
  115.   public static @NotNull Resource getSyntheticLinkResource(@NotNull ResourceResolver resourceResolver,
  116.       @NotNull String path, @NotNull String pageRef) {
  117.     Map<String, Object> map = new HashMap<>();
  118.     map.put(LinkNameConstants.PN_LINK_TYPE, ID);
  119.     map.put(LinkNameConstants.PN_LINK_CROSSCONTEXT_CONTENT_REF, pageRef);
  120.     return new SyntheticLinkResource(resourceResolver, path, map);
  121.   }

  122.   @Override
  123.   public String toString() {
  124.     return ID;
  125.   }

  126. }