001/* 002 * (C) Copyright 2010-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 * Nuxeo - initial API and implementation 018 */ 019package org.nuxeo.ecm.platform.rendition.service; 020 021import static org.nuxeo.ecm.core.api.security.SecurityConstants.ADD_CHILDREN; 022import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_ID_PROPERTY; 023import static org.nuxeo.ecm.platform.rendition.Constants.RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY; 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.Deque; 029import java.util.HashMap; 030import java.util.LinkedList; 031import java.util.List; 032import java.util.Map; 033import java.util.stream.Collectors; 034 035import javax.script.Invocable; 036import javax.script.ScriptContext; 037import javax.script.ScriptEngine; 038import javax.script.ScriptEngineManager; 039import javax.script.ScriptException; 040 041import org.apache.commons.lang3.StringUtils; 042import org.apache.logging.log4j.LogManager; 043import org.apache.logging.log4j.Logger; 044import org.nuxeo.ecm.core.api.Blob; 045import org.nuxeo.ecm.core.api.CoreInstance; 046import org.nuxeo.ecm.core.api.CoreSession; 047import org.nuxeo.ecm.core.api.DocumentModel; 048import org.nuxeo.ecm.core.api.DocumentRef; 049import org.nuxeo.ecm.core.api.DocumentSecurityException; 050import org.nuxeo.ecm.core.api.IdRef; 051import org.nuxeo.ecm.core.api.IterableQueryResult; 052import org.nuxeo.ecm.core.api.NuxeoException; 053import org.nuxeo.ecm.core.api.NuxeoPrincipal; 054import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 055import org.nuxeo.ecm.core.api.VersioningOption; 056import org.nuxeo.ecm.core.api.local.ClientLoginModule; 057import org.nuxeo.ecm.core.query.sql.NXQL; 058import org.nuxeo.ecm.platform.query.nxql.NXQLQueryBuilder; 059import org.nuxeo.ecm.platform.rendition.Constants; 060import org.nuxeo.ecm.platform.rendition.Rendition; 061import org.nuxeo.ecm.platform.rendition.extension.DefaultAutomationRenditionProvider; 062import org.nuxeo.ecm.platform.rendition.extension.RenditionProvider; 063import org.nuxeo.ecm.platform.rendition.impl.LazyRendition; 064import org.nuxeo.ecm.platform.rendition.impl.LiveRendition; 065import org.nuxeo.ecm.platform.rendition.impl.StoredRendition; 066import org.nuxeo.runtime.model.ComponentContext; 067import org.nuxeo.runtime.model.ComponentInstance; 068import org.nuxeo.runtime.model.DefaultComponent; 069import org.nuxeo.runtime.transaction.TransactionHelper; 070 071/** 072 * Default implementation of {@link RenditionService}. 073 * 074 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a> 075 * @since 5.4.1 076 */ 077public class RenditionServiceImpl extends DefaultComponent implements RenditionService { 078 079 public static final String RENDITION_DEFINITIONS_EP = "renditionDefinitions"; 080 081 public static final String RENDITON_DEFINION_PROVIDERS_EP = "renditionDefinitionProviders"; 082 083 public static final String DEFAULT_RENDITION_EP = "defaultRendition"; 084 085 /** 086 * @since 8.1 087 */ 088 public static final String STORED_RENDITION_MANAGERS_EP = "storedRenditionManagers"; 089 090 private static final Logger log = LogManager.getLogger(RenditionServiceImpl.class); 091 092 /** 093 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 094 */ 095 @Deprecated 096 protected Map<String, RenditionDefinition> renditionDefinitions; 097 098 /** 099 * @since 7.3. 100 */ 101 protected RenditionDefinitionRegistry renditionDefinitionRegistry; 102 103 protected RenditionDefinitionProviderRegistry renditionDefinitionProviderRegistry; 104 105 protected List<DefaultRenditionDescriptor> defaultRenditionDescriptors = new ArrayList<>(); 106 107 protected static final StoredRenditionManager DEFAULT_STORED_RENDITION_MANAGER = new DefaultStoredRenditionManager(); 108 109 /** 110 * @since 8.1 111 */ 112 protected Deque<StoredRenditionManagerDescriptor> storedRenditionManagerDescriptors = new LinkedList<>(); 113 114 protected final ScriptEngineManager scriptEngineManager; 115 116 public RenditionServiceImpl() { 117 scriptEngineManager = new ScriptEngineManager(); 118 } 119 120 /** 121 * @since 8.1 122 */ 123 public StoredRenditionManager getStoredRenditionManager() { 124 StoredRenditionManagerDescriptor descr = storedRenditionManagerDescriptors.peekLast(); 125 return descr == null ? DEFAULT_STORED_RENDITION_MANAGER : descr.getStoredRenditionManager(); 126 } 127 128 @Override 129 public void activate(ComponentContext context) { 130 renditionDefinitions = new HashMap<>(); 131 renditionDefinitionRegistry = new RenditionDefinitionRegistry(); 132 renditionDefinitionProviderRegistry = new RenditionDefinitionProviderRegistry(); 133 super.activate(context); 134 } 135 136 @Override 137 public void deactivate(ComponentContext context) { 138 renditionDefinitions = null; 139 renditionDefinitionRegistry = null; 140 renditionDefinitionProviderRegistry = null; 141 super.deactivate(context); 142 } 143 144 /** 145 * Shoudn't be used since it doesn't take into account the rendition definitions made available for a given document 146 * by the contributed {@link RenditionDefinitionProvider}s. 147 * 148 * @deprecated since 10.10, use {@link #getAvailableRenditionDefinition(DocumentModel, String)} instead 149 */ 150 @Deprecated 151 public RenditionDefinition getRenditionDefinition(String name) { 152 return renditionDefinitionRegistry.getRenditionDefinition(name); 153 } 154 155 /** 156 * @deprecated since 7.2 because unused 157 */ 158 @Override 159 @Deprecated 160 public List<RenditionDefinition> getDeclaredRenditionDefinitions() { 161 return new ArrayList<>(renditionDefinitionRegistry.descriptors.values()); 162 } 163 164 /** 165 * @deprecated since 7.2 because unused 166 */ 167 @Override 168 @Deprecated 169 public List<RenditionDefinition> getDeclaredRenditionDefinitionsForProviderType(String providerType) { 170 List<RenditionDefinition> defs = new ArrayList<>(); 171 for (RenditionDefinition def : getDeclaredRenditionDefinitions()) { 172 if (def.getProviderType().equals(providerType)) { 173 defs.add(def); 174 } 175 } 176 return defs; 177 } 178 179 @Override 180 public List<RenditionDefinition> getAvailableRenditionDefinitions(DocumentModel doc) { 181 182 List<RenditionDefinition> defs = new ArrayList<>(); 183 defs.addAll(renditionDefinitionRegistry.getRenditionDefinitions(doc)); 184 defs.addAll(renditionDefinitionProviderRegistry.getRenditionDefinitions(doc)); 185 186 // XXX what about "lost renditions" ? 187 return defs; 188 } 189 190 @Override 191 public DocumentRef storeRendition(DocumentModel source, String renditionDefinitionName) { 192 Rendition rendition = getRendition(source, renditionDefinitionName, true); 193 return rendition == null ? null : rendition.getHostDocument().getRef(); 194 } 195 196 /** 197 * @deprecated since 8.1 198 */ 199 @Deprecated 200 protected DocumentModel storeRendition(DocumentModel sourceDocument, Rendition rendition, String name) { 201 StoredRendition storedRendition = storeRendition(sourceDocument, rendition); 202 return storedRendition == null ? null : storedRendition.getHostDocument(); 203 } 204 205 /** 206 * @since 8.1 207 * @deprecated since 10.10, use {@link #storeRendition(DocumentModel, Rendition, RenditionDefinition)} instead 208 */ 209 @Deprecated 210 protected StoredRendition storeRendition(DocumentModel sourceDocument, Rendition rendition) { 211 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(sourceDocument, rendition.getName()); 212 return storeRendition(sourceDocument, rendition, renditionDefinition); 213 } 214 215 /** 216 * @since 10.10 217 */ 218 protected StoredRendition storeRendition(DocumentModel sourceDocument, Rendition rendition, 219 RenditionDefinition renditionDefinition) { 220 if (!rendition.isCompleted()) { 221 return null; 222 } 223 List<Blob> renderedBlobs = rendition.getBlobs(); 224 Blob renderedBlob = renderedBlobs.get(0); 225 String mimeType = renderedBlob.getMimeType(); 226 if (mimeType != null 227 && (mimeType.contains(LazyRendition.ERROR_MARKER) || mimeType.contains(LazyRendition.STALE_MARKER))) { 228 return null; 229 } 230 231 CoreSession session = sourceDocument.getCoreSession(); 232 DocumentModel version = null; 233 boolean isVersionable = sourceDocument.isVersionable(); 234 if (sourceDocument.isVersion()) { 235 version = sourceDocument; 236 sourceDocument = session.getDocument(new IdRef(version.getSourceId())); 237 } else if (isVersionable) { 238 DocumentRef versionRef = createVersionIfNeeded(sourceDocument, session); 239 version = session.getDocument(versionRef); 240 } 241 242 return getStoredRenditionManager().createStoredRendition(sourceDocument, version, renderedBlob, 243 renditionDefinition); 244 } 245 246 protected DocumentRef createVersionIfNeeded(DocumentModel source, CoreSession session) { 247 if (source.isVersionable()) { 248 if (source.isVersion()) { 249 return source.getRef(); 250 } else if (source.isCheckedOut()) { 251 DocumentRef versionRef = session.checkIn(source.getRef(), VersioningOption.MINOR, null); 252 source.refresh(DocumentModel.REFRESH_STATE, null); 253 return versionRef; 254 } else { 255 return session.getLastDocumentVersionRef(source.getRef()); 256 } 257 } 258 return null; 259 } 260 261 @Override 262 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 263 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 264 RenditionDefinition renditionDefinition = (RenditionDefinition) contribution; 265 renditionDefinitionRegistry.addContribution(renditionDefinition); 266 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 267 renditionDefinitionProviderRegistry.addContribution((RenditionDefinitionProviderDescriptor) contribution); 268 } else if (STORED_RENDITION_MANAGERS_EP.equals(extensionPoint)) { 269 storedRenditionManagerDescriptors.add(((StoredRenditionManagerDescriptor) contribution)); 270 } else if (DEFAULT_RENDITION_EP.equals(extensionPoint)) { 271 // Save contribution 272 defaultRenditionDescriptors.add((DefaultRenditionDescriptor) contribution); 273 } 274 } 275 276 /** 277 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 278 */ 279 @Deprecated 280 protected void registerRendition(RenditionDefinition renditionDefinition) { 281 String name = renditionDefinition.getName(); 282 if (name == null) { 283 log.error("Cannot register rendition without a name"); 284 return; 285 } 286 boolean enabled = renditionDefinition.isEnabled(); 287 if (renditionDefinitions.containsKey(name)) { 288 log.info("Overriding rendition with name: " + name); 289 if (enabled) { 290 renditionDefinition = mergeRenditions(renditionDefinitions.get(name), renditionDefinition); 291 } else { 292 log.info("Disabled rendition with name " + name); 293 renditionDefinitions.remove(name); 294 } 295 } 296 if (enabled) { 297 log.info("Registering rendition with name: " + name); 298 renditionDefinitions.put(name, renditionDefinition); 299 } 300 301 // setup the Provider 302 setupProvider(renditionDefinition); 303 } 304 305 /** 306 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 307 */ 308 @Deprecated 309 protected void setupProvider(RenditionDefinition definition) { 310 if (definition.getProviderClass() == null) { 311 definition.setProvider(new DefaultAutomationRenditionProvider()); 312 } else { 313 try { 314 RenditionProvider provider = definition.getProviderClass().newInstance(); 315 definition.setProvider(provider); 316 } catch (Exception e) { 317 log.error("Unable to create RenditionProvider", e); 318 } 319 } 320 } 321 322 protected RenditionDefinition mergeRenditions(RenditionDefinition oldRenditionDefinition, 323 RenditionDefinition newRenditionDefinition) { 324 String label = newRenditionDefinition.getLabel(); 325 if (label != null) { 326 oldRenditionDefinition.label = label; 327 } 328 329 String operationChain = newRenditionDefinition.getOperationChain(); 330 if (operationChain != null) { 331 oldRenditionDefinition.operationChain = operationChain; 332 } 333 334 return oldRenditionDefinition; 335 } 336 337 @Override 338 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 339 if (RENDITION_DEFINITIONS_EP.equals(extensionPoint)) { 340 renditionDefinitionRegistry.removeContribution((RenditionDefinition) contribution); 341 } else if (RENDITON_DEFINION_PROVIDERS_EP.equals(extensionPoint)) { 342 renditionDefinitionProviderRegistry.removeContribution( 343 (RenditionDefinitionProviderDescriptor) contribution); 344 } else if (STORED_RENDITION_MANAGERS_EP.equals(extensionPoint)) { 345 storedRenditionManagerDescriptors.remove((contribution)); 346 } else if (DEFAULT_RENDITION_EP.equals(extensionPoint)) { 347 defaultRenditionDescriptors.remove(contribution); 348 } 349 } 350 351 /** 352 * @deprecated since 7.3. RenditionDefinitions are store in {@link #renditionDefinitionRegistry}. 353 */ 354 @Deprecated 355 protected void unregisterRendition(RenditionDefinition renditionDefinition) { 356 String name = renditionDefinition.getName(); 357 renditionDefinitions.remove(name); 358 log.info("Unregistering rendition with name: " + name); 359 } 360 361 @Override 362 public Rendition getRendition(DocumentModel doc, String renditionName) { 363 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(doc, renditionName); 364 return getRendition(doc, renditionDefinition, renditionDefinition.isStoreByDefault()); 365 } 366 367 @Override 368 public Rendition getRendition(DocumentModel doc, String renditionName, boolean store) { 369 RenditionDefinition renditionDefinition = getAvailableRenditionDefinition(doc, renditionName); 370 return getRendition(doc, renditionDefinition, store); 371 } 372 373 protected Rendition getRendition(DocumentModel doc, RenditionDefinition renditionDefinition, boolean store) { 374 375 Rendition rendition; 376 boolean isVersionable = doc.isVersionable(); 377 if (!isVersionable || !doc.isCheckedOut()) { 378 // stored renditions are only done against a non-versionable doc 379 // or a versionable doc that is not checkedout 380 rendition = getStoredRenditionManager().findStoredRendition(doc, renditionDefinition); 381 if (rendition != null) { 382 return rendition; 383 } 384 } 385 386 rendition = new LiveRendition(doc, renditionDefinition); 387 388 if (store) { 389 StoredRendition storedRendition = storeRendition(doc, rendition, renditionDefinition); 390 if (storedRendition != null) { 391 return storedRendition; 392 } 393 } 394 return rendition; 395 } 396 397 @Override 398 public RenditionDefinition getAvailableRenditionDefinition(DocumentModel doc, String renditionName) { 399 RenditionDefinition renditionDefinition = renditionDefinitionRegistry.getRenditionDefinition(renditionName); 400 if (renditionDefinition == null) { 401 renditionDefinition = renditionDefinitionProviderRegistry.getRenditionDefinition(renditionName, doc); 402 if (renditionDefinition == null) { 403 String message = "The rendition definition '%s' is not registered"; 404 throw new NuxeoException(String.format(message, renditionName)); 405 } 406 } else { 407 // we have a rendition definition but we must check that it can be used for this doc 408 if (!renditionDefinitionRegistry.canUseRenditionDefinition(renditionDefinition, doc)) { 409 throw new NuxeoException("Rendition " + renditionName + " cannot be used for this doc " + doc.getId()); 410 } 411 } 412 if (renditionDefinition.getProvider() == null) { 413 throw new NuxeoException( 414 String.format("Rendition definition %s isn't bound to any rendition provider", renditionName)); 415 } 416 if (!renditionDefinition.getProvider().isAvailable(doc, renditionDefinition)) { 417 throw new NuxeoException( 418 String.format("Rendition %s not available for this doc %s", renditionName, doc.getPathAsString())); 419 } 420 return renditionDefinition; 421 } 422 423 @Override 424 public List<Rendition> getAvailableRenditions(DocumentModel doc) { 425 return getAvailableRenditions(doc, false); 426 } 427 428 @Override 429 public List<Rendition> getAvailableRenditions(DocumentModel doc, boolean onlyVisible) { 430 List<Rendition> renditions = new ArrayList<>(); 431 432 List<RenditionDefinition> defs = getAvailableRenditionDefinitions(doc); 433 if (defs != null) { 434 for (RenditionDefinition def : defs) { 435 if (!onlyVisible || def.isVisible()) { 436 Rendition rendition = getRendition(doc, def.getName(), false); 437 if (rendition != null) { 438 renditions.add(rendition); 439 } 440 } 441 } 442 } 443 444 return renditions; 445 } 446 447 @Override 448 public void deleteStoredRenditions(String repositoryName) { 449 StoredRenditionsCleaner cleaner = new StoredRenditionsCleaner(repositoryName); 450 cleaner.runUnrestricted(); 451 } 452 453 private final class StoredRenditionsCleaner extends UnrestrictedSessionRunner { 454 455 private static final int BATCH_SIZE = 100; 456 457 private StoredRenditionsCleaner(String repositoryName) { 458 super(repositoryName); 459 } 460 461 @Override 462 public void run() { 463 Map<String, List<String>> sourceIdToRenditionRefs = computeLiveDocumentRefsToRenditionRefs(); 464 removeStoredRenditions(sourceIdToRenditionRefs); 465 } 466 467 /** 468 * Computes only live documents renditions, the related versions will be deleted by Nuxeo. 469 */ 470 private Map<String, List<String>> computeLiveDocumentRefsToRenditionRefs() { 471 Map<String, List<String>> liveDocumentRefsToRenditionRefs = new HashMap<>(); 472 String query = String.format("SELECT %s, %s, %s FROM Document WHERE %s IS NOT NULL AND ecm:isVersion = 0", 473 NXQL.ECM_UUID, RENDITION_SOURCE_ID_PROPERTY, RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY, 474 RENDITION_SOURCE_ID_PROPERTY); 475 try (IterableQueryResult result = session.queryAndFetch(query, NXQL.NXQL)) { 476 for (Map<String, Serializable> res : result) { 477 String renditionRef = res.get(NXQL.ECM_UUID).toString(); 478 String sourceId = res.get(RENDITION_SOURCE_ID_PROPERTY).toString(); 479 Serializable sourceVersionableId = res.get(RENDITION_SOURCE_VERSIONABLE_ID_PROPERTY); 480 481 String key = sourceVersionableId != null ? sourceVersionableId.toString() : sourceId; 482 liveDocumentRefsToRenditionRefs.computeIfAbsent(key, k -> new ArrayList<>()).add(renditionRef); 483 } 484 } 485 return liveDocumentRefsToRenditionRefs; 486 } 487 488 private void removeStoredRenditions(Map<String, List<String>> liveDocumentRefsToRenditionRefs) { 489 List<String> liveDocumentRefs = new ArrayList<>(liveDocumentRefsToRenditionRefs.keySet()); 490 if (liveDocumentRefs.isEmpty()) { 491 // no more document to check 492 return; 493 } 494 495 int processedSourceIds = 0; 496 while (processedSourceIds < liveDocumentRefs.size()) { 497 // compute the batch of source ids to check for existence 498 int limit = processedSourceIds + BATCH_SIZE > liveDocumentRefs.size() ? liveDocumentRefs.size() 499 : processedSourceIds + BATCH_SIZE; 500 List<String> batchSourceIds = liveDocumentRefs.subList(processedSourceIds, limit); 501 502 // retrieve still existing documents 503 List<String> existingSourceIds = new ArrayList<>(); 504 String query = NXQLQueryBuilder.getQuery("SELECT ecm:uuid FROM Document WHERE ecm:uuid IN ?", 505 new Object[] { batchSourceIds }, true, true, null); 506 try (IterableQueryResult result = session.queryAndFetch(query, NXQL.NXQL)) { 507 result.forEach(res -> existingSourceIds.add(res.get(NXQL.ECM_UUID).toString())); 508 } 509 batchSourceIds.removeAll(existingSourceIds); 510 511 List<String> renditionRefsToDelete = batchSourceIds.stream() 512 .map(liveDocumentRefsToRenditionRefs::get) 513 .reduce(new ArrayList<>(), (allRefs, refs) -> { 514 allRefs.addAll(refs); 515 return allRefs; 516 }); 517 518 if (!renditionRefsToDelete.isEmpty()) { 519 session.removeDocuments( 520 renditionRefsToDelete.stream().map(IdRef::new).collect(Collectors.toList()).toArray( 521 new DocumentRef[renditionRefsToDelete.size()])); 522 } 523 524 if (TransactionHelper.isTransactionActiveOrMarkedRollback()) { 525 TransactionHelper.commitOrRollbackTransaction(); 526 TransactionHelper.startTransaction(); 527 } 528 529 // next batch 530 processedSourceIds += BATCH_SIZE; 531 } 532 } 533 } 534 535 @Override 536 public Rendition getDefaultRendition(DocumentModel doc, String reason, Map<String, Serializable> extendedInfos) { 537 return getDefaultRendition(doc, reason, false, extendedInfos); 538 } 539 540 @Override 541 public Rendition getDefaultRendition(DocumentModel doc, String reason, boolean store, 542 Map<String, Serializable> extendedInfos) { 543 Map<String, Object> context = new HashMap<>(); 544 Map<String, Serializable> ei = extendedInfos == null ? Collections.emptyMap() : extendedInfos; 545 NuxeoPrincipal currentUser = ClientLoginModule.getCurrentPrincipal(); 546 context.put("Document", doc); 547 context.put("Infos", ei); 548 context.put("CurrentUser", currentUser); 549 ScriptEngine engine = null; 550 for (int i = defaultRenditionDescriptors.size() - 1; i >= 0; i--) { 551 DefaultRenditionDescriptor desc = defaultRenditionDescriptors.get(i); 552 if ((StringUtils.isEmpty(reason) && StringUtils.isEmpty(desc.reason)) 553 || (reason != null && reason.equals(desc.reason))) { 554 String scriptLanguage = desc.getScriptLanguage(); 555 if (engine == null || !engine.getFactory().getNames().contains(scriptLanguage)) { 556 // Instantiating an engine may be costly, let's keep previous one if same language 557 engine = scriptEngineManager.getEngineByName(scriptLanguage); 558 if (engine == null) { 559 throw new NuxeoException("Engine not found for language: " + scriptLanguage); 560 } 561 } 562 if (!(engine instanceof Invocable)) { 563 throw new NuxeoException( 564 "Engine " + engine.getClass().getName() + " not Invocable for language: " + scriptLanguage); 565 } 566 try { 567 engine.eval(desc.getScript()); 568 engine.getBindings(ScriptContext.ENGINE_SCOPE).putAll(context); 569 Object result = ((Invocable) engine).invokeFunction("run"); 570 if (result == null && desc.override) { 571 break; 572 } else { 573 String defaultRenditionName = (String) result; 574 if (!StringUtils.isBlank(defaultRenditionName)) { 575 try { 576 return getRendition(doc, defaultRenditionName, store); 577 } catch (NuxeoException e) { 578 log.error("Unable to use default rendition " + defaultRenditionName, e); 579 } 580 } 581 } 582 583 } catch (NoSuchMethodException e) { 584 throw new NuxeoException("Script does not contain function: run() in defaultRendition: ", e); 585 } catch (ScriptException e) { 586 log.error("Failed to evaluate script: ", e); 587 } 588 } 589 } 590 if (log.isWarnEnabled()) { 591 log.warn(String.format("Failed to get rendition name for reason %s", reason)); 592 } 593 return null; 594 } 595 596 @Override 597 public DocumentModel publishRendition(DocumentModel doc, DocumentModel target, String renditionName, 598 boolean override) { 599 CoreSession session = doc.getCoreSession(); 600 if (!session.hasPermission(target.getRef(), ADD_CHILDREN)) { 601 log.error("Permission '{}' is not granted to '{}' on document '{}'", ADD_CHILDREN, 602 session.getPrincipal().getName(), target.getPath()); 603 throw new DocumentSecurityException( 604 "Privilege '" + ADD_CHILDREN + "' is not granted to '" + session.getPrincipal().getName() + "'"); 605 } 606 Rendition rendition = StringUtils.isEmpty(renditionName) 607 ? getDefaultRendition(doc, Constants.DEFAULT_RENDTION_PUBLISH_REASON, true, null) 608 : getRendition(doc, renditionName, true); 609 if (rendition == null) { 610 throw new NuxeoException("Unable to render the document"); 611 } 612 DocumentModel renditionDocument = rendition.getHostDocument(); 613 /* 614 * We've checked above that the current user is allowed to add new documents in the target. We need the 615 * privileged session to publish the rendition which is a placeless document. 616 */ 617 DocumentRef publishedDocumentRef = CoreInstance.doPrivileged(session, 618 (CoreSession s) -> s.publishDocument(renditionDocument, target, override).getRef()); 619 DocumentModel publishedDocument = session.getDocument(publishedDocumentRef); 620 if (override) { 621 RenditionsRemover remover = new RenditionsRemover(publishedDocument); 622 remover.runUnrestricted(); 623 } 624 return publishedDocument; 625 } 626 627}