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