001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.core.ui;
017
018import java.io.IOException;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.avalon.framework.context.Context;
025import org.apache.avalon.framework.context.ContextException;
026import org.apache.avalon.framework.context.Contextualizable;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.Constants;
030import org.apache.cocoon.ProcessingException;
031import org.apache.cocoon.environment.ObjectModelHelper;
032import org.apache.cocoon.environment.Request;
033import org.apache.cocoon.generation.ServiceableGenerator;
034import org.apache.cocoon.xml.AttributesImpl;
035import org.apache.cocoon.xml.XMLUtils;
036import org.apache.commons.lang3.StringUtils;
037import org.apache.excalibur.source.Source;
038import org.apache.excalibur.source.SourceResolver;
039import org.xml.sax.SAXException;
040
041import org.ametys.core.ui.ClientSideElement;
042import org.ametys.core.ui.ClientSideElementDependenciesManager;
043import org.ametys.core.ui.MessageTargetFactoriesManager;
044import org.ametys.core.ui.RelationsManager;
045import org.ametys.core.ui.RibbonConfigurationManager;
046import org.ametys.core.ui.RibbonControlsManager;
047import org.ametys.core.ui.RibbonImportManager;
048import org.ametys.core.ui.RibbonManager;
049import org.ametys.core.ui.RibbonManagerCache;
050import org.ametys.core.ui.RibbonTabsManager;
051import org.ametys.core.ui.SAXClientSideElementHelper;
052import org.ametys.core.ui.StaticFileImportsManager;
053import org.ametys.core.ui.UIToolsConfigurationManager;
054import org.ametys.core.ui.UIToolsFactoriesManager;
055import org.ametys.core.ui.ribbonconfiguration.RibbonConfigurationSource;
056import org.ametys.core.ui.widgets.ClientSideWidget;
057import org.ametys.core.ui.widgets.WidgetsManager;
058import org.ametys.core.user.CurrentUserProvider;
059import org.ametys.core.user.UserIdentity;
060import org.ametys.core.util.JSONUtils;
061import org.ametys.plugins.core.user.UserHelper;
062import org.ametys.runtime.plugin.PluginsManager;
063import org.ametys.runtime.workspace.WorkspaceMatcher;
064
065/**
066 * Generates the uitools factories definition using the component associated 
067 */
068public class WorkspaceGenerator extends ServiceableGenerator implements Contextualizable
069{
070    /** The ribbon control manager */
071    protected RibbonControlsManager _ribbonControlManager;
072    /** The ribbon tab manager */
073    protected RibbonTabsManager _ribbonTabManager;
074    /** The list of existing message target factories */
075    protected MessageTargetFactoriesManager _messageTargetFactoriesManager;
076    /** The ui tools factories manager */
077    protected UIToolsFactoriesManager _uitoolsFactoriesManager;
078    /** The relations manager */
079    protected RelationsManager _relationsManager;
080    /** The widgets manager */
081    protected WidgetsManager _widgetsManager;
082    /** The sax clientside element helper */
083    protected SAXClientSideElementHelper _saxClientSideElementHelper;
084    /** The static files import manager */
085    protected StaticFileImportsManager _fileImportsManager;
086    /** The Excalibur source resolver */
087    protected SourceResolver _resolver;
088    /** Cocoon context */
089    protected org.apache.cocoon.environment.Context _cocoonContext;
090    /** The current user provider component */
091    protected CurrentUserProvider _currentUserProvider;
092    /** The json utils component */
093    protected JSONUtils _jsonUtils;
094    /** The User Helper */
095    protected UserHelper _userHelper;
096    /** The ribbon manager cache helper */
097    protected RibbonManagerCache _ribbonManagerCache;
098    /** The ribbon import manager */
099    protected RibbonImportManager _ribbonImportManager;
100    
101    @Override
102    public void service(ServiceManager smanager) throws ServiceException
103    {
104        super.service(smanager);
105        _messageTargetFactoriesManager = (MessageTargetFactoriesManager) smanager.lookup(MessageTargetFactoriesManager.ROLE);
106        _uitoolsFactoriesManager = (UIToolsFactoriesManager) smanager.lookup(UIToolsFactoriesManager.ROLE);
107        _ribbonTabManager = (RibbonTabsManager) smanager.lookup(RibbonTabsManager.ROLE);
108        _ribbonControlManager = (RibbonControlsManager) smanager.lookup(RibbonControlsManager.ROLE);
109        _relationsManager = (RelationsManager) smanager.lookup(RelationsManager.ROLE);
110        _widgetsManager = (WidgetsManager) smanager.lookup(WidgetsManager.ROLE);
111        _saxClientSideElementHelper = (SAXClientSideElementHelper) smanager.lookup(SAXClientSideElementHelper.ROLE);
112        _fileImportsManager = (StaticFileImportsManager) smanager.lookup(StaticFileImportsManager.ROLE);
113        _resolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
114        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
115        _jsonUtils = (JSONUtils) smanager.lookup(JSONUtils.ROLE);
116        _userHelper = (UserHelper) smanager.lookup(UserHelper.ROLE);
117        _ribbonManagerCache = (RibbonManagerCache) smanager.lookup(RibbonManagerCache.ROLE);
118        _ribbonImportManager = (RibbonImportManager) smanager.lookup(RibbonImportManager.ROLE);
119    }
120    
121    @Override
122    public void contextualize(Context context) throws ContextException
123    {
124        _cocoonContext = (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
125    }
126    
127    @Override
128    public void generate() throws IOException, SAXException, ProcessingException
129    {
130        doGenerate(getContextualParameters());
131    }
132    
133    /**
134     * Get the contextual parameters
135     * @return The contextual parameters
136     */
137    protected Map<String, Object> getContextualParameters()
138    {
139        Request request = ObjectModelHelper.getRequest(objectModel);
140        String workspaceName = (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_NAME);
141        
142        Map<String, Object> contextParameters = new HashMap<>();
143        contextParameters.put(WorkspaceMatcher.WORKSPACE_NAME, workspaceName);
144        
145        return contextParameters;
146    }
147    
148    /**
149     * Generates the UI factories definitions, with parameters
150     * @param contextParameters context parameters.
151     * @throws IOException if an error occurred
152     * @throws SAXException if an error occurred
153     * @throws ProcessingException if an error occurred
154     */
155    protected void doGenerate(Map<String, Object> contextParameters) throws IOException, SAXException, ProcessingException
156    {
157        long startTime = new Date().getTime();
158        
159        contentHandler.startDocument();
160        XMLUtils.startElement(contentHandler, "workspace");
161        
162        UserIdentity currentUser = _currentUserProvider.getUser();
163        if (currentUser != null)
164        {
165            String login = currentUser.getLogin();
166            String userPopulationId = currentUser.getPopulationId();
167            if (StringUtils.isNotBlank(login))
168            {
169                UserIdentity userIdentity = new UserIdentity(login, userPopulationId);
170                _userHelper.saxUserIdentity(userIdentity, contentHandler);
171            }
172        }
173        
174        ClientSideElementDependenciesManager dependenciesManager = new ClientSideElementDependenciesManager(this.manager);
175        
176        RibbonConfigurationSource ribbonConfig = getRibbonConfiguration();
177        Map<String, List<ClientSideElement>> elementsToSax;
178        RibbonManager ribbonManager = null;
179        try
180        {
181            ribbonManager = _ribbonManagerCache.getManager(ribbonConfig.getUri());
182            String workspaceName = (String) ObjectModelHelper.getRequest(objectModel).getAttribute("workspaceName");
183            RibbonConfigurationManager ribbonConfigurationManager = new RibbonConfigurationManager();
184            ribbonConfigurationManager.setup(_ribbonControlManager, _ribbonTabManager, _ribbonImportManager, _saxClientSideElementHelper, _resolver, _ribbonManagerCache);
185            ribbonConfigurationManager.configure(ribbonManager, dependenciesManager, ribbonConfig, workspaceName);
186            ribbonConfigurationManager.saxRibbonDefinition(contentHandler, contextParameters);
187            elementsToSax = getElementsToSax(dependenciesManager, ribbonConfigurationManager, contextParameters);
188        }
189        catch (Exception e)
190        {
191            throw new ProcessingException("Unable to get or create a ribbon manager for ribbon specific components", e);
192        }
193        finally
194        {
195            _ribbonManagerCache.dispose(ribbonManager);
196        }
197        
198        saxUITools(contextParameters, elementsToSax.get(UIToolsFactoriesManager.ROLE));
199        saxMessageTargetFactories(contextParameters, elementsToSax.get(MessageTargetFactoriesManager.ROLE));
200        saxRelationsHandlers(contextParameters, elementsToSax.get(RelationsManager.ROLE));
201        saxWidgets(contextParameters);
202        saxStaticFileImports (contextParameters, elementsToSax.get(StaticFileImportsManager.ROLE));
203        saxAdditionnalInfo(contextParameters);
204        
205        XMLUtils.endElement(contentHandler, "workspace");
206        contentHandler.endDocument();
207
208        
209        long endTime = new Date().getTime();
210        getLogger().debug("Workspace generated in " + (endTime - startTime) + " ms");
211    }
212
213    /**
214     * Retrieve the list of elements to generate the Workspace
215     * @param dependenciesManager The dependencies manager
216     * @param ribbonManager The ribbon manager for this workspace
217     * @param contextParameters Contextuals parameters transmitted by the environment.
218     * @return The list of elements, mapped by extension points.
219     * @throws SAXException If an error occurs
220     */
221    protected Map<String, List<ClientSideElement>> getElementsToSax(ClientSideElementDependenciesManager dependenciesManager, RibbonConfigurationManager ribbonManager, Map<String, Object> contextParameters) throws SAXException
222    {
223        List<ClientSideElement> ribbonControls = ribbonManager.getControls(contextParameters);
224        for (ClientSideElement control : ribbonControls)
225        {
226            dependenciesManager.register(control);
227        }
228        List<ClientSideElement> ribbonTabs = ribbonManager.getTabs();
229        for (ClientSideElement control : ribbonTabs)
230        {
231            dependenciesManager.register(control);
232        }
233        
234        for (String extensionId: _widgetsManager.getExtensionsIds())
235        {
236            ClientSideWidget element = _widgetsManager.getExtension(extensionId);
237            dependenciesManager.register(element);
238        }
239        
240        try
241        {
242            return dependenciesManager.computeDependencies();
243        }
244        catch (ServiceException e)
245        {
246            throw new SAXException("Unable to compute dependencies", e);
247        }
248    }
249
250    /**
251     * SAX the UI Tools
252     * @param contextParameters the context parameters
253     * @param elements The list of elements to sax
254     * @throws IOException if an error occurred
255     * @throws SAXException if an error occurred
256     */
257    protected void saxUITools(Map<String, Object> contextParameters, List<ClientSideElement> elements) throws IOException, SAXException
258    {
259        Source configSource = getUIToolsConfiguration();
260        UIToolsConfigurationManager uitoolsManager = new UIToolsConfigurationManager(_uitoolsFactoriesManager, _saxClientSideElementHelper, configSource, ObjectModelHelper.getRequest(objectModel));
261        uitoolsManager.saxDefaultState(contentHandler, contextParameters, elements);
262    }
263
264    /**
265     * Get the ribbon configuration
266     * @return the ribbon configuration
267     * @throws IOException if an errors occurs getting the ribbon configuration
268     */
269    protected RibbonConfigurationSource getRibbonConfiguration() throws IOException
270    {
271        String ribbonFileName = parameters.getParameter("ribbonFileName", "ribbon");
272        String mode = parameters.getParameter("mode", null);
273        return RibbonConfigurationSource.createFromUri("context://WEB-INF/param/" + ribbonFileName + (mode != null ? "-" + mode : "") + ".xml", _resolver);
274    }
275    
276    /**
277     * Get the UI tools configuration
278     * @return the UI tools configuration
279     * @throws IOException if an errors occurs getting the UI tools configuration
280     */
281    protected Source getUIToolsConfiguration() throws IOException
282    {
283        String toolsFileName = parameters.getParameter("toolsFileName", "uitools");
284        String mode = parameters.getParameter("mode", null);
285        return _resolver.resolveURI("context://WEB-INF/param/" + toolsFileName + (mode != null ? "-" + mode : "") + ".xml");
286    }
287    
288    /**
289     * SAX the message target factories
290     * @param contextParameters the context parameters
291     * @param elements The list of elements for the message target factories 
292     * @throws SAXException if an error occurred
293     */
294    protected void saxMessageTargetFactories(Map<String, Object> contextParameters, List<ClientSideElement> elements) throws SAXException
295    {
296        contentHandler.startPrefixMapping("i18n", "http://apache.org/cocoon/i18n/2.1");
297        XMLUtils.startElement(contentHandler, "messagetarget-factories");
298
299        if (elements != null)
300        {
301            for (ClientSideElement element : elements)
302            {
303                _saxClientSideElementHelper.saxDefinition("messagetarget-factory", element, MessageTargetFactoriesManager.ROLE, contentHandler, contextParameters);
304            }
305        }
306        
307        XMLUtils.endElement(contentHandler, "messagetarget-factories");
308        contentHandler.endPrefixMapping("i18n");
309    }
310    
311    /**
312     * SAX the relations handlers
313     * @param contextParameters the context parameters
314     * @param elements The list of relation handlers
315     * @throws SAXException if an error occurred
316     */
317    protected void saxRelationsHandlers(Map<String, Object> contextParameters, List<ClientSideElement> elements) throws SAXException
318    {
319        contentHandler.startPrefixMapping("i18n", "http://apache.org/cocoon/i18n/2.1");
320        XMLUtils.startElement(contentHandler, "relations-handlers");
321
322        if (elements != null)
323        {
324            for (ClientSideElement element: elements)
325            {
326                _saxClientSideElementHelper.saxDefinition("relation-handler", element, RelationsManager.ROLE, contentHandler, contextParameters);
327            }
328        }
329        
330        XMLUtils.endElement(contentHandler, "relations-handlers");
331        contentHandler.endPrefixMapping("i18n");
332    }
333    
334    /**
335     * SAX the widgets
336     * @param contextParameters the context parameters
337     * @throws SAXException if an error occurred
338     */
339    protected void saxWidgets(Map<String, Object> contextParameters) throws SAXException
340    {
341        contentHandler.startPrefixMapping("i18n", "http://apache.org/cocoon/i18n/2.1");
342        
343        Map<String, Map<String, Map<String, String>>> defaultWidgets = _widgetsManager.getDefaultWidgets();
344        AttributesImpl wattrs = new AttributesImpl();
345        wattrs.addCDATAAttribute("default-widgets", _jsonUtils.convertObjectToJson(defaultWidgets));
346        
347        XMLUtils.startElement(contentHandler, "widgets", wattrs);
348
349        for (String extensionId: _widgetsManager.getExtensionsIds())
350        {
351            ClientSideWidget element = _widgetsManager.getExtension(extensionId);
352            
353            AttributesImpl attrs = new AttributesImpl();
354            attrs.addCDATAAttribute("ftypes", StringUtils.join(element.getFormTypes(contextParameters), ","));
355            attrs.addCDATAAttribute("supports-enumerated", Boolean.toString(element.supportsEnumerated(contextParameters)));
356            attrs.addCDATAAttribute("supports-non-enumerated", Boolean.toString(element.supportsNonEnumerated(contextParameters)));
357            attrs.addCDATAAttribute("supports-multiple", Boolean.toString(element.supportsMultiple(contextParameters)));
358            attrs.addCDATAAttribute("supports-non-multiple", Boolean.toString(element.supportsNonMultiple(contextParameters)));
359            
360            XMLUtils.startElement(contentHandler, "widget-wrapper", attrs);
361            _saxClientSideElementHelper.saxDefinition("widget", element, WidgetsManager.ROLE, contentHandler, contextParameters);
362            XMLUtils.endElement(contentHandler, "widget-wrapper"); 
363        }
364        
365        XMLUtils.endElement(contentHandler, "widgets");
366        contentHandler.endPrefixMapping("i18n");
367    }
368    
369    /**
370     * SAX the static file imports
371     * @param contextParameters the context parameters
372     * @param elements The list of static file imports elements
373     * @throws SAXException if an error occurred
374     */
375    protected void saxStaticFileImports (Map<String, Object> contextParameters, List<ClientSideElement> elements) throws SAXException
376    {
377        XMLUtils.startElement(contentHandler, "static-imports");
378        if (elements != null)
379        {
380            for (ClientSideElement element : elements)
381            {
382                _saxClientSideElementHelper.saxDefinition("import", element, StaticFileImportsManager.ROLE, contentHandler, contextParameters);
383            }
384        }
385        XMLUtils.endElement(contentHandler, "static-imports");
386    }
387
388    /**
389     * Use this method when inheriting the WorkspaceGenerator to sax additional data 
390     * @param contextParameters the context parameters
391     * @throws SAXException if an error occurred
392     */
393    protected void saxAdditionnalInfo(Map<String, Object> contextParameters) throws SAXException
394    {
395        if (PluginsManager.getInstance().isSafeMode())
396        {
397            XMLUtils.createElement(contentHandler, "safe-mode", PluginsManager.getInstance().getStatus().toString());
398        }
399    }
400}