Skip to content

Content of file BluetteGalleryAdapter.java

/*
 * #%L
 * *********************************************************************************************************************
 *
 * NorthernWind - lightweight CMS
 * http://northernwind.tidalwave.it - git clone https://bitbucket.org/tidalwave/northernwind-src.git
 * %%
 * Copyright (C) 2011 - 2023 Tidalwave s.a.s. (http://tidalwave.it)
 * %%
 * *********************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * *********************************************************************************************************************
 *
 *
 * *********************************************************************************************************************
 * #L%
 */
package it.tidalwave.northernwind.frontend.ui.component.gallery.htmltemplate.bluette;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Optional;
import it.tidalwave.util.Id;
import it.tidalwave.util.Key;
import it.tidalwave.northernwind.core.model.ModelFactory;
import it.tidalwave.northernwind.core.model.ResourcePath;
import it.tidalwave.northernwind.core.model.ResourceProperties;
import it.tidalwave.northernwind.core.model.Site;
import it.tidalwave.northernwind.core.model.SiteNode;
import it.tidalwave.northernwind.core.model.Template;
import it.tidalwave.northernwind.core.model.Template.Aggregate;
import it.tidalwave.northernwind.core.model.Template.Aggregates;
import it.tidalwave.northernwind.frontend.ui.RenderContext;
import it.tidalwave.northernwind.frontend.ui.component.htmltemplate.TextHolder;
import it.tidalwave.northernwind.frontend.ui.component.gallery.GalleryView;
import it.tidalwave.northernwind.frontend.ui.component.gallery.spi.GalleryAdapterSupport;
import it.tidalwave.northernwind.frontend.ui.component.gallery.GalleryViewController.GalleryItem;
import lombok.extern.slf4j.Slf4j;
import static it.tidalwave.northernwind.core.model.Template.Aggregates.toAggregates;

/***********************************************************************************************************************
 *
 * @author  Fabrizio Giudici
 *
 **********************************************************************************************************************/
