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.ModifiablePage;
049import org.ametys.web.repository.page.ModifiableZone;
050import org.ametys.web.repository.page.ModifiableZoneItem;
051import org.ametys.web.repository.page.Page.PageType;
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            // FIXME API Test if not modifiable
091            ModifiablePage page = _resolver.resolveById(pageId);
092            
093            if (page.getType() != PageType.CONTAINER)
094            {
095                throw new IllegalArgumentException("Page '" + pageId + "' is not of type Container");
096            }
097         
098            ModifiableZone zone;
099            if (page.hasZone(zoneName))
100            {
101                zone = page.getZone(zoneName);
102            }
103            else
104            {
105                zone = page.createZone(zoneName);
106            }
107            
108            Content content = _resolver.resolveById(contentId);
109            String contentSiteName = null;
110            if (content instanceof WebContent)
111            {
112                contentSiteName = ((WebContent) content).getSiteName();
113            }
114            
115            Set<String> validCTypes = _cTypesAssignmentHandler.getAvailableContentTypes(page, zoneName, true);
116            Set<String> contentTypes = new HashSet<>(Arrays.asList(content.getTypes())); 
117            if (!CollectionUtils.containsAny(contentTypes, validCTypes))
118            {
119                return Collections.singletonMap("error", "invalid-content-type");
120            }
121            
122            Content newContent = null;
123            if (contentSiteName == null || page.getSiteName().equals(contentSiteName))
124            {
125                // The content comes from the same site as the page: insert the content as a new zone item.
126                newContent = addContentReference(zone, content, viewName);
127            }
128            else
129            {
130                // The content is from a different site: create a shared content in the zone.
131                if (!(content instanceof DefaultContent) || content instanceof SharedContent)
132                {
133                    throw new IllegalArgumentException("The source content must be a DefaultContent but not a SharedContent.");
134                }
135                
136                DefaultContent defaultContent = (DefaultContent) content;
137                
138                if (!ArrayUtils.contains(defaultContent.getAllLabels(), CmsConstants.LIVE_LABEL))
139                {
140                    return Collections.singletonMap("error", "content-not-validated");
141                }
142                
143                newContent = createSharedContent(zone, defaultContent, viewName);
144            }
145            
146            page.saveChanges();
147            
148            Map<String, Object> eventParams = new HashMap<>();
149            eventParams.put(ObservationConstants.ARGS_CONTENT, newContent);
150            eventParams.put(ObservationConstants.ARGS_CONTENT_ID, newContent.getId());
151            eventParams.put(org.ametys.web.ObservationConstants.ARGS_PAGE, page);
152            
153            _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_ADDED, _getCurrentUser(), eventParams));
154        }
155        catch (UnknownAmetysObjectException e)
156        {
157            throw new IllegalArgumentException("An error occurred adding the shared content '" + contentId + "' on the page '" + pageId + "'", e);
158        }
159                
160        return EMPTY_MAP;
161        
162    }
163
164    /**
165     * Add the given content as a zone item in the given zone.
166     * @param zone the zone to add the content in.
167     * @param content the content to add.
168     * @param viewName the view name.
169     * @return the same content.
170     */
171    protected Content addContentReference(ModifiableZone zone, Content content, String viewName)
172    {
173        ModifiableZoneItem zoneItem = zone.addZoneItem();
174        zoneItem.setType(ZoneType.CONTENT);
175        
176        zoneItem.setContent(content);
177        zoneItem.setViewName(viewName);
178        
179        return content;
180    }
181    
182    /**
183     * Create a shared content referencing the given content and add the shared one to the zone.
184     * @param zone the zone to create the shared content in.
185     * @param originalContent the original content.
186     * @param viewName the view name.
187     * @return the created shared content.
188     */
189    protected Content createSharedContent(ModifiableZone zone, DefaultContent originalContent, String viewName)
190    {
191        ModifiableZoneItem zoneItem = zone.addZoneItem();
192        zoneItem.setType(ZoneType.CONTENT);
193        
194        DefaultSharedContent content = _sharedContentManager.createSharedContent(zone.getPage().getSite(), originalContent);
195        
196        zoneItem.setContent(content);
197        zoneItem.setViewName(viewName);
198        
199        return content;
200    }
201}