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