@Slf4j
public class BluetteGalleryAdapter extends GalleryAdapterSupport
  {
    private static final Key<String> P_COPYRIGHT = Key.of("copyright", String.class);

    private static final Key<ResourcePath> P_BLUETTE_TEMPLATE_PATH = Key.of("bluettePath", ResourcePath.class);

    /** The path to the template for fallback rendering of a single gallery page, when JavaScript is not available. */
    private static final Key<ResourcePath> P_BLUETTE_FALLBACK_TEMPLATE_PATH = Key.of("bluetteFallbackPath", ResourcePath.class);

    /** The path to the template for fallback rendering of the light box, when JavaScript is not available. */
    private static final Key<ResourcePath> P_BLUETTE_LIGHTBOX_FALLBACK_TEMPLATE_PATH = Key.of("bluetteLightboxFallbackPath", ResourcePath.class);

    /** The path to the template for the script that redirects a page to the dynamic counterpart (with #!). */
    private static final Key<ResourcePath> P_BLUETTE_REDIRECT_SCRIPT_TEMPLATE_PATH = Key.of("bluetteRedirectScriptPath", ResourcePath.class);

    /** The path to the template for the image catalog. */
    private static final Key<ResourcePath> P_BLUETTE_CATALOG_TEMPLATE_PATH = Key.of("bluetteCatalogPath", ResourcePath.class);

    private static final Key<ResourcePath> P_BLUETTE_VARIABLES_TEMPLATE_PATH = Key.of("bluetteVariablesPath", ResourcePath.class);

    private static final Key<String> P_CATALOG_URL = Key.of("bluetteCatalogUrl", String.class);

    @Nonnull
    private final Site site;

    @Nonnull
    private final Template defaultTemplate;

    @Nonnull
    private final Template fallbackTemplate;

    @Nonnull
    private final Template lightboxFallbackTemplate;

    @Nonnull
    private final Template redirectScriptTemplate;

    @Nonnull
    private final Template catalogTemplate;

    @Nonnull
    private final Template variablesTemplate;

    @Nonnull
    private final ResourceProperties bluetteConfiguration;

    @Nonnull
    private final ResourcePath baseUrl;

    @Nonnull
    private final String copyright;

    private final int fallbackImageSize = 1280; // FIXME: parametrise size

    private final int fallbackThumbnailSize = 100; // FIXME: parametrise size

    private final ResourcePath lightboxSegmentUri = ResourcePath.of("lightbox");

    @Nonnull
    private final String homeUrl;

    private String rendered = "";

    private boolean catalog;

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    public BluetteGalleryAdapter (@Nonnull final SiteNode siteNode,
                                  @Nonnull final GalleryView view,
                                  @Nonnull final ModelFactory modelFactory)
      {
        super(siteNode, view, modelFactory);
        this.site = siteNode.getSite();
        bluetteConfiguration = siteNode.getPropertyGroup(new Id("bluetteConfiguration"));
        baseUrl              = siteNode.getRelativeUri().prependedWith(site.getContextPath());
        homeUrl              = site.createLink(ResourcePath.of("/blog")); // FIXME
        copyright            = bluetteConfiguration.getProperty(P_COPYRIGHT).orElse("");

        defaultTemplate          = loadTemplate(P_BLUETTE_TEMPLATE_PATH,                   "Default.st");
        fallbackTemplate         = loadTemplate(P_BLUETTE_FALLBACK_TEMPLATE_PATH,          "Fallback.st");
        lightboxFallbackTemplate = loadTemplate(P_BLUETTE_LIGHTBOX_FALLBACK_TEMPLATE_PATH, "LightBoxFallback.st");
        redirectScriptTemplate   = loadTemplate(P_BLUETTE_REDIRECT_SCRIPT_TEMPLATE_PATH,   "RedirectScript.st");
        catalogTemplate          = loadTemplate(P_BLUETTE_CATALOG_TEMPLATE_PATH,           "Catalog.st");
        variablesTemplate        = loadTemplate(P_BLUETTE_VARIABLES_TEMPLATE_PATH,         "Variables.st");
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override @Nonnull
    public String getInlinedScript()
      {
        final String link = site.createLink(siteNode.getRelativeUri().appendedWith("images.xml"));
        final ResourceProperties properties = bluetteConfiguration.withProperty(P_CATALOG_URL, "'" + link + "'");
        // FIXME: since key doesn't have dynamic type, we can't properly escape strings.
        final Aggregates variables = properties.getKeys().stream()
               .filter(k -> k.getName().startsWith("bluette") || "logging".equals(k.getName()))
               .flatMap(k -> toAggregate(properties, k).stream())
               .collect(toAggregates("entries"));

        return variablesTemplate.render(variables) + "\n" + redirectScriptTemplate.render();
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void prepareCatalog (@Nonnull final List<GalleryItem> items)
      {
        final Aggregates entries = items.stream().map(this::toAggregate).collect(toAggregates("entries"));
        rendered = catalogTemplate.render(entries);
        catalog = true;
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    // TODO: Could we manage #! params here and select the proper item?
    @Override
    public void prepareGallery (@Nonnull final GalleryItem item, @Nonnull final List<GalleryItem> items)
      {
        prepare(item, items, defaultTemplate);
        rendered = defaultTemplate.render();
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void prepareFallbackGallery (@Nonnull final GalleryItem item, @Nonnull final List<GalleryItem> items)
      {
        prepare(item, items, fallbackTemplate);
        rendered = fallbackTemplate.render();
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void prepareFallbackLightbox (@Nonnull final List<GalleryItem> items)
      {
        final Aggregates entries = items.stream().map(this::toAggregate).collect(toAggregates("entries"));
        final String redirectUrl = site.createLink(baseUrl.appendedWith("#!").appendedWith(lightboxSegmentUri)).replaceAll("/$", "");
        redirectScriptTemplate.addAttribute("redirectUrl", redirectUrl);
        lightboxFallbackTemplate.addAttribute("copyright", copyright);
        rendered = lightboxFallbackTemplate.render(entries);
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void render (@Nonnull final RenderContext context)
      {
        final TextHolder textHolder = (TextHolder)view;

        if (!catalog)
          {
            textHolder.addAttribute("content", rendered);
          }
        else
          {
            textHolder.setTemplate("$content$\n");
            textHolder.setContent(rendered);
            textHolder.setMimeType("text/xml");
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private void prepare (// final @Nonnull RenderContext context,
                          @Nonnull final GalleryItem item,
                          @Nonnull final List<GalleryItem> items,
                          @Nonnull final Template template)
      {
        final int count          = items.size();
        final int index          = items.indexOf(item);
        final int prevIndex      = (index - 1 + count) % count;
        final int nextIndex      = (index + 1) % count;

        final String imageId     = item.getId().stringValue();
        final String imageUrl    = createImageLink(imageId, fallbackImageSize);

        final String redirectUrl = site.createLink(baseUrl.appendedWith("#!").appendedWith(imageId)).replaceAll("/$", "");
        final String previousUrl = site.createLink(baseUrl.appendedWith(items.get(prevIndex).getId().stringValue()));
        final String nextUrl     = site.createLink(baseUrl.appendedWith(items.get(nextIndex).getId().stringValue()));
        final String lightboxUrl = site.createLink(baseUrl.appendedWith(lightboxSegmentUri));

        template.addAttribute("caption",   item.getDescription())
                .addAttribute("previous",  previousUrl)
                .addAttribute("next",      nextUrl)
                .addAttribute("lightbox",  lightboxUrl)
                .addAttribute("home",      homeUrl)
                .addAttribute("imageId",   imageId)
                .addAttribute("imageUrl",  imageUrl)
                .addAttribute("copyright", copyright);

        if (template != defaultTemplate)
          {
            redirectScriptTemplate.addAttribute("redirectUrl", redirectUrl);

    //        context.setDynamicNodeProperty(PD_IMAGE_ID, imageId);
            // FIXME: these should be dynamic properties
these should be dynamic properties
// siteNodeProperties.getProperty().ifPresent(id -> view.addAttribute("imageId", id)); // siteNodeProperties.getProperty(PD_URL).ifPresent(id -> view.addAttribute("url", id)); final TextHolder textHolder = (TextHolder)view; textHolder.addAttribute("description", item.getDescription()); textHolder.addAttribute("imageId", imageId); textHolder.addAttribute("imageUrl", imageUrl); } } /******************************************************************************************************************* * ******************************************************************************************************************/ @Nonnull private Aggregate toAggregate (@Nonnull final GalleryItem item) { final String id = item.getId().stringValue(); final String link = site.createLink(baseUrl.appendedWith(id)); return Aggregate.of("id", id) .with("link", link) .with("url", createImageLink(id, fallbackThumbnailSize)) .with("title", item.getDescription().replaceAll("\n+", " ")); } /******************************************************************************************************************* * ******************************************************************************************************************/ @Nonnull private static Optional<Aggregate> toAggregate (@Nonnull final ResourceProperties properties, @Nonnull final Key<?> key) { // A space in front of [ ] makes them interpreted as a string and not a list. return properties.getProperty(key).map(v -> Aggregate.of("name", key.stringValue()).with("value", v.toString().replace("[", " ["))); } /******************************************************************************************************************* * ******************************************************************************************************************/ @Nonnull private String createImageLink (@Nonnull final String id, @Nonnegative final int size) { return site.createLink(ResourcePath.of(String.format("/media/stillimages/%d/%s.jpg", size, id))); } }