001/*
002 *  Copyright 2016 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.plugins.workspaces.documents.generators;
017
018import java.io.IOException;
019import java.util.Optional;
020import java.util.Set;
021
022import org.apache.avalon.framework.context.Context;
023import org.apache.avalon.framework.context.ContextException;
024import org.apache.avalon.framework.context.Contextualizable;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.generation.ServiceableGenerator;
029import org.apache.cocoon.xml.AttributesImpl;
030import org.apache.cocoon.xml.XMLUtils;
031import org.apache.commons.lang3.StringUtils;
032import org.xml.sax.SAXException;
033
034import org.ametys.cms.search.Sort;
035import org.ametys.cms.search.Sort.Order;
036import org.ametys.cms.search.query.DocumentTypeQuery;
037import org.ametys.cms.search.solr.SearcherFactory;
038import org.ametys.cms.transformation.xslt.ResolveURIComponent;
039import org.ametys.core.right.RightManager;
040import org.ametys.core.user.CurrentUserProvider;
041import org.ametys.core.user.UserIdentity;
042import org.ametys.core.user.UserManager;
043import org.ametys.core.util.DateUtils;
044import org.ametys.core.util.LambdaUtils;
045import org.ametys.plugins.core.user.UserHelper;
046import org.ametys.plugins.explorer.resources.Resource;
047import org.ametys.plugins.explorer.resources.ResourceCollection;
048import org.ametys.plugins.repository.AmetysObjectIterable;
049import org.ametys.plugins.workspaces.documents.DocumentWorkspaceModule;
050import org.ametys.plugins.workspaces.indexing.solr.SolrWorkspacesConstants;
051import org.ametys.plugins.workspaces.members.ProjectMemberManager;
052import org.ametys.plugins.workspaces.project.ProjectManager;
053import org.ametys.plugins.workspaces.project.ProjectsCatalogueManager;
054import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint;
055import org.ametys.plugins.workspaces.project.objects.Project;
056import org.ametys.web.repository.page.Page;
057
058/**
059 * Document stream generator for the simple service.
060 */
061public class DocumentStreamGenerator extends ServiceableGenerator implements Contextualizable
062{
063    /** The project manager */
064    protected ProjectManager _projectManager;
065    
066    /** The project member manager */
067    protected ProjectMemberManager _projectMemberManager;
068    
069    /** The project catalog helper */
070    protected ProjectsCatalogueManager _projectCatalogManager;
071    
072    /** The document manager */
073    protected DocumentWorkspaceModule _documentModule;
074    
075    /** Current user provider */
076    protected CurrentUserProvider _currentUserProvider;
077    
078    /** The searcher factory */
079    protected SearcherFactory _searcherFactory;
080    
081    /** The user manager */
082    protected UserManager _userManager;
083    
084    /** The user helper */
085    protected UserHelper _userHelper;
086    
087    /** The right manager */
088    protected RightManager _rightManager;
089    
090    /** Avalon context */
091    protected Context _context;
092    
093    @Override
094    public void contextualize(Context context) throws ContextException
095    {
096        _context = context;
097    }
098    
099    @Override
100    public void service(ServiceManager serviceManager) throws ServiceException
101    {
102        _projectManager = (ProjectManager) serviceManager.lookup(ProjectManager.ROLE);
103        _projectMemberManager = (ProjectMemberManager) serviceManager.lookup(ProjectMemberManager.ROLE);
104        _projectCatalogManager = (ProjectsCatalogueManager)  serviceManager.lookup(ProjectsCatalogueManager.ROLE);
105        WorkspaceModuleExtensionPoint moduleManagerEP = (WorkspaceModuleExtensionPoint) serviceManager.lookup(WorkspaceModuleExtensionPoint.ROLE);
106        _documentModule = moduleManagerEP.getModule(DocumentWorkspaceModule.DOCUMENT_MODULE_ID);
107        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
108        _searcherFactory = (SearcherFactory) serviceManager.lookup(SearcherFactory.ROLE);
109        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
110        _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE);
111        _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE);
112    }
113
114    @Override
115    public void generate() throws IOException, SAXException, ProcessingException
116    {
117        int max = parameters.getParameterAsInteger("max-results", 0);
118        
119        contentHandler.startDocument();
120        XMLUtils.startElement(contentHandler, "documents");
121        
122        UserIdentity user = _currentUserProvider.getUser();
123        
124        if (user != null)
125        {
126            _getLastDocuments(user, max).stream().forEach(this::_saxDocument);
127        }
128        
129        XMLUtils.endElement(contentHandler, "documents");
130        contentHandler.endDocument();
131    }
132
133    private AmetysObjectIterable<Resource> _getLastDocuments(UserIdentity user, int max)
134    {
135        try
136        {
137            return _searcherFactory.create()
138                .addFilterQuery(new DocumentTypeQuery(SolrWorkspacesConstants.TYPE_PROJECT_RESOURCE))
139                .setCheckRights(true)
140                .withLimits(0, max > 0 ? max : Integer.MAX_VALUE)
141                .withSort(new Sort("date-for-sorting", Order.DESC))
142                .search();
143        }
144        catch (Exception e)
145        {
146            throw new RuntimeException(String.format("An error occurred while requesting last documents for user '%s'.", user), e);
147        }
148    }
149    
150    /**
151     * SAX necessary document properties for the simple document stream service.
152     * @param resource The resource
153     */
154    protected void _saxDocument(Resource resource)
155    {
156        try
157        {
158            Project project = _projectManager.getParentProject(resource);
159            ResourceCollection folder = resource.getParent();
160            
161            AttributesImpl attrs = new AttributesImpl();
162            
163            attrs.addCDATAAttribute("id", resource.getId());
164            XMLUtils.startElement(contentHandler, "document", attrs);
165            
166            XMLUtils.createElement(contentHandler, "name", resource.getName());
167           
168            attrs.clear();
169            attrs.addCDATAAttribute("id", folder.getId());
170            String documentUrl = _getDocumentUrl(project, folder);
171            attrs.addCDATAAttribute("url", documentUrl);
172            XMLUtils.createElement(contentHandler, "folder", attrs, folder.getName());
173            
174            attrs.clear();
175            attrs.addCDATAAttribute("id", project.getId());
176            Optional<UserIdentity> creator = Optional.ofNullable(resource.getCreator());
177            attrs.addCDATAAttribute("hasAccess", String.valueOf(creator.isPresent() && _projectMemberManager.isProjectMember(project, creator.get())));
178            attrs.addCDATAAttribute("url", _projectManager.getProjectUrl(project, StringUtils.EMPTY));
179            XMLUtils.createElement(contentHandler, "project", attrs, project.getTitle());
180            
181            _projectCatalogManager.saxCategory(contentHandler, project, "projectCategory");
182            
183            // Last modification: date and user
184            XMLUtils.createElement(contentHandler, "lastModified", DateUtils.dateToString(resource.getLastModified()));
185            
186            creator.map(_userManager::getUser)
187                .ifPresent(LambdaUtils.wrapConsumer(lastContributor -> _userHelper.saxUser(lastContributor, contentHandler)));
188            
189            XMLUtils.endElement(contentHandler, "document");
190        }
191        catch (SAXException e)
192        {
193            throw new RuntimeException("An error occurred while gathering the documents' information.");
194        }
195    }
196    
197    /**
198     * Retrieves the URL of a folder in the document module of a project
199     * @param project The project
200     * @param folder The folder
201     * @return The url or null if no module page is found for this project and the current language
202     */
203    protected String _getDocumentUrl(Project project, ResourceCollection folder)
204    {
205        Set<Page> documentModulePages = _projectManager.getModulePages(project, _documentModule);
206        if (!documentModulePages.isEmpty())
207        {
208            Page documentModulePage = documentModulePages.iterator().next();
209            StringBuilder sb = new StringBuilder();
210            sb.append(ResolveURIComponent.resolve("page", documentModulePage.getId()));
211            sb.append("#").append(folder.getId());
212            return sb.toString();
213        }
214        
215        return null;
216    }
217}