001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2017-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.portal.base.events;
020
021import java.io.IOException;
022import java.io.Writer;
023import java.net.URI;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.List;
027
028import org.jdrupes.json.JsonArray;
029import org.jgrapes.portal.base.RenderSupport;
030import org.jgrapes.portal.base.events.AddPageResources.ScriptResource;
031
032/**
033 * Adds a portlet type with its global resources (JavaScript and/or CSS) 
034 * to the portal page. Specifying global resources result in the respective
035 * `<link .../>` or `<script ...></script>` nodes
036 * being added to the page's `<head>` node.
037 * 
038 * This in turn causes the browser to issue `GET` request that
039 * (usually) refer to the portlet's resources. These requests are
040 * converted to {@link PortletResourceRequest}s by the portal and
041 * sent to the portlets, which must respond to the requests.
042 * 
043 * The sequence of events is shown in the diagram.
044 * 
045 * ![Portal Ready Event Sequence](AddPortletTypeSeq.svg)
046 * 
047 * See {@link ResourceRequest} for details about the processing
048 * of the {@link PortletResourceRequest}.
049 * 
050 * A portelt's JavaScript may (and probably must) make use of
051 * the functions provided by the portal page. See the 
052 * <a href="../../../../jsdoc/index.html">JavaScript
053 * documentation of these functions</a> for details.
054 * 
055 * @startuml AddPortletTypeSeq.svg
056 * hide footbox
057 * 
058 * activate Browser
059 * Browser -> Portal: "portalReady"
060 * deactivate Browser
061 * activate Portal
062 * Portal -> PortletX: PortalReady 
063 * deactivate Portal
064 * activate PortletX
065 * PortletX -> Portal: AddPortletType 
066 * deactivate PortletX
067 * activate Portal
068 * Portal -> Browser: "addPortletType"
069 * activate Browser
070 * deactivate Portal
071 * Browser -> Portal: "GET <portlet resource URI>"
072 * activate Portal
073 * Portal -> PortletX: PortletResourceRequest
074 * deactivate Browser
075 * activate PortletX
076 * deactivate PortletX
077 * 
078 * @enduml
079 */
080public class AddPortletType extends PortalCommand {
081
082    private final String portletType;
083    private String displayName = "";
084    private boolean instantiable;
085    private final List<URI> cssUris = new ArrayList<>();
086    private final List<ScriptResource> scriptResources = new ArrayList<>();
087
088    /**
089     * Create a new event for the given portlet type.
090     * 
091     * @param portletType a unique id for the portklet type (usually
092     * the class name)
093     */
094    public AddPortletType(String portletType) {
095        this.portletType = portletType;
096    }
097
098    /**
099     * Return the portlet type.
100     * 
101     * @return the portlet type
102     */
103    public String portletType() {
104        return portletType;
105    }
106
107    /**
108     * Sets the display name.
109     * 
110     * @param displayName the display name
111     * @return the event for easy chaining
112     */
113    public AddPortletType setDisplayName(String displayName) {
114        this.displayName = displayName;
115        return this;
116    }
117
118    /**
119     * Return the display name.
120     * 
121     * @return the displayName
122     */
123    public String displayName() {
124        return displayName;
125    }
126
127    /**
128     * Mark the portlet type as instantiable.
129     * 
130     * @return the event for easy chaining
131     */
132    public AddPortletType setInstantiable() {
133        instantiable = true;
134        return this;
135    }
136
137    /**
138     * Return if the portelt is instantiable.
139     * 
140     * @return the result
141     */
142    public boolean isInstantiable() {
143        return instantiable;
144    }
145
146    /**
147     * Add a script resource to be requested by the browser.
148     * 
149     * @param scriptResource the script resource
150     * @return the event for easy chaining
151     */
152    public AddPortletType addScript(ScriptResource scriptResource) {
153        scriptResources.add(scriptResource);
154        return this;
155    }
156
157    /**
158     * Add the URI of a CSS resource that is to be added to the
159     * header section of the portal page.
160     *
161     * @param renderSupport the render support for mapping the `uri`
162     * @param uri the URI
163     * @return the event for easy chaining
164     */
165    public AddPortletType addCss(RenderSupport renderSupport, URI uri) {
166        cssUris.add(renderSupport.portletResource(portletType(), uri));
167        return this;
168    }
169
170    /**
171     * Return all script resources.
172     * 
173     * @return the result
174     */
175    public ScriptResource[] scriptResources() {
176        return scriptResources.toArray(new ScriptResource[0]);
177    }
178
179    /**
180     * Return all CSS URIs.
181     * 
182     * @return the result
183     */
184    public URI[] cssUris() {
185        return cssUris.toArray(new URI[0]);
186    }
187
188    @Override
189    public void toJson(Writer writer) throws IOException {
190        JsonArray strArray = JsonArray.create();
191        for (ScriptResource scriptResource : scriptResources()) {
192            strArray.append(scriptResource.toJsonValue());
193        }
194        toJson(writer, "addPortletType", portletType(), displayName(),
195            Arrays.stream(cssUris()).map(
196                uri -> uri.toString()).toArray(String[]::new),
197            strArray, isInstantiable());
198    }
199}