001/* 002 * Copyright 2015 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.threads; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Optional; 024import java.util.function.Function; 025 026import org.apache.avalon.framework.context.Context; 027import org.apache.avalon.framework.context.ContextException; 028import org.apache.avalon.framework.context.Contextualizable; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.cocoon.components.ContextHelper; 032import org.apache.cocoon.environment.Request; 033import org.apache.commons.io.IOUtils; 034import org.apache.commons.lang.IllegalClassException; 035import org.apache.excalibur.source.Source; 036import org.apache.excalibur.source.SourceResolver; 037 038import org.ametys.core.ui.Callable; 039import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 040import org.ametys.plugins.explorer.resources.ResourceCollection; 041import org.ametys.plugins.explorer.threads.actions.ThreadDAO; 042import org.ametys.plugins.explorer.threads.jcr.JCRPost; 043import org.ametys.plugins.explorer.threads.jcr.JCRThread; 044import org.ametys.plugins.repository.AmetysObject; 045import org.ametys.plugins.repository.AmetysObjectIterable; 046import org.ametys.plugins.repository.AmetysRepositoryException; 047import org.ametys.plugins.workspaces.html.HTMLTransformer; 048import org.ametys.plugins.workspaces.project.ProjectManager; 049import org.ametys.plugins.workspaces.project.modules.WorkspaceModule; 050import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint; 051import org.ametys.plugins.workspaces.project.objects.Project; 052import org.ametys.runtime.plugin.component.PluginAware; 053 054/** 055 * DAO for manipulating thread of a project 056 * 057 */ 058public class WorkspaceThreadDAO extends ThreadDAO implements PluginAware, Contextualizable 059{ 060 /** Avalon Role */ 061 @SuppressWarnings("hiding") 062 public static final String ROLE = WorkspaceThreadDAO.class.getName(); 063 064 private HTMLTransformer _htmlTransformer; 065 private SourceResolver _sourceResolver; 066 private String _pluginName; 067 private ProjectManager _projectManager; 068 069 private Context _context; 070 071 private WorkspaceModuleExtensionPoint _moduleEP; 072 073 @Override 074 public void service(ServiceManager manager) throws ServiceException 075 { 076 super.service(manager); 077 _htmlTransformer = (HTMLTransformer) manager.lookup(HTMLTransformer.ROLE); 078 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 079 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 080 _moduleEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE); 081 } 082 083 @Override 084 public void setPluginInfo(String pluginName, String featureName, String id) 085 { 086 _pluginName = pluginName; 087 } 088 089 @Override 090 public void contextualize(Context context) throws ContextException 091 { 092 _context = context; 093 } 094 095 /** 096 * Get the list of threads of a project 097 * @param includeChildren True to also include threads messages 098 * @return The list of threads 099 */ 100 @Callable 101 public Map<String, Object> getThreadsList(boolean includeChildren) 102 { 103 Request request = ContextHelper.getRequest(_context); 104 String projectName = (String) request.getAttribute("projectName"); 105 106 Project project = _projectManager.getProject(projectName); 107 108 WorkspaceModule module = _moduleEP.getModule(ThreadWorkspaceModule.THREAD_MODULE_ID); 109 ModifiableResourceCollection threadRoot = module.getModuleRoot(project, false); 110 111 Map<String, Object> result = new HashMap<>(); 112 List<Map<String, Object>> threadsList = new ArrayList<>(); 113 if (threadRoot != null) 114 { 115 try (AmetysObjectIterable<AmetysObject> children = threadRoot.getChildren()) 116 { 117 for (AmetysObject ametysObject : children) 118 { 119 if (!(ametysObject instanceof JCRThread)) 120 { 121 throw new IllegalClassException(JCRThread.class, ametysObject.getClass()); 122 } 123 124 JCRThread thread = (JCRThread) ametysObject; 125 if (_rightManager.currentUserHasReadAccess(thread)) 126 { 127 threadsList.add(getThreadData(thread, includeChildren)); 128 } 129 } 130 } 131 } 132 133 result.put("threads", threadsList); 134 return result; 135 } 136 137 /** 138 * Add a thread 139 * @param name The desired name for the thread 140 * @param description The thread description 141 * @return The result map with id, parentId and name keys 142 * @throws IllegalAccessException If the user has no sufficient rights 143 * @throws AmetysRepositoryException If a repository error occurred 144 */ 145 @Callable 146 public Map<String, Object> addThread(String name, String description) throws IllegalAccessException, AmetysRepositoryException 147 { 148 Request request = ContextHelper.getRequest(_context); 149 String projectName = (String) request.getAttribute("projectName"); 150 151 Project project = _projectManager.getProject(projectName); 152 153 WorkspaceModule module = _moduleEP.getModule(ThreadWorkspaceModule.THREAD_MODULE_ID); 154 ModifiableResourceCollection threadRoot = module.getModuleRoot(project, false); 155 156 assert threadRoot != null; 157 158 return addThread(threadRoot.getId(), name, description); 159 } 160 161 @Override 162 protected void setPostContent(JCRPost post, String content) 163 { 164 try 165 { 166 _htmlTransformer.transform(content, post.getContent()); 167 } 168 catch (IOException e) 169 { 170 throw new AmetysRepositoryException("Failed to transform post content into rich text", e); 171 } 172 } 173 174 @Override 175 protected String getPostContent(JCRPost post) throws AmetysRepositoryException 176 { 177 Source contentSource = null; 178 try 179 { 180 Map<String, Object> parameters = new HashMap<>(); 181 parameters.put("source", post.getContent().getInputStream()); 182 contentSource = _sourceResolver.resolveURI("cocoon://_plugins/" + _pluginName + "/convert/html2html", null, parameters); 183 return IOUtils.toString(contentSource.getInputStream(), "UTF-8"); 184 } 185 catch (IOException e) 186 { 187 throw new AmetysRepositoryException("Failed to transform rich text into string", e); 188 } 189 finally 190 { 191 _sourceResolver.release(contentSource); 192 } 193 } 194 195 @Override 196 protected String getPostContentForEditing(JCRPost post) throws AmetysRepositoryException 197 { 198 try 199 { 200 StringBuilder sb = new StringBuilder(); 201 _htmlTransformer.transformForEditing(post.getContent(), sb); 202 return sb.toString(); 203 } 204 catch (IOException e) 205 { 206 throw new AmetysRepositoryException("Failed to transform rich text into string", e); 207 } 208 } 209 210 /** 211 * Get the number of threads in the project 212 * @param project The project 213 * @return The number of threads, or null if the module is not activated 214 */ 215 public Long getThreadsCount(Project project) 216 { 217 Function<Project, ResourceCollection> getModuleRoot = proj -> _moduleEP.getModule(ThreadWorkspaceModule.THREAD_MODULE_ID).getModuleRoot(proj, false); 218 Function<ResourceCollection, Long> countThreads = root -> root.getChildren().stream() 219 .filter(JCRThread.class::isInstance) 220 .count(); 221 222 return Optional.ofNullable(project) 223 .map(getModuleRoot) 224 .map(countThreads) 225 .orElse(null); 226 } 227}