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

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

/*
 * semanticcms-core-servlet - Java API for modeling web page content and relationships in a Servlet environment.
 * Copyright (C) 2016  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.aoindustries.taglib.Link;
import com.semanticcms.core.model.Author;
import com.semanticcms.core.model.Book;
import com.semanticcms.core.model.Copyright;
import com.semanticcms.core.model.Page;
import com.semanticcms.core.model.PageRef;
import java.io.IOException;
import java.net.URLEncoder;
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.
 */
abstract public 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();
	}

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

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

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

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

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

	/**
	 * Checks if a view applies in global navigation context.
	 * 
	 * @implSpec  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.
	 *
	 * @implSpec  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.
	 *
	 * @return  the ID or null for none
	 *
	 * @implSpec  returns {@code null} by default
	 */
	public String getLinkId() {
		return null;
	}

	/**
	 * Gets the CSS class to use for the main navigation link to this view.
	 *
	 * @return  the CSS class or null for none
	 *
	 * @implSpec  returns {@code null} by default
	 */
	public String getLinkCssClass(
		ServletContext servletContext,
		HttpServletRequest request,
		HttpServletResponse response
	) {
		return null;
	}

	/**
	 * Gets the optional additional parameter to a view link.
	 *
	 * @implSpec  returns empty map
	 */
	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(); Book book = pageRef.getBook(); // TODO: Should we use servletPath here, then remove the book prefix? // We're passing a partial path to response.encodeURL String encodedServletPath; { String servletPath = pageRef.getServletPath(); if(!isDefault()) { servletPath += "?view=" + URLEncoder.encode(getName(), response.getCharacterEncoding()); } encodedServletPath = response.encodeURL(servletPath); } // To be safe, we're encoding the servletPath, then picking it back into a bookPath String encodedBookPath; { String bookPrefix = book.getPathPrefix(); if(bookPrefix.isEmpty()) { encodedBookPath = encodedServletPath; } else { if(!encodedServletPath.startsWith(bookPrefix)) throw new IllegalStateException("Encoded servlet path is outside this book, unable to canonicalize: encodedServletPath = " + encodedServletPath); encodedBookPath = encodedServletPath.substring(bookPrefix.length()); } } return BookUtils.getCanonicalBase(servletContext, request, book) + encodedBookPath; } /** * Gets the effective last modified time, if known, for the given page in this view. * This is used for things such as sitemaps. * * @implSpec 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. */ abstract public String getDescription(Page page); /** * Gets the keywords for this view of the given page or {@code null} for none. */ abstract public String getKeywords(Page page); /** * Gets an optional set of additional links to include for this view * in the order they should be added. */ public Collection getLinks( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, 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. * * @see SemanticCMS#getScripts() * * @implSpec returns empty map by default */ 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. */ abstract public 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? */ abstract public void doView( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Page page ) throws ServletException, IOException, SkipPageException; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy