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