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 (OutgoingReferences references : referencesByPath.values())
127                {
128                    for (String referenceType : references.keySet())
129                    {
130                        for (String referenceValue : references.get(referenceType))
131                        {
132                            CHECK check = CHECK.SERVER_ERROR;
133                            try
134                            {
135                                check = _consistencyChecker.checkConsistency(referenceType, referenceValue, shortTest);
136                            }
137                            catch (Exception e)
138                            {
139                                // Ignore, consider it a failure.
140                            }
141                            
142                            switch (check)
143                            {
144                                case SUCCESS:
145                                    successCount++;
146                                    break;
147                                case UNKNOWN:
148                                    unknownCount++;
149                                    break;
150                                case UNAUTHORIZED:
151                                    unauthorizedCount++;
152                                    break;
153                                case NOT_FOUND:
154                                    notFoundCount++;
155                                    break;
156                                case SERVER_ERROR:
157                                default:
158                                    serverErrorCount++;
159                                    break;
160                            }
161                        }
162                    }
163                }
164                
165                if (!omitConsistent || unauthorizedCount > 0 || notFoundCount > 0 || serverErrorCount > 0 || unknownCount > 0)
166                {
167                    _saxContentConsistency(content, successCount, unknownCount, unauthorizedCount, notFoundCount, serverErrorCount);
168                }
169            }
170        }
171        
172        XMLUtils.endElement(contentHandler, "contents");
173        contentHandler.endDocument();
174    }
175
176    /**
177     * Get the contents with inconsistency information.
178     * @return an iterator on contents.
179     */
180    protected AmetysObjectIterable<Content> _getContents()
181    {
182        Expression expression = new ConsistencyExpression();
183        
184        String query = ContentQueryHelper.getContentXPathQuery(expression);
185        
186        return _resolver.query(query);
187    }
188
189    /**
190     * Generate information on content consistency. 
191     * @param content the content.
192     * @param successCount the count of consistent information.
193     * @param unknownCount the count of information of unknown consistency.
194     * @param unauthorizedCount the count of unauthorized links.
195     * @param notFoundCount the count of not found links.
196     * @param serverErrorCount the count of inconsistent information.
197     * @throws SAXException if an errors occurs generating the data.
198     */
199    protected void _saxContentConsistency(Content content, int successCount, int unknownCount, int unauthorizedCount, int notFoundCount, int serverErrorCount) throws SAXException
200    {
201        AttributesImpl atts = new AttributesImpl();
202        atts.addCDATAAttribute("id", content.getId());
203        atts.addCDATAAttribute("title", content.getTitle(null));
204        atts.addCDATAAttribute("smallIcon", _cTypesHelper.getSmallIcon(content));
205        
206        atts.addCDATAAttribute("unauthorized-count", Integer.toString(unauthorizedCount));
207        atts.addCDATAAttribute("not-found-count", Integer.toString(notFoundCount));
208        atts.addCDATAAttribute("server-error-count", Integer.toString(serverErrorCount));
209        atts.addCDATAAttribute("unknown-count", Integer.toString(unknownCount));
210        atts.addCDATAAttribute("success-count", Integer.toString(successCount));
211        
212        _saxAdditionalContentAttributes(content, atts);
213        
214        XMLUtils.startElement(contentHandler, "content", atts);
215        
216        XMLUtils.createElement(contentHandler, "lastModified", DateUtils.dateToString(content.getLastModified()));
217        
218        UserIdentity user = content.getLastContributor();
219        AttributesImpl attr = new AttributesImpl();
220        attr.addAttribute("", "login", "login", "CDATA", user.getLogin());
221        attr.addAttribute("", "populationId", "populationId", "CDATA", user.getPopulationId());
222        XMLUtils.createElement(contentHandler, "contributor", attr, getName(user));
223        
224        XMLUtils.endElement(contentHandler, "content");
225    }
226    
227    /**
228     * Sax additional data on the content 
229     * @param content the content.
230     * @param atts the attributes the will be saxed?
231     */
232    protected void _saxAdditionalContentAttributes (Content content, AttributesImpl atts)
233    {
234        // Nothing to do
235    }
236    
237    /**
238     * Get the user name
239     * @param userIdentity the user
240     * @return the user name
241     */
242    protected String getName(UserIdentity userIdentity)
243    {
244        
245        String name = _getProfilesCache().get(userIdentity);
246        if (name != null)
247        {
248            return name;
249        }
250
251        User user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
252        if (user == null)
253        {
254            return "";
255        }
256
257        name = user.getFullName();
258        _getProfilesCache().put(userIdentity, name);
259        return name;
260    }
261    
262    /**
263     * Expression which tests if contents have consistency informations.
264     */
265    public class ConsistencyExpression implements Expression
266    {
267        @Override
268        public String build()
269        {
270            StringBuilder sb = new StringBuilder();
271            sb.append(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL).append(':').append(DefaultContent.METADATA_ROOT_OUTGOING_REFERENCES);
272            sb.append('/').append(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL).append(':').append(DefaultContent.METADATA_OUTGOING_REFERENCES);
273            sb.append("/*");
274            sb.append("/@").append(RepositoryConstants.NAMESPACE_PREFIX).append(':').append(DefaultContent.METADATA_OUTGOING_REFERENCE_PROPERTY);
275            
276            return sb.toString();
277        }
278    }
279
280    private Cache<UserIdentity, String> _getProfilesCache()
281    {
282        return this._cacheManager.get(NAME_CACHE);
283    }
284}