001/* 002 * Copyright 2020 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.minisite; 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.commons.lang.IllegalClassException; 025import org.apache.commons.lang.StringUtils; 026 027import org.ametys.cms.FilterNameHelper; 028import org.ametys.cms.repository.Content; 029import org.ametys.cms.repository.ModifiableWorkflowAwareContent; 030import org.ametys.core.observation.Event; 031import org.ametys.plugins.explorer.ExplorerNode; 032import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 033import org.ametys.plugins.explorer.resources.jcr.JCRResourcesCollectionFactory; 034import org.ametys.plugins.repository.AmetysRepositoryException; 035import org.ametys.plugins.workflow.AbstractWorkflowComponent; 036import org.ametys.plugins.workflow.support.WorkflowProvider; 037import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 038import org.ametys.plugins.workspaces.AbstractWorkspaceModule; 039import org.ametys.plugins.workspaces.WorkspacesConstants; 040import org.ametys.plugins.workspaces.project.ProjectConstants; 041import org.ametys.plugins.workspaces.project.objects.Project; 042import org.ametys.runtime.i18n.I18nizableText; 043import org.ametys.web.ObservationConstants; 044import org.ametys.web.repository.page.ModifiablePage; 045import org.ametys.web.repository.page.ModifiableZone; 046import org.ametys.web.repository.page.ModifiableZoneItem; 047import org.ametys.web.repository.page.Page.PageType; 048import org.ametys.web.repository.page.ZoneItem.ZoneType; 049import org.ametys.web.repository.site.Site; 050import org.ametys.web.workflow.CreateContentFunction; 051 052import com.google.common.collect.ImmutableSet; 053import com.opensymphony.workflow.WorkflowException; 054 055/** 056 * Workspaces module for editorial pages 057 */ 058public class MiniSiteWorkspaceModule extends AbstractWorkspaceModule 059{ 060 /** Avalon ROLE */ 061 public static final String MINISITE_MODULE_ID = MiniSiteWorkspaceModule.class.getName(); 062 063 /** Workspaces tasks list node name */ 064 private static final String __WORKSPACES_MINISITE_NODE_NAME = "minisite"; 065 066 private static final int __INITIAL_WORKFLOW_ACTION_ID = 13; 067 068 /** The workflow provider */ 069 protected WorkflowProvider _workflowProvider; 070 071 @Override 072 public void service(ServiceManager manager) throws ServiceException 073 { 074 super.service(manager); 075 _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE); 076 } 077 078 @Override 079 public String getId() 080 { 081 return MINISITE_MODULE_ID; 082 } 083 084 public boolean isUnactivatedByDefault() 085 { 086 return true; 087 } 088 089 @Override 090 public String getModuleName() 091 { 092 return __WORKSPACES_MINISITE_NODE_NAME; 093 } 094 095 public int getOrder() 096 { 097 return ORDER_MINISITE; 098 } 099 100 @Override 101 protected String getModulePageName() 102 { 103 return "minisite"; 104 } 105 106 public I18nizableText getModuleTitle() 107 { 108 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_MINISITE_LABEL"); 109 } 110 public I18nizableText getModuleDescription() 111 { 112 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_MINISITE_DESCRIPTION"); 113 } 114 @Override 115 protected I18nizableText getModulePageTitle() 116 { 117 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_MINISITE_TITLE"); 118 } 119 120 /** 121 * Get the name of the first section of minisite 122 * @return the section's name 123 */ 124 protected String getFirstSectionName() 125 { 126 return "section"; 127 } 128 129 /** 130 * Get the title of the first section of minisite 131 * @return the section's title 132 */ 133 protected I18nizableText getFirstSectionTitle() 134 { 135 return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_MINISITE_FIRST_SECTION_TITLE"); 136 } 137 138 @Override 139 protected String getModulePageTemplate() 140 { 141 // the module page is a NODE page 142 return null; 143 } 144 145 @Override 146 protected void initializeModulePage(ModifiablePage modulePage) 147 { 148 modulePage.untag("SECTION"); 149 150 ModifiablePage firstSection = _createAndInitializeFirstSectionPage(modulePage, getFirstSectionName(), getFirstSectionTitle()); 151 if (firstSection != null) 152 { 153 _initializeFirstSectionDefaultZone(firstSection); 154 } 155 } 156 157 /** 158 * Create the first section of minisite if does not exist 159 * @param modulePage the module page 160 * @param name the name of section to create 161 * @param pageTitle the page title 162 * @return the created page or null if already exist 163 */ 164 protected ModifiablePage _createAndInitializeFirstSectionPage(ModifiablePage modulePage, String name, I18nizableText pageTitle) 165 { 166 if (!modulePage.hasChild(name)) 167 { 168 ModifiablePage page = modulePage.createChild(name, "ametys:defaultPage"); 169 170 // Title should not be missing, but just in case if the i18n message or the whole catalog does not exists in the requested language 171 // to prevent a non-user-friendly error and still generate the project workspace. 172 page.setTitle(StringUtils.defaultIfEmpty(_i18nUtils.translate(pageTitle, modulePage.getSitemapName()), "Missing title")); 173 page.setType(PageType.CONTAINER); 174 page.setSiteName(modulePage.getSiteName()); 175 page.setSitemapName(modulePage.getSitemapName()); 176 page.setTemplate(ProjectConstants.MINISITE_TEMPLATE); 177 page.tag("SECTION"); 178 179 page.saveChanges(); 180 181 Map<String, Object> eventParams = new HashMap<>(); 182 eventParams.put(ObservationConstants.ARGS_PAGE, page); 183 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ADDED, _currentUserProvider.getUser(), eventParams)); 184 185 return page; 186 } 187 else 188 { 189 return null; 190 } 191 } 192 193 /** 194 * Initialize the default zone for the first section of mini site 195 * @param page The first page of minisite 196 */ 197 protected void _initializeFirstSectionDefaultZone(ModifiablePage page) 198 { 199 ModifiableZone defaultZone = page.createZone("default"); 200 201 ModifiableZoneItem defaultZoneItem = defaultZone.addZoneItem(); 202 defaultZoneItem.setType(ZoneType.CONTENT); 203 204 try 205 { 206 ModifiableWorkflowAwareContent content = _createDefaultContent(page.getSite(), page.getSitemapName(), page.getTitle()); 207 defaultZoneItem.setContent(content); 208 content.saveChanges(); 209 } 210 catch (WorkflowException e) 211 { 212 getLogger().error("Unable to initialize the first page for minisite module, the page will not be editable until the content is manually created in the BackOffice", e); 213 } 214 } 215 216 /** 217 * Create a new content for a minisite page of the minisite module 218 * @param site The site 219 * @param sitemapName the name of the sitemap 220 * @param title The content title 221 * @return The content 222 * @throws WorkflowException if an error occurred 223 */ 224 protected ModifiableWorkflowAwareContent _createDefaultContent(Site site, String sitemapName, String title) throws WorkflowException 225 { 226 Map<String, Object> inputs = new HashMap<>(); 227 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_TITLE_KEY, title); 228 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_NAME_KEY, FilterNameHelper.filterName(title)); 229 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_TYPES_KEY, new String[] {WorkspacesConstants.PROJECT_ARTICLE_CONTENT_TYPE}); 230 inputs.put(org.ametys.cms.workflow.CreateContentFunction.CONTENT_LANGUAGE_KEY, sitemapName); 231 inputs.put(CreateContentFunction.SITE_KEY, site.getName()); 232 233 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(); 234 workflow.initialize(WorkspacesConstants.CONTENT_WORKFLOW_NAME, __INITIAL_WORKFLOW_ACTION_ID, inputs); 235 236 @SuppressWarnings("unchecked") 237 Map<String, Object> results = (Map<String, Object>) inputs.get(AbstractWorkflowComponent.RESULT_MAP_KEY); 238 ModifiableWorkflowAwareContent content = (ModifiableWorkflowAwareContent) results.get(Content.class.getName()); 239 240 return content; 241 } 242 243 @Override 244 public ModifiableResourceCollection getModuleRoot(Project project, boolean create) 245 { 246 try 247 { 248 ExplorerNode projectRootNode = project.getExplorerRootNode(); 249 250 if (projectRootNode instanceof ModifiableResourceCollection) 251 { 252 ModifiableResourceCollection projectRootNodeRc = (ModifiableResourceCollection) projectRootNode; 253 return _getAmetysObject(projectRootNodeRc, __WORKSPACES_MINISITE_NODE_NAME, JCRResourcesCollectionFactory.RESOURCESCOLLECTION_NODETYPE, create); 254 } 255 else 256 { 257 throw new IllegalClassException(ModifiableResourceCollection.class, projectRootNode.getClass()); 258 } 259 } 260 catch (AmetysRepositoryException e) 261 { 262 throw new AmetysRepositoryException("Error getting the minisite root node.", e); 263 } 264 } 265 266 @Override 267 public Set<String> getAllowedEventTypes() 268 { 269 return ImmutableSet.of("minisite.page.created", "minisite.page.updated", "minisite.page.renamed", "minisite.page.deleted"); 270 } 271 272}