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