AbstractView.java

/*
 * Copyright 2010-2024 James Pether Sörling
 *
 * 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.
 *
 *	$Id$
 *  $HeadURL$
*/
package com.hack23.cia.web.impl.ui.application.views.common;

import java.util.Map;

import javax.annotation.PostConstruct;

import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.context.request.RequestContextHolder;

import com.hack23.cia.service.api.action.application.LogoutRequest;
import com.hack23.cia.web.impl.ui.application.action.PageActionEventHelper;
import com.hack23.cia.web.impl.ui.application.util.UserContextUtil;
import com.hack23.cia.web.impl.ui.application.views.common.labelfactory.LabelFactory;
import com.hack23.cia.web.impl.ui.application.views.common.menufactory.api.ApplicationMenuItemFactory;
import com.hack23.cia.web.impl.ui.application.views.common.pagelinks.api.PageLinkFactory;
import com.hack23.cia.web.impl.ui.application.views.common.pagemode.PageModeContentFactory;
import com.hack23.cia.web.impl.ui.application.views.common.sizing.ContentRatio;
import com.hack23.cia.web.impl.ui.application.views.pageclicklistener.LogoutClickListener;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.ThemeResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Image;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;

/**
 * The Class AbstractView.
 */
public abstract class AbstractView extends Panel implements View {

