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.workspaces.editionfo; 017 018import java.util.HashMap; 019import java.util.Map; 020import java.util.Set; 021 022import org.apache.avalon.framework.service.ServiceException; 023import org.apache.avalon.framework.service.ServiceManager; 024import org.apache.cocoon.components.ContextHelper; 025import org.apache.cocoon.environment.Request; 026import org.apache.commons.lang.IllegalClassException; 027import org.apache.commons.lang.StringUtils; 028 029import org.ametys.cms.FilterNameHelper; 030import org.ametys.cms.repository.Content; 031import org.ametys.cms.repository.ModifiableWorkflowAwareContent; 032import org.ametys.cms.transformation.xslt.ResolveURIComponent; 033import org.ametys.core.observation.Event; 034import org.ametys.core.right.RightManager.RightResult; 035import org.ametys.core.ui.Callable; 036import org.ametys.plugins.explorer.ExplorerNode; 037import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 038import org.ametys.plugins.explorer.resources.jcr.JCRResourcesCollectionFactory; 039import org.ametys.plugins.repository.AmetysRepositoryException; 040import org.ametys.plugins.workflow.AbstractWorkflowComponent; 041import org.ametys.plugins.workflow.support.WorkflowProvider; 042import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 043import org.ametys.plugins.workspaces.AbstractWorkspaceModule; 044import org.ametys.plugins.workspaces.WorkspacesConstants; 045import org.ametys.plugins.workspaces.project.ProjectConstants; 046import org.ametys.plugins.workspaces.project.objects.Project; 047import org.ametys.runtime.i18n.I18nizableText; 048import org.ametys.web.ObservationConstants; 049import org.ametys.web.explorer.ExplorerResourcesDAO; 050import org.ametys.web.repository.page.ModifiablePage; 051import org.ametys.web.repository.page.ModifiableZone; 052import org.ametys.web.repository.page.ModifiableZoneItem; 053import org.ametys.web.repository.page.Page; 054import org.ametys.web.repository.page.Page.PageType; 055import org.ametys.web.repository.page.ZoneItem.ZoneType; 056import org.ametys.web.repository.site.Site; 057import org.ametys.web.repository.site.SiteManager; 058import org.ametys.web.repository.sitemap.Sitemap; 059import org.ametys.web.skin.Skin; 060import org.ametys.web.skin.SkinTemplate; 061import org.ametys.web.skin.SkinTemplateZone; 062import org.ametys.web.workflow.CreateContentFunction; 063 064import com.google.common.collect.ImmutableSet; 065import com.opensymphony.workflow.WorkflowException; 066 067/** 068 * Manager for the Edition FO 069 */ 070public class EditionFOWorkspaceModule extends AbstractWorkspaceModule 071{ 072 /** Avalon ROLE */ 073 public static final String EDITIONFO_MODULE_ID = EditionFOWorkspaceModule.class.getName(); 074 075 /** Workspaces tasks list node name */ 076 private static final String __WORKSPACES_EDITIONFO_NODE_NAME = "wiki"; 077 078 private static final String __RIGHT_CREATE_PAGE = "Web_Rights_Page_Create"; 079 private static final String __RIGHT_DELETE_PAGE = "Web_Rights_Page_Delete"; 080 081 /** The workflow provider */ 082 protected WorkflowProvider _workflowProvider; 083 084 private SiteManager _siteManager; 085 private ExplorerResourcesDAO _explorerResourcesDAO; 086 087 @Override 088 public void service(ServiceManager manager) throws ServiceException 089 { 090 super.service(manager); 091 _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE); 092 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 093 _explorerResourcesDAO = (ExplorerResourcesDAO) manager.lookup(org.ametys.plugins.explorer.resources.actions.ExplorerResourcesDAO.ROLE); 094 } 095 096 097 @Override 098 public String getId() 099 { 100 return EDITIONFO_MODULE_ID; 101 } 102 103 @Override 104 public String getModuleName() 105 { 106 return __WORKSPACES_EDITIONFO_NODE_NAME; 107 } 108 109 public int getOrder() 110 { 111 return ORDER_WIKI; 112 } 113 114 @Override 115 protected String getModulePageName() 116 { 117 return "about"; 118 } 119 120 public I18nizableText getModuleTitle() 121 { 122 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_EDITIONFO_LABEL"); 123 } 124 public I18nizableText getModuleDescription() 125 { 126 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_EDITIONFO_DESCRIPTION"); 127 } 128 @Override 129 protected I18nizableText getModulePageTitle() 130 { 131 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_WIKI_TITLE"); 132 } 133 134 @Override 135 protected String getModulePageTemplate() 136 { 137 return ProjectConstants.EDITIONFO_TEMPLATE; 138 } 139 140 @Override 141 protected void initializeModulePage(ModifiablePage page) 142 { 143 String contentTitle = StringUtils.defaultIfEmpty(_i18nUtils.translate(getModulePageTitle(), page.getSitemapName()), "Missing title"); 144 _initializeWikiDefaultZone(page, page.getSite(), page.getSitemapName(), contentTitle); 145 } 146 147 /** 148 * Initialize the default zone for the wiki page 149 * @param wikiPage The wiki page 150 * @param site The site 151 * @param sitemapName The sitemap name 152 * @param title The content title 153 */ 154 protected void _initializeWikiDefaultZone(ModifiablePage wikiPage, Site site, String sitemapName, String title) 155 { 156 ModifiableZone defaultZone = wikiPage.createZone("default"); 157 158 ModifiableZoneItem defaultZoneItem = defaultZone.addZoneItem(); 159 defaultZoneItem.setType(ZoneType.CONTENT); 160 161 try 162 { 163 ModifiableWorkflowAwareContent content = createNewWikiContent(site, sitemapName, title); 164 defaultZoneItem.setContent(content); 165 content.saveChanges(); 166 } 167 catch (WorkflowException e) 168 { 169 getLogger().error("Unable to initialize the Wiki Page content for the new workspace, the wiki index page will not be editable until the content is manually created in the BackOffice", e); 170 } 171 } 172 173 /** 174 * Retrieves the rights for the current user in the project 175 * @return The project 176 */ 177 @Callable 178 public Map<String, Object> getModuleRights() 179 { 180 Map<String, Object> rights = new HashMap<>(); 181 182 Request request = ContextHelper.getRequest(_context); 183 String projectName = (String) request.getAttribute("projectName"); 184 Project project = _projectManager.getProject(projectName); 185 ModifiableResourceCollection editionFORoot = getModuleRoot(project, false); 186 187 rights.put("create", editionFORoot != null && _rightManager.currentUserHasRight(__RIGHT_CREATE_PAGE, editionFORoot) == RightResult.RIGHT_ALLOW); 188 rights.put("delete", editionFORoot != null && _rightManager.currentUserHasRight(__RIGHT_DELETE_PAGE, editionFORoot) == RightResult.RIGHT_ALLOW); 189 190 return rights; 191 } 192 193 /** 194 * Create a new content for a wiki page of the wiki module 195 * @param site The site 196 * @param sitemapName the name of the sitemap 197 * @param title The content title 198 * @return The content 199 * @throws WorkflowException if an error occurred 200 */ 201 public ModifiableWorkflowAwareContent createNewWikiContent(Site site, String sitemapName, String title) throws WorkflowException 202 { 203 Map<String, Object> inputs = new HashMap<>(); 204 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_TITLE_KEY, title); 205 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_NAME_KEY, FilterNameHelper.filterName(title)); 206 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_TYPES_KEY, new String[] {WorkspacesConstants.EDITIONFO_CONTENT_TYPE}); 207 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_LANGUAGE_KEY, sitemapName); 208 inputs.put(CreateContentFunction.SITE_KEY, site.getName()); 209 210 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(); 211 workflow.initialize("editionfo", 1, inputs); 212 213 @SuppressWarnings("unchecked") 214 Map<String, Object> results = (Map<String, Object>) inputs.get(AbstractWorkflowComponent.RESULT_MAP_KEY); 215 ModifiableWorkflowAwareContent content = (ModifiableWorkflowAwareContent) results.get(Content.class.getName()); 216 217 return content; 218 } 219 220 /** 221 * Create a new wiki page 222 * @param data The data 223 * @return The url 224 * @throws WorkflowException If an error occurred 225 * @throws IllegalAccessException If a user with insufficient rights try to create a page 226 */ 227 @Callable 228 public Map<String, Object> createPage(Map<String, Object> data) throws WorkflowException, IllegalAccessException 229 { 230 Map<String, Object> result = new HashMap<>(); 231 232 Request request = ContextHelper.getRequest(_context); 233 String siteName = (String) request.getAttribute("siteName"); 234 String sitemapLanguage = (String) request.getAttribute("sitemapLanguage"); 235 String projectName = (String) request.getAttribute("projectName"); 236 Project project = _projectManager.getProject(projectName); 237 238 ModifiableResourceCollection editionFORoot = getModuleRoot(project, false); 239 _explorerResourcesDAO.checkUserRight(editionFORoot, __RIGHT_CREATE_PAGE); 240 241 Site site = _siteManager.getSite(siteName); 242 Sitemap sitemap = site.getSitemap(sitemapLanguage); 243 String title = (String) data.get("title"); 244 245 246 ModifiablePage wikiRootPage = sitemap.getChild(getModulePageName()); 247 248 String originalPageName = FilterNameHelper.filterName(title); 249 String pageName = originalPageName; 250 int index = 2; 251 while (wikiRootPage.hasChild(pageName)) 252 { 253 pageName = originalPageName + "-" + index++; 254 } 255 ModifiablePage page = wikiRootPage.createChild(pageName, "ametys:defaultPage"); 256 page.setTitle(title); 257 page.setSiteName(siteName); 258 page.setSitemapName(sitemapLanguage); 259 260 _initializeNewPage(page, site, sitemapLanguage, title, wikiRootPage); 261 262 wikiRootPage.saveChanges(); 263 264 Map<String, Object> eventParams = new HashMap<>(); 265 eventParams.put(ObservationConstants.ARGS_PAGE, page); 266 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ADDED, _currentUserProvider.getUser(), eventParams)); 267 268 eventParams.put(org.ametys.plugins.workspaces.ObservationConstants.ARGS_PROJECT, project); 269 270 _observationManager.notify(new Event(org.ametys.plugins.workspaces.ObservationConstants.EVENT_EDITIONFO_CREATED, _currentUserProvider.getUser(), eventParams)); 271 272 result.put("url", ResolveURIComponent.resolve("page", page.getId())); 273 return result; 274 } 275 276 277 /** 278 * Initialize the new wiki page 279 * @param wikiPage The page 280 * @param site The site 281 * @param sitemapName The sitemap name 282 * @param title The page title 283 * @param parentPage The parent page 284 */ 285 protected void _initializeNewPage(ModifiablePage wikiPage, Site site, String sitemapName, String title, Page parentPage) 286 { 287 Skin skin = _skinsManager.getSkin(site.getSkinId()); 288 SkinTemplate template = skin.getTemplate(ProjectConstants.EDITIONFO_TEMPLATE); 289 290 if (template != null) 291 { 292 // Set the type and template. 293 wikiPage.setType(PageType.CONTAINER); 294 wikiPage.setTemplate(ProjectConstants.EDITIONFO_TEMPLATE); 295 296 // Initialize the zones. 297 Map<String, SkinTemplateZone> templateZones = template.getZones(); 298 if (templateZones.containsKey("default")) 299 { 300 _initializeWikiDefaultZone(wikiPage, site, sitemapName, title); 301 } 302 else 303 { 304 getLogger().error("A 'default' zone is mandatory in the editionfo template!"); 305 return; 306 } 307 308 // Tag page as a sitemap section. 309 wikiPage.tag("SECTION"); 310 } 311 else 312 { 313 String errorMsg = String.format( 314 "The project workspace '%s' was created with the skin '%s' which doesn't possess the mandatory template '%s'.\nThe '%s' page of the project workspace could not be initialized.", 315 site.getName(), site.getSkinId(), ProjectConstants.EDITIONFO_TEMPLATE, wikiPage.getName()); 316 317 getLogger().error(errorMsg); 318 } 319 } 320 321 @Override 322 public ModifiableResourceCollection getModuleRoot(Project project, boolean create) 323 { 324 try 325 { 326 ExplorerNode projectRootNode = project.getExplorerRootNode(); 327 328 if (projectRootNode instanceof ModifiableResourceCollection) 329 { 330 ModifiableResourceCollection projectRootNodeRc = (ModifiableResourceCollection) projectRootNode; 331 return _getAmetysObject(projectRootNodeRc, __WORKSPACES_EDITIONFO_NODE_NAME, JCRResourcesCollectionFactory.RESOURCESCOLLECTION_NODETYPE, create); 332 } 333 else 334 { 335 throw new IllegalClassException(ModifiableResourceCollection.class, projectRootNode.getClass()); 336 } 337 } 338 catch (AmetysRepositoryException e) 339 { 340 throw new AmetysRepositoryException("Error getting the documents root node.", e); 341 } 342 } 343 344 /** 345 * Delete a page in the edition FO module 346 * @param pageId The page to delete 347 * @return The result 348 * @throws IllegalAccessException If a user with insufficient rights try to delete a page 349 */ 350 @Callable 351 public Map<String, Object> deletePage(String pageId) throws IllegalAccessException 352 { 353 Map<String, Object> result = new HashMap<>(); 354 355 Request request = ContextHelper.getRequest(_context); 356 String siteName = (String) request.getAttribute("siteName"); 357 String sitemapLanguage = (String) request.getAttribute("sitemapLanguage"); 358 String projectName = (String) request.getAttribute("projectName"); 359 Project project = _projectManager.getProject(projectName); 360 ModifiablePage page = _resolver.resolveById(pageId); 361 String pageTitle = page.getTitle(); 362 363 ModifiableResourceCollection editionFORoot = getModuleRoot(project, false); 364 _explorerResourcesDAO.checkUserRight(editionFORoot, __RIGHT_DELETE_PAGE); 365 366 _pageDAO.deletePage(page, true); 367 368 Site site = _siteManager.getSite(siteName); 369 Sitemap sitemap = site.getSitemap(sitemapLanguage); 370 if (sitemap.hasChild(getModulePageName())) 371 { 372 result.put("url", ResolveURIComponent.resolve("page", sitemap.getChild(getModulePageName()).getId())); 373 } 374 375 Map<String, Object> eventParams = new HashMap<>(); 376 eventParams.put(ObservationConstants.ARGS_PAGE_ID, pageId); 377 eventParams.put(org.ametys.plugins.workspaces.ObservationConstants.ARGS_PAGE_TITLE, pageTitle); 378 eventParams.put(org.ametys.plugins.workspaces.ObservationConstants.ARGS_PROJECT, project); 379 _observationManager.notify(new Event(org.ametys.plugins.workspaces.ObservationConstants.EVENT_EDITIONFO_DELETED, _currentUserProvider.getUser(), eventParams)); 380 381 return result; 382 } 383 384 @Override 385 public Set<String> getAllowedEventTypes() 386 { 387 return ImmutableSet.of("wiki.page.created", "wiki.page.updated", "wiki.page.renamed", "wiki.page.deleted"); 388 } 389}