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 threadsList.add(getThreadData(thread, includeChildren)); 123 } 124 } 125 126 result.put("threads", threadsList); 127 return result; 128 } 129 130 /** 131 * Add a thread 132 * @param name The desired name for the thread 133 * @param description The thread description 134 * @return The result map with id, parentId and name keys 135 * @throws IllegalAccessException If the user has no sufficient rights 136 * @throws AmetysRepositoryException If a repository error occurred 137 */ 138 @Callable 139 public Map<String, Object> addThread(String name, String description) throws IllegalAccessException, AmetysRepositoryException 140 { 141 Request request = ContextHelper.getRequest(_context); 142 String projectName = (String) request.getAttribute("projectName"); 143 144 Project project = _projectManager.getProject(projectName); 145 146 WorkspaceModule module = _moduleEP.getModule(ThreadWorkspaceModule.THREAD_MODULE_ID); 147 ModifiableResourceCollection threadRoot = module.getModuleRoot(project, false); 148 149 assert threadRoot != null; 150 151 return addThread(threadRoot.getId(), name, description); 152 } 153 154 @Override 155 protected void setPostContent(JCRPost post, String content) 156 { 157 try 158 { 159 _htmlTransformer.transform(content, post.getContent()); 160 } 161 catch (IOException e) 162 { 163 throw new AmetysRepositoryException("Failed to transform post content into rich text", e); 164 } 165 } 166 167 @Override 168 protected String getPostContent(JCRPost post) throws AmetysRepositoryException 169 { 170 Source contentSource = null; 171 try 172 { 173 Map<String, Object> parameters = new HashMap<>(); 174 parameters.put("source", post.getContent().getInputStream()); 175 contentSource = _sourceResolver.resolveURI("cocoon://_plugins/" + _pluginName + "/convert/html2html", null, parameters); 176 return IOUtils.toString(contentSource.getInputStream(), "UTF-8"); 177 } 178 catch (IOException e) 179 { 180 throw new AmetysRepositoryException("Failed to transform rich text into string", e); 181 } 182 finally 183 { 184 _sourceResolver.release(contentSource); 185 } 186 } 187 188 @Override 189 protected String getPostContentForEditing(JCRPost post) throws AmetysRepositoryException 190 { 191 try 192 { 193 StringBuilder sb = new StringBuilder(); 194 _htmlTransformer.transformForEditing(post.getContent(), sb); 195 return sb.toString(); 196 } 197 catch (IOException e) 198 { 199 throw new AmetysRepositoryException("Failed to transform rich text into string", e); 200 } 201 } 202 203 /** 204 * Get the number of threads in the project 205 * @param project The project 206 * @return The number of threads, or null if the module is not activated 207 */ 208 public Long getThreadsCount(Project project) 209 { 210 Function<Project, ResourceCollection> getModuleRoot = proj -> _moduleEP.getModule(ThreadWorkspaceModule.THREAD_MODULE_ID).getModuleRoot(proj, false); 211 Function<ResourceCollection, Long> countThreads = root -> root.getChildren().stream() 212 .filter(JCRThread.class::isInstance) 213 .count(); 214 215 return Optional.ofNullable(project) 216 .map(getModuleRoot) 217 .map(countThreads) 218 .orElse(null); 219 } 220}