001/* 002 * (C) Copyright 2007 Nuxeo SA (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 * 019 * $Id: DocumentModelFunctions.java 30568 2008-02-25 18:52:49Z ogrisel $ 020 */ 021 022package org.nuxeo.ecm.platform.ui.web.tag.fn; 023 024import java.io.UnsupportedEncodingException; 025import java.net.URLEncoder; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031 032import javax.faces.context.FacesContext; 033import javax.servlet.http.Cookie; 034import javax.servlet.http.HttpServletRequest; 035 036import org.apache.commons.lang.StringUtils; 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039import org.jboss.seam.Component; 040import org.jboss.seam.ScopeType; 041import org.jboss.seam.core.Manager; 042import org.nuxeo.ecm.core.NXCore; 043import org.nuxeo.ecm.core.api.Blob; 044import org.nuxeo.ecm.core.api.CoreSession; 045import org.nuxeo.ecm.core.api.DocumentLocation; 046import org.nuxeo.ecm.core.api.DocumentModel; 047import org.nuxeo.ecm.core.api.DocumentNotFoundException; 048import org.nuxeo.ecm.core.api.IdRef; 049import org.nuxeo.ecm.core.api.LifeCycleException; 050import org.nuxeo.ecm.core.api.NuxeoException; 051import org.nuxeo.ecm.core.api.PathRef; 052import org.nuxeo.ecm.core.api.PropertyException; 053import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 054import org.nuxeo.ecm.core.api.model.Property; 055import org.nuxeo.ecm.core.api.security.SecurityConstants; 056import org.nuxeo.ecm.core.io.download.DownloadService; 057import org.nuxeo.ecm.core.lifecycle.LifeCycle; 058import org.nuxeo.ecm.core.lifecycle.LifeCycleService; 059import org.nuxeo.ecm.core.schema.FacetNames; 060import org.nuxeo.ecm.core.schema.SchemaManager; 061import org.nuxeo.ecm.core.schema.types.Field; 062import org.nuxeo.ecm.core.schema.types.ListType; 063import org.nuxeo.ecm.core.schema.types.Schema; 064import org.nuxeo.ecm.core.schema.types.Type; 065import org.nuxeo.ecm.core.utils.DocumentModelUtils; 066import org.nuxeo.ecm.directory.DirectoryException; 067import org.nuxeo.ecm.directory.Session; 068import org.nuxeo.ecm.directory.api.DirectoryService; 069import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry; 070import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 071import org.nuxeo.ecm.platform.types.TypeManager; 072import org.nuxeo.ecm.platform.types.adapter.TypeInfo; 073import org.nuxeo.ecm.platform.ui.web.directory.DirectoryFunctions; 074import org.nuxeo.ecm.platform.ui.web.directory.DirectoryHelper; 075import org.nuxeo.ecm.platform.ui.web.rest.RestHelper; 076import org.nuxeo.ecm.platform.ui.web.rest.api.URLPolicyService; 077import org.nuxeo.ecm.platform.ui.web.util.BaseURL; 078import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 079import org.nuxeo.ecm.platform.url.DocumentViewImpl; 080import org.nuxeo.ecm.platform.url.api.DocumentView; 081import org.nuxeo.ecm.platform.url.codec.DocumentFileCodec; 082import org.nuxeo.runtime.api.Framework; 083 084/** 085 * Document model functions. 086 * 087 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 088 * @author <a href="mailto:og@nuxeo.com">Olivier Grisel</a> 089 */ 090public final class DocumentModelFunctions implements LiveEditConstants { 091 092 private static final Log log = LogFactory.getLog(DocumentModelFunctions.class); 093 094 private static final String JSESSIONID = "JSESSIONID"; 095 096 private static final String i18n_prefix = "%i18n"; 097 098 private static final String NXEDIT_URL_VIEW_ID = "nxliveedit.faces"; 099 100 private static final String NXEDIT_URL_SCHEME = "nxedit"; 101 102 private static MimetypeRegistry mimetypeService; 103 104 private static TypeManager typeManagerService; 105 106 private static DirectoryService dirService; 107 108 private static LifeCycleService lifeCycleService; 109 110 // static cache of default viewId per document type shared all among 111 // threads 112 private static final Map<String, String> defaultViewCache = Collections.synchronizedMap( 113 new HashMap<String, String>()); 114 115 // Utility class. 116 private DocumentModelFunctions() { 117 } 118 119 private static DirectoryService getDirectoryService() { 120 if (dirService == null) { 121 dirService = DirectoryHelper.getDirectoryService(); 122 } 123 return dirService; 124 } 125 126 private static MimetypeRegistry getMimetypeService() { 127 if (mimetypeService == null) { 128 mimetypeService = Framework.getService(MimetypeRegistry.class); 129 } 130 return mimetypeService; 131 } 132 133 private static TypeManager getTypeManager() { 134 if (typeManagerService == null) { 135 typeManagerService = Framework.getService(TypeManager.class); 136 } 137 return typeManagerService; 138 } 139 140 private static String getDefaultView(DocumentModel doc) { 141 String docType = doc.getType(); 142 143 if (defaultViewCache.containsKey(docType)) { 144 return defaultViewCache.get(docType); 145 } else { 146 org.nuxeo.ecm.platform.types.Type type = getTypeManager().getType(docType); 147 if (type == null) { 148 return null; 149 } 150 String defaultView = type.getDefaultView(); 151 defaultViewCache.put(docType, defaultView); 152 return defaultView; 153 } 154 } 155 156 private static LifeCycleService geLifeCycleService() { 157 if (lifeCycleService == null) { 158 lifeCycleService = NXCore.getLifeCycleService(); 159 if (lifeCycleService == null) { 160 log.error("No Life Cycle service registered"); 161 } 162 } 163 return lifeCycleService; 164 } 165 166 public static TypeInfo typeInfo(DocumentModel document) { 167 if (document != null) { 168 return document.getAdapter(TypeInfo.class); 169 } else { 170 return null; 171 } 172 } 173 174 public static String typeLabel(DocumentModel document) { 175 String label = ""; 176 if (document != null) { 177 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 178 if (typeInfo != null) { 179 label = typeInfo.getLabel(); 180 } 181 } 182 return label; 183 } 184 185 public static String typeView(DocumentModel document, String viewId) { 186 String viewValue = ""; 187 if (document != null) { 188 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 189 if (typeInfo != null) { 190 viewValue = typeInfo.getView(viewId); 191 } 192 } 193 return viewValue; 194 } 195 196 public static String iconPath(DocumentModel document) { 197 String iconPath = ""; 198 if (document != null) { 199 try { 200 iconPath = (String) document.getProperty("common", "icon"); 201 } catch (PropertyException e) { 202 iconPath = null; 203 } 204 if (iconPath == null || iconPath.length() == 0 || document.getType().equals("Workspace")) { 205 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 206 if (typeInfo != null) { 207 iconPath = typeInfo.getIcon(); 208 } 209 } 210 } 211 return iconPath; 212 } 213 214 public static String iconExpandedPath(DocumentModel document) { 215 String iconPath = ""; 216 if (document != null) { 217 try { 218 iconPath = (String) document.getProperty("common", "icon-expanded"); 219 } catch (PropertyException e) { 220 iconPath = null; 221 } 222 if (iconPath == null || iconPath.length() == 0) { 223 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 224 if (typeInfo != null) { 225 iconPath = typeInfo.getIconExpanded(); 226 // Set to default icon if expanded is not set 227 if (iconPath == null || iconPath.equals("")) { 228 iconPath = iconPath(document); 229 } 230 } 231 232 } 233 } 234 return iconPath; 235 } 236 237 public static String bigIconPath(DocumentModel document) { 238 String iconPath = ""; 239 if (document != null) { 240 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 241 if (typeInfo != null) { 242 iconPath = typeInfo.getBigIcon(); 243 } 244 } 245 return iconPath; 246 } 247 248 public static String bigIconExpandedPath(DocumentModel document) { 249 String iconPath = ""; 250 if (document != null) { 251 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 252 if (typeInfo != null) { 253 iconPath = typeInfo.getIconExpanded(); 254 // Set to default icon if expanded is not set 255 if (iconPath == null || iconPath.equals("")) { 256 iconPath = bigIconPath(document); 257 } 258 } 259 } 260 return iconPath; 261 } 262 263 public static String fileIconPath(Blob blob) { 264 String iconPath = ""; 265 if (blob != null) { 266 MimetypeEntry mimeEntry = getMimetypeService().getMimetypeEntryByMimeType(blob.getMimeType()); 267 if (mimeEntry != null) { 268 if (mimeEntry.getIconPath() != null) { 269 // FIXME: above Context should find it 270 iconPath = "/icons/" + mimeEntry.getIconPath(); 271 } 272 } 273 } 274 return iconPath; 275 } 276 277 public static String titleOrId(DocumentModel document) { 278 String title = null; 279 if (document != null) { 280 try { 281 title = (String) document.getProperty("dublincore", "title"); 282 } catch (PropertyException e) { 283 title = null; 284 } 285 if (title == null || title.length() == 0) { 286 // handle root document title 287 if ("/".equals(document.getPathAsString())) { 288 title = "/"; 289 } else { 290 title = document.getId(); 291 } 292 } 293 } 294 295 if (title == null) { 296 title = "<Unknown>"; 297 } 298 299 if (title.startsWith(i18n_prefix)) { 300 String i18nTitle = title.substring(i18n_prefix.length()); 301 FacesContext context = FacesContext.getCurrentInstance(); 302 title = ComponentUtils.translate(context, i18nTitle); 303 } 304 return title; 305 } 306 307 /** 308 * @since 6.0 309 */ 310 public static String titleFromId(final String documentId) { 311 final CoreSession coreSession = (CoreSession) Component.getInstance("documentManager"); 312 if (StringUtils.isNotBlank(documentId)) { 313 try { 314 DocumentModel doc = coreSession.getDocument(new IdRef(documentId)); 315 if (doc != null) { 316 return titleOrId(doc); 317 } 318 } catch (DocumentNotFoundException e) { 319 log.info("Could not find document with id " + documentId); 320 } 321 } 322 return documentId; 323 } 324 325 public static boolean isDocumentModel(Object value) { 326 return value instanceof DocumentModel; 327 } 328 329 public static boolean isDirty(DocumentModel doc) { 330 if (doc == null) { 331 return false; 332 } 333 for (String schema : doc.getSchemas()) { 334 for (Property property : doc.getPropertyObjects(schema)) { 335 if (property.isDirty() && isPropertyValueDirty(property.getValue())) { 336 return true; 337 } 338 } 339 } 340 return false; 341 } 342 343 @SuppressWarnings("rawtypes") 344 protected static boolean isPropertyValueDirty(Object value) { 345 if (value == null) { 346 // common case 347 return false; 348 } 349 if (value instanceof String) { 350 if (!StringUtils.isBlank((String) value)) { 351 return true; 352 } 353 } else if (value instanceof List) { 354 List<?> list = (List) value; 355 if (!list.isEmpty()) { 356 return true; 357 } 358 } else if (value instanceof Collection) { 359 Collection<?> col = (Collection) value; 360 if (!col.isEmpty()) { 361 return true; 362 } 363 } else if (value instanceof Object[]) { 364 Object[] col = (Object[]) value; 365 if (col.length > 0) { 366 return true; 367 } 368 } else if (value instanceof Map) { 369 Map<?, ?> map = (Map) value; 370 if (map.isEmpty()) { 371 return true; 372 } 373 for (Object mapItem : map.values()) { 374 if (isPropertyValueDirty(mapItem)) { 375 return true; 376 } 377 } 378 } else if (value != null) { 379 // in any other case, test if object is null (int, calendar, 380 // etc...) 381 return true; 382 } 383 return false; 384 } 385 386 public static boolean hasPermission(DocumentModel document, String permission) { 387 if (document == null) { 388 return false; 389 } 390 CoreSession session = document.getCoreSession(); 391 if (session == null) { 392 session = (CoreSession) Component.getInstance("documentManager", ScopeType.CONVERSATION); 393 } 394 if (session == null) { 395 log.error("Cannot retrieve CoreSession for " + document); 396 return false; 397 } 398 boolean granted = session.hasPermission(document.getRef(), permission); 399 return granted; 400 } 401 402 /** 403 * Returns true if document can be modified. 404 * <p> 405 * A document can be modified if current user has 'Write' permission on it and document is mutable (no archived 406 * version). 407 * 408 * @param document 409 * @return true if document can be modified. 410 */ 411 public static boolean canModify(DocumentModel document) { 412 if (document == null) { 413 return false; 414 } 415 return hasPermission(document, SecurityConstants.WRITE) && !document.hasFacet(FacetNames.IMMUTABLE); 416 } 417 418 /** 419 * Returns the default value for given schema field. 420 * 421 * @param schemaName the schema name 422 * @param fieldName the field name 423 * @return the default value. 424 * @deprecated use defaultValue(propertyName) instead 425 */ 426 @Deprecated 427 public static Object defaultValue(String schemaName, String fieldName) { 428 Object value = null; 429 SchemaManager tm = Framework.getService(SchemaManager.class); 430 Schema schema = tm.getSchema(schemaName); 431 Field field = schema.getField(fieldName); 432 Type type = field.getType(); 433 if (type.isListType()) { 434 Type itemType = ((ListType) type).getFieldType(); 435 value = itemType.newInstance(); 436 } 437 return value; 438 } 439 440 /** 441 * Returns the default value for given property name. 442 * 443 * @param propertyName as xpath 444 * @return the default value. 445 */ 446 public static Object defaultValue(String propertyName) { 447 SchemaManager tm = Framework.getService(SchemaManager.class); 448 Field field = tm.getField(propertyName); 449 Object value = null; 450 if (field != null) { 451 Type type = field.getType(); 452 if (type.isListType()) { 453 Type itemType = ((ListType) type).getFieldType(); 454 value = itemType.newInstance(); 455 } else if (type.isComplexType()) { 456 value = type.newInstance(); 457 } else { 458 value = field.getDefaultValue(); 459 } 460 } 461 return value; 462 } 463 464 /** 465 * @since 6.0 466 */ 467 public static String fileUrl(String baseURL, String patternName, DocumentModel doc, String blobPropertyName, 468 String filename) { 469 if (doc == null) { 470 return null; 471 } 472 DocumentLocation docLoc = new DocumentLocationImpl(doc); 473 Map<String, String> params = new HashMap<String, String>(); 474 params.put(DocumentFileCodec.FILE_PROPERTY_PATH_KEY, blobPropertyName); 475 params.put(DocumentFileCodec.FILENAME_KEY, filename); 476 DocumentView docView = new DocumentViewImpl(docLoc, null, params); 477 478 // generate url 479 URLPolicyService service = Framework.getService(URLPolicyService.class); 480 if (patternName == null) { 481 patternName = service.getDefaultPatternName(); 482 } 483 return service.getUrlFromDocumentView(patternName, docView, baseURL); 484 } 485 486 public static String fileUrl(String patternName, DocumentModel doc, String blobPropertyName, String filename) { 487 return fileUrl(BaseURL.getBaseURL(), patternName, doc, blobPropertyName, filename); 488 } 489 490 public static String bigFileUrl(DocumentModel doc, String blobPropertyName, String filename) { 491 if (doc == null) { 492 return null; 493 } 494 DownloadService downloadService = Framework.getService(DownloadService.class); 495 return BaseURL.getBaseURL() + downloadService.getDownloadUrl(doc, blobPropertyName, filename); 496 } 497 498 public static String fileDescription(DocumentModel document, String blobPropertyName, String filePropertyName, 499 String filename) { 500 String fileInfo = ""; 501 if (document != null) { 502 Long blobLength = null; 503 try { 504 Blob blob = (Blob) document.getPropertyValue(blobPropertyName); 505 if (blob != null) { 506 blobLength = blob.getLength(); 507 } 508 } catch (PropertyException e) { 509 // no prop by that name with that type 510 } 511 if (filename == null && filePropertyName != null) { 512 try { 513 filename = (String) document.getPropertyValue(filePropertyName); 514 } catch (PropertyException e) { 515 // no prop by that name with that type 516 } 517 } 518 if (blobLength != null && filename != null) { 519 fileInfo = filename + " [" + Functions.printFileSize(String.valueOf(blobLength)) + "]"; 520 } else if (blobLength != null) { 521 fileInfo = "[" + Functions.printFileSize(String.valueOf(blobLength)) + "]"; 522 } else if (filename != null) { 523 fileInfo = filename; 524 } 525 } 526 return fileInfo; 527 } 528 529 /** 530 * Convenient method to get the REST URL of a blob inside the <code>Files</code> schema. 531 * 532 * @param patternName 533 * @param doc The document model. 534 * @param index index of the element containing the blob. <code>index</code> starts at 0. 535 * @param filename The filename of the blob. 536 * @return the REST URL for the blob, or <code>null</code> if an error occurred. 537 */ 538 public static String complexFileUrl(String patternName, DocumentModel doc, int index, String filename) { 539 return complexFileUrl(patternName, doc, "files:files", index, DEFAULT_SUB_BLOB_FIELD, filename); 540 } 541 542 /** 543 * Get the REST URL for a blob inside a list of complex type. For instance, 544 * <code>http://localhost/nuxeo/nxfile/server/docId/files:files%5B0%5D/file/image.png</code> for the blob property 545 * 'file' of the first element inside the 'files:files' list. 546 * 547 * @param patternName 548 * @param doc The document model. 549 * @param listElement Element containing a list of complex type. 550 * @param index Index of the element containing the blob inside the list. <code>index</code> starts at 0. 551 * @param blobPropertyName The property containing the blob. 552 * @param filename Filename of the blob. 553 * @return the REST URL for the blob, or <code>null</code> if an error occurred. 554 */ 555 public static String complexFileUrl(String patternName, DocumentModel doc, String listElement, int index, 556 String blobPropertyName, String filename) { 557 DocumentLocation docLoc = new DocumentLocationImpl(doc); 558 Map<String, String> params = new HashMap<String, String>(); 559 560 String fileProperty = getPropertyPath(listElement, index, blobPropertyName); 561 562 params.put(DocumentFileCodec.FILE_PROPERTY_PATH_KEY, fileProperty); 563 params.put(DocumentFileCodec.FILENAME_KEY, filename); 564 DocumentView docView = new DocumentViewImpl(docLoc, null, params); 565 566 // generate url 567 URLPolicyService service = Framework.getService(URLPolicyService.class); 568 if (patternName == null) { 569 patternName = service.getDefaultPatternName(); 570 } 571 return service.getUrlFromDocumentView(patternName, docView, BaseURL.getBaseURL()); 572 } 573 574 // TODO: add method accepting filename property name (used for edit online) 575 576 public static String documentUrl(DocumentModel doc, HttpServletRequest req) { 577 return documentUrl(null, doc, null, null, false, BaseURL.getBaseURL(req)); 578 } 579 580 public static String documentUrl(DocumentModel doc) { 581 return documentUrl(null, doc, null, null, false, BaseURL.getBaseURL()); 582 } 583 584 /** 585 * @deprecated since 7.3, use {@link #documentUrl(String, DocumentLocation, String, Map, boolean, String)} instead. 586 */ 587 @Deprecated 588 public static String documentUrl(String patternName, DocumentModel doc, String viewId, 589 Map<String, String> parameters, boolean newConversation) { 590 return documentUrl(patternName, doc, viewId, parameters, newConversation, BaseURL.getBaseURL()); 591 } 592 593 /** 594 * @deprecated since 7.3, use {@link #documentUrl(String, DocumentLocation, String, Map, boolean, String)} instead. 595 */ 596 @Deprecated 597 public static String documentUrl(String patternName, DocumentModel doc, String viewId, 598 Map<String, String> parameters, boolean newConversation, HttpServletRequest req) { 599 String baseURL = null; 600 if (req == null) { 601 baseURL = BaseURL.getBaseURL(); 602 } else { 603 baseURL = BaseURL.getBaseURL(req); 604 } 605 return documentUrl(patternName, doc, viewId, parameters, newConversation, baseURL); 606 } 607 608 /** 609 * Computes a document URL. 610 * 611 * @since 7.3 612 */ 613 public static String documentUrl(String patternName, DocumentModel doc, String viewId, 614 Map<String, String> parameters, boolean newConversation, String baseURL) { 615 DocumentLocation docLoc = new DocumentLocationImpl(doc); 616 if (viewId == null || viewId.length() == 0) { 617 viewId = getDefaultView(doc); 618 } 619 parameters = parameters == null ? new HashMap<String, String>() : parameters; 620 621 if (doc.isVersion()) { 622 parameters.put("version", "true"); 623 } 624 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, baseURL); 625 } 626 627 /** 628 * @since 5.7 629 */ 630 public static String documentUrl(String patternName, DocumentLocation docLoc, String viewId, 631 Map<String, String> parameters, boolean newConversation, HttpServletRequest req) { 632 String baseURL = null; 633 if (req == null) { 634 baseURL = BaseURL.getBaseURL(); 635 } else { 636 baseURL = BaseURL.getBaseURL(req); 637 } 638 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, baseURL); 639 } 640 641 /** 642 * Computes a document URL. 643 * 644 * @since 7.3 645 */ 646 public static String documentUrl(String patternName, DocumentLocation docLoc, String viewId, 647 Map<String, String> parameters, boolean newConversation, String baseURL) { 648 DocumentView docView = new DocumentViewImpl(docLoc, viewId, parameters); 649 650 // generate url 651 URLPolicyService service = Framework.getService(URLPolicyService.class); 652 PathRef pathRef = docLoc.getPathRef(); 653 if (pathRef != null && !pathRef.toString().contains("/")) { 654 // Use "id" pattern for placeless document 655 patternName = "id"; 656 } else if (patternName == null || patternName.length() == 0) { 657 patternName = service.getDefaultPatternName(); 658 } 659 660 String url = service.getUrlFromDocumentView(patternName, docView, baseURL); 661 662 // pass conversation info if needed 663 if (!newConversation && url != null) { 664 url = RestHelper.addCurrentConversationParameters(url); 665 } 666 667 return url; 668 } 669 670 /** 671 * Computes an URL for a {@code repositoryName} only. 672 * 673 * @since 5.7 674 * @deprecated since 7.3, use {@link #repositoryUrl(String, String, String, Map, boolean, String)} instead. 675 */ 676 @Deprecated 677 public static String repositoryUrl(String patternName, String repositoryName, String viewId, 678 Map<String, String> parameters, boolean newConversation) { 679 return repositoryUrl(patternName, repositoryName, viewId, parameters, newConversation, 680 (HttpServletRequest) null); 681 } 682 683 /** 684 * Computes an URL for a {@code repositoryName} only. 685 * 686 * @since 5.7 687 * @deprecated since 7.3, use {@link #repositoryUrl(String, String, String, Map, boolean, String)} instead. 688 */ 689 @Deprecated 690 public static String repositoryUrl(String patternName, String repositoryName, String viewId, 691 Map<String, String> parameters, boolean newConversation, HttpServletRequest req) { 692 DocumentLocation docLoc = new DocumentLocationImpl(repositoryName, null); 693 parameters = parameters == null ? new HashMap<String, String>() : parameters; 694 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, req); 695 } 696 697 /** 698 * Computes an URL for a {@code repositoryName} only. 699 * 700 * @since 7.3 701 */ 702 public static String repositoryUrl(String patternName, String repositoryName, String viewId, 703 Map<String, String> parameters, boolean newConversation, String baseURL) { 704 DocumentLocation docLoc = new DocumentLocationImpl(repositoryName, null); 705 parameters = parameters == null ? new HashMap<String, String>() : parameters; 706 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, baseURL); 707 } 708 709 protected static void addQueryParameter(StringBuilder sb, String name, String value, boolean isFirst) { 710 if (isFirst) { 711 sb.append("?"); 712 } else { 713 sb.append("&"); 714 } 715 if (value == null) { 716 return; 717 } 718 sb.append(name); 719 sb.append("="); 720 try { 721 sb.append(URLEncoder.encode(value, URL_ENCODE_CHARSET)); 722 } catch (UnsupportedEncodingException e) { 723 throw new NuxeoException("Could not encode URL parameter: " + name + "=" + value, e); 724 } 725 } 726 727 /** 728 * Build the nxedit URL for the "edit existing document" use case for a document using the file:content field as 729 * Blob holder 730 * 731 * @return the encoded URL string 732 */ 733 public static String liveEditUrl(DocumentModel doc) { 734 return liveEditUrl(doc, DEFAULT_SCHEMA, DEFAULT_BLOB_FIELD, DEFAULT_FILENAME_FIELD); 735 } 736 737 /** 738 * Build the nxedit URL for the "edit existing document" use case 739 * 740 * @return the encoded URL string 741 */ 742 public static String liveEditUrl(DocumentModel doc, String schemaName, String blobFieldName, 743 String filenameFieldName) { 744 if (doc == null) { 745 return ""; // JSF DebugUtil.printTree may call this 746 } 747 StringBuilder queryParamBuilder = new StringBuilder(); 748 addQueryParameter(queryParamBuilder, ACTION, ACTION_EDIT_DOCUMENT, true); 749 addQueryParameter(queryParamBuilder, REPO_ID, doc.getRepositoryName(), false); 750 addQueryParameter(queryParamBuilder, DOC_REF, doc.getRef().toString(), false); 751 if (schemaName == null || "".equals(schemaName)) { 752 // try to extract it from blob field name 753 schemaName = DocumentModelUtils.getSchemaName(blobFieldName); 754 blobFieldName = DocumentModelUtils.getFieldName(blobFieldName); 755 filenameFieldName = DocumentModelUtils.getFieldName(filenameFieldName); 756 } 757 addQueryParameter(queryParamBuilder, SCHEMA, schemaName, false); 758 addQueryParameter(queryParamBuilder, BLOB_FIELD, blobFieldName, false); 759 addQueryParameter(queryParamBuilder, FILENAME_FIELD, filenameFieldName, false); 760 return buildNxEditUrl(queryParamBuilder.toString()); 761 } 762 763 /** 764 * Build the nxedit URL for the "edit existing document" use case 765 * 766 * @return the encoded URL string 767 */ 768 public static String complexLiveEditUrl(DocumentModel doc, String listPropertyName, int index, 769 String blobPropertyName, String filenamePropertyName) { 770 771 StringBuilder queryParamBuilder = new StringBuilder(); 772 addQueryParameter(queryParamBuilder, ACTION, ACTION_EDIT_DOCUMENT, true); 773 addQueryParameter(queryParamBuilder, REPO_ID, doc.getRepositoryName(), false); 774 addQueryParameter(queryParamBuilder, DOC_REF, doc.getRef().toString(), false); 775 addQueryParameter(queryParamBuilder, BLOB_PROPERTY_NAME, 776 getPropertyPath(listPropertyName, index, blobPropertyName), false); 777 addQueryParameter(queryParamBuilder, FILENAME_PROPERTY_NAME, 778 getPropertyPath(listPropertyName, index, filenamePropertyName), false); 779 return buildNxEditUrl(queryParamBuilder.toString()); 780 } 781 782 /** 783 * Build the nxedit URL for the "create new document" use case with a document using the file:content field as Blob 784 * holder 785 * 786 * @param mimetype the mime type of the newly created document 787 * @return the encoded URL string 788 */ 789 public static String liveCreateUrl(String mimetype) { 790 return liveCreateUrl(mimetype, DEFAULT_DOCTYPE, DEFAULT_SCHEMA, DEFAULT_BLOB_FIELD, DEFAULT_FILENAME_FIELD); 791 } 792 793 /** 794 * Build the nxedit URL for the "create new document" use case 795 * 796 * @param mimetype the mime type of the newly created document 797 * @param docType the document type of the document to create 798 * @param schemaName the schema of the blob to hold the new attachment 799 * @param blobFieldName the field name of the blob to hold the new attachment 800 * @param filenameFieldName the field name of the filename of the new attachment 801 * @return the encoded URL string 802 */ 803 public static String liveCreateUrl(String mimetype, String docType, String schemaName, String blobFieldName, 804 String filenameFieldName) { 805 806 StringBuilder queryParamBuilder = new StringBuilder(); 807 addQueryParameter(queryParamBuilder, ACTION, ACTION_CREATE_DOCUMENT, true); 808 addQueryParameter(queryParamBuilder, MIMETYPE, mimetype, false); 809 addQueryParameter(queryParamBuilder, SCHEMA, schemaName, false); 810 addQueryParameter(queryParamBuilder, BLOB_FIELD, blobFieldName, false); 811 addQueryParameter(queryParamBuilder, FILENAME_FIELD, filenameFieldName, false); 812 addQueryParameter(queryParamBuilder, DOC_TYPE, docType, false); 813 return buildNxEditUrl(queryParamBuilder.toString()); 814 } 815 816 /** 817 * Build the nxedit URL for the "create new document from template" use case with "File" doc type and "file" schema 818 * 819 * @param template the document holding the blob to be used as template 820 * @return the encoded URL string 821 */ 822 public static String liveCreateFromTemplateUrl(DocumentModel template) { 823 return liveCreateFromTemplateUrl(template, DEFAULT_SCHEMA, DEFAULT_BLOB_FIELD, DEFAULT_DOCTYPE, DEFAULT_SCHEMA, 824 DEFAULT_BLOB_FIELD, DEFAULT_FILENAME_FIELD); 825 } 826 827 /** 828 * Build the nxedit URL for the "create new document from template" use case 829 * 830 * @param template the document holding the blob to be used as template 831 * @param templateSchemaName the schema of the blob holding the template 832 * @param templateBlobFieldName the field name of the blob holding the template 833 * @param docType the document type of the new document to create 834 * @param schemaName the schema of the new blob to be saved as attachment 835 * @param blobFieldName the field name of the new blob to be saved as attachment 836 * @param filenameFieldName the field name of the filename of the attachment 837 * @return the encoded URL string 838 */ 839 public static String liveCreateFromTemplateUrl(DocumentModel template, String templateSchemaName, 840 String templateBlobFieldName, String docType, String schemaName, String blobFieldName, 841 String filenameFieldName) { 842 843 StringBuilder queryParamBuilder = new StringBuilder(); 844 addQueryParameter(queryParamBuilder, ACTION, ACTION_CREATE_DOCUMENT_FROM_TEMPLATE, true); 845 addQueryParameter(queryParamBuilder, TEMPLATE_REPO_ID, template.getRepositoryName(), false); 846 addQueryParameter(queryParamBuilder, TEMPLATE_DOC_REF, template.getRef().toString(), false); 847 addQueryParameter(queryParamBuilder, TEMPLATE_SCHEMA, templateSchemaName, false); 848 addQueryParameter(queryParamBuilder, TEMPLATE_BLOB_FIELD, templateBlobFieldName, false); 849 addQueryParameter(queryParamBuilder, SCHEMA, schemaName, false); 850 addQueryParameter(queryParamBuilder, BLOB_FIELD, blobFieldName, false); 851 addQueryParameter(queryParamBuilder, FILENAME_FIELD, filenameFieldName, false); 852 addQueryParameter(queryParamBuilder, DOC_TYPE, docType, false); 853 return buildNxEditUrl(queryParamBuilder.toString()); 854 } 855 856 private static String buildNxEditUrl(String queryParameters) { 857 FacesContext context = FacesContext.getCurrentInstance(); 858 HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); 859 860 // build the URL prefix by concatenating nxedit: scheme with the 861 // http:// or https:// base URL from the current request context and 862 // the LiveEditBoostrapHelper JSF view 863 StringBuilder nxeditUrlBuilder = new StringBuilder(NXEDIT_URL_SCHEME); 864 nxeditUrlBuilder.append(":"); 865 nxeditUrlBuilder.append(BaseURL.getBaseURL(request)); 866 nxeditUrlBuilder.append(NXEDIT_URL_VIEW_ID); 867 868 // add the query parameters them selves 869 nxeditUrlBuilder.append(queryParameters); 870 871 // add seam conversation and JSESSION ids 872 addQueryParameter(nxeditUrlBuilder, Manager.instance().getConversationIdParameter(), 873 Manager.instance().getCurrentConversationId(), false); 874 addQueryParameter(nxeditUrlBuilder, JSESSIONID, extractJSessionId(request), false); 875 return nxeditUrlBuilder.toString(); 876 } 877 878 /** 879 * Extract the current JSESSIONID value from the request context 880 * 881 * @param request the current HttpServletRequest request 882 * @return the current JSESSIONID string 883 */ 884 public static String extractJSessionId(HttpServletRequest request) { 885 if (request.getSession() != null) { 886 return request.getSession().getId(); 887 } 888 if (request.getCookies() != null) { 889 for (Cookie cookie : request.getCookies()) { 890 if (cookie.getName().equalsIgnoreCase("jsessionid")) { 891 return cookie.getValue(); 892 } 893 } 894 } 895 return null; 896 } 897 898 /** 899 * Returns the label for given directory and id. 900 * 901 * @param directoryName the directory name 902 * @param id the label id 903 * @return the label. 904 * @throws DirectoryException 905 * @deprecated use {@link DirectoryFunctions#getDirectoryEntry(String, String)} 906 */ 907 @Deprecated 908 public static String getLabelFromId(String directoryName, String id) throws DirectoryException { 909 if (id == null) { 910 return ""; 911 } 912 try (Session directory = getDirectoryService().open(directoryName)) { 913 // XXX hack, directory entries have only one datamodel 914 DocumentModel documentModel = directory.getEntry(id); 915 String schemaName = documentModel.getSchemas()[0]; 916 return (String) documentModel.getProperty(schemaName, "label"); 917 } catch (PropertyException e) { 918 return ""; 919 } 920 } 921 922 public static String getPropertyPath(String listPropertyName, int index, String subPropertyName) { 923 return listPropertyName + "/" + index + "/" + subPropertyName; 924 } 925 926 /** 927 * Returns all the available transitions given the current state. 928 * 929 * @param lifeCycleName the Life Cycle name 930 * @param currentState the state from which the transitions should start 931 * @since 5.4.2 932 */ 933 public static Collection<String> getAvailableLifeCycleTransitions(String lifeCycleName, String currentState) 934 throws LifeCycleException { 935 LifeCycle lf = geLifeCycleService().getLifeCycleByName(lifeCycleName); 936 return lf.getAllowedStateTransitionsFrom(currentState); 937 } 938 939 /** 940 * Reset default view cache. 941 * 942 * @since 5.8 943 */ 944 public static void resetDefaultViewCache() { 945 defaultViewCache.clear(); 946 } 947 948}