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