	/** The Constant LOGGER. */
	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractView.class);

	/** The Constant LOGOUT. */
	private static final String LOGOUT = "Logout";

	/** The Constant ROLE_ADMIN. */
	private static final String ROLE_ADMIN = "ROLE_ADMIN";

	/** The Constant ROLE_USER. */
	private static final String ROLE_USER = "ROLE_USER";

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 1L;

	/** The barmenu. */
	private final MenuBar barmenu = new MenuBar();

	/** The menu item factory. */
	@Autowired
	private transient ApplicationMenuItemFactory menuItemFactory;

	/** The page action event helper. */
	@Autowired
	protected transient PageActionEventHelper pageActionEventHelper;

	/** The page link factory. */
	@Autowired
	protected transient PageLinkFactory pageLinkFactory;

	/** The page mode content factory map. */
	private transient Map<String, PageModeContentFactory> pageModeContentFactoryMap;

	/** The application name. */
	@Value("${application.name}")
	protected String applicationName;

	/** The application version. */
	@Value("${application.version}")
	protected String applicationVersion;

	/** The application url. */
	@Value("${application.url}")
	protected String applicationUrl;

	/** The page name. */
	private final String pageName;

	/** The panel. */
	private Panel panel;

	/** The top header right panel. */
	private final HorizontalLayout topHeaderRightPanel = new HorizontalLayout();


	/**
	 * Instantiates a new abstract view.
	 *
	 * @param pageModeContentFactoryMap the page mode content factory map
	 * @param pageName the page name
	 */
	protected AbstractView(final Map<String, PageModeContentFactory> pageModeContentFactoryMap, final String pageName) {
		super();
		this.pageModeContentFactoryMap = pageModeContentFactoryMap;
		this.pageName = pageName;
	}

	/**
	 * Adds the logo to header.
	 *
	 * @param topHeader
	 *            the top header
	 */
	private static void addLogoToHeader(final HorizontalLayout topHeader) {
		final ThemeResource ciaLogoResource = new ThemeResource("cia-logo.png");
		final Image ciaLogoImage = new Image(null,ciaLogoResource);
		topHeader.addComponent(ciaLogoImage);
		ciaLogoImage.setWidth("60px");
		ciaLogoImage.setHeight("60px");
		topHeader.setComponentAlignment(ciaLogoImage, Alignment.MIDDLE_LEFT);
		topHeader.setExpandRatio(ciaLogoImage, ContentRatio.SMALL);
	}


	/**
	 * Creates the full size vertical layout.
	 *
	 * @return the vertical layout
	 */
	private static VerticalLayout createFullSizeVerticalLayout() {
		return createFullSizeVerticalLayout(true,true);
	}

	/**
	 * Creates the full size vertical layout.
	 *
	 * @param margin
	 *            the margin
	 * @param spacing
	 *            the spacing
	 * @return the vertical layout
	 */
	private static VerticalLayout createFullSizeVerticalLayout(final boolean margin, final boolean spacing) {
		final VerticalLayout layout = new VerticalLayout();
		layout.setMargin(margin);
		layout.setSpacing(spacing);
		layout.setWidth(100, Unit.PERCENTAGE);
		layout.setHeight(100, Unit.PERCENTAGE);
		return layout;
	}

	/**
	 * Creates the top title header.
	 *
	 * @param topHeader
	 *            the top header
	 */
	private static void createTopTitleHeader(final HorizontalLayout topHeader) {
		final HorizontalLayout topTitleHeadertPanel = new HorizontalLayout();


		final Label titleLabel = new Label("Citizen Intelligence Agency");
		titleLabel.setStyleName("Header");
		topTitleHeadertPanel.addComponent(titleLabel);
		topTitleHeadertPanel.setComponentAlignment(titleLabel, Alignment.MIDDLE_LEFT);

		final Label sloganLabel = new Label("// Tracking politicians like bugs!");
		sloganLabel.setStyleName("HeaderSlogan");
		topTitleHeadertPanel.addComponent(sloganLabel);
		topTitleHeadertPanel.setComponentAlignment(sloganLabel, Alignment.MIDDLE_RIGHT);

		topHeader.addComponent(topTitleHeadertPanel);
		topHeader.setComponentAlignment(topTitleHeadertPanel, Alignment.MIDDLE_LEFT);
		topHeader.setExpandRatio(topTitleHeadertPanel, ContentRatio.GRID);
	}

	/**
	 * Creates the basic layout with panel and footer.
	 *
	 * @param panelName
	 *            the panel name
	 */
	protected final void createBasicLayoutWithPanelAndFooter(final String panelName) {
		final VerticalLayout layout = createFullSizeVerticalLayout();
		final VerticalLayout pageModeContent = createFullSizeVerticalLayout(false,false);
		layout.addComponent(pageModeContent);

		final HorizontalLayout topHeader = new HorizontalLayout();

		addLogoToHeader(topHeader);
		createTopTitleHeader(topHeader);

		topHeaderRightPanel.removeAllComponents();
		topHeader.addComponent(topHeaderRightPanel);
		topHeader.setComponentAlignment(topHeaderRightPanel, Alignment.MIDDLE_RIGHT);
		topHeader.setExpandRatio(topHeaderRightPanel, ContentRatio.LARGE);

		createTopHeaderActionsForUserContext();

		topHeaderRightPanel.setWidth("100%");
		topHeaderRightPanel.setHeight("50px");

		topHeader.setWidth("100%");
		topHeader.setHeight("50px");

		pageModeContent.addComponent(topHeader);
		pageModeContent.setComponentAlignment(topHeader, Alignment.TOP_CENTER);

		pageModeContent.addComponent(getBarmenu());
		pageModeContent.setComponentAlignment(getBarmenu(), Alignment.TOP_CENTER);

		panel = new Panel(panelName);
		panel.addStyleName("v-panel-page-panel");

		panel.setSizeFull();
		pageModeContent.addComponent(panel);
		pageModeContent.setExpandRatio(panel, ContentRatio.FULL_SIZE);

		final HorizontalLayout footerTop = new HorizontalLayout();
		final HorizontalLayout footerBottom = new HorizontalLayout();
		final Link createMainViewPageLink = pageLinkFactory.createMainViewPageLink();
		final Label appVersion = new Label(applicationName + " (" + applicationVersion +")(Apache License 2.0)");
		final Link spdxLink = new Link("SBOM(spdx)", new ExternalResource("https://github.com/Hack23/cia/releases/download/" + applicationVersion +"/com.hack23.cia_citizen-intelligence-agency-" + applicationVersion +".spdx.json"));
		final Link sourcCodeLink = new Link("https://github.com/Hack23/cia", new ExternalResource("https://github.com/Hack23/cia"));
		final Label licenseLink = new Label("Open Source");

		footerTop.addComponent(createMainViewPageLink);
		footerTop.addComponent(appVersion);
		footerBottom.addComponent(licenseLink);
		footerBottom.addComponent(sourcCodeLink);
		footerBottom.addComponent(spdxLink);
		footerTop.setComponentAlignment(createMainViewPageLink, Alignment.MIDDLE_LEFT);
		footerTop.setComponentAlignment(appVersion, Alignment.MIDDLE_LEFT);

		footerBottom.setComponentAlignment(licenseLink, Alignment.MIDDLE_LEFT);
		footerBottom.setComponentAlignment(sourcCodeLink, Alignment.MIDDLE_LEFT);
		footerBottom.setComponentAlignment(spdxLink, Alignment.MIDDLE_LEFT);

		footerTop.setWidth("100%");
		footerTop.setHeight("25px");
		footerBottom.setWidth("100%");
		footerBottom.setHeight("25px");

		pageModeContent.addComponent(footerTop);
		pageModeContent.addComponent(footerBottom);

		setContent(layout);

		setWidth(100, Unit.PERCENTAGE);
		setHeight(100, Unit.PERCENTAGE);
		setSizeFull();

	}

	/**
	 * Creates the top header actions for user context.
	 */
	private void createTopHeaderActionsForUserContext() {
		if (UserContextUtil.allowRoleInSecurityContext(ROLE_ADMIN) || UserContextUtil.allowRoleInSecurityContext(ROLE_USER)) {
			final Link userHomePageLink = pageLinkFactory.createUserHomeViewPageLink();
			topHeaderRightPanel.addComponent(userHomePageLink);
			topHeaderRightPanel.setComponentAlignment(userHomePageLink, Alignment.MIDDLE_RIGHT);

			final Button logoutButton = new Button(LOGOUT,VaadinIcons.SIGN_OUT);

			final LogoutRequest logoutRequest = new LogoutRequest();
			logoutRequest.setSessionId(RequestContextHolder.currentRequestAttributes().getSessionId());
			logoutButton.addClickListener(new LogoutClickListener(logoutRequest));

			topHeaderRightPanel.addComponent(logoutButton);
			topHeaderRightPanel.setComponentAlignment(logoutButton, Alignment.MIDDLE_RIGHT);

		} else {
			final Link createRegisterPageLink = pageLinkFactory.createRegisterPageLink();
			topHeaderRightPanel.addComponent(createRegisterPageLink);
			topHeaderRightPanel.setComponentAlignment(createRegisterPageLink, Alignment.MIDDLE_RIGHT);

			final Link createLoginPageLink = pageLinkFactory.createLoginPageLink();
			topHeaderRightPanel.addComponent(createLoginPageLink);
			topHeaderRightPanel.setComponentAlignment(createLoginPageLink, Alignment.MIDDLE_RIGHT);
		}
	}

	@Override
	public final void enter(final ViewChangeEvent event) {
		try {

			final String parameters = Jsoup.clean(event.getParameters(), Safelist.basic());

			for (final PageModeContentFactory pageModeContentFactory : pageModeContentFactoryMap.values()) {
				if (pageModeContentFactory.matches(pageName, parameters) && pageModeContentFactory.validReference(parameters)) {
					getPanel().setContent(pageModeContentFactory.createContent(parameters, getBarmenu(), getPanel()));
					return;
				}
			}

			LOGGER.warn("SECURITY:Invalid reference, content not found:{}/{}",pageName, parameters);
			final VerticalLayout panelContent = createFullSizeVerticalLayout();

			menuItemFactory.createMainPageMenuBar(getBarmenu());

			LabelFactory.createHeader2Label(panelContent,"Invalid reference, content not found:" +pageName+ "/"+ parameters);

			getPanel().setContent(panelContent);
			getPanel().setCaption("Invalid Reference");
		} catch (final AccessDeniedException e ) {
			LOGGER.warn("Security:Authorization Failure:: {} : {}  exception : {}",e.getMessage(),pageName,e.getClass().getName());
			final VerticalLayout panelContent = createFullSizeVerticalLayout();
			LabelFactory.createHeader2Label(panelContent,"Access denied:" +pageName);
			getPanel().setContent(panelContent);
			getPanel().setCaption("Access denied");
		}
	}

	/**
	 * Gets the barmenu.
	 *
	 * @return the barmenu
	 */
	public final MenuBar getBarmenu() {
		return barmenu;
	}

	/**
	 * Gets the panel.
	 *
	 * @return the panel
	 */
	protected final Panel getPanel() {
		return panel;
	}

	/**
	 * Post construct.
	 */
	@PostConstruct
	public final void postConstruct() {
		setSizeFull();
		createBasicLayoutWithPanelAndFooter(pageName);
	}

}