001/*
002 *  Copyright 2017 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.frontedition;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.LinkedHashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import javax.jcr.RepositoryException;
028
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.commons.lang3.ArrayUtils;
033import org.apache.commons.lang3.StringUtils;
034
035import org.ametys.cms.repository.Content;
036import org.ametys.cms.repository.WorkflowAwareContent;
037import org.ametys.cms.workflow.ContentWorkflowHelper;
038import org.ametys.core.right.RightManager;
039import org.ametys.core.right.RightManager.RightResult;
040import org.ametys.core.ui.ClientSideElement;
041import org.ametys.core.ui.ClientSideElement.Script;
042import org.ametys.core.ui.ClientSideElement.ScriptFile;
043import org.ametys.core.ui.widgets.richtext.RichTextConfiguration;
044import org.ametys.core.ui.widgets.richtext.RichTextConfigurationExtensionPoint;
045import org.ametys.core.util.JSONUtils;
046import org.ametys.plugins.core.ui.minimize.HashCache;
047import org.ametys.plugins.repository.AmetysObject;
048import org.ametys.plugins.repository.AmetysObjectResolver;
049import org.ametys.plugins.repository.AmetysRepositoryException;
050import org.ametys.plugins.repository.UnknownAmetysObjectException;
051import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject;
052import org.ametys.plugins.repository.jcr.JCRAmetysObject;
053import org.ametys.plugins.repository.version.VersionableAmetysObject;
054import org.ametys.plugins.workflow.support.WorkflowHelper;
055import org.ametys.web.WebConstants;
056import org.ametys.web.cache.PageHelper;
057import org.ametys.web.renderingcontext.RenderingContext;
058import org.ametys.web.repository.page.ModifiablePage;
059import org.ametys.web.repository.page.ModifiableZone;
060import org.ametys.web.repository.page.Page;
061import org.ametys.web.repository.page.ZoneItem;
062import org.ametys.web.repository.site.Site;
063import org.ametys.web.repository.site.SiteManager;
064import org.ametys.web.repository.sitemap.Sitemap;
065import org.ametys.web.transformation.xslt.AmetysXSLTHelper;
066
067/**
068 * Helper for preparing the Ametys Edition
069 */
070public class AmetysFrontEditionHelper implements Serviceable
071{
072    /** The right's id from front-edition access */
073    public static final String FRONT_EDITION_RIGHT_ID = "Front_Edition_Access_Right";
074    
075    private static AmetysObjectResolver _ametysObjectResolver;
076    private static HashCache _hashCache;
077    private static RichTextConfigurationExtensionPoint _richTextConfigurationExtensionPoint;
078    private static RightManager _rightManager;
079    private static ContentWorkflowHelper _contentWorkflowHelper;
080    private static PageHelper _pageHelper;
081    private static WorkflowHelper _workflowHelper;
082    private static JSONUtils _jsonUtils;
083    private static SiteManager _siteManager;
084
085    public void service(ServiceManager manager) throws ServiceException
086    {
087        _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
088        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
089        _hashCache = (HashCache) manager.lookup(HashCache.ROLE);
090        _richTextConfigurationExtensionPoint = (RichTextConfigurationExtensionPoint) manager.lookup(RichTextConfigurationExtensionPoint.ROLE);
091        _rightManager = (RightManager) manager.lookup(RightManager.ROLE);
092        _contentWorkflowHelper = (ContentWorkflowHelper) manager.lookup(ContentWorkflowHelper.ROLE);
093        _pageHelper = (PageHelper) manager.lookup(PageHelper.ROLE);
094        _workflowHelper = (WorkflowHelper) manager.lookup(WorkflowHelper.ROLE);
095        _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE);
096    }
097    
098    /**
099     * Prepare a hashcode for js files
100     * @param locale The language code to use
101     * @param rtlMode Is for right-to-left language?
102     * @param theme The url for the theme
103     * @return The new hashcode to read the minimified concatened file
104     */
105    public static String prepareJSFiles(String locale, boolean rtlMode, String theme)
106    {
107        String themeName = StringUtils.substringAfterLast(theme, "/");
108        
109        Map<String, String> filesInfos = new HashMap<>();
110        filesInfos.put("media", "");
111        filesInfos.put("tag", "script");
112        
113        Map<String, Map<String, String>> files = new LinkedHashMap<>();
114        files.put("/plugins/extjs6/resources/ext-all.js", filesInfos);
115        files.put("/plugins/extjs6/resources/classic/locale/locale-" + locale + ".js", filesInfos);
116        files.put(theme + "/" + themeName + ".js", filesInfos);
117
118        files.put("/plugins/core-ui/resources/js/Ext.fixes.js", filesInfos);
119        files.put("/plugins/core-ui/resources/js/Ext.enhancements.js", filesInfos);
120        
121        files.put("/plugins/core-ui/resources/js/Ametys.js", filesInfos);
122        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys.js", filesInfos);
123        files.put("/plugins/core-ui/resources/js/Ametys/mask/GlobalLoadMask.js", filesInfos);
124        
125        files.put("/plugins/core-ui/resources/js/Ametys/log/Logger.js", filesInfos);
126        files.put("/plugins/core-ui/resources/js/Ametys/log/Logger/Entry.js", filesInfos);
127        files.put("/plugins/core-ui/resources/js/Ametys/log/LoggerFactory.js", filesInfos);
128        files.put("/plugins/core-ui/resources/js/Ametys/log/ErrorDialog.js", filesInfos);
129        
130        files.put("/plugins/core-ui/resources/js/Ametys/window/DialogBox.js", filesInfos);
131        files.put("/plugins/core-ui/resources/js/Ametys/window/MessageBox.js", filesInfos);
132        
133        files.put("/plugins/core-ui/resources/js/Ametys/form/AbstractField.js", filesInfos);
134        files.put("/plugins/core-ui/resources/js/Ametys/form/AbstractFieldsWrapper.js", filesInfos);
135        files.put("/plugins/core-ui/resources/js/Ametys/form/AbstractQueryableComboBox.js", filesInfos);
136        files.put("/plugins/core-ui/resources/js/Ametys/form/field/DateTime.js", filesInfos);
137        files.put("/plugins/core-ui/resources/js/Ametys/form/field/StringTime.js", filesInfos);
138        files.put("/plugins/core-ui/resources/js/Ametys/form/field/Password.js", filesInfos);
139        files.put("/plugins/core-ui/resources/js/Ametys/form/field/ChangePassword.js", filesInfos);
140        files.put("/plugins/core-ui/resources/js/Ametys/form/field/ReferencedNumberField.js", filesInfos);
141        files.put("/plugins/core-ui/resources/js/Ametys/form/field/RichText.js", filesInfos);
142        files.put("/plugins/core-ui/resources/js/Ametys/form/field/RichText/SplitterTracker.js", filesInfos);
143        files.put("/plugins/core-ui/resources/js/Ametys/form/field/TextArea.js", filesInfos);
144        files.put("/plugins/core-ui/resources/js/Ametys/form/field/ColorSelector.js", filesInfos);
145        files.put("/plugins/core-ui/resources/js/Ametys/form/field/SelectUserDirectory.js", filesInfos);
146        files.put("/plugins/core-ui/resources/js/Ametys/form/field/SelectGroupDirectories.js", filesInfos);
147        files.put("/plugins/core-ui/resources/js/Ametys/form/field/Code.js", filesInfos);
148        files.put("/plugins/tiny_mce/resources/js/tinymce.js", filesInfos);
149        files.put("/plugins/codemirror/resources/js/codemirror.js", filesInfos);
150        files.put("/plugins/codemirror/resources/js/addon/edit/matchbrackets.js", filesInfos);
151        files.put("/plugins/codemirror/resources/js/addon/selection/active-line.js", filesInfos);
152        files.put("/plugins/codemirror/resources/js/mode/xml/xml.js", filesInfos);
153        files.put("/plugins/codemirror/resources/js/mode/javascript/javascript.js", filesInfos);
154        files.put("/plugins/codemirror/resources/js/mode/css/css.js", filesInfos);
155        files.put("/plugins/codemirror/resources/js/mode/htmlmixed/htmlmixed.js", filesInfos);
156        
157        files.put("/plugins/core-ui/resources/js/Ametys/data/ServerCaller.js", filesInfos);
158        files.put("/plugins/core-ui/resources/js/Ametys/data/ServerComm.js", filesInfos);
159        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/data/ServerComm.js", filesInfos);
160        files.put("/plugins/core-ui/resources/js/Ametys/data/ServerCommProxy.js", filesInfos);
161        files.put("/plugins/core-ui/resources/js/Ametys/data/ServerComm/TimeoutDialog.js", filesInfos);
162
163        files.put("/plugins/core-ui/resources/js/Ametys/form/widget/RichText/RichTextConfiguration.js", filesInfos);
164        
165        files.put("/plugins/core-ui/resources/js/Ametys/plugins/coreui/system/devmode/StacktraceHelper.js", filesInfos);
166
167        files.put("/plugins/front-edition/resources/js/front-comm.js", filesInfos);
168        files.put("/plugins/front-edition/resources/js/front-widget.js", filesInfos);
169//        files.put("/plugins/front-edition/resources/js/front-page.js", filesInfos);
170        files.put("/plugins/core-ui/resources/js/Ametys/helper/EnterURL.js", filesInfos);
171        files.put("/plugins/core-ui/resources/js/Ametys/helper/FileUpload.js", filesInfos);
172        files.put("/plugins/web/resources/js/Ametys/web/helper/ChoosePage.js", filesInfos);
173        files.put("/plugins/web/resources/js/Ametys/web/helper/ContextToolbar.js", filesInfos);
174        files.put("/plugins/web/resources/js/Ametys/web/sitemap/SitemapTree.js", filesInfos);
175        files.put("/plugins/web/resources/js/Ametys/web/sitemap/SitemapTree/Page.js", filesInfos);
176        files.put("/plugins/web/resources/js/Ametys/web/sitemap/SitemapTree/Sitemap.js", filesInfos);
177        files.put("/plugins/web/resources/js/Ametys/web/site/SitesTree/Site.js", filesInfos);
178        files.put("/plugins/cms/resources/js/Ametys/cms/editor/LinkHandler.js", filesInfos);
179        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/editor/Links.js", filesInfos);
180        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/editor/Links/LinkHandler.js", filesInfos);
181        files.put("/plugins/web/resources/js/Ametys/plugins/web/editor/Links.js", filesInfos);
182        files.put("/plugins/web/resources/js/Ametys/plugins/web/editor/PageLinkHandler.js", filesInfos);
183        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/editor/Images.js", filesInfos);
184        
185        files.put("/plugins/front-edition/resources/js/tinymce/lists.js", filesInfos);
186        files.put("/plugins/front-edition/resources/js/tinymce/links.js", filesInfos);
187        files.put("/plugins/front-edition/resources/js/tinymce/styles.js", filesInfos);
188        files.put("/plugins/front-edition/resources/js/tinymce/images.js", filesInfos);
189        files.put("/plugins/front-edition/resources/js/tinymce/save.js", filesInfos);
190        files.put("/plugins/front-edition/resources/js/tinymce/stickyToolbar.js", filesInfos);
191
192        //TinyMCE Tables
193        files.put("/plugins/front-edition/resources/js/tinymce/tables.js", filesInfos);
194        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/editor/Tables.js", filesInfos);
195        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/editor/BasicActions.js", filesInfos);
196        //TinyMCE Images
197        files.put("/plugins/cms/resources/js/Ametys/cms/uihelper/ChooseResource.js", filesInfos);
198        files.put("/plugins/explorer/resources/js/Ametys/explorer/tree/ExplorerTree.js", filesInfos);
199        files.put("/plugins/explorer/resources/js/Ametys/explorer/tree/ExplorerTree/NodeEntry.js", filesInfos);
200        files.put("/plugins/explorer/resources/js/Ametys/explorer/Resource.js", filesInfos);
201        files.put("/plugins/explorer/resources/js/Ametys/explorer/ExplorerNodeDAO.js", filesInfos);
202        files.put("/plugins/explorer/resources/js/Ametys/explorer/applications/ExplorerApplicationProvider.js", filesInfos);
203        files.put("/plugins/cms/resources/js/Ametys/cms/attach/AttachmentsExplorerTree.js", filesInfos);
204        files.put("/plugins/cms/resources/js/Ametys/cms/uihelper/ChooseAttachmentFile.js", filesInfos);
205        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/cms/uihelper/ChooseAttachmentFile.js", filesInfos);
206        files.put("/plugins/explorer/resources/js/Ametys/explorer/resources/actions/FileActions.js", filesInfos);
207        files.put("/plugins/explorer/resources/js/Ametys/explorer/resources/helper/ResourceUpload.js", filesInfos);
208        files.put("/plugins/core-ui/resources/js/Ametys/file/AbstractFileExplorerTree/FileNode.js", filesInfos);
209        files.put("/plugins/core-ui/resources/js/Ametys/file/AbstractFileExplorerTree.js", filesInfos);
210
211        //Empty Message class
212        files.put("/plugins/core-ui/resources/js/Ametys/message/Message.js", filesInfos);
213        files.put("/plugins/core-ui/resources/js/Ametys/message/MessageTarget.js", filesInfos);
214        files.put("/plugins/core-ui/resources/js/Ametys/message/MessageTargetFactory.js", filesInfos);
215        files.put("/plugins/core-ui/resources/js/Ametys/message/factory/DefaultMessageTargetFactory.js", filesInfos);
216        files.put("/plugins/front-edition/resources/js/Ametys/message/MessageTargetHelper.js", filesInfos);
217        //add page
218        files.put("/plugins/web/resources/js/Ametys/plugins/web/page/AddPageWizard.js", filesInfos);
219        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/plugins/web/page/AddPageWizard.js", filesInfos);
220        files.put("/plugins/web/resources/js/Ametys/web/sitemap/SitemapDAO.js", filesInfos);
221        files.put("/plugins/web/resources/js/Ametys/web/sitemap/Sitemap.js", filesInfos);
222        files.put("/plugins/web/resources/js/Ametys/web/page/PageDAO.js", filesInfos);
223        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/web/page/PageDAO.js", filesInfos);
224        files.put("/plugins/web/resources/js/Ametys/plugins/web/page/AddPageWizard/Card.js", filesInfos);
225        files.put("/plugins/web/resources/js/Ametys/plugins/web/page/AddPageWizard/CreatePageCard.js", filesInfos);
226        files.put("/plugins/web/resources/js/Ametys/plugins/web/page/AddPageWizard/PageContentCard.js", filesInfos);
227        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/plugins/web/page/AddPageWizard/PageContentCard.js", filesInfos);
228        files.put("/plugins/cms/resources/js/Ametys/cms/content/ContentDAO.js", filesInfos);
229        files.put("/plugins/web/resources/js/Ametys/web/content/ContentDAO.js", filesInfos);
230        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/cms/content/ContentDAO.js", filesInfos);
231        files.put("/plugins/cms/resources/js/Ametys/cms/content/Content.js", filesInfos);
232        files.put("/plugins/core-ui/resources/js/Ametys/navhistory/HistoryDAO.js", filesInfos);
233        files.put("/plugins/core-ui/resources/js/Ametys/window/MessageBox.js", filesInfos);
234        files.put("/plugins/front-edition/resources/js/Ametys/tool/ToolsManager.js", filesInfos);
235
236        files.put("/plugins/web/resources/js/Ametys/plugins/web/page/AddPageWizard/PageTypeCard.js", filesInfos);
237        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/plugins/web/page/AddPageWizard/PageTypeCard.js", filesInfos);
238        files.put("/plugins/web/resources/js/Ametys/web/form/widget/SelectPage.js", filesInfos);
239        files.put("/plugins/front-edition/resources/js/Ametys/override/Ametys/web/form/widget/SelectPage.js", filesInfos);
240        files.put("/plugins/web/resources/js/Ametys/web/form/widget/SelectSite.js", filesInfos);
241        files.put("/plugins/web/resources/js/Ametys/web/helper/ChooseSite.js", filesInfos);
242        files.put("/plugins/web/resources/js/Ametys/web/site/SitesTree.js", filesInfos);
243        files.put("/plugins/web/resources/js/Ametys/web/site/SitesTree/Site.js", filesInfos);
244        files.put("/plugins/web/resources/js/Ametys/web/helper/ContextToolbar.js", filesInfos);
245        files.put("/plugins/web/resources/js/Ametys/web/site/SitesTree/Site.js", filesInfos);
246        files.put("/plugins/web/resources/js/Ametys/web/form/widget/SelectPage/PageEntry.js", filesInfos);
247        files.put("/plugins/web/resources/js/Ametys/plugins/web/page/AddPageWizard/TagsCard.js", filesInfos);
248        files.put("/plugins/web/resources/js/Ametys/plugins/web/page/AddPageWizard/TagsCardLite.js", filesInfos);
249        files.put("/plugins/web/resources/js/Ametys/web/page/Page.js", filesInfos);
250        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/tag/TagsTreePanel.js", filesInfos);
251        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/tag/TagsTreePanel/TagNode.js", filesInfos);
252
253        //delete page
254        files.put("/plugins/web/resources/js/Ametys/plugins/web/sitemap/SitemapActions.js", filesInfos);
255
256        //page tags
257        files.put("/plugins/web/resources/js/Ametys/plugins/web/tag/AffectTagAction.js", filesInfos);
258        files.put("/plugins/cms/resources/js/Ametys/cms/uihelper/ChooseTagHelper.js", filesInfos);
259        files.put("/plugins/cms/resources/js/Ametys/cms/uihelper/ChooseTag.js", filesInfos);
260        files.put("/plugins/web/resources/js/Ametys/web/helper/ChooseTag.js", filesInfos);
261        files.put("/plugins/cms/resources/js/Ametys/plugins/cms/tag/TagActions.js", filesInfos);
262        files.put("/plugins/web/resources/js/Ametys/plugins/web/tag/TagsTreePanel.js", filesInfos);
263
264        // ping
265        files.put("/plugins/core-ui/resources/js/Ametys/plugins/coreui/system/StartTimeChecker.js", filesInfos);
266
267        // actions on content
268        files.put("/plugins/web/resources/js/Ametys/plugins/web/zone/ZoneActions.js", filesInfos);
269        files.put("/plugins/web/resources/js/Ametys/plugins/web/zone/ZoneDAO.js", filesInfos);
270
271        // file widget (+cropping for image filter)
272        files.put("/plugins/core-ui/resources/js/Ametys/form/widget/File.js", filesInfos);
273        files.put("/plugins/core-ui/resources/js/Ametys/form/widget/File/FileSource.js", filesInfos);
274        files.put("/plugins/core-ui/resources/js/Ametys/form/widget/File/External.js", filesInfos);
275        files.put("/plugins/cms/resources/js/Ametys/cms/form/widget/File/Resource.js", filesInfos);
276        files.put("/plugins/core-ui/resources/js/Ametys/helper/crop/CropDialog.js", filesInfos);
277        files.put("/plugins/core-ui/resources/js/Jcrop/jquery.Jcrop.js", filesInfos);
278
279        // Let's add all richtextconfiguration files
280        _addRichTextConfigurationFiles(files, filesInfos, locale, rtlMode, true);
281
282        return _hashCache.createHash(files, locale);
283    }
284
285    /**
286     * Prepare a hashcode for css files
287     * @param locale The language code to use
288     * @param rtlMode Is for right-to-left language?
289     * @param theme The url for the theme
290     * @return The new hashcode to read the minimified concatened file
291     */
292    public static String prepareCSSFiles(String locale, boolean rtlMode, String theme)
293    {
294        Map<String, String> filesInfos = new HashMap<>();
295        filesInfos.put("media", "");
296        filesInfos.put("tag", "link");
297        
298        Map<String, Map<String, String>> files = new LinkedHashMap<>();
299        String themeName = StringUtils.substringAfterLast(theme, "/");
300        files.put(theme + "/resources/" + themeName + "-all.css", filesInfos); 
301        files.put("/plugins/front-edition/resources/css/theme-ametys-base-light.css", filesInfos);
302        files.put("/plugins/core-ui/resources/font/ametys/AmetysIcon.css", filesInfos);
303        files.put("/plugins/core-ui/resources/css/Jcrop/jquery.Jcrop.css", filesInfos);
304        
305        // Let's add all richtextconfiguration files
306        _addRichTextConfigurationFiles(files, filesInfos, locale, rtlMode, false);
307        
308        return _hashCache.createHash(files, locale);
309    }
310
311    private static List<ScriptFile> _getRichTextConfigurationFiles(boolean scripts, Map<String, Object> contextParameters)
312    {
313        List<ScriptFile> files = new ArrayList<>();
314        
315        for (String richTextConfigurationId : _richTextConfigurationExtensionPoint.getExtensionsIds())
316        {
317            RichTextConfiguration richTextConfiguration = _richTextConfigurationExtensionPoint.getExtension(richTextConfigurationId);
318            for (String category : richTextConfiguration.getCategories())
319            {
320                Set<ClientSideElement> convertors = richTextConfiguration.getConvertors(category, contextParameters);
321                if (convertors != null)
322                {
323                    for (ClientSideElement convertor : convertors)
324                    {
325                        List<Script> scriptsToAdd = convertor.getScripts(Collections.EMPTY_MAP);
326                        if (scriptsToAdd != null)
327                        {
328                            for (Script script : scriptsToAdd)
329                            {
330                                List<ScriptFile> scriptFiles = scripts ? script.getScriptFiles() : script.getCSSFiles();
331                                if (scriptFiles != null)
332                                {
333                                    files.addAll(scriptFiles);
334                                }
335                            }
336                        }
337                    }
338                }
339
340                Set<ClientSideElement> validators = richTextConfiguration.getValidators(category, contextParameters);
341                if (validators != null)
342                {
343                    for (ClientSideElement validator : validators)
344                    {
345                        List<Script> scriptsToAdd = validator.getScripts(Collections.EMPTY_MAP);
346                        if (scriptsToAdd != null)
347                        {
348                            for (Script script : scriptsToAdd)
349                            {
350                                List<ScriptFile> scriptFiles = scripts ? script.getScriptFiles() : script.getCSSFiles();
351                                if (scriptFiles != null)
352                                {
353                                    files.addAll(scriptFiles);
354                                }
355                            }
356                        }
357                    }
358                }
359            }
360        }
361
362        return files;
363    }
364
365    private static void _addRichTextConfigurationFiles(Map<String, Map<String, String>> files, Map<String, String> filesInfos, String locale, boolean rtlMode, boolean scripts)
366    {
367        Map<String, Object> contextParameters = new HashMap<>();
368        contextParameters.put("siteName", AmetysXSLTHelper.site());
369        
370        List<ScriptFile> richTextConfigurationFiles = _getRichTextConfigurationFiles(scripts, contextParameters);
371        for (ScriptFile file : richTextConfigurationFiles)
372        {
373            String uri = null;
374            if (file.isLangSpecific())
375            {
376                if (file.getLangPaths().containsKey(locale))
377                {
378                    uri = file.getLangPaths().get(locale);
379                }
380                else if (file.getLangPaths().containsKey(file.getDefaultLang()))
381                {
382                    uri = file.getLangPaths().get(file.getDefaultLang());
383                }
384            }
385            else
386            {
387                if (file.getRtlMode() == null || "all".equals(file.getRtlMode()) || Boolean.toString(rtlMode).equals(file.getRtlMode()))
388                {
389                    uri = file.getPath();
390                }
391            }
392            
393            if (uri != null)
394            {
395                files.put(uri, filesInfos);
396            }
397        }
398    }
399    /**
400     * Check if we can display the front edition button for a specific right
401     * Checks :
402     * * Rendering context == front
403     * * Edition mode
404     * * Front_Edition_Access_Right
405     * * RightId available
406     * @param rightId right to check (can be null)
407     * @param inputPageId page to check
408     * @return true if all is OK, false if at least one is false
409     * @deprecated Use the version with three arguments
410     */
411    @Deprecated
412    public static boolean hasFrontEditionRight(String rightId, String inputPageId)
413    {
414        return hasFrontEditionRight(rightId, inputPageId, true);
415    }
416    /**
417     * Check if we can display the front edition button for a specific right
418     * Checks :
419     * * Rendering context == front
420     * * Edition mode
421     * * Front_Edition_Access_Right
422     * * RightId available
423     * @param rightId right to check (can be null)
424     * @param objectId page/content to check
425     * @param editionModeOnly Check if the user is in edition mode
426     * @return true if all is OK, false if at least one is false
427     */
428    public static boolean hasFrontEditionRight(String rightId, String objectId, boolean editionModeOnly)
429    {
430        String pageId = objectId;
431        if (StringUtils.isEmpty(pageId))
432        {
433            pageId = AmetysXSLTHelper.pageId();
434        }
435        
436        if (StringUtils.isEmpty(pageId)                                 // No page to check
437            || !RenderingContext.FRONT.toString().equals(AmetysXSLTHelper.renderingContext())     // We are no on front
438            || (editionModeOnly && !AmetysXSLTHelper.isEditionMode())   // Not in edition mode
439            || !hasFrontEditionRight())                                 // The user has the front edition right
440        {
441            
442            return false;
443        }
444        
445        if (StringUtils.contains(pageId, ";"))
446        {
447            String[] pageIds = StringUtils.split(pageId, ";");
448            for (String pid : pageIds)
449            {
450                if (_rightManager.currentUserHasRight(rightId, _ametysObjectResolver.resolveById(pid)) == RightResult.RIGHT_ALLOW)   // Check right on page (If this page does not exists an exception will be thrown... this is fine)
451                {
452                    return true;
453                }
454                // else check the next one
455            }
456            return false; // No right inside the list
457        }
458        else
459        {
460            return StringUtils.isEmpty(rightId) // There is no right to check OR the user has the right
461                || _rightManager.currentUserHasRight(rightId, _ametysObjectResolver.resolveById(pageId)) == RightResult.RIGHT_ALLOW;   // Check right on page (If this page does not exists an exception will be thrown... this is fine)
462        }
463    }
464    /**
465     * Check if the current user has the Front_Edition_Access_Right right
466     * @return true if right granted
467     */
468    public static boolean hasFrontEditionRight()
469    {
470        String pageId = AmetysXSLTHelper.pageId();
471        if (StringUtils.isNotEmpty(pageId))
472        {
473            Page page = _ametysObjectResolver.resolveById(pageId);
474            return _rightManager.currentUserHasRight(FRONT_EDITION_RIGHT_ID, page) == RightResult.RIGHT_ALLOW;
475        }
476        
477        String lang = AmetysXSLTHelper.lang();
478        String siteName = AmetysXSLTHelper.site();
479        
480        if (StringUtils.isNotEmpty(siteName) && StringUtils.isNoneEmpty(lang))
481        {
482            Site site = _siteManager.getSite(siteName);
483            Sitemap sitemap = site.getSitemap(lang);
484            return _rightManager.currentUserHasRight(FRONT_EDITION_RIGHT_ID, sitemap) == RightResult.RIGHT_ALLOW;
485        }
486        
487        return false;
488    }
489    /**
490     * Check if the current workflow action is available on this content
491     * @param actionId action to check
492     * @param contentId content to check
493     * @param checkEditionMode check also if we are in edition mode
494     * @return true if right is granted
495     */
496    public static boolean hasWorkflowRight(int actionId, String contentId, boolean checkEditionMode)
497    {
498        List<Integer> actionIds = new ArrayList<>();
499        actionIds.add(actionId);
500        return hasWorkflowRight(actionIds, contentId, checkEditionMode);
501    }
502    /**
503     * Check if a list of workflow actions are available on this content
504     * @param actionIds list of action ids
505     * @param contentId id of the content to check
506     * @param checkEditionMode check also if we are in edition mode
507     * @return true if all actions are available
508     */
509    public static boolean hasWorkflowRight(List<Integer> actionIds, String contentId, boolean checkEditionMode)
510    {
511        Content content = _ametysObjectResolver.resolveById(contentId);
512        return hasWorkflowRight(actionIds, content, checkEditionMode);
513    }
514    /**
515     * Check if a list of workflow actions are available on this content
516     * @param actionIds list of action ids
517     * @param content content to check
518     * @param checkEditionMode check also if we are in edition mode
519     * @return true if all actions are available
520     */
521    public static boolean hasWorkflowRight(List<Integer> actionIds, Content content, boolean checkEditionMode)
522    {
523        if (checkEditionMode)
524        {
525            boolean editionModeOk = AmetysXSLTHelper.isEditionMode();
526            if (!editionModeOk)
527            {
528                return false;
529            }
530        }
531        boolean result = false;
532        if (content instanceof WorkflowAwareContent)
533        {
534            WorkflowAwareContent wcontent = (WorkflowAwareContent) content;
535            int[] availableActions = _contentWorkflowHelper.getAvailableActions(wcontent);
536            for (int actionId : actionIds)
537            {
538                result = result || ArrayUtils.contains(availableActions, actionId);
539                if (result)
540                {
541                    break;
542                }
543            }
544        }
545        return result;
546    }
547    /**
548     * Get a string representing a json map of the contentIds with true/false if they can/can't be modified 
549     * @param actionId action to check
550     * @param inputPageId page (can be null)
551     * @param checkEditionMode check also if we are in edition mode
552     * @return {contentId : true/false, ....}
553     */
554    public static String getContentModifiables(int actionId, String inputPageId, boolean checkEditionMode)
555    {
556        Map<String, Boolean> jsonObject = new HashMap<>();
557        List<Integer> actionIds = new ArrayList<>();
558        actionIds.add(actionId);
559        String pageId = inputPageId;
560        if (StringUtils.isEmpty(inputPageId))
561        {
562            pageId = AmetysXSLTHelper.pageId();
563        }
564        if (!StringUtils.isEmpty(pageId))
565        {
566            AmetysObject resolveById = _ametysObjectResolver.resolveById(pageId);
567            if (resolveById instanceof Page)
568            {
569                Page page = (Page) resolveById;
570                List<Content> contents = _pageHelper.getAllContents(page);
571                for (Content content : contents)
572                {
573                    jsonObject.put(content.getId(), hasWorkflowRight(actionIds, content, checkEditionMode));
574                }
575            }
576        }
577
578        return _jsonUtils.convertObjectToJson(jsonObject);
579    }
580    /**
581     * Get the name of a workflow action
582     * @param workflowName workflow name
583     * @param actionId action id in the workflow
584     * @return name of the workflow action
585     */
586    public static String getWorkflowName(String workflowName, int actionId)
587    {
588        String workflowNameKey = _workflowHelper.getActionName(workflowName, actionId);
589        String translatedName = org.ametys.core.util.AmetysXSLTHelper.translate(workflowNameKey);
590        return translatedName;
591    }
592    /**
593     * Get the status (validated/draft) of all contents in a page
594     * @param pageId id of the page to check
595     * @return "" if not a page, "draft" if all contents are drafts, "validated" if all contents are validated, and "mixed" if there are both
596     */
597    public String getPageStatus(String pageId)
598    {
599        boolean hasDraft = false;
600        boolean hasValidated = false;
601        AmetysObject resolveById = _ametysObjectResolver.resolveById(pageId);
602        if (resolveById instanceof Page)
603        {
604            Page page = (Page) resolveById;
605            List<Content> contents = _pageHelper.getAllContents(page);
606            for (Content content : contents)
607            {
608                if (isContentLive(content))
609                {
610                    hasValidated = true;
611                }
612                else
613                {
614                    hasDraft = true;
615                }
616            }
617            String result = "none";
618            if (hasDraft && !hasValidated)
619            {
620                result = "draft";
621            }
622            else if (!hasDraft && hasValidated)
623            {
624                result = "validated";
625            }
626            else if (hasDraft && hasValidated)
627            {
628                result = "mixed";
629            }
630            return result;
631        }
632        else
633        {
634            return "";
635        }
636    }
637
638    /**
639     * Get the workflow step id of a content
640     * @param contentId id of the content
641     * @return the step Id
642     */
643    public long getContentWorkflowId(String contentId)
644    {
645        AmetysObject resolvedById = _ametysObjectResolver.resolveById(contentId);
646        if (resolvedById instanceof Content)
647        {
648            Content content = (Content) resolvedById;
649            return getContentWorkflowId(content);
650        }
651        else
652        {
653            return 0;
654        }
655    }
656    /**
657     * Get the workflow step id of a content
658     * @param content content
659     * @return the step Id
660     */
661    public long getContentWorkflowId(Content content)
662    {
663        WorkflowAwareContent wContent = (WorkflowAwareContent) content;
664        return wContent.getCurrentStepId();
665    }
666
667    /**
668     * check if a content is published ot not
669     * @param contentId id of the content
670     * @return "validated" or "draft", "" if not found
671     */
672    public String getContentStatus(String contentId)
673    {
674        AmetysObject resolvedById = _ametysObjectResolver.resolveById(contentId);
675        if (resolvedById instanceof Content)
676        {
677            Content content = (Content) resolvedById;
678            return getContentStatus(content);
679        }
680        else
681        {
682            return "";
683        }
684    }
685    /**
686     * check if a content is published ot not
687     * @param content content
688     * @return "validated" or "draft"
689     */
690    public String getContentStatus(Content content)
691    {
692        return isContentLive(content) ? "validated" : "draft";
693    }
694    /**
695     * check if a content is published ot not
696     * @param contentId id of the content
697     * @return true if validated
698     */
699    public boolean isContentLive(String contentId)
700    {
701        AmetysObject resolvedById = _ametysObjectResolver.resolveById(contentId);
702        if (resolvedById instanceof Content)
703        {
704            Content content = (Content) resolvedById;
705            return isContentLive(content);
706        }
707        else
708        {
709            return false;
710        }
711    }
712    /**
713     * check if a content is published ot not
714     * @param content content
715     * @return true if validated
716     */
717    public boolean isContentLive(Content content)
718    {
719        return Arrays.asList(((VersionableAmetysObject) content).getLabels()).contains(WebConstants.LIVE_LABEL);
720    }
721    /**
722     * Get the position of this zoneItem in the zone, or -1 if not found
723     * @param zoneItemId zoneitem to search
724     * @param pageId page id
725     * @return zero based position, -1 if not found
726     * @throws UnknownAmetysObjectException If an error occurred
727     * @throws AmetysRepositoryException If an error occurred
728     * @throws RepositoryException If an error occurred
729     */
730    public long getZoneItemPosition(String zoneItemId, String pageId) throws UnknownAmetysObjectException, AmetysRepositoryException, RepositoryException
731    {
732        ModifiablePage page = _ametysObjectResolver.resolveById(pageId);
733        ZoneItem zoneItem = page instanceof JCRAmetysObject ? (ZoneItem) _ametysObjectResolver.resolveById(zoneItemId, ((JCRAmetysObject) page).getNode().getSession()) : (ZoneItem) _ametysObjectResolver.resolveById(zoneItemId);
734        AmetysObject parent = zoneItem.getParent();
735        if (parent instanceof DefaultTraversableAmetysObject)
736        {
737            return ((DefaultTraversableAmetysObject) parent).getChildPosition(zoneItem);
738        }
739        return -1;
740    }
741    /**
742     * Return the size of a zone
743     * @param zoneName zone name
744     * @param pageId page Id
745     * @return size of the zone (-1 if not found)
746     */
747    public long getZoneSize(String zoneName, String pageId)
748    {
749        ModifiablePage page = _ametysObjectResolver.resolveById(pageId);
750        ModifiableZone zone = page.getZone(zoneName);
751        if (zone != null)
752        {
753            return zone.getZoneItems().getSize();
754        }
755        return -1;
756    }
757}