001/* 002 * (C) Copyright 2018-2020 Nuxeo (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 * Funsho David 018 * Nuno Cunha <ncunha@nuxeo.com> 019 */ 020 021package org.nuxeo.ecm.platform.comment.impl; 022 023import static java.util.Collections.singletonMap; 024import static org.nuxeo.ecm.platform.comment.api.AnnotationConstants.ANNOTATION_DOC_TYPE; 025import static org.nuxeo.ecm.platform.comment.api.AnnotationConstants.ANNOTATION_XPATH_PROPERTY; 026import static org.nuxeo.ecm.platform.comment.api.CommentConstants.COMMENT_SCHEMA; 027import static org.nuxeo.ecm.platform.comment.api.CommentManager.Feature.COMMENTS_ARE_SPECIAL_CHILDREN; 028import static org.nuxeo.ecm.platform.comment.api.CommentManager.Feature.COMMENTS_LINKED_WITH_PROPERTY; 029import static org.nuxeo.ecm.platform.comment.impl.AbstractCommentManager.COMMENTS_DIRECTORY; 030import static org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider.CORE_SESSION_PROPERTY; 031 032import java.io.Serializable; 033import java.util.List; 034import java.util.Map; 035import java.util.stream.Collectors; 036import java.util.stream.Stream; 037 038import org.nuxeo.ecm.core.api.CoreInstance; 039import org.nuxeo.ecm.core.api.CoreSession; 040import org.nuxeo.ecm.core.api.DocumentModel; 041import org.nuxeo.ecm.core.api.DocumentNotFoundException; 042import org.nuxeo.ecm.core.api.DocumentRef; 043import org.nuxeo.ecm.core.api.IdRef; 044import org.nuxeo.ecm.core.api.security.SecurityConstants; 045import org.nuxeo.ecm.platform.comment.api.Annotation; 046import org.nuxeo.ecm.platform.comment.api.AnnotationService; 047import org.nuxeo.ecm.platform.comment.api.CommentManager; 048import org.nuxeo.ecm.platform.comment.api.exceptions.CommentNotFoundException; 049import org.nuxeo.ecm.platform.comment.api.exceptions.CommentSecurityException; 050import org.nuxeo.ecm.platform.query.api.PageProvider; 051import org.nuxeo.ecm.platform.query.api.PageProviderService; 052import org.nuxeo.runtime.api.Framework; 053import org.nuxeo.runtime.model.DefaultComponent; 054 055/** 056 * @since 10.1 057 */ 058public class AnnotationServiceImpl extends DefaultComponent implements AnnotationService { 059 060 /** @deprecated since 11.1, because unused. */ 061 @Deprecated(since = "11.1") 062 protected static final String GET_ANNOTATION_PAGEPROVIDER_NAME = "GET_ANNOTATION_AS_EXTERNAL_ENTITY"; 063 064 /** @deprecated since 11.1, because unused. */ 065 @Deprecated(since = "11.1") 066 @SuppressWarnings("DeprecatedIsStillUsed") 067 protected static final String GET_ANNOTATIONS_FOR_DOC_PAGEPROVIDER_NAME = "GET_ANNOTATIONS_FOR_DOCUMENT"; 068 069 /** @since 11.1 */ 070 protected static final String GET_ANNOTATIONS_FOR_DOCUMENT_PAGE_PROVIDER_NAME = "GET_ANNOTATIONS_FOR_DOCUMENT_BY_ECM_PARENT"; 071 072 @Override 073 public Annotation createAnnotation(CoreSession session, Annotation annotation) throws CommentSecurityException { 074 return (Annotation) Framework.getService(CommentManager.class).createComment(session, annotation); 075 } 076 077 @Override 078 public Annotation getAnnotation(CoreSession s, String annotationId) 079 throws CommentNotFoundException, CommentSecurityException { 080 return (Annotation) Framework.getService(CommentManager.class).getComment(s, annotationId); 081 } 082 083 @Override 084 public List<Annotation> getAnnotations(CoreSession session, String documentId, String xpath) 085 throws CommentNotFoundException, CommentSecurityException { 086 DocumentRef docRef = new IdRef(documentId); 087 try { 088 if (!session.hasPermission(docRef, SecurityConstants.READ)) { 089 throw new CommentSecurityException("The user " + session.getPrincipal().getName() 090 + " does not have access to the annotations of document " + documentId); 091 } 092 } catch (DocumentNotFoundException dnfe) { 093 throw new CommentNotFoundException(String.format("The document %s does not exist.", docRef), dnfe); 094 } 095 return streamAnnotations(session, documentId, xpath).map(doc -> doc.getAdapter(Annotation.class)) 096 .collect(Collectors.toList()); 097 } 098 099 protected Stream<DocumentModel> streamAnnotations(CoreSession session, String documentId, String xpath) { 100 DocumentModel annotatedDoc = session.getDocument(new IdRef(documentId)); 101 CommentManager commentManager = Framework.getService(CommentManager.class); 102 return CoreInstance.doPrivileged(session, s -> { 103 if (commentManager.hasFeature(COMMENTS_LINKED_WITH_PROPERTY)) { 104 Map<String, Serializable> props = Map.of(CORE_SESSION_PROPERTY, (Serializable) s); 105 List<DocumentModel> docs = getPageProviderPage(GET_ANNOTATIONS_FOR_DOC_PAGEPROVIDER_NAME, props, documentId, xpath); 106 docs.forEach(doc -> doc.detach(true)); // due to privileged session 107 return docs; 108 } else if (commentManager.hasFeature(COMMENTS_ARE_SPECIAL_CHILDREN)) { 109 // handle first comment/reply cases 110 String parentId = documentId; 111 if (!annotatedDoc.hasSchema(COMMENT_SCHEMA) && s.hasChild(annotatedDoc.getRef(), COMMENTS_DIRECTORY)) { 112 DocumentModel commentsFolder = s.getChild(annotatedDoc.getRef(), COMMENTS_DIRECTORY); 113 parentId = commentsFolder.getId(); 114 } 115 // when comments are special children we can leverage inherited acls 116 Map<String, Serializable> props = Map.of(CORE_SESSION_PROPERTY, (Serializable) session); 117 return getPageProviderPage(GET_ANNOTATIONS_FOR_DOCUMENT_PAGE_PROVIDER_NAME, props, parentId, xpath); 118 } 119 // provide a poor support for deprecated implementations 120 return commentManager.getComments(session, annotatedDoc) 121 .stream() 122 .filter(annotationModel -> ANNOTATION_DOC_TYPE.equals(annotationModel.getType()) 123 && xpath.equals(annotationModel.getPropertyValue(ANNOTATION_XPATH_PROPERTY))) 124 .collect(Collectors.toList()); 125 }).stream(); 126 } 127 128 @SuppressWarnings("unchecked") 129 protected List<DocumentModel> getPageProviderPage(String ppName, Map<String, Serializable> props, 130 Object... parameters) { 131 var ppService = Framework.getService(PageProviderService.class); 132 var pageProvider = (PageProvider<DocumentModel>) ppService.getPageProvider(ppName, null, null, null, props, 133 parameters); 134 return pageProvider.getCurrentPage(); 135 } 136 137 @Override 138 public void updateAnnotation(CoreSession session, String annotationId, Annotation annotation) 139 throws CommentNotFoundException, CommentSecurityException { 140 Framework.getService(CommentManager.class).updateComment(session, annotationId, annotation); 141 } 142 143 @Override 144 public void deleteAnnotation(CoreSession session, String annotationId) throws CommentNotFoundException { 145 Framework.getService(CommentManager.class).deleteComment(session, annotationId); 146 } 147 148 @Override 149 public Annotation getExternalAnnotation(CoreSession session, String documentId, String entityId) 150 throws CommentNotFoundException, CommentSecurityException { 151 return (Annotation) Framework.getService(CommentManager.class) 152 .getExternalComment(session, documentId, entityId); 153 } 154 155 @Override 156 public Annotation updateExternalAnnotation(CoreSession session, String documentId, String entityId, 157 Annotation annotation) throws CommentNotFoundException, CommentSecurityException { 158 return (Annotation) Framework.getService(CommentManager.class) 159 .updateExternalComment(session, documentId, entityId, annotation); 160 } 161 162 @Override 163 public void deleteExternalAnnotation(CoreSession session, String documentId, String entityId) 164 throws CommentNotFoundException, CommentSecurityException { 165 Framework.getService(CommentManager.class).deleteExternalComment(session, documentId, entityId); 166 } 167 168 /** 169 * @deprecated since 11.1. No used any more. 170 */ 171 @SuppressWarnings("unchecked") 172 @Deprecated(since = "11.1", forRemoval = true) 173 protected DocumentModel getAnnotationModel(CoreSession session, String entityId) { 174 PageProviderService ppService = Framework.getService(PageProviderService.class); 175 Map<String, Serializable> props = singletonMap(CORE_SESSION_PROPERTY, (Serializable) session); 176 List<DocumentModel> results = ((PageProvider<DocumentModel>) ppService.getPageProvider( 177 GET_ANNOTATION_PAGEPROVIDER_NAME, null, 1L, 0L, props, entityId)).getCurrentPage(); 178 if (results.isEmpty()) { 179 return null; 180 } 181 return results.get(0); 182 } 183 184}