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.Collections;
027import java.util.List;
028import java.util.Locale;
029import java.util.Map;
030import java.util.stream.Collectors;
031
032import org.jdrupes.json.JsonArray;
033import org.jgrapes.portal.base.Portlet.RenderMode;
034import org.jgrapes.portal.base.RenderSupport;
035import org.jgrapes.portal.base.events.AddPageResources.ScriptResource;
036
037/**
038 * Adds a portlet type with its global resources (JavaScript and/or CSS) 
039 * to the portal page. Specifying global resources result in the respective
040 * `<link .../>` or `<script ...></script>` nodes
041 * being added to the page's `<head>` node.
042 * 
043 * This in turn causes the browser to issue `GET` requests that
044 * (usually) refer to the portlet's resources. These requests are
045 * converted to {@link PortletResourceRequest}s by the portal and
046 * sent to the portlets, which must respond to the requests.
047 * 
048 * The sequence of events is shown in the diagram.
049 * 
050 * ![Portal Ready Event Sequence](AddPortletTypeSeq.svg)
051 * 
052 * See {@link ResourceRequest} for details about the processing
053 * of the {@link PortletResourceRequest}.
054 * 
055 * A portelt's JavaScript may (and probably must) make use of
056 * the functions provided by the portal page. See the 
057 * <a href="../jsdoc/module-jgportal.html">JavaScript
058 * documentation of these functions</a> for details.
059 * 
060 * @startuml AddPortletTypeSeq.svg
061 * hide footbox
062 * 
063 * activate Browser
064 * Browser -> Portal: "portalReady"
065 * deactivate Browser
066 * activate Portal
067 * Portal -> PortletX: PortalReady 
068 * deactivate Portal
069 * activate PortletX
070 * PortletX -> Portal: AddPortletType 
071 * deactivate PortletX
072 * activate Portal
073 * Portal -> Browser: "addPortletType"
074 * activate Browser
075 * deactivate Portal
076 * Browser -> Portal: "GET <portlet resource URI>"
077 * activate Portal
078 * Portal -> PortletX: PortletResourceRequest
079 * deactivate Browser
080 * activate PortletX
081 * deactivate PortletX
082 * 
083 * @enduml
084 */
085public class AddPortletType extends PortalCommand {
086
087    private final String portletType;
088    private Map<Locale, String> displayNames = Collections.emptyMap();
089    private final List<URI> cssUris = new ArrayList<>();
090    private final List<ScriptResource> scriptResources = new ArrayList<>();
091    private List<RenderMode> renderModes;
092
093    /**
094     * Create a new event for the given portlet type.
095     * 
096     * @param portletType a unique id for the portlet type (usually
097     * the class name)
098     */
099    public AddPortletType(String portletType) {
100        this.portletType = portletType;
101    }
102
103    /**
104     * Return the portlet type.
105     * 
106     * @return the portlet type
107     */
108    public String portletType() {
109        return portletType;
110    }
111
112    /**
113     * Sets the display names.
114     * 
115     * @param displayNames the display names
116     * @return the event for easy chaining
117     */
118    public AddPortletType setDisplayNames(Map<Locale, String> displayNames) {
119        this.displayNames = displayNames;
120        return this;
121    }
122
123    /**
124     * Return the display names.
125     * 
126     * @return the displayNames
127     */
128    public Map<Locale, String> displayNames() {
129        return displayNames;
130    }
131
132    /**
133     * Add a render mode. The render mode determines how the portlet
134     * is initially rendered (i.e. when added). Several modes may be
135     * added in order of preference. The default mode (i.e. none 
136     * specified) is {@link RenderMode#Preview}.
137     *
138     * @param mode the mode
139     * @return the event for easy chaining
140     */
141    public AddPortletType addRenderMode(RenderMode mode) {
142        if (renderModes == null) {
143            renderModes = new ArrayList<>();
144        }
145        renderModes.add(mode);
146        return this;
147    }
148
149    /**
150     * Return the render modes.
151     * 
152     * @return the result
153     */
154    public List<RenderMode> renderModes() {
155        if (renderModes == null) {
156            return Arrays.asList(RenderMode.Preview);
157        }
158        return renderModes;
159    }
160
161    /**
162     * Add a script resource to be requested by the browser.
163     * 
164     * @param scriptResource the script resource
165     * @return the event for easy chaining
166     */
167    public AddPortletType addScript(ScriptResource scriptResource) {
168        scriptResources.add(scriptResource);
169        return this;
170    }
171
172    /**
173     * Add the URI of a CSS resource that is to be added to the
174     * header section of the portal page.
175     *
176     * @param renderSupport the render support for mapping the `uri`
177     * @param uri the URI
178     * @return the event for easy chaining
179     */
180    public AddPortletType addCss(RenderSupport renderSupport, URI uri) {
181        cssUris.add(renderSupport.portletResource(portletType(), uri));
182        return this;
183    }
184
185    /**
186     * Return all script resources.
187     * 
188     * @return the result
189     */
190    public ScriptResource[] scriptResources() {
191        return scriptResources.toArray(new ScriptResource[0]);
192    }
193
194    /**
195     * Return all CSS URIs.
196     * 
197     * @return the result
198     */
199    public URI[] cssUris() {
200        return cssUris.toArray(new URI[0]);
201    }
202
203    @Override
204    public void toJson(Writer writer) throws IOException {
205        JsonArray strArray = JsonArray.create();
206        for (ScriptResource scriptResource : scriptResources()) {
207            strArray.append(scriptResource.toJsonValue());
208        }
209        toJson(writer, "addPortletType", portletType(),
210            displayNames().entrySet().stream()
211                .collect(Collectors.toMap(e -> e.getKey().toLanguageTag(),
212                    e -> e.getValue())),
213            Arrays.stream(cssUris()).map(
214                uri -> uri.toString()).toArray(String[]::new),
215            strArray, renderModes().stream().map(RenderMode::name)
216                .toArray(size -> new String[size]));
217    }
218}