001/* 002 * (C) Copyright 2012-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 * Antoine Taillefer 018 */ 019package org.nuxeo.ecm.diff.web; 020 021import static org.jboss.seam.ScopeType.CONVERSATION; 022import static org.jboss.seam.ScopeType.PAGE; 023 024import java.io.Serializable; 025import java.util.ArrayList; 026import java.util.List; 027 028import org.apache.commons.lang3.StringUtils; 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.jboss.seam.annotations.Factory; 032import org.jboss.seam.annotations.In; 033import org.jboss.seam.annotations.Name; 034import org.jboss.seam.annotations.Scope; 035import org.jboss.seam.international.LocaleSelector; 036import org.nuxeo.ecm.core.api.CoreSession; 037import org.nuxeo.ecm.core.api.DocumentModel; 038import org.nuxeo.ecm.core.api.IdRef; 039import org.nuxeo.ecm.core.api.NuxeoException; 040import org.nuxeo.ecm.core.api.VersionModel; 041import org.nuxeo.ecm.core.api.impl.VersionModelImpl; 042import org.nuxeo.ecm.diff.content.ContentDiffHelper; 043import org.nuxeo.ecm.diff.model.DiffDisplayBlock; 044import org.nuxeo.ecm.diff.model.DifferenceType; 045import org.nuxeo.ecm.diff.model.DocumentDiff; 046import org.nuxeo.ecm.diff.service.DiffDisplayService; 047import org.nuxeo.ecm.diff.service.DocumentDiffService; 048import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 049import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager; 050import org.nuxeo.ecm.webapp.versioning.VersionedActions; 051import org.nuxeo.runtime.api.Framework; 052 053/** 054 * Handles document diff actions. 055 * 056 * @author <a href="mailto:[email protected]">Antoine Taillefer</a> 057 * @since 5.6 058 */ 059@Name("diffActions") 060@Scope(CONVERSATION) 061public class DiffActionsBean implements Serializable { 062 063 private static final long serialVersionUID = -5507491210664361778L; 064 065 private static final Log log = LogFactory.getLog(DiffActionsBean.class); 066 067 private static final String DOC_DIFF_VIEW = "view_doc_diff"; 068 069 private static final String CONTENT_DIFF_DIFFERENCE_TYPE_MSG_KEY_PREFIX = "diff.content.differenceType.message."; 070 071 private static final String LAST_VERSION_PROPERTY = "lastVersion"; 072 073 @In(create = true, required = false) 074 protected transient CoreSession documentManager; 075 076 @In(create = true, required = false) 077 protected transient NavigationContext navigationContext; 078 079 @In(create = true, required = false) 080 protected transient DocumentsListsManager documentsListsManager; 081 082 @In(create = true, required = false) 083 protected transient VersionedActions versionedActions; 084 085 @In(create = true) 086 protected transient LocaleSelector localeSelector; 087 088 protected DocumentModel leftDoc; 089 090 protected DocumentModel rightDoc; 091 092 protected String selectedVersionId; 093 094 protected String diffSelectionType = DiffSelectionType.content.name(); 095 096 /** 097 * Checks if the diff action is available for the {@link DocumentsListsManager#CURRENT_DOCUMENT_SELECTION} working 098 * list. 099 * 100 * @return true if can diff the current document selection 101 */ 102 public boolean getCanDiffCurrentDocumentSelection() { 103 104 return getCanDiffWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION); 105 } 106 107 /** 108 * Checks if the diff action is available for the {@link DocumentsListsManager#CURRENT_DOCUMENT_TRASH_SELECTION} 109 * working list. 110 * 111 * @return true if can diff the current document trash selection 112 */ 113 public boolean getCanDiffCurrentTrashSelection() { 114 115 return getCanDiffWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_TRASH_SELECTION); 116 } 117 118 /** 119 * Checks if the diff action is available for the {@link DocumentsListsManager#CURRENT_DOCUMENT_SECTION_SELECTION} 120 * working list. 121 * 122 * @return true if can diff the current section selection 123 */ 124 public boolean getCanDiffCurrentSectionSelection() { 125 126 return getCanDiffWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SECTION_SELECTION); 127 } 128 129 /** 130 * Checks if the diff action is available for the {@link VersionDocumentsListsConstants#CURRENT_VERSION_SELECTION} 131 * working list. 132 * 133 * @return true if can diff the current version selection 134 */ 135 public boolean getCanDiffCurrentVersionSelection() { 136 137 return getCanDiffWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION); 138 } 139 140 /** 141 * Checks if the diff action is available for the {@link DocumentsListsManager#DEFAULT_WORKING_LIST} working list. 142 * 143 * @return true if can diff the current default working list selection 144 */ 145 public boolean getCanDiffCurrentDefaultSelection() { 146 147 return getCanDiffWorkingList(DocumentsListsManager.DEFAULT_WORKING_LIST); 148 } 149 150 /** 151 * Checks if the diff action is available for the {@code listName} working list. 152 * <p> 153 * Condition: the working list has exactly 2 documents. 154 * 155 * @param listName the list name 156 * @return true if can diff the {@code listName} working list 157 */ 158 public boolean getCanDiffWorkingList(String listName) { 159 160 List<DocumentModel> currentSelectionWorkingList = documentsListsManager.getWorkingList(listName); 161 return currentSelectionWorkingList != null && currentSelectionWorkingList.size() == 2; 162 } 163 164 /** 165 * Prepares a diff of the current document selection. 166 * 167 * @return the view id 168 */ 169 public String prepareCurrentDocumentSelectionDiff() { 170 171 diffSelectionType = DiffSelectionType.content.name(); 172 return prepareWorkingListDiff(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION); 173 } 174 175 /** 176 * Prepares a diff of the current document trash selection. 177 * 178 * @return the view id 179 */ 180 public String prepareCurrentTrashSelectionDiff() { 181 182 diffSelectionType = DiffSelectionType.trash.name(); 183 return prepareWorkingListDiff(DocumentsListsManager.CURRENT_DOCUMENT_TRASH_SELECTION); 184 } 185 186 /** 187 * Prepares a diff of the current section selection. 188 * 189 * @return the view id 190 */ 191 public String prepareCurrentSectionSelectionDiff() { 192 193 diffSelectionType = DiffSelectionType.content.name(); 194 return prepareWorkingListDiff(DocumentsListsManager.CURRENT_DOCUMENT_SECTION_SELECTION); 195 } 196 197 /** 198 * Prepares a diff of the current version selection. 199 * 200 * @return the view id 201 */ 202 public String prepareCurrentVersionSelectionDiff() { 203 204 diffSelectionType = DiffSelectionType.version.name(); 205 return prepareWorkingListDiff(DocumentsListsManager.CURRENT_VERSION_SELECTION); 206 } 207 208 /** 209 * Prepares a diff of the current default selection. 210 * 211 * @return the view id 212 */ 213 public String prepareCurrentDefaultSelectionDiff() { 214 215 diffSelectionType = DiffSelectionType.content.name(); 216 return prepareWorkingListDiff(DocumentsListsManager.DEFAULT_WORKING_LIST); 217 } 218 219 /** 220 * Prepares a diff of the {@code listName} working list. 221 * 222 * @return the view id 223 */ 224 public String prepareWorkingListDiff(String listName) { 225 226 List<DocumentModel> workingList = getWorkingList(listName); 227 228 leftDoc = workingList.get(0); 229 rightDoc = workingList.get(1); 230 231 return refresh(); 232 } 233 234 /** 235 * Prepare a diff of the current document with a specific version 236 * 237 * @param versionLabel version label to look for, if you want the last version use org.nuxeo.ecm.diff.web 238 * .DiffActionsBean#LAST_VERSION_PROPERTY 239 */ 240 public String prepareCurrentVersionDiff(String versionLabel) { 241 if (StringUtils.isBlank(versionLabel)) { 242 versionLabel = LAST_VERSION_PROPERTY; 243 } 244 245 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 246 if (currentDocument.isVersion()) { 247 log.info("Unable to diff, current document is a version document"); 248 return null; 249 } 250 251 DocumentModel documentVersion; 252 if (LAST_VERSION_PROPERTY.equals(versionLabel)) { 253 documentVersion = documentManager.getLastDocumentVersion(currentDocument.getRef()); 254 255 if (documentVersion == null) { 256 log.info("Unable to diff, current document do not have any versions yet."); 257 return null; 258 } 259 } else { 260 VersionModel versionModel = new VersionModelImpl(); 261 versionModel.setLabel(versionLabel); 262 documentVersion = documentManager.getDocumentWithVersion(currentDocument.getRef(), versionModel); 263 264 if (documentVersion == null) { 265 log.info("Unable to found " + versionLabel + " on current document to diff."); 266 return null; 267 } 268 } 269 270 setLeftDoc(currentDocument); 271 setRightDoc(documentVersion); 272 273 diffSelectionType = DiffSelectionType.version.name(); 274 275 return DOC_DIFF_VIEW; 276 } 277 278 /** 279 * Prepares a diff of the selected version with the live doc. 280 * 281 * @return the view id 282 */ 283 public String prepareCurrentVersionDiff() { 284 285 String selectedVersionId = versionedActions.getSelectedVersionId(); 286 if (selectedVersionId != null) { 287 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 288 if (currentDocument == null) { 289 throw new NuxeoException( 290 "Cannot make a diff between selected version and current document since current document is null."); 291 } 292 293 VersionModel selectedVersion = new VersionModelImpl(); 294 selectedVersion.setId(selectedVersionId); 295 DocumentModel docVersion = documentManager.getDocumentWithVersion(currentDocument.getRef(), selectedVersion); 296 if (docVersion == null) { 297 throw new NuxeoException( 298 "Cannot make a diff between selected version and current document since selected version document is null."); 299 } 300 301 leftDoc = docVersion; 302 rightDoc = currentDocument; 303 304 diffSelectionType = DiffSelectionType.version.name(); 305 306 return DOC_DIFF_VIEW; 307 } 308 return null; 309 } 310 311 /** 312 * Refreshes the diff between leftDoc and rightDoc. 313 * 314 * @return the view id 315 */ 316 public String refresh() { 317 318 // Fetch docs from repository 319 if (isDocumentDiffAvailable()) { 320 leftDoc = documentManager.getDocument(leftDoc.getRef()); 321 rightDoc = documentManager.getDocument(rightDoc.getRef()); 322 } 323 324 return DOC_DIFF_VIEW; 325 } 326 327 /** 328 * Checks if document diff is available. 329 * 330 * @return true, if is document diff available 331 */ 332 public boolean isDocumentDiffAvailable() { 333 return leftDoc != null && rightDoc != null; 334 } 335 336 /** 337 * Gets the document diff. 338 * 339 * @return the document diff between leftDoc and rightDoc if leftDoc and rightDoc aren't null, else null 340 */ 341 @Factory(value = "defaultDiffDisplayBlocks", scope = PAGE) 342 public List<DiffDisplayBlock> getDefaultDiffDisplayBlocks() { 343 344 if (leftDoc == null || rightDoc == null) { 345 return new ArrayList<>(); 346 } 347 348 DocumentDiff docDiff = getDocumentDiffService().diff(documentManager, leftDoc, rightDoc); 349 return getDiffDisplayService().getDiffDisplayBlocks(docDiff, leftDoc, rightDoc); 350 } 351 352 /** 353 * Gets the content diff fancybox URL for the property with xpath {@code propertyXPath}. 354 * 355 * @param propertyLabel the property label 356 * @param propertyXPath the property xpath 357 * @return the content diff fancybox URL 358 */ 359 public String getContentDiffFancyBoxURL(String propertyLabel, String propertyXPath) { 360 361 return getContentDiffFancyBoxURL(propertyLabel, propertyXPath, null); 362 } 363 364 /** 365 * Gets the content diff fancybox URL for the property with xpath {@code propertyXPath} using {@code conversionType} 366 * . 367 * 368 * @param propertyLabel the property label 369 * @param propertyXPath the property xpath 370 * @param conversionType the conversion type 371 * @return the content diff fancybox URL 372 */ 373 public String getContentDiffFancyBoxURL(String propertyLabel, String propertyXPath, String conversionType) 374 { 375 376 if (StringUtils.isEmpty(propertyXPath)) { 377 log.error("Cannot get content diff fancybox URL with a null propertyXPath."); 378 return null; 379 } 380 return ContentDiffHelper.getContentDiffFancyBoxURL(navigationContext.getCurrentDocument(), propertyLabel, 381 propertyXPath, conversionType); 382 } 383 384 /** 385 * Gets the content diff URL of two documents independently of the current document 386 * 387 * @param docLeftId a DocumentModel id, not a path. 388 * @param docRightId a DocumentModel id, not a path. 389 */ 390 public String getContentDiffURL(String docLeftId, String docRightId, String propertyXPath, 391 String conversionTypeParam) { 392 DocumentModel leftDoc = null; 393 DocumentModel rightDoc = null; 394 if (!StringUtils.isBlank(docLeftId)) { 395 leftDoc = documentManager.getDocument(new IdRef(docLeftId)); 396 } 397 398 if (!StringUtils.isBlank(docRightId)) { 399 rightDoc = documentManager.getDocument(new IdRef(docRightId)); 400 } 401 402 if (rightDoc == null || leftDoc == null) { 403 log.error("Cannot get content diff URL with a null leftDoc or a null rightDoc."); 404 return null; 405 } 406 407 if (StringUtils.isEmpty(propertyXPath)) { 408 log.error("Cannot get content diff URL with a null schemaName or a null fieldName."); 409 return null; 410 } 411 412 String conversionType = null; 413 if (!StringUtils.isEmpty(conversionTypeParam)) { 414 conversionType = conversionTypeParam; 415 } 416 417 return ContentDiffHelper.getContentDiffURL(navigationContext.getCurrentDocument().getRepositoryName(), leftDoc, 418 rightDoc, propertyXPath, conversionType, localeSelector.getLocaleString()); 419 } 420 421 /** 422 * Gets the content diff URL. 423 * 424 * @param propertyXPath the property xpath 425 * @param conversionTypeParam the conversion type param 426 * @return the content diff URL 427 */ 428 public String getContentDiffURL(String propertyXPath, String conversionTypeParam) { 429 430 if (leftDoc == null || rightDoc == null) { 431 log.error("Cannot get content diff URL with a null leftDoc or a null rightDoc."); 432 return null; 433 } 434 if (StringUtils.isEmpty(propertyXPath)) { 435 log.error("Cannot get content diff URL with a null schemaName or a null fieldName."); 436 return null; 437 } 438 String conversionType = null; 439 if (!StringUtils.isEmpty(conversionTypeParam)) { 440 conversionType = conversionTypeParam; 441 } 442 return ContentDiffHelper.getContentDiffURL(navigationContext.getCurrentDocument().getRepositoryName(), leftDoc, 443 rightDoc, propertyXPath, conversionType, localeSelector.getLocaleString()); 444 } 445 446 /** 447 * Gets the content diff with blob post processing URL. 448 * 449 * @param propertyXPath the property xpath 450 * @param conversionTypeParam the conversion type param 451 * @return the content diff with blob post processing URL 452 */ 453 public String getContentDiffWithBlobPostProcessingURL(String propertyXPath, String conversionTypeParam) { 454 return getContentDiffURL(propertyXPath, conversionTypeParam) + "?blobPostProcessing=true"; 455 } 456 457 /** 458 * Checks if is different filename. 459 */ 460 public boolean isDifferentFilename(DifferenceType differenceType) { 461 return DifferenceType.differentFilename.equals(differenceType); 462 } 463 464 /** 465 * Gets the content diff difference type message key. 466 */ 467 public String getContentDiffDifferenceTypeMsgKey(DifferenceType differenceType) { 468 return CONTENT_DIFF_DIFFERENCE_TYPE_MSG_KEY_PREFIX + differenceType.name(); 469 } 470 471 /** 472 * Gets the {@code listName} working list. 473 * 474 * @return the {@code listName} working list 475 */ 476 protected final List<DocumentModel> getWorkingList(String listName) { 477 478 List<DocumentModel> currentSelectionWorkingList = documentsListsManager.getWorkingList(listName); 479 480 if (currentSelectionWorkingList == null || currentSelectionWorkingList.size() != 2) { 481 throw new NuxeoException(String.format( 482 "Cannot make a diff of the %s working list: need to have exactly 2 documents in the working list.", 483 listName)); 484 } 485 return currentSelectionWorkingList; 486 } 487 488 /** 489 * Gets the document diff service. 490 * 491 * @return the document diff service 492 */ 493 protected final DocumentDiffService getDocumentDiffService() { 494 return Framework.getService(DocumentDiffService.class); 495 } 496 497 /** 498 * Gets the diff display service. 499 * 500 * @return the diff display service 501 */ 502 protected final DiffDisplayService getDiffDisplayService() { 503 return Framework.getService(DiffDisplayService.class); 504 } 505 506 public DocumentModel getLeftDoc() { 507 return leftDoc; 508 } 509 510 public void setLeftDoc(DocumentModel leftDoc) { 511 this.leftDoc = leftDoc; 512 } 513 514 public DocumentModel getRightDoc() { 515 return rightDoc; 516 } 517 518 public void setRightDoc(DocumentModel rightDoc) { 519 this.rightDoc = rightDoc; 520 } 521 522 public String getDiffSelectionType() { 523 return diffSelectionType; 524 } 525 526 public void setDiffSelectionType(String diffSelectionType) { 527 this.diffSelectionType = diffSelectionType; 528 } 529}