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 * 019 */ 020 021package org.nuxeo.ecm.annotation; 022 023import static org.nuxeo.ecm.annotation.AnnotationConstants.ANNOTATION_DOCUMENT_ID_PROPERTY; 024import static org.nuxeo.ecm.annotation.AnnotationConstants.ANNOTATION_DOC_TYPE; 025import static org.nuxeo.ecm.annotation.AnnotationConstants.ANNOTATION_ENTITY_PROPERTY; 026import static org.nuxeo.ecm.annotation.AnnotationConstants.ANNOTATION_ID_PROPERTY; 027import static org.nuxeo.ecm.annotation.AnnotationConstants.ANNOTATION_XPATH_PROPERTY; 028 029import java.io.Serializable; 030import java.util.Collections; 031import java.util.List; 032import java.util.Map; 033import java.util.stream.Collectors; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.nuxeo.ecm.core.api.CoreInstance; 038import org.nuxeo.ecm.core.api.CoreSession; 039import org.nuxeo.ecm.core.api.DocumentModel; 040import org.nuxeo.ecm.core.api.IdRef; 041import org.nuxeo.ecm.core.api.PathRef; 042import org.nuxeo.ecm.platform.query.api.PageProvider; 043import org.nuxeo.ecm.platform.query.api.PageProviderService; 044import org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider; 045import org.nuxeo.runtime.api.Framework; 046import org.nuxeo.runtime.model.DefaultComponent; 047import org.nuxeo.runtime.services.config.ConfigurationService; 048 049/** 050 * @since 10.1 051 */ 052public class AnnotationServiceImpl extends DefaultComponent implements AnnotationService { 053 054 private static final Log log = LogFactory.getLog(AnnotationServiceImpl.class); 055 056 protected static final String ANNOTATION_NAME = "annotation"; 057 058 protected static final String GET_ANNOTATION_PAGEPROVIDER_NAME = "GET_ANNOTATION"; 059 060 protected static final String GET_ANNOTATIONS_FOR_DOC_PAGEPROVIDER_NAME = "GET_ANNOTATIONS_FOR_DOCUMENT"; 061 062 protected static final String ANNOTATIONS_PLACELESS_STORAGE_PROPERTY = "nuxeo.annotations.placeless.storage"; 063 064 protected static final String HIDDEN_FOLDER_TYPE = "HiddenFolder"; 065 066 protected static final String ANNOTATION_FOLDER_NAME = "Annotations"; 067 068 @Override 069 public Annotation createAnnotation(CoreSession session, Annotation annotation) { 070 071 ConfigurationService configurationService = Framework.getService(ConfigurationService.class); 072 boolean annotationPlaceless = configurationService.isBooleanPropertyTrue( 073 ANNOTATIONS_PLACELESS_STORAGE_PROPERTY); 074 075 return CoreInstance.doPrivileged(session, s -> { 076 String path = null; 077 if (!annotationPlaceless) { 078 // Create or retrieve the folder to store the annotation. 079 // If the document is under a domain, the folder is a child of this domain. 080 // Otherwise, it is a child of the root document. 081 DocumentModel annotatedDoc = s.getDocument(new IdRef(annotation.getDocumentId())); 082 String parentPath = s.getRootDocument().getPathAsString(); 083 if (annotatedDoc.getPath().segmentCount() > 1) { 084 parentPath += annotatedDoc.getPath().segment(0); 085 } 086 PathRef ref = new PathRef(parentPath, ANNOTATION_FOLDER_NAME); 087 DocumentModel annotationFolderDoc = s.createDocumentModel(parentPath, ANNOTATION_FOLDER_NAME, 088 HIDDEN_FOLDER_TYPE); 089 s.getOrCreateDocument(annotationFolderDoc); 090 s.save(); 091 path = ref.toString(); 092 } 093 DocumentModel annotationModel = s.createDocumentModel(path, ANNOTATION_NAME, ANNOTATION_DOC_TYPE); 094 setAnnotationProperties(annotationModel, annotation); 095 annotationModel = s.createDocument(annotationModel); 096 return new AnnotationImpl(annotationModel); 097 }); 098 } 099 100 @Override 101 public Annotation getAnnotation(CoreSession session, String annotationId) { 102 return CoreInstance.doPrivileged(session, s -> { 103 DocumentModel annotationModel = getAnnotationModel(s, annotationId); 104 if (annotationModel == null) { 105 return null; 106 } 107 return new AnnotationImpl(annotationModel); 108 }); 109 } 110 111 @Override 112 @SuppressWarnings("unchecked") 113 public List<Annotation> getAnnotations(CoreSession session, String documentId, String xpath) { 114 return CoreInstance.doPrivileged(session, s -> { 115 PageProviderService ppService = Framework.getService(PageProviderService.class); 116 Map<String, Serializable> props = Collections.singletonMap( 117 CoreQueryAndFetchPageProvider.CORE_SESSION_PROPERTY, (Serializable) s); 118 List<DocumentModel> annotationList = // 119 ((PageProvider<DocumentModel>) ppService.getPageProvider(GET_ANNOTATIONS_FOR_DOC_PAGEPROVIDER_NAME, 120 null, null, null, props, documentId, xpath)).getCurrentPage(); 121 return annotationList.stream().map(AnnotationImpl::new).collect(Collectors.toList()); 122 }); 123 } 124 125 @Override 126 public void updateAnnotation(CoreSession session, Annotation annotation) { 127 CoreInstance.doPrivileged(session, s -> { 128 DocumentModel annotationModel = getAnnotationModel(s, annotation.getId()); 129 if (annotationModel == null) { 130 if (log.isWarnEnabled()) { 131 log.warn("The annotation " + annotation.getId() + " on document blob " + annotation.getXpath() 132 + " does not exist. Update operation is ignored."); 133 } 134 return; 135 } 136 setAnnotationProperties(annotationModel, annotation); 137 s.saveDocument(annotationModel); 138 }); 139 } 140 141 @Override 142 public void deleteAnnotation(CoreSession session, String annotationId) throws IllegalArgumentException { 143 CoreInstance.doPrivileged(session, s -> { 144 DocumentModel annotationModel = getAnnotationModel(s, annotationId); 145 if (annotationModel == null) { 146 throw new IllegalArgumentException("The annotation " + annotationId + " does not exist."); 147 } 148 s.removeDocument(annotationModel.getRef()); 149 }); 150 } 151 152 protected void setAnnotationProperties(DocumentModel annotationModel, Annotation annotation) { 153 annotationModel.setPropertyValue(ANNOTATION_ID_PROPERTY, annotation.getId()); 154 annotationModel.setPropertyValue(ANNOTATION_DOCUMENT_ID_PROPERTY, annotation.getDocumentId()); 155 annotationModel.setPropertyValue(ANNOTATION_XPATH_PROPERTY, annotation.getXpath()); 156 annotationModel.setPropertyValue(ANNOTATION_ENTITY_PROPERTY, annotation.getEntity()); 157 } 158 159 /** 160 * Session must be privileged. 161 */ 162 @SuppressWarnings("unchecked") 163 protected DocumentModel getAnnotationModel(CoreSession session, String annotationId) { 164 PageProviderService ppService = Framework.getService(PageProviderService.class); 165 Map<String, Serializable> props = Collections.singletonMap(CoreQueryAndFetchPageProvider.CORE_SESSION_PROPERTY, 166 (Serializable) session); 167 List<DocumentModel> results = ((PageProvider<DocumentModel>) ppService.getPageProvider( 168 GET_ANNOTATION_PAGEPROVIDER_NAME, null, null, null, props, annotationId)).getCurrentPage(); 169 if (results.isEmpty()) { 170 return null; 171 } 172 return results.get(0); 173 } 174 175}