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