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}