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