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