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}