001/* 002 * Copyright 2016 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.project.observers; 017 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021 022import org.apache.avalon.framework.logger.AbstractLogEnabled; 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.avalon.framework.service.Serviceable; 026import org.apache.commons.lang.StringUtils; 027 028import org.ametys.core.observation.Event; 029import org.ametys.core.observation.ObservationManager; 030import org.ametys.core.observation.Observer; 031import org.ametys.core.user.CurrentUserProvider; 032import org.ametys.core.util.I18nUtils; 033import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 034import org.ametys.plugins.workflow.support.WorkflowProvider; 035import org.ametys.plugins.workspaces.project.ProjectConstants; 036import org.ametys.plugins.workspaces.project.ProjectManager; 037import org.ametys.plugins.workspaces.project.objects.Project; 038import org.ametys.runtime.i18n.I18nizableText; 039import org.ametys.runtime.plugin.component.PluginAware; 040import org.ametys.web.ObservationConstants; 041import org.ametys.web.repository.page.ModifiablePage; 042import org.ametys.web.repository.page.ModifiableZone; 043import org.ametys.web.repository.page.ModifiableZoneItem; 044import org.ametys.web.repository.page.Page.PageType; 045import org.ametys.web.repository.page.ZoneItem.ZoneType; 046import org.ametys.web.repository.site.Site; 047import org.ametys.web.repository.sitemap.Sitemap; 048import org.ametys.web.service.Service; 049import org.ametys.web.service.ServiceExtensionPoint; 050import org.ametys.web.service.ServiceParameter; 051import org.ametys.web.skin.Skin; 052import org.ametys.web.skin.SkinTemplate; 053import org.ametys.web.skin.SkinTemplateZone; 054import org.ametys.web.skin.SkinsManager; 055 056/** 057 * Initializes all project workspace pages when a sitemap is created. 058 */ 059public class InitializeProjectSitemapObserver extends AbstractLogEnabled implements Observer, Serviceable, PluginAware 060{ 061 /** Workspaces project manager */ 062 protected ProjectManager _projectManager; 063 064 /** The i18n utils. */ 065 protected I18nUtils _i18nUtils; 066 067 /** The observation manager. */ 068 protected ObservationManager _observationManager; 069 070 /** The skins manager. */ 071 protected SkinsManager _skinsManager; 072 073 /** Current user provider */ 074 protected CurrentUserProvider _currentUserProvider; 075 076 /** The service extension point. */ 077 protected ServiceExtensionPoint _serviceEP; 078 079 /** The plugin name. */ 080 protected String _pluginName; 081 082 /** The i18n catalogue. */ 083 protected String _i18nCatalogue; 084 085 /** The workflow provider */ 086 protected WorkflowProvider _workflowProvider; 087 088 @Override 089 public void setPluginInfo(String pluginName, String featureName, String id) 090 { 091 _pluginName = pluginName; 092 _i18nCatalogue = "plugin." + pluginName; 093 } 094 095 @Override 096 public void service(ServiceManager manager) throws ServiceException 097 { 098 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 099 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 100 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 101 _skinsManager = (SkinsManager) manager.lookup(SkinsManager.ROLE); 102 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 103 _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 104 _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE); 105 } 106 107 @Override 108 public boolean supports(Event event) 109 { 110 // Listen on site updated because it is too early to listen on sitemap added (we need the site be configured and synchronized in live) 111 return event.getId().equals(ObservationConstants.EVENT_SITE_UPDATED); 112 } 113 114 @Override 115 public int getPriority(Event event) 116 { 117 // Will be processed after live synchronization observers 118 return MAX_PRIORITY + 2000; 119 } 120 121 @Override 122 public void observe(Event event, Map<String, Object> transientVars) throws Exception 123 { 124 Site site = (Site) event.getArguments().get(ObservationConstants.ARGS_SITE); 125 if (site != null) 126 { 127 List<Project> projects = _projectManager.getProjectsForSite(site); 128 if (!projects.isEmpty()) 129 { 130 Project project = projects.get(0); 131 132 // Initialize each sitemap with a set of predefined and configured pages 133 for (Sitemap sitemap : site.getSitemaps()) 134 { 135 _initializeSitemap(sitemap, project); 136 } 137 } 138 } 139 } 140 141 /** 142 * Initialize the given sitemap. 143 * @param sitemap the Sitemap object. 144 * @param project the corresponding project 145 */ 146 protected void _initializeSitemap(Sitemap sitemap, Project project) 147 { 148 // Home page (index) 149 ModifiablePage indexPage = _createPage(sitemap, "index", new I18nizableText(_i18nCatalogue, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_INDEX_TITLE")); 150 if (indexPage != null) 151 { 152 _initializeIndexPage(indexPage); 153 } 154 155 _projectManager.initializeModulesSitemap(project, sitemap); 156 157 // Notify of the sitemap change. 158 Map<String, Object> eventParams = new HashMap<>(); 159 eventParams.put(ObservationConstants.ARGS_SITEMAP, sitemap); 160 _observationManager.notify(new Event(ObservationConstants.EVENT_SITEMAP_UPDATED, _currentUserProvider.getUser(), eventParams)); 161 } 162 163 /** 164 * Create a new page if not already exists 165 * @param sitemap The sitemap where the page will be created 166 * @param name The page's name 167 * @param i18nTitle The page's title 168 * @return the created page or <code>null</code> if page already exists 169 */ 170 protected ModifiablePage _createPage(Sitemap sitemap, String name, I18nizableText i18nTitle) 171 { 172 if (!sitemap.hasChild(name)) 173 { 174 ModifiablePage page = sitemap.createChild(name, "ametys:defaultPage"); 175 176 // Title should not be missing, but just in case if the i18n message or the whole catalog does not exists in the requested language 177 // to prevent a non-user-friendly error and still generate the project workspace. 178 page.setTitle(StringUtils.defaultIfEmpty(_i18nUtils.translate(i18nTitle, sitemap.getName()), "Missing title")); 179 page.setType(PageType.NODE); 180 page.setSiteName(sitemap.getSiteName()); 181 page.setSitemapName(sitemap.getName()); 182 183 sitemap.saveChanges(); 184 185 return page; 186 } 187 else 188 { 189 return null; 190 } 191 } 192 193 /** 194 * Initialize the index page. 195 * @param indexPage the index page. 196 */ 197 protected void _initializeIndexPage(ModifiablePage indexPage) 198 { 199 Site site = indexPage.getSite(); 200 Skin skin = _skinsManager.getSkin(site.getSkinId()); 201 202 SkinTemplate template = skin.getTemplate("index"); 203 204 // Check the template index contains the required zones 205 if (template == null || !template.getZones().containsKey("bottom-left") || !template.getZones().containsKey("bottom-right")) 206 { 207 // switch to template 'project' if exists with the required zones 208 SkinTemplate projectTpl = skin.getTemplate(ProjectConstants.PROJECT_TEMPLATE); 209 if (projectTpl != null && projectTpl.getZones().containsKey("bottom-left") && projectTpl.getZones().containsKey("bottom-right")) 210 { 211 template = projectTpl; 212 } 213 } 214 215 if (template != null) 216 { 217 // Set the type and template. 218 indexPage.setType(PageType.CONTAINER); 219 indexPage.setTemplate("index"); 220 221 // Initialize the zones. 222 Map<String, SkinTemplateZone> templateZones = template.getZones(); 223 if (templateZones.containsKey("default")) 224 { 225 _initializeIndexDefaultZone(indexPage); 226 } 227 else 228 { 229 getLogger().error("A 'default' zone is mandatory for template " + template.getId() + " to be used for project's home page"); 230 return; 231 } 232 233 _initializeIndexBottomLeftZone(indexPage, templateZones.containsKey("bottom-left") ? "bottom-left" : "default"); 234 _initializeIndexBottomRightZone(indexPage, templateZones.containsKey("bottom-right") ? "bottom-right" : "default"); 235 236 // Do not tag index page as a section. 237 Map<String, Object> eventParams = new HashMap<>(); 238 eventParams.put(ObservationConstants.ARGS_PAGE, indexPage); 239 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ADDED, _currentUserProvider.getUser(), eventParams)); 240 } 241 else 242 { 243 String errorMsg = String.format( 244 "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.", 245 site.getName(), site.getSkinId(), ProjectConstants.PROJECT_TEMPLATE, indexPage.getName()); 246 247 getLogger().error(errorMsg); 248 } 249 } 250 251 /** 252 * Initialize the default zone for the index page 253 * @param indexPage The index page 254 */ 255 protected void _initializeIndexDefaultZone(ModifiablePage indexPage) 256 { 257 @SuppressWarnings("unused") 258 ModifiableZone defaultZone = indexPage.createZone("default"); 259 260 // TODO add project presentation zone 261 // -> editable zone containing a pre-filled content 262 } 263 264 /** 265 * Initialize the bottom left zone for the index page 266 * @param indexPage the index page 267 * @param zoneName The zone name where to insert the service 268 */ 269 protected void _initializeIndexBottomLeftZone(ModifiablePage indexPage, String zoneName) 270 { 271 ModifiableZone zone = indexPage.createZone(zoneName); 272 273 String serviceId = "org.ametys.plugins.workspaces.module.Members"; 274 ModifiableZoneItem defaultZoneItem = zone.addZoneItem(); 275 defaultZoneItem.setType(ZoneType.SERVICE); 276 defaultZoneItem.setServiceId(serviceId); 277 278 ModifiableCompositeMetadata serviceMetadata = defaultZoneItem.getServiceParameters(); 279 I18nizableText i18nTitle = new I18nizableText(_i18nCatalogue, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_INDEX_MODULE_USERS_TITLE"); 280 serviceMetadata.setMetadata("title", StringUtils.defaultIfEmpty(_i18nUtils.translate(i18nTitle, indexPage.getSitemapName()), "Missing title")); 281 serviceMetadata.setMetadata("results-per-page", 5); 282 serviceMetadata.setMetadata("xslt", _getDefaultXslt(serviceId)); 283 } 284 285 /** 286 * Initialize the bottom right zone for the index page 287 * @param indexPage the index page 288 * @param zoneName The zone name where to insert the service 289 */ 290 protected void _initializeIndexBottomRightZone(ModifiablePage indexPage, String zoneName) 291 { 292 ModifiableZone zone = indexPage.createZone(zoneName); 293 294 String serviceId = "org.ametys.plugins.workspaces.service.ActivityStream"; 295 ModifiableZoneItem defaultZoneItem = zone.addZoneItem(); 296 defaultZoneItem.setType(ZoneType.SERVICE); 297 defaultZoneItem.setServiceId(serviceId); 298 299 ModifiableCompositeMetadata serviceMetadata = defaultZoneItem.getServiceParameters(); 300 I18nizableText i18nTitle = new I18nizableText(_i18nCatalogue, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_INDEX_ACTIVITY_STREAM_TITLE"); 301 serviceMetadata.setMetadata("title", StringUtils.defaultIfEmpty(_i18nUtils.translate(i18nTitle, indexPage.getSitemapName()), "Missing title")); 302 serviceMetadata.setMetadata("activities-per-page", 5); 303 serviceMetadata.setMetadata("max-activities", 20); 304 serviceMetadata.setMetadata("chronological-order", false); 305 serviceMetadata.setMetadata("xslt", _getDefaultXslt(serviceId)); 306 } 307 308 private String _getDefaultXslt(String serviceId) 309 { 310 Service service = _serviceEP.hasExtension(serviceId) ? _serviceEP.getExtension(serviceId) : null; 311 if (service != null) 312 { 313 ServiceParameter xsltParam = (ServiceParameter) service.getParameters().get("xslt"); 314 315 if (xsltParam != null) 316 { 317 return xsltParam.getDefaultValue().toString(); 318 } 319 } 320 321 return StringUtils.EMPTY; 322 } 323}