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 */
016package org.ametys.cms.content.consistency;
017
018import java.io.IOException;
019import java.util.Date;
020import java.util.Map;
021
022import org.apache.avalon.framework.activity.Initializable;
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.cocoon.ProcessingException;
026import org.apache.cocoon.generation.ServiceableGenerator;
027import org.apache.cocoon.xml.AttributesImpl;
028import org.apache.cocoon.xml.XMLUtils;
029import org.xml.sax.SAXException;
030
031import org.ametys.cms.content.references.OutgoingReferences;
032import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
033import org.ametys.cms.contenttype.ContentTypesHelper;
034import org.ametys.cms.repository.Content;
035import org.ametys.cms.repository.ContentQueryHelper;
036import org.ametys.cms.repository.DefaultContent;
037import org.ametys.cms.transformation.ConsistencyChecker;
038import org.ametys.cms.transformation.ConsistencyChecker.CHECK;
039import org.ametys.core.cache.AbstractCacheManager;
040import org.ametys.core.cache.Cache;
041import org.ametys.core.user.User;
042import org.ametys.core.user.UserIdentity;
043import org.ametys.core.user.UserManager;
044import org.ametys.core.util.DateUtils;
045import org.ametys.plugins.repository.AmetysObjectIterable;
046import org.ametys.plugins.repository.AmetysObjectResolver;
047import org.ametys.plugins.repository.RepositoryConstants;
048import org.ametys.plugins.repository.query.expression.Expression;
049import org.ametys.runtime.i18n.I18nizableText;
050
051/**
052 * Generate content with consistency information.<br>
053 * Parameters:
054 * <ul>
055 *  <li>short-test: set to true to make a short test, false to make a full one (default false).</li>
056 *  <li>omit-consistent: set to true to omit consistent contents and generate only contents with unknown or failed consistency information (default false).</li>
057 * </ul>
058 */
059public class GlobalContentConsistencyGenerator extends ServiceableGenerator implements Initializable
060{
061    /** profile rights cache id */
062    private static final String NAME_CACHE = GlobalContentConsistencyGenerator.class.getName() + "$profileRigths";
063    
064    /** The ametys object resolver. */
065    protected AmetysObjectResolver _resolver;
066    
067    /** The consistency checker */
068    protected ConsistencyChecker _consistencyChecker;
069    
070    /** The user manager */
071    protected UserManager _userManager;
072    /** The content type extension point */
073    protected ContentTypeExtensionPoint _cTypeExtPt;
074    /** Helper for content types */
075    protected ContentTypesHelper _cTypesHelper;
076    /** CacheManager used to create and get cache */
077    protected AbstractCacheManager _cacheManager;
078    
079    @Override
080    public void service(ServiceManager serviceManager) throws ServiceException
081    {
082        super.service(serviceManager);
083        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
084        _consistencyChecker = (ConsistencyChecker) serviceManager.lookup(ConsistencyChecker.ROLE);
085        _cTypeExtPt = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
086        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
087        _cTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE);
088        _cacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE);
089    }
090
091
092    public void initialize() throws Exception
093    {
094        _cacheManager.createRequestCache(NAME_CACHE, 
095                new I18nizableText("plugin.cms", "PLUGINS_CMS_CACHE_NAME_LABEL"),
096                new I18nizableText("plugin.cms", "PLUGINS_CMS_CACHE_NAME_DESCRIPTION"),
097                true);
098    }
099    
100    
101    @Override
102    public void generate() throws IOException, SAXException, ProcessingException
103    {
104        boolean shortTest = parameters.getParameterAsBoolean("short-test", false);
105        boolean omitConsistent = parameters.getParameterAsBoolean("omit-consistent", false);
106        
107        contentHandler.startDocument();
108        
109        AttributesImpl atts = new AttributesImpl();
110        
111        atts.addCDATAAttribute("date", DateUtils.dateToString(new Date()));
112        XMLUtils.startElement(contentHandler, "contents", atts);
113        
114        try (AmetysObjectIterable<Content> contents = _getContents())
115        {
116            for (Content content : contents)
117            {
118                int successCount = 0;
119                int unknownCount = 0;
120                int unauthorizedCount = 0;
121                int notFoundCount = 0;
122                int serverErrorCount = 0;
123                
124                Map<String, OutgoingReferences> referencesByPath = content.getOutgoingReferences();
125                
126                for (String dataPath : referencesByPath.keySet())
127                {
128                    OutgoingReferences references = referencesByPath.get(dataPath);
129                    for (String referenceType : references.keySet())
130                    {
131                        for (String referenceValue : references.get(referenceType))
132                        {
133                            CHECK check = CHECK.SERVER_ERROR;
134                            try
135                            {
136                                check = _consistencyChecker.checkConsistency(referenceType, referenceValue, content.getId(), dataPath, shortTest);
137                            }
138                            catch (Exception e)
139                            {
140                                // Ignore, consider it a failure.
141                            }
142                            
143                            switch (check)
144                            {
145                                case SUCCESS:
146                                    successCount++;
147                                    break;
148                                case UNKNOWN:
149                                    unknownCount++;
150                                    break;
151                                case UNAUTHORIZED:
152                                    unauthorizedCount++;
153                                    break;
154                                case NOT_FOUND:
155                                    notFoundCount++;
156                                    break;
157                                case SERVER_ERROR:
158                                default:
159                                    serverErrorCount++;
160                                    break;
161                            }
162                        }
163                    }
164                }
165                
166                if (!omitConsistent || unauthorizedCount > 0 || notFoundCount > 0 || serverErrorCount > 0 || unknownCount > 0)
167                {
168                    _saxContentConsistency(content, successCount, unknownCount, unauthorizedCount, notFoundCount, serverErrorCount);
169                }
170            }
171        }
172        
173        XMLUtils.endElement(contentHandler, "contents");
174        contentHandler.endDocument();
175    }
176
177    /**
178     * Get the contents with inconsistency information.
179     * @return an iterator on contents.
180     */
181    protected AmetysObjectIterable<Content> _getContents()
182    {
183        Expression expression = new ConsistencyExpression();
184        
185        String query = ContentQueryHelper.getContentXPathQuery(expression);
186        
187        return _resolver.query(query);
188    }
189
190    /**
191     * Generate information on content consistency. 
192     * @param content the content.
193     * @param successCount the count of consistent information.
194     * @param unknownCount the count of information of unknown consistency.
195     * @param unauthorizedCount the count of unauthorized links.
196     * @param notFoundCount the count of not found links.
197     * @param serverErrorCount the count of inconsistent information.
198     * @throws SAXException if an errors occurs generating the data.
199     */
200    protected void _saxContentConsistency(Content content, int successCount, int unknownCount, int unauthorizedCount, int notFoundCount, int serverErrorCount) throws SAXException
201    {
202        AttributesImpl atts = new AttributesImpl();
203        atts.addCDATAAttribute("id", content.getId());
204        atts.addCDATAAttribute("title", content.getTitle(null));
205        atts.addCDATAAttribute("smallIcon", _cTypesHelper.getSmallIcon(content));
206        
207        atts.addCDATAAttribute("unauthorized-count", Integer.toString(unauthorizedCount));
208        atts.addCDATAAttribute("not-found-count", Integer.toString(notFoundCount));
209        atts.addCDATAAttribute("server-error-count", Integer.toString(serverErrorCount));
210        atts.addCDATAAttribute("unknown-count", Integer.toString(unknownCount));
211        atts.addCDATAAttribute("success-count", Integer.toString(successCount));
212        
213        _saxAdditionalContentAttributes(content, atts);
214        
215        XMLUtils.startElement(contentHandler, "content", atts);
216        
217        XMLUtils.createElement(contentHandler, "lastModified", DateUtils.dateToString(content.getLastModified()));
218        
219        UserIdentity user = content.getLastContributor();
220        AttributesImpl attr = new AttributesImpl();
221        attr.addAttribute("", "login", "login", "CDATA", user.getLogin());
222        attr.addAttribute("", "populationId", "populationId", "CDATA", user.getPopulationId());
223        XMLUtils.createElement(contentHandler, "contributor", attr, getName(user));
224        
225        XMLUtils.endElement(contentHandler, "content");
226    }
227    
228    /**
229     * Sax additional data on the content 
230     * @param content the content.
231     * @param atts the attributes the will be saxed?
232     */
233    protected void _saxAdditionalContentAttributes (Content content, AttributesImpl atts)
234    {
235        // Nothing to do
236    }
237    
238    /**
239     * Get the user name
240     * @param userIdentity the user
241     * @return the user name
242     */
243    protected String getName(UserIdentity userIdentity)
244    {
245        
246        String name = _getProfilesCache().get(userIdentity);
247        if (name != null)
248        {
249            return name;
250        }
251
252        User user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
253        if (user == null)
254        {
255            return "";
256        }
257
258        name = user.getFullName();
259        _getProfilesCache().put(userIdentity, name);
260        return name;
261    }
262    
263    /**
264     * Expression which tests if contents have consistency informations.
265     */
266    public class ConsistencyExpression implements Expression
267    {
268        @Override
269        public String build()
270        {
271            StringBuilder sb = new StringBuilder();
272            sb.append(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL).append(':').append(DefaultContent.METADATA_ROOT_OUTGOING_REFERENCES);
273            sb.append('/').append(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL).append(':').append(DefaultContent.METADATA_OUTGOING_REFERENCES);
274            sb.append("/*");
275            sb.append("/@").append(RepositoryConstants.NAMESPACE_PREFIX).append(':').append(DefaultContent.METADATA_OUTGOING_REFERENCE_PROPERTY);
276            
277            return sb.toString();
278        }
279    }
280
281    private Cache<UserIdentity, String> _getProfilesCache()
282    {
283        return this._cacheManager.get(NAME_CACHE);
284    }
285}