001/*
002 *  Copyright 2010 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 */
016
017package org.ametys.web.repository.page.actions;
018
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.Map;
024import java.util.Set;
025
026import org.apache.avalon.framework.parameters.Parameters;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.environment.ObjectModelHelper;
030import org.apache.cocoon.environment.Redirector;
031import org.apache.cocoon.environment.SourceResolver;
032import org.apache.commons.collections.CollectionUtils;
033import org.apache.commons.lang.ArrayUtils;
034
035import org.ametys.cms.CmsConstants;
036import org.ametys.cms.ObservationConstants;
037import org.ametys.cms.repository.Content;
038import org.ametys.cms.repository.DefaultContent;
039import org.ametys.core.observation.AbstractNotifierAction;
040import org.ametys.core.observation.Event;
041import org.ametys.plugins.repository.AmetysObjectResolver;
042import org.ametys.plugins.repository.UnknownAmetysObjectException;
043import org.ametys.web.repository.content.SharedContent;
044import org.ametys.web.repository.content.WebContent;
045import org.ametys.web.repository.content.jcr.DefaultSharedContent;
046import org.ametys.web.repository.content.shared.SharedContentManager;
047import org.ametys.web.repository.page.ContentTypesAssignmentHandler;
048import org.ametys.web.repository.page.ModifiableSitemapElement;
049import org.ametys.web.repository.page.ModifiableZone;
050import org.ametys.web.repository.page.ModifiableZoneItem;
051import org.ametys.web.repository.page.SitemapElement;
052import org.ametys.web.repository.page.ZoneItem.ZoneType;
053
054/**
055 * This action add an existing content to a zone of a page 
056 */
057public class AddSharedContentAction extends AbstractNotifierAction
058{
059    /** The shared content manager. */
060    protected SharedContentManager _sharedContentManager;
061    
062    /** The content types assignment handler. */
063    protected ContentTypesAssignmentHandler _cTypesAssignmentHandler;
064    
065    /** Ametys object resolver */
066    protected AmetysObjectResolver _resolver;
067    
068    @Override
069    public void service(ServiceManager serviceManager) throws ServiceException
070    {
071        super.service(serviceManager);
072        _sharedContentManager = (SharedContentManager) serviceManager.lookup(SharedContentManager.ROLE);
073        _cTypesAssignmentHandler = (ContentTypesAssignmentHandler) serviceManager.lookup(ContentTypesAssignmentHandler.ROLE);
074        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
075    }
076    
077    @Override
078    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
079    {
080        @SuppressWarnings("unchecked")
081        Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
082        
083        String pageId = (String) jsParameters.get("zoneitem-org.ametys.web.AddSharedContentAction$pageId");
084        String zoneName = (String) jsParameters.get("zoneitem-org.ametys.web.AddSharedContentAction$zoneName");
085        String contentId = (String) jsParameters.get("zoneitem-org.ametys.web.AddSharedContentAction$contentId");
086        String viewName = (String) jsParameters.get("zoneitem-org.ametys.web.AddSharedContentAction$viewName");
087        
088        try
089        {
090            SitemapElement pc = _resolver.resolveById(pageId);
091            
092            if (!(pc instanceof ModifiableSitemapElement sitemapElement))
093            {
094                throw new IllegalArgumentException("Page '" + pageId + "' is not modifiable");
095            }
096            
097            if (sitemapElement.getTemplate() == null)
098            {
099                throw new IllegalArgumentException("Page '" + pageId + "' is not of type Container");
100            }
101         
102            ModifiableZone zone;
103            if (sitemapElement.hasZone(zoneName))
104            {
105                zone = sitemapElement.getZone(zoneName);
106            }
107            else
108            {
109                zone = sitemapElement.createZone(zoneName);
110            }
111            
112            Content content = _resolver.resolveById(contentId);
113            String contentSiteName = null;
114            if (content instanceof WebContent)
115            {
116                contentSiteName = ((WebContent) content).getSiteName();
117            }
118            
119            Set<String> validCTypes = _cTypesAssignmentHandler.getAvailableContentTypes(sitemapElement, zoneName, true);
120            Set<String> contentTypes = new HashSet<>(Arrays.asList(content.getTypes())); 
121            if (!CollectionUtils.containsAny(contentTypes, validCTypes))
122            {
123                return Collections.singletonMap("error", "invalid-content-type");
124            }
125            
126            Content newContent = null;
127            if (contentSiteName == null || sitemapElement.getSiteName().equals(contentSiteName))
128            {
129                // The content comes from the same site as the page: insert the content as a new zone item.
130                newContent = addContentReference(zone, content, viewName);
131            }
132            else
133            {
134                // The content is from a different site: create a shared content in the zone.
135                if (!(content instanceof DefaultContent) || content instanceof SharedContent)
136                {
137                    throw new IllegalArgumentException("The source content must be a DefaultContent but not a SharedContent.");
138                }
139                
140                DefaultContent defaultContent = (DefaultContent) content;
141                
142                if (!ArrayUtils.contains(defaultContent.getAllLabels(), CmsConstants.LIVE_LABEL))
143                {
144                    return Collections.singletonMap("error", "content-not-validated");
145                }
146                
147                newContent = createSharedContent(zone, defaultContent, viewName);
148            }
149            
150            sitemapElement.saveChanges();
151            
152            Map<String, Object> eventParams = new HashMap<>();
153            eventParams.put(ObservationConstants.ARGS_CONTENT, newContent);
154            eventParams.put(ObservationConstants.ARGS_CONTENT_ID, newContent.getId());
155            eventParams.put(org.ametys.web.ObservationConstants.ARGS_SITEMAP_ELEMENT, sitemapElement);
156            
157            _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_ADDED, _getCurrentUser(), eventParams));
158        }
159        catch (UnknownAmetysObjectException e)
160        {
161            throw new IllegalArgumentException("An error occurred adding the shared content '" + contentId + "' on the page '" + pageId + "'", e);
162        }
163                
164        return EMPTY_MAP;
165        
166    }
167
168    /**
169     * Add the given content as a zone item in the given zone.
170     * @param zone the zone to add the content in.
171     * @param content the content to add.
172     * @param viewName the view name.
173     * @return the same content.
174     */
175    protected Content addContentReference(ModifiableZone zone, Content content, String viewName)
176    {
177        ModifiableZoneItem zoneItem = zone.addZoneItem();
178        zoneItem.setType(ZoneType.CONTENT);
179        
180        zoneItem.setContent(content);
181        zoneItem.setViewName(viewName);
182        
183        return content;
184    }
185    
186    /**
187     * Create a shared content referencing the given content and add the shared one to the zone.
188     * @param zone the zone to create the shared content in.
189     * @param originalContent the original content.
190     * @param viewName the view name.
191     * @return the created shared content.
192     */
193    protected Content createSharedContent(ModifiableZone zone, DefaultContent originalContent, String viewName)
194    {
195        ModifiableZoneItem zoneItem = zone.addZoneItem();
196        zoneItem.setType(ZoneType.CONTENT);
197        
198        DefaultSharedContent content = _sharedContentManager.createSharedContent(zone.getSitemapElement().getSite(), originalContent);
199        
200        zoneItem.setContent(content);
201        zoneItem.setViewName(viewName);
202        
203        return content;
204    }
205}