001/* 002 * (C) Copyright 2018 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 <[email protected]> 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.CommentManager.Feature.COMMENTS_LINKED_WITH_PROPERTY; 027import static org.nuxeo.ecm.platform.comment.api.ExternalEntityConstants.EXTERNAL_ENTITY_FACET; 028import static org.nuxeo.ecm.platform.comment.workflow.utils.CommentsConstants.COMMENT_AUTHOR; 029import static org.nuxeo.ecm.platform.comment.workflow.utils.CommentsConstants.COMMENT_PARENT_ID; 030import static org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider.CORE_SESSION_PROPERTY; 031 032import java.io.Serializable; 033import java.util.Collections; 034import java.util.List; 035import java.util.Map; 036import java.util.stream.Collectors; 037 038import org.apache.commons.logging.Log; 039import org.apache.commons.logging.LogFactory; 040import org.nuxeo.ecm.core.api.CoreInstance; 041import org.nuxeo.ecm.core.api.CoreSession; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.DocumentNotFoundException; 044import org.nuxeo.ecm.core.api.DocumentRef; 045import org.nuxeo.ecm.core.api.IdRef; 046import org.nuxeo.ecm.core.api.NuxeoPrincipal; 047import org.nuxeo.ecm.core.api.security.SecurityConstants; 048import org.nuxeo.ecm.platform.comment.api.Annotation; 049import org.nuxeo.ecm.platform.comment.api.AnnotationService; 050import org.nuxeo.ecm.platform.comment.api.CommentManager; 051import org.nuxeo.ecm.platform.comment.api.Comments; 052import org.nuxeo.ecm.platform.comment.api.ExternalEntity; 053import org.nuxeo.ecm.platform.comment.api.exceptions.CommentNotFoundException; 054import org.nuxeo.ecm.platform.comment.api.exceptions.CommentSecurityException; 055import org.nuxeo.ecm.platform.query.api.PageProvider; 056import org.nuxeo.ecm.platform.query.api.PageProviderService; 057import org.nuxeo.runtime.api.Framework; 058import org.nuxeo.runtime.model.DefaultComponent; 059 060/** 061 * @since 10.1 062 */ 063public class AnnotationServiceImpl extends DefaultComponent implements AnnotationService { 064 065 private static final Log log = LogFactory.getLog(AnnotationServiceImpl.class); 066 067 protected static final String GET_ANNOTATION_PAGEPROVIDER_NAME = "GET_ANNOTATION_AS_EXTERNAL_ENTITY"; 068 069 protected static final String GET_ANNOTATIONS_FOR_DOC_PAGEPROVIDER_NAME = "GET_ANNOTATIONS_FOR_DOCUMENT"; 070 071 @Override 072 public Annotation createAnnotation(CoreSession session, Annotation annotation) throws CommentSecurityException { 073 String parentId = annotation.getParentId(); 074 if (!session.hasPermission(new IdRef(parentId), SecurityConstants.READ)) { 075 throw new CommentSecurityException("The user " + session.getPrincipal().getName() 076 + " can not create annotations on document " + parentId); 077 } 078 return CoreInstance.doPrivileged(session, s -> { 079 // Create base comment in the annotation 080 DocumentModel docToAnnotate = s.getDocument(new IdRef(annotation.getParentId())); 081 DocumentModel annotationModel = s.createDocumentModel(ANNOTATION_DOC_TYPE); 082 Comments.annotationToDocumentModel(annotation, annotationModel); 083 if (annotation instanceof ExternalEntity) { 084 annotationModel.addFacet(EXTERNAL_ENTITY_FACET); 085 Comments.externalEntityToDocumentModel((ExternalEntity) annotation, annotationModel); 086 } 087 annotationModel = Framework.getService(CommentManager.class).createComment(docToAnnotate, annotationModel); 088 return Comments.newAnnotation(annotationModel); 089 }); 090 } 091 092 @Override 093 public Annotation getAnnotation(CoreSession session, String annotationId) 094 throws CommentNotFoundException, CommentSecurityException { 095 DocumentRef annotationRef = new IdRef(annotationId); 096 if (!session.exists(annotationRef)) { 097 throw new CommentNotFoundException("The document " + annotationId + " does not exist."); 098 } 099 NuxeoPrincipal principal = session.getPrincipal(); 100 return CoreInstance.doPrivileged(session, s -> { 101 DocumentModel annotationModel = s.getDocument(annotationRef); 102 String parentId = (String) annotationModel.getPropertyValue(COMMENT_PARENT_ID); 103 if (!s.hasPermission(principal, new IdRef(parentId), SecurityConstants.READ)) { 104 throw new CommentSecurityException("The user " + principal.getName() 105 + " does not have access to the annotations of document " + parentId); 106 } 107 return Comments.newAnnotation(annotationModel); 108 }); 109 } 110 111 @Override 112 @SuppressWarnings("unchecked") 113 public List<Annotation> getAnnotations(CoreSession session, String documentId, String xpath) 114 throws CommentNotFoundException, CommentSecurityException { 115 DocumentRef docRef = new IdRef(documentId); 116 if (!session.exists(docRef)) { 117 throw new CommentNotFoundException("The document " + documentId + " does not exist."); 118 } 119 if (!session.hasPermission(docRef, SecurityConstants.READ)) { 120 throw new CommentSecurityException("The user " + session.getPrincipal().getName() 121 + " does not have access to the annotations of document " + documentId); 122 } 123 DocumentModel annotatedDoc = session.getDocument(docRef); 124 CommentManager commentManager = Framework.getService(CommentManager.class); 125 if (commentManager.hasFeature(COMMENTS_LINKED_WITH_PROPERTY)) { 126 PageProviderService ppService = Framework.getService(PageProviderService.class); 127 return CoreInstance.doPrivileged(session, s -> { 128 Map<String, Serializable> props = Collections.singletonMap(CORE_SESSION_PROPERTY, (Serializable) s); 129 PageProvider<DocumentModel> pageProvider = (PageProvider<DocumentModel>) ppService.getPageProvider( 130 GET_ANNOTATIONS_FOR_DOC_PAGEPROVIDER_NAME, null, null, null, props, documentId, xpath); 131 return pageProvider.getCurrentPage().stream().map(Comments::newAnnotation).collect(Collectors.toList()); 132 }); 133 } 134 return CoreInstance.doPrivileged(session, s -> { 135 return commentManager 136 .getComments(s, annotatedDoc) 137 .stream() 138 .filter(annotationModel -> ANNOTATION_DOC_TYPE.equals(annotationModel.getType()) 139 && xpath.equals(annotationModel.getPropertyValue(ANNOTATION_XPATH_PROPERTY))) 140 .map(Comments::newAnnotation) 141 .collect(Collectors.toList()); 142 }); 143 } 144 145 @Override 146 public void updateAnnotation(CoreSession session, String annotationId, Annotation annotation) 147 throws CommentNotFoundException, CommentSecurityException { 148 IdRef annotationRef = new IdRef(annotationId); 149 if (!session.exists(annotationRef)) { 150 throw new CommentNotFoundException("The annotation " + annotationId + " does not exist."); 151 } 152 NuxeoPrincipal principal = session.getPrincipal(); 153 if (!principal.isAdministrator() && !annotation.getAuthor().equals(principal.getName())) { 154 throw new CommentSecurityException("The user " + principal.getName() 155 + " can not edit annotations of document " + annotation.getParentId()); 156 } 157 CoreInstance.doPrivileged(session, s -> { 158 DocumentModel annotationModel = s.getDocument(annotationRef); 159 Comments.annotationToDocumentModel(annotation, annotationModel); 160 if (annotation instanceof ExternalEntity) { 161 Comments.externalEntityToDocumentModel((ExternalEntity) annotation, annotationModel); 162 } 163 s.saveDocument(annotationModel); 164 }); 165 } 166 167 @Override 168 public void deleteAnnotation(CoreSession session, String annotationId) throws CommentNotFoundException { 169 NuxeoPrincipal principal = session.getPrincipal(); 170 CoreInstance.doPrivileged(session, s -> { 171 try { 172 DocumentModel annotation = s.getDocument(new IdRef(annotationId)); 173 String parentId = (String) annotation.getPropertyValue(COMMENT_PARENT_ID); 174 DocumentRef parentRef = new IdRef(parentId); 175 if (!principal.isAdministrator() 176 && !annotation.getPropertyValue(COMMENT_AUTHOR).equals(principal.getName()) 177 && !s.hasPermission(principal, parentRef, SecurityConstants.EVERYTHING)) { 178 throw new CommentSecurityException( 179 "The user " + principal.getName() + " can not delete annotations of document " + parentId); 180 } 181 } catch (DocumentNotFoundException e) { 182 throw new CommentNotFoundException(e); 183 } 184 Framework.getService(CommentManager.class).deleteComment(s, annotationId); 185 }); 186 } 187 188 @Override 189 public Annotation getExternalAnnotation(CoreSession session, String entityId) 190 throws CommentNotFoundException, CommentSecurityException { 191 DocumentModel annotationModel = getAnnotationModel(session, entityId); 192 if (annotationModel == null) { 193 throw new CommentNotFoundException("The external annotation " + entityId + " does not exist."); 194 } 195 String parentId = (String) annotationModel.getPropertyValue(COMMENT_PARENT_ID); 196 if (!session.hasPermission(new IdRef(parentId), SecurityConstants.READ)) { 197 throw new CommentSecurityException("The user " + session.getPrincipal().getName() 198 + " does not have access to the annotations of document " + parentId); 199 } 200 return Comments.newAnnotation(annotationModel); 201 } 202 203 @Override 204 public void updateExternalAnnotation(CoreSession session, String entityId, Annotation annotation) 205 throws CommentNotFoundException, CommentSecurityException { 206 NuxeoPrincipal principal = session.getPrincipal(); 207 if (!principal.isAdministrator() && !annotation.getAuthor().equals(principal.getName())) { 208 throw new CommentSecurityException("The user " + session.getPrincipal().getName() 209 + " can not edit annotations of document " + annotation.getParentId()); 210 } 211 DocumentModel annotationModel = getAnnotationModel(session, entityId); 212 if (annotationModel == null) { 213 throw new CommentNotFoundException("The external annotation " + entityId + " does not exist."); 214 } 215 Comments.annotationToDocumentModel(annotation, annotationModel); 216 if (annotation instanceof ExternalEntity) { 217 Comments.externalEntityToDocumentModel((ExternalEntity) annotation, annotationModel); 218 } 219 session.saveDocument(annotationModel); 220 } 221 222 @Override 223 public void deleteExternalAnnotation(CoreSession session, String entityId) 224 throws CommentNotFoundException, CommentSecurityException { 225 DocumentModel annotationModel = getAnnotationModel(session, entityId); 226 if (annotationModel == null) { 227 throw new CommentNotFoundException("The external annotation " + entityId + " does not exist."); 228 } 229 NuxeoPrincipal principal = session.getPrincipal(); 230 String parentId = (String) annotationModel.getPropertyValue(COMMENT_PARENT_ID); 231 if (!principal.isAdministrator() 232 && !annotationModel.getPropertyValue(COMMENT_AUTHOR).equals(principal.getName()) 233 && !session.hasPermission(new IdRef(parentId), SecurityConstants.EVERYTHING)) { 234 throw new CommentSecurityException( 235 "The user " + principal.getName() + " can not delete annotations of document " + parentId); 236 } 237 Framework.getService(CommentManager.class).deleteComment(session, annotationModel.getId()); 238 } 239 240 @SuppressWarnings("unchecked") 241 protected DocumentModel getAnnotationModel(CoreSession session, String entityId) { 242 PageProviderService ppService = Framework.getService(PageProviderService.class); 243 Map<String, Serializable> props = singletonMap(CORE_SESSION_PROPERTY, (Serializable) session); 244 List<DocumentModel> results = ((PageProvider<DocumentModel>) ppService.getPageProvider( 245 GET_ANNOTATION_PAGEPROVIDER_NAME, null, 1L, 0L, props, entityId)).getCurrentPage(); 246 if (results.isEmpty()) { 247 return null; 248 } 249 return results.get(0); 250 } 251 252}