001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id:ContentHistoryActionsBean.java 4487 2006-10-19 22:27:14Z janguenot $ 018 */ 019 020package org.nuxeo.ecm.platform.audit.web.listener.ejb; 021 022import static org.jboss.seam.ScopeType.EVENT; 023 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.Comparator; 027import java.util.Date; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031 032import org.apache.commons.lang.StringUtils; 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.jboss.seam.annotations.Factory; 036import org.jboss.seam.annotations.In; 037import org.jboss.seam.annotations.Name; 038import org.jboss.seam.annotations.Scope; 039import org.jboss.seam.annotations.web.RequestParameter; 040import org.nuxeo.ecm.core.api.CoreSession; 041import org.nuxeo.ecm.core.api.DocumentModel; 042import org.nuxeo.ecm.core.api.NuxeoException; 043import org.nuxeo.ecm.core.api.SortInfo; 044import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 045import org.nuxeo.ecm.core.api.event.DocumentEventTypes; 046import org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData; 047import org.nuxeo.ecm.platform.audit.api.FilterMapEntry; 048import org.nuxeo.ecm.platform.audit.api.LogEntry; 049import org.nuxeo.ecm.platform.audit.api.Logs; 050import org.nuxeo.ecm.platform.audit.api.comment.CommentProcessorHelper; 051import org.nuxeo.ecm.platform.audit.api.comment.LinkedDocument; 052import org.nuxeo.ecm.platform.audit.web.listener.ContentHistoryActions; 053import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 054import org.nuxeo.runtime.api.Framework; 055 056/** 057 * Content history actions bean. 058 * <p> 059 * :XXX: http://jira.nuxeo.org/browse/NXP-514 060 * 061 * @author <a href="mailto:ja@nuxeo.com">Julien Anguenot</a> 062 */ 063@Name("contentHistoryActions") 064@Scope(EVENT) 065public class ContentHistoryActionsBean implements ContentHistoryActions { 066 067 private static final long serialVersionUID = -6110545879809627627L; 068 069 private static final String EVENT_DATE = "eventDate"; 070 071 private static final Log log = LogFactory.getLog(ContentHistoryActionsBean.class); 072 073 // @Out(required = false) 074 protected List<LogEntry> logEntries; 075 076 private Map<Long, String> logEntriesComments; 077 078 private Map<Long, LinkedDocument> logEntriesLinkedDocs; 079 080 // :FIXME: Should disappear with Seam 1.1 method params. 081 // @Out(required = false) 082 private List<LogEntry> latestLogEntries; 083 084 // :FIXME: Hardcoded. See interface for more details about the reason 085 protected final int nbLogEntries = 5; 086 087 @In(create = true, required = false) 088 protected transient CoreSession documentManager; 089 090 @In(create = true) 091 private transient NavigationContext navigationContext; 092 093 @RequestParameter("sortColumn") 094 protected String newSortColumn; 095 096 protected SortInfo sortInfo; 097 098 protected Map<String, FilterMapEntry> filterMap = Collections.emptyMap(); 099 100 protected Comparator<LogEntry> comparator; 101 102 public ContentHistoryActionsBean() { 103 // init sorting information 104 sortInfo = new SortInfo(EVENT_DATE, false); 105 } 106 107 @Factory(value = "latestLogEntries", scope = EVENT) 108 public List<LogEntry> computeLatestLogEntries() { 109 if (latestLogEntries == null) { 110 if (logEntries == null) { 111 logEntries = computeLogEntries(navigationContext.getCurrentDocument()); 112 } 113 if (logEntries != null) { 114 if (logEntries.size() > nbLogEntries) { 115 latestLogEntries = new ArrayList<LogEntry>(logEntries.subList(0, nbLogEntries)); 116 } else { 117 latestLogEntries = logEntries; 118 } 119 } 120 } 121 return latestLogEntries; 122 } 123 124 @Factory(value = "logEntries", scope = EVENT) 125 public List<LogEntry> computeLogEntries() { 126 if (logEntries == null) { 127 logEntries = computeLogEntries(navigationContext.getCurrentDocument()); 128 } 129 return logEntries; 130 } 131 132 @Factory(value = "logEntriesComments", scope = EVENT) 133 public Map<Long, String> computeLogEntriesComments() { 134 if (logEntriesComments == null) { 135 computeLogEntries(); 136 postProcessComments(logEntries); 137 } 138 return logEntriesComments; 139 } 140 141 @Factory(value = "logEntriesLinkedDocs", scope = EVENT) 142 public Map<Long, LinkedDocument> computeLogEntrieslinkedDocs() { 143 if (logEntriesLinkedDocs == null) { 144 computeLogEntries(); 145 postProcessComments(logEntries); 146 } 147 return logEntriesLinkedDocs; 148 } 149 150 public List<LogEntry> computeLogEntries(DocumentModel document) { 151 if (document == null) { 152 return null; 153 } else { 154 Logs service = Framework.getLocalService(Logs.class); 155 Logs logsBean = service; 156 /* 157 * In case the document is a proxy,meaning is the result of a publishing,to have the history of the document 158 * from which this proxy was created,first we have to get to the version that was created when the document 159 * was publish,and to which the proxy document indicates,and then from that version we have to get to the 160 * root document. 161 */ 162 boolean doDefaultSort = comparator == null; 163 if (document.isProxy()) { 164 // all users should have access to logs 165 GetVersionInfoForDocumentRunner runner = new GetVersionInfoForDocumentRunner(documentManager, document); 166 runner.runUnrestricted(); 167 if (runner.sourceDocForVersionId == null || runner.version == null) { 168 String message = "An error occurred while grabbing log entries for " + document.getId(); 169 throw new NuxeoException(message); 170 } 171 172 Date versionCreationDate = getCreationDateForVersion(logsBean, runner.version); 173 // add all the logs from the source document until the 174 // version was created 175 addLogEntries(getLogsForDocUntilDate(logsBean, runner.sourceDocForVersionId, versionCreationDate, 176 doDefaultSort)); 177 178 // !! add the first publishing 179 // event after the version is created; since the publishing 180 // event is logged few milliseconds after the version is 181 // created 182 183 List<LogEntry> publishingLogs = getLogsForDocUntilDateWithEvent(logsBean, runner.sourceDocForVersionId, 184 versionCreationDate, DocumentEventTypes.DOCUMENT_PUBLISHED, doDefaultSort); 185 if (!publishingLogs.isEmpty()) { 186 addLogEntry(publishingLogs.get(0)); 187 } 188 // add logs from the actual version 189 filterMap = new HashMap<String, FilterMapEntry>(); 190 addLogEntries(logsBean.getLogEntriesFor(runner.version.getId(), filterMap, doDefaultSort)); 191 192 } else { 193 addLogEntries(logsBean.getLogEntriesFor(document.getId(), filterMap, doDefaultSort)); 194 } 195 196 if (log.isDebugEnabled()) { 197 log.debug("logEntries computed .................!"); 198 } 199 return logEntries; 200 } 201 } 202 203 public String doSearch() { 204 // toggle newOrderDirection 205 if (StringUtils.isEmpty(newSortColumn)) { 206 newSortColumn = EVENT_DATE; 207 } 208 String sortColumn = sortInfo.getSortColumn(); 209 boolean sortAscending = sortInfo.getSortAscending(); 210 if (newSortColumn.equals(sortColumn)) { 211 sortAscending = !sortAscending; 212 } else { 213 sortColumn = newSortColumn; 214 sortAscending = true; 215 } 216 sortInfo = new SortInfo(sortColumn, sortAscending); 217 logEntries = null; 218 return null; 219 } 220 221 /** 222 * Post-process log entries comments to add links. e5e7b4ba-0ffb-492d-8bf2-f2f2e6683ae2 223 */ 224 private void postProcessComments(List<LogEntry> logEntries) { 225 logEntriesComments = new HashMap<Long, String>(); 226 logEntriesLinkedDocs = new HashMap<Long, LinkedDocument>(); 227 228 CommentProcessorHelper cph = new CommentProcessorHelper(documentManager); 229 230 if (logEntries == null) { 231 return; 232 } 233 234 for (LogEntry entry : logEntries) { 235 logEntriesComments.put(entry.getId(), cph.getLogComment(entry)); 236 LinkedDocument linkedDoc = cph.getLogLinkedDocument(entry); 237 if (linkedDoc != null) { 238 logEntriesLinkedDocs.put(entry.getId(), linkedDoc); 239 } 240 } 241 } 242 243 @Deprecated 244 public String getLogComment(LogEntry entry) { 245 CommentProcessorHelper cph = new CommentProcessorHelper(documentManager); 246 return cph.getLogComment(entry); 247 } 248 249 @Deprecated 250 public LinkedDocument getLogLinkedDocument(LogEntry entry) { 251 CommentProcessorHelper cph = new CommentProcessorHelper(documentManager); 252 return cph.getLogLinkedDocument(entry); 253 } 254 255 public SortInfo getSortInfo() { 256 return sortInfo; 257 } 258 259 private Date getCreationDateForVersion(Logs logsService, DocumentModel version) { 260 List<LogEntry> logs = logsService.getLogEntriesFor(version.getId(), filterMap, true); 261 for (LogEntry logEntry : logs) { 262 if (logEntry.getEventId().equals(DocumentEventTypes.DOCUMENT_CREATED)) { 263 return logEntry.getEventDate(); 264 } 265 } 266 return null; 267 } 268 269 private void addLogEntries(List<LogEntry> entries) { 270 if (logEntries != null) { 271 logEntries.addAll(entries); 272 } else { 273 logEntries = entries; 274 } 275 } 276 277 private void addLogEntry(LogEntry entry) { 278 if (logEntries != null) { 279 logEntries.add(entry); 280 } else { 281 logEntries = new ArrayList<LogEntry>(); 282 logEntries.add(entry); 283 } 284 } 285 286 private static FilterMapEntry computeQueryForLogsOnDocUntilDate(Date date) { 287 FilterMapEntry filterByDate = new FilterMapEntry(); 288 filterByDate.setColumnName(BuiltinLogEntryData.LOG_EVENT_DATE); 289 filterByDate.setOperator("<="); 290 filterByDate.setQueryParameterName(BuiltinLogEntryData.LOG_EVENT_DATE); 291 filterByDate.setObject(date); 292 return filterByDate; 293 } 294 295 private static FilterMapEntry computeQueryForLogsOnDocAfterDate(Date date) { 296 FilterMapEntry filterByDate = new FilterMapEntry(); 297 filterByDate.setColumnName(BuiltinLogEntryData.LOG_EVENT_DATE); 298 filterByDate.setOperator(">="); 299 filterByDate.setQueryParameterName(BuiltinLogEntryData.LOG_EVENT_DATE); 300 filterByDate.setObject(date); 301 return filterByDate; 302 } 303 304 private static FilterMapEntry computeQueryForLogsWithEvent(String eventName) { 305 FilterMapEntry filterByDate = new FilterMapEntry(); 306 filterByDate.setColumnName(BuiltinLogEntryData.LOG_EVENT_ID); 307 filterByDate.setOperator("LIKE"); 308 filterByDate.setQueryParameterName(BuiltinLogEntryData.LOG_EVENT_ID); 309 filterByDate.setObject(eventName); 310 return filterByDate; 311 } 312 313 private List<LogEntry> getLogsForDocUntilDate(Logs logsService, String docId, Date date, boolean doDefaultSort) { 314 filterMap = new HashMap<String, FilterMapEntry>(); 315 filterMap.put(BuiltinLogEntryData.LOG_EVENT_DATE, computeQueryForLogsOnDocUntilDate(date)); 316 return logsService.getLogEntriesFor(docId, filterMap, doDefaultSort); 317 } 318 319 private List<LogEntry> getLogsForDocUntilDateWithEvent(Logs logsService, String docId, Date date, String eventName, 320 boolean doDefaultSort) { 321 filterMap = new HashMap<String, FilterMapEntry>(); 322 filterMap.put(BuiltinLogEntryData.LOG_EVENT_DATE, computeQueryForLogsOnDocAfterDate(date)); 323 filterMap.put(BuiltinLogEntryData.LOG_EVENT_ID, computeQueryForLogsWithEvent(eventName)); 324 return logsService.getLogEntriesFor(docId, filterMap, doDefaultSort); 325 326 } 327 328 private class GetVersionInfoForDocumentRunner extends UnrestrictedSessionRunner { 329 330 public String sourceDocForVersionId; 331 332 public DocumentModel version; 333 334 DocumentModel document; 335 336 public GetVersionInfoForDocumentRunner(CoreSession session, DocumentModel document) { 337 super(session); 338 this.document = document; 339 } 340 341 @Override 342 public void run() { 343 version = documentManager.getSourceDocument(document.getRef()); 344 if (version != null) { 345 sourceDocForVersionId = session.getSourceDocument(version.getRef()).getId(); 346 } 347 } 348 } 349 350}