001/*
002 *  Copyright 2018 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.cms.contenttype;
017
018import java.util.HashMap;
019import java.util.Map;
020
021import org.apache.avalon.framework.service.ServiceException;
022import org.apache.cocoon.ProcessingException;
023import org.apache.cocoon.xml.XMLUtils;
024import org.xml.sax.ContentHandler;
025import org.xml.sax.SAXException;
026
027import org.ametys.cms.model.restrictions.ContentRestrictedModelItemHelper;
028import org.ametys.cms.model.restrictions.RestrictedModelItem;
029import org.ametys.cms.model.restrictions.Restrictions;
030import org.ametys.cms.repository.Content;
031import org.ametys.plugins.repository.data.external.ExternalizableDataProviderExtensionPoint;
032import org.ametys.runtime.i18n.I18nizableText;
033import org.ametys.runtime.model.DefinitionContext;
034import org.ametys.runtime.model.ElementDefinition;
035import org.ametys.runtime.model.ModelItem;
036
037/**
038 * The definition of a content type attribute
039 * @param <T> Type of the attribute value
040 */
041public class AttributeDefinition<T> extends ElementDefinition<T> implements RestrictedModelItem<Content>
042{
043    private ContentRestrictedModelItemHelper _restrictedModelItemHelper;
044    private ExternalizableDataProviderExtensionPoint _externalizableDataProviderExtensionPoint;
045
046    private Restrictions _restrictions;
047    
048    /**
049     * Set the restrictions for the attribute
050     * @param restrictions the restrictions to set
051     */
052    public void setRestrictions(Restrictions restrictions)
053    {
054        _restrictions = restrictions;
055    }
056
057    public boolean canRead(Content content)
058    {
059        ContentRestrictedModelItemHelper restrictedModelItemHelper = _getRestrictedModelItemHelper();
060        ContentType contentType = (ContentType) getModel();
061        return restrictedModelItemHelper.canRead(content, this, _restrictions) && contentType.canRead(content, this);
062    }
063
064    public boolean canWrite(Content content)
065    {
066        ContentRestrictedModelItemHelper restrictedModelItemHelper = _getRestrictedModelItemHelper();
067        ContentType contentType = (ContentType) getModel();
068        return restrictedModelItemHelper.canWrite(content, this, _restrictions) && contentType.canWrite(content, this);
069    }
070    
071    /**
072     * Retrieves the helper for {@link ModelItem}s with restrictions on contents 
073     * @return the {@link ContentRestrictedModelItemHelper}
074     */
075    protected ContentRestrictedModelItemHelper _getRestrictedModelItemHelper()
076    {
077        if (_restrictedModelItemHelper == null)
078        {
079            try
080            {
081                _restrictedModelItemHelper = (ContentRestrictedModelItemHelper) __serviceManager.lookup(ContentRestrictedModelItemHelper.ROLE);
082            }
083            catch (ServiceException e)
084            {
085                throw new RuntimeException("Unable to lookup after the restricted model item helper component", e);
086            }
087        }
088        
089        return _restrictedModelItemHelper;
090    }
091    
092    @SuppressWarnings("unchecked")
093    @Override
094    protected Map<String, Object> _toJSON(DefinitionContext context) throws ProcessingException
095    {
096        Map<String, Object> result = super._toJSON(context);
097        
098        Content content = context.getObject()
099                .filter(Content.class::isInstance)
100                .map(Content.class::cast)
101                .orElse(null);
102        
103        if (content != null)
104        {
105            if (context.isEdition() && _getExternalizableDataProviderExtensionPoint().isDataExternalizable(content, this))
106            {
107                // Wrap the widget
108                result.put("widget", "edition.externalizable");
109
110                if (getWidget() != null)
111                {
112                    Map<String, I18nizableText> widgetParameters = new HashMap<>();
113                    if (result.containsKey("widget-params"))
114                    {
115                        widgetParameters = (Map<String, I18nizableText>) result.get("widget-params");
116                    }
117
118                    widgetParameters.put("wrapped-widget", new I18nizableText(getWidget()));
119
120                    if (!widgetParameters.isEmpty())
121                    {
122                        result.put("widget-params", widgetParameters);
123                    }
124                }
125            }
126        }
127        
128        if (!canWrite(content))
129        {
130            result.put("can-not-write", true);
131        }
132        
133        return result;
134    }
135    
136    @Override
137    protected boolean _shouldJSONBeEmpty(DefinitionContext context)
138    {
139        return context.getObject()
140                .filter(Content.class::isInstance)
141                .map(Content.class::cast)
142                .map(content -> !canRead(content))
143                .orElse(false);
144    }
145    
146    @Override
147    public void toSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException
148    {
149        super.toSAX(contentHandler, context);
150
151        Content content = context.getObject()
152                .filter(Content.class::isInstance)
153                .map(Content.class::cast)
154                .orElse(null);
155
156        if (!canWrite(content))
157        {
158            XMLUtils.createElement(contentHandler, "can-not-write", String.valueOf(true));
159        }
160    }
161    
162    @Override
163    protected void _widgetToSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException
164    {
165        Content content = context.getObject()
166                .filter(Content.class::isInstance)
167                .map(Content.class::cast)
168                .orElse(null);
169        
170        if (content != null && context.isEdition() && _getExternalizableDataProviderExtensionPoint().isDataExternalizable(content, this))
171        {
172            // Wrap the widget
173            XMLUtils.createElement(contentHandler, "widget", "edition.externalizable");
174
175            if (getWidget() != null)
176            {
177                XMLUtils.startElement(contentHandler, "widget-params");
178
179                Map<String, I18nizableText> widgetParameters = getWidgetParameters();
180                if (widgetParameters != null && !widgetParameters.isEmpty())
181                {
182                    for (Map.Entry<String, I18nizableText> param : widgetParameters.entrySet())
183                    {
184                        _widgetParameterToSAX(contentHandler, param.getKey(), param.getValue(), context);
185                    }
186                }
187
188                _widgetParameterToSAX(contentHandler, "wrapped-widget", new I18nizableText(getWidget()), context);
189
190                XMLUtils.endElement(contentHandler, "widget-params");
191            }
192        }
193        else
194        {
195            super._widgetToSAX(contentHandler, context);
196        }
197    }
198    
199    /**
200     * Retrieves the {@link ExternalizableDataProviderExtensionPoint} 
201     * @return the {@link ExternalizableDataProviderExtensionPoint}
202     */
203    protected ExternalizableDataProviderExtensionPoint _getExternalizableDataProviderExtensionPoint()
204    {
205        if (_externalizableDataProviderExtensionPoint == null)
206        {
207            try
208            {
209                _externalizableDataProviderExtensionPoint = (ExternalizableDataProviderExtensionPoint) __serviceManager.lookup(ExternalizableDataProviderExtensionPoint.ROLE);
210            }
211            catch (ServiceException e)
212            {
213                throw new RuntimeException("Unable to lookup after the externalizable data provider extension point", e);
214            }
215        }
216        
217        return _externalizableDataProviderExtensionPoint;
218    }
219}