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}