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