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