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; 017 018import java.util.HashMap; 019import java.util.Map; 020 021import javax.jcr.Node; 022import javax.jcr.NodeIterator; 023import javax.jcr.RepositoryException; 024 025import org.apache.avalon.framework.context.Context; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.context.Contextualizable; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.commons.lang.StringUtils; 032 033import org.ametys.core.observation.Event; 034import org.ametys.core.observation.ObservationManager; 035import org.ametys.core.right.RightManager; 036import org.ametys.core.user.CurrentUserProvider; 037import org.ametys.core.user.UserManager; 038import org.ametys.core.util.I18nUtils; 039import org.ametys.plugins.core.user.UserHelper; 040import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 041import org.ametys.plugins.repository.AmetysObjectIterable; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.plugins.repository.events.JCREventHelper; 044import org.ametys.plugins.workspaces.project.ProjectConstants; 045import org.ametys.plugins.workspaces.project.ProjectManager; 046import org.ametys.plugins.workspaces.project.modules.WorkspaceModule; 047import org.ametys.plugins.workspaces.project.objects.Project; 048import org.ametys.plugins.workspaces.project.rights.ProjectRightHelper; 049import org.ametys.runtime.i18n.I18nizableText; 050import org.ametys.runtime.plugin.component.AbstractLogEnabled; 051import org.ametys.runtime.plugin.component.PluginAware; 052import org.ametys.web.ObservationConstants; 053import org.ametys.web.repository.page.ModifiablePage; 054import org.ametys.web.repository.page.Page; 055import org.ametys.web.repository.page.Page.PageType; 056import org.ametys.web.repository.page.PageDAO; 057import org.ametys.web.repository.site.Site; 058import org.ametys.web.repository.sitemap.Sitemap; 059import org.ametys.web.service.Service; 060import org.ametys.web.service.ServiceExtensionPoint; 061import org.ametys.web.service.ServiceParameter; 062import org.ametys.web.skin.Skin; 063import org.ametys.web.skin.SkinTemplate; 064import org.ametys.web.skin.SkinsManager; 065 066/** 067 * Abstract class for {@link WorkspaceModule} implementation 068 * 069 */ 070public abstract class AbstractWorkspaceModule extends AbstractLogEnabled implements WorkspaceModule, Serviceable, Contextualizable, PluginAware 071{ 072 /** Project manager */ 073 protected ProjectManager _projectManager; 074 /** Project right helper */ 075 protected ProjectRightHelper _projectRightHelper; 076 /** User manager */ 077 protected UserManager _userManager; 078 /** Ametys resolver */ 079 protected AmetysObjectResolver _resolver; 080 /** The rights manager */ 081 protected RightManager _rightManager; 082 /** Observer manager. */ 083 protected ObservationManager _observationManager; 084 /** The current user provider. */ 085 protected CurrentUserProvider _currentUserProvider; 086 /** The users manager */ 087 protected UserHelper _userHelper; 088 /** The i18n utils. */ 089 protected I18nUtils _i18nUtils; 090 /** The skins manager. */ 091 protected SkinsManager _skinsManager; 092 /** The page DAO */ 093 protected PageDAO _pageDAO; 094 /** The avalon context */ 095 protected Context _context; 096 /** The plugin name */ 097 protected String _pluginName; 098 /** The services handler */ 099 protected ServiceExtensionPoint _serviceEP; 100 101 @Override 102 public void service(ServiceManager manager) throws ServiceException 103 { 104 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 105 _projectRightHelper = (ProjectRightHelper) manager.lookup(ProjectRightHelper.ROLE); 106 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 107 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 108 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 109 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 110 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 111 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 112 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 113 _skinsManager = (SkinsManager) manager.lookup(SkinsManager.ROLE); 114 _pageDAO = (PageDAO) manager.lookup(PageDAO.ROLE); 115 _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 116 } 117 118 @Override 119 public void contextualize(Context context) throws ContextException 120 { 121 _context = context; 122 } 123 124 public void setPluginInfo(String pluginName, String featureName, String id) 125 { 126 _pluginName = pluginName; 127 } 128 129 @Override 130 public void deactivateModule(Project project) 131 { 132 _deletePages(project, getModulePageName()); 133 _internalDeactivateModule(project); 134 135 // Delete root 136 ModifiableResourceCollection moduleRoot = getModuleRoot(project, false); 137 if (moduleRoot != null) 138 { 139 moduleRoot.remove(); 140 } 141 142 // Delete events 143 try 144 { 145 NodeIterator events = JCREventHelper.getEvents(project, getAllowedEventTypes().toArray(new String[]{})); 146 while (events.hasNext()) 147 { 148 Node event = (Node) events.next(); 149 event.remove(); 150 } 151 } 152 catch (RepositoryException e) 153 { 154 getLogger().warn("Unable to delete project '" + project.getName() + "' events for module '" + this.getId() + "'", e); 155 } 156 } 157 158 @Override 159 public void activateModule(Project project) 160 { 161 for (Site site : project.getSites()) 162 { 163 for (Sitemap sitemap : site.getSitemaps()) 164 { 165 initializeSitemap(sitemap); 166 } 167 } 168 169 _internalActivateModule(project); 170 171 // create the resources root node 172 getModuleRoot(project, true); 173 } 174 175 @Override 176 public void initializeSitemap(Sitemap sitemap) 177 { 178 ModifiablePage page = _createModulePage(sitemap, getModulePageName(), getModulePageTitle(), getModulePageTemplate()); 179 180 if (page != null) 181 { 182 page.tag("SECTION"); 183 page.tag(getModuleTagName()); 184 185 initializeModulePage(page); 186 187 page.saveChanges(); 188 189 Map<String, Object> eventParams = new HashMap<>(); 190 eventParams.put(ObservationConstants.ARGS_PAGE, page); 191 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ADDED, _currentUserProvider.getUser(), eventParams)); 192 } 193 } 194 195 @Override 196 public AmetysObjectIterable<Page> getModulePages(Project project, String language) 197 { 198 return _projectManager.getProjectPages(project, getModuleTagName(), language); 199 } 200 201 202 /** 203 * Create a new page if not already exists 204 * @param sitemap The sitemap where the page will be created 205 * @param name The page's name 206 * @param pageTitle The page's title as i18nizable text 207 * @param skinTemplate The template from the skin to apply on the page 208 * @return the created page or <code>null</code> if page already exists 209 */ 210 protected ModifiablePage _createModulePage(Sitemap sitemap, String name, I18nizableText pageTitle, String skinTemplate) 211 { 212 if (!sitemap.hasChild(name)) 213 { 214 ModifiablePage page = sitemap.createChild(name, "ametys:defaultPage"); 215 216 // Title should not be missing, but just in case if the i18n message or the whole catalog does not exists in the requested language 217 // to prevent a non-user-friendly error and still generate the project workspace. 218 page.setTitle(StringUtils.defaultIfEmpty(_i18nUtils.translate(pageTitle, sitemap.getName()), "Missing title")); 219 page.setType(PageType.NODE); 220 page.setSiteName(sitemap.getSiteName()); 221 page.setSitemapName(sitemap.getName()); 222 223 Site site = page.getSite(); 224 Skin skin = _skinsManager.getSkin(site.getSkinId()); 225 SkinTemplate template = skin.getTemplate(skinTemplate); 226 if (template != null) 227 { 228 // Set the type and template. 229 page.setType(PageType.CONTAINER); 230 page.setTemplate(skinTemplate); 231 } 232 else 233 { 234 getLogger().error(String.format( 235 "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.", 236 site.getName(), site.getSkinId(), skinTemplate, page.getName())); 237 } 238 239 sitemap.saveChanges(); 240 241 return page; 242 } 243 else 244 { 245 return null; 246 } 247 } 248 249 /** 250 * Delete all pages of the project sites matching the page name 251 * @param project The project 252 * @param pageName The page name 253 */ 254 protected void _deletePages(Project project, String pageName) 255 { 256 for (Site site : project.getSites()) 257 { 258 for (Sitemap sitemap : site.getSitemaps()) 259 { 260 if (sitemap.hasChild(pageName)) 261 { 262 ModifiablePage page = sitemap.getChild(pageName); 263 _pageDAO.deletePage(page, true); 264 } 265 } 266 267 site.saveChanges(); 268 } 269 } 270 271 /** 272 * Get the default value of the XSLT parameter of the given service. 273 * @param serviceId the service ID. 274 * @return the default XSLT parameter value. 275 */ 276 protected String _getDefaultXslt(String serviceId) 277 { 278 Service service = _serviceEP.hasExtension(serviceId) ? _serviceEP.getExtension(serviceId) : null; 279 if (service != null) 280 { 281 ServiceParameter xsltParam = (ServiceParameter) service.getParameters().get("xslt"); 282 283 if (xsltParam != null) 284 { 285 return xsltParam.getDefaultValue().toString(); 286 } 287 } 288 289 return StringUtils.EMPTY; 290 } 291 292 /** 293 * Returns the tag of apply to module page 294 * @return The tag for module page 295 */ 296 protected abstract String getModuleTagName(); 297 298 /** 299 * Returns the module page's name 300 * @return The module page's name 301 */ 302 protected abstract String getModulePageName(); 303 304 /** 305 * Returns the module page's title as i18n 306 * @return The module page's title 307 */ 308 protected abstract I18nizableText getModulePageTitle(); 309 310 /** 311 * Returns the template to use for module's page 312 * @return The template 313 */ 314 protected String getModulePageTemplate() 315 { 316 return ProjectConstants.PROJECT_TEMPLATE; 317 } 318 319 /** 320 * Initialize the module page 321 * @param modulePage The module page 322 */ 323 protected abstract void initializeModulePage(ModifiablePage modulePage); 324 325 /** 326 * Internal process when module is deactivated 327 * @param project The project 328 */ 329 protected void _internalDeactivateModule(Project project) 330 { 331 // Empty 332 } 333 334 /** 335 * Internal process when module is activated 336 * @param project The project 337 */ 338 protected void _internalActivateModule(Project project) 339 { 340 // Empty 341 } 342}