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}