All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.semanticcms.core.servlet.View Maven / Gradle / Ivy

There is a newer version: 1.20.0
Show newest version
/*
 * semanticcms-core-servlet - Java API for modeling web page content and relationships in a Servlet environment.
 * Copyright (C) 2016, 2017, 2019, 2020, 2021, 2022  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of semanticcms-core-servlet.
 *
 * semanticcms-core-servlet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * semanticcms-core-servlet 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with semanticcms-core-servlet.  If not, see .
 */

package com.semanticcms.core.servlet;

import com.aoapps.html.servlet.FlowContent;
import com.aoapps.net.URIEncoder;
import com.aoapps.servlet.http.Canonical;
import com.aoapps.taglib.Link;
import com.aoapps.web.resources.registry.Registry;
import com.semanticcms.core.model.Author;
import com.semanticcms.core.model.Copyright;
import com.semanticcms.core.model.Page;
import com.semanticcms.core.model.PageRef;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.SkipPageException;
import org.joda.time.ReadableInstant;

/**
 * A site may provide multiple views of the data.  Except the default content view,
 * views will typically show something about the current page and all of its children.
 */
public abstract class View implements Comparable {

  /**
   * The separator used between segments of the title.
   * Should this be provided by the template?
   */
  protected static final String TITLE_SEPARATOR = " - ";

  /**
   * View groupings, in order.
   */
  public enum Group {
    /**
     * Things that should be placed absolutely first.
     */
    FIRST,

    /**
     * The first set of views are those that are more fixed - typically displayed on every page.
     */
    FIXED,

    /**
     * The second set of views are those that are hidden when not relevant to the current page or any of its children.
     * This often includes the per-element-type views.
     */
    VARIABLE
  }

  /**
   * Orders by group, display, then name.
   */
  @Override
  public int compareTo(View o) {
    int diff = getGroup().compareTo(o.getGroup());
    if (diff != 0) {
      return diff;
    }
    diff = getDisplay().compareTo(o.getDisplay());
    if (diff != 0) {
      return diff;
    }
    diff = getName().compareTo(o.getName());
    return diff;
  }

  /**
   * Two views with the same name are considered equal.
   */
  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof View)) {
      return false;
    }
    View o = (View) obj;
    return getName().equals(o.getName());
  }

  /**
   * Consistent with equals, hashCode based on name.
   */
  @Override
  public int hashCode() {
    return getName().hashCode();
  }

  /**
   * {@inheritDoc}
   *
   * @see  #getDisplay()
   */
  @Override
  public String toString() {
    return getDisplay();
  }

  /**
   * Gets the grouping for this view.
   */
  public abstract Group getGroup();

  /**
   * Gets the display name for this view.
   */
  public abstract String getDisplay();

  /**
   * Gets the unique name of this view.
   */
  public abstract String getName();

  /**
   * Checks if this is the default view.
   */
  public final boolean isDefault() {
    return SemanticCMS.DEFAULT_VIEW_NAME.equals(getName());
  }

  /**
   * Checks if a view applies in global navigation context.
   * 

* Implementation Note:
* returns {@code true} by default *

*/ public boolean getAppliesGlobally() { return true; } /** * Checks if a view is applicable the given request and page. * For correct determination, the page must have been captured at {@link CaptureLevel#META} * level or higher. *

* TODO: Store the captureLevel in effect when a page is captured, and confirm that here and other places where * certain capture levels are required for correct behavior. Could also automatically re-capture at a higher level * instead of throwing an exception. *

*

* Implementation Note:
* returns {@code true} by default *

*/ public boolean isApplicable( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) throws ServletException, IOException { return true; } /** * Gets an id to use for the main navigation link to this view. *

* Implementation Note:
* returns {@code null} by default *

* * @return the ID or null for none */ public String getLinkId() { return null; } /** * Gets the CSS class to use for the main navigation link to this view. *

* Implementation Note:
* returns {@code null} by default *

* * @return the CSS class or null for none */ public String getLinkCssClass( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response ) { return null; } /** * Gets the optional additional parameter to a view link. *

* Implementation Note:
* returns empty map by default *

*/ public Map> getLinkParams(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page) { return Collections.emptyMap(); } /** * Gets the canonical URL for the given page in this view. * Can not get canonical URLs for missing books. * This might be called even when a page is not applicable to this view, such as when browing to an empty TODO list. * By default, {@link #getLinkParams(javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, com.semanticcms.core.model.Page) link parameters} * are not added. *

* This URL is absolute and has already been response encoded. *

* * @see BookUtils#getCanonicalBase(javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, com.semanticcms.core.model.Book) */ public String getCanonicalUrl( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) throws ServletException, IOException { PageRef pageRef = page.getPageRef(); // TODO: Should we use servletPath here, then remove the book prefix? // We're passing a partial path to response.encodeURL String encodedBookPrefix = URIEncoder.encodeURI(pageRef.getBookPrefix()); String encodedServletPath; { StringBuilder servletPath = new StringBuilder(); servletPath.append(encodedBookPrefix); URIEncoder.encodeURI(pageRef.getPath(), servletPath); if (!isDefault()) { servletPath.append("?view="); URIEncoder.encodeURIComponent(getName(), servletPath); } encodedServletPath = Canonical.encodeCanonicalURL(response, servletPath.toString()); } // To be safe, we're encoding the servletPath, then picking it back into a bookPath // TODO: How would this interact with things like PrettyUrlFilter? String encodedBookPath; { if (encodedBookPrefix.isEmpty()) { encodedBookPath = encodedServletPath; } else { if (!encodedServletPath.startsWith(encodedBookPrefix)) { throw new IllegalStateException("Encoded servlet path is outside this book, unable to canonicalize: encodedServletPath = " + encodedServletPath); } encodedBookPath = encodedServletPath.substring(encodedBookPrefix.length()); } } return BookUtils.getCanonicalBase( servletContext, request, pageRef.getBook() ) + encodedBookPath; } /** * Gets the effective last modified time, if known, for the given page in this view. * This is used for things such as sitemaps. *

* Implementation Note:
* This default implementation returns {@code null} indicating not applicable to this view. *

* * @return The effective last modified time or {@code null} if unknown or not applicable. */ public ReadableInstant getLastModified( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) throws ServletException, IOException { return null; } /** * Gets the copyright information for the view on the given page. * * @see CopyrightUtils#findCopyright(javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, com.semanticcms.core.model.Page) */ public Copyright getCopyright( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) throws ServletException, IOException { return CopyrightUtils.findCopyright(servletContext, request, response, page); } /** * Gets the author(s) for the view on the given page. * * @see AuthorUtils#findAuthors(javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, com.semanticcms.core.model.Page) */ public Set getAuthors( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) throws ServletException, IOException { return AuthorUtils.findAuthors(servletContext, request, response, page); } /** * Gets the page title for the view on the given page. *

* Defaults to: "view.display - page.title[ - page.pageRef.book.title]" *

*/ public String getTitle( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) { String bookTitle = page.getPageRef().getBook().getTitle(); if (bookTitle != null && !bookTitle.isEmpty()) { return getDisplay() + TITLE_SEPARATOR + page.getTitle() + TITLE_SEPARATOR + bookTitle; } else { return getDisplay() + TITLE_SEPARATOR + page.getTitle(); } } /** * Gets the description for this view of the given page or {@code null} for none. */ public abstract String getDescription(Page page); /** * Gets the keywords for this view of the given page or {@code null} for none. */ public abstract String getKeywords(Page page); /** * Configures the {@linkplain com.aoapps.web.resources.servlet.RegistryEE.Request request-scope web resources} that this view uses. *

* Implementers should call super.configureResources(…) as a matter of convention, despite this default implementation doing nothing. *

*/ @SuppressWarnings("NoopMethodInAbstractClass") public void configureResources(ServletContext servletContext, HttpServletRequest req, HttpServletResponse resp, Theme theme, Page page, Registry requestRegistry) { // Do nothing } /** * Gets an optional set of additional links to include for this view * in the order they should be added. *

* Please note, that any links to stylesheets here are never optimized. Please * prefer {@link #configureResources(javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, com.semanticcms.core.servlet.Theme, com.semanticcms.core.model.Page, com.aoapps.web.resources.registry.Registry)}. *

* * @see #configureResources(javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, com.semanticcms.core.servlet.Theme, com.semanticcms.core.model.Page, com.aoapps.web.resources.registry.Registry) */ public Collection getLinks( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, // TODO: Theme here, too? Page page ) throws ServletException, IOException { return Collections.emptySet(); } /** * Gets any per-view scripts, when have the same name as globally registered * scripts, must have matching src. *

* Implementation Note:
* returns empty map by default *

* * @see SemanticCMS#getScripts() */ public Map getScripts() { return Collections.emptyMap(); } /** * Gets whether robots are allowed to access this view to the given page. When true will include both * "noindex, nofollow" in the head and put "nofollow" on links to this view. */ public abstract boolean getAllowRobots( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) throws ServletException, IOException; /** * Renders the view. This is called by the template to fill-out the main content area. *

* TODO: Is SkipPageException acceptable at the view rendering stage? *

*/ public abstract <__ extends FlowContent<__>> void doView( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, __ flow, Page page ) throws ServletException, IOException, SkipPageException; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy