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