001/* 002 * (C) Copyright 2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id: DocumentModelFunctions.java 30568 2008-02-25 18:52:49Z ogrisel $ 018 */ 019 020package org.nuxeo.ecm.platform.ui.web.tag.fn; 021 022import java.io.Serializable; 023import java.io.UnsupportedEncodingException; 024import java.net.URLEncoder; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.List; 030import java.util.Map; 031 032import javax.faces.context.FacesContext; 033import javax.servlet.http.Cookie; 034import javax.servlet.http.HttpServletRequest; 035 036import org.apache.commons.lang.StringUtils; 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039import org.jboss.seam.Component; 040import org.jboss.seam.ScopeType; 041import org.jboss.seam.core.Manager; 042import org.nuxeo.ecm.core.NXCore; 043import org.nuxeo.ecm.core.api.Blob; 044import org.nuxeo.ecm.core.api.CoreSession; 045import org.nuxeo.ecm.core.api.DocumentLocation; 046import org.nuxeo.ecm.core.api.DocumentModel; 047import org.nuxeo.ecm.core.api.DocumentNotFoundException; 048import org.nuxeo.ecm.core.api.IdRef; 049import org.nuxeo.ecm.core.api.LifeCycleException; 050import org.nuxeo.ecm.core.api.NuxeoException; 051import org.nuxeo.ecm.core.api.PropertyException; 052import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 053import org.nuxeo.ecm.core.api.model.DocumentPart; 054import org.nuxeo.ecm.core.api.model.Property; 055import org.nuxeo.ecm.core.api.security.SecurityConstants; 056import org.nuxeo.ecm.core.io.download.DownloadService; 057import org.nuxeo.ecm.core.lifecycle.LifeCycle; 058import org.nuxeo.ecm.core.lifecycle.LifeCycleService; 059import org.nuxeo.ecm.core.schema.FacetNames; 060import org.nuxeo.ecm.core.schema.SchemaManager; 061import org.nuxeo.ecm.core.schema.types.Field; 062import org.nuxeo.ecm.core.schema.types.ListType; 063import org.nuxeo.ecm.core.schema.types.Schema; 064import org.nuxeo.ecm.core.schema.types.Type; 065import org.nuxeo.ecm.core.utils.DocumentModelUtils; 066import org.nuxeo.ecm.directory.DirectoryException; 067import org.nuxeo.ecm.directory.Session; 068import org.nuxeo.ecm.directory.api.DirectoryService; 069import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry; 070import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 071import org.nuxeo.ecm.platform.types.TypeManager; 072import org.nuxeo.ecm.platform.types.adapter.TypeInfo; 073import org.nuxeo.ecm.platform.ui.web.directory.DirectoryFunctions; 074import org.nuxeo.ecm.platform.ui.web.directory.DirectoryHelper; 075import org.nuxeo.ecm.platform.ui.web.rest.RestHelper; 076import org.nuxeo.ecm.platform.ui.web.rest.api.URLPolicyService; 077import org.nuxeo.ecm.platform.ui.web.util.BaseURL; 078import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 079import org.nuxeo.ecm.platform.url.DocumentViewImpl; 080import org.nuxeo.ecm.platform.url.api.DocumentView; 081import org.nuxeo.ecm.platform.url.codec.DocumentFileCodec; 082import org.nuxeo.runtime.api.Framework; 083 084/** 085 * Document model functions. 086 * 087 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 088 * @author <a href="mailto:og@nuxeo.com">Olivier Grisel</a> 089 */ 090public final class DocumentModelFunctions implements LiveEditConstants { 091 092 private static final Log log = LogFactory.getLog(DocumentModelFunctions.class); 093 094 private static final String JSESSIONID = "JSESSIONID"; 095 096 private static final String i18n_prefix = "%i18n"; 097 098 private static final String NXEDIT_URL_VIEW_ID = "nxliveedit.faces"; 099 100 private static final String NXEDIT_URL_SCHEME = "nxedit"; 101 102 private static MimetypeRegistry mimetypeService; 103 104 private static TypeManager typeManagerService; 105 106 private static DirectoryService dirService; 107 108 private static LifeCycleService lifeCycleService; 109 110 // static cache of default viewId per document type shared all among 111 // threads 112 private static final Map<String, String> defaultViewCache = Collections.synchronizedMap(new HashMap<String, String>()); 113 114 // Utility class. 115 private DocumentModelFunctions() { 116 } 117 118 private static DirectoryService getDirectoryService() { 119 if (dirService == null) { 120 dirService = DirectoryHelper.getDirectoryService(); 121 } 122 return dirService; 123 } 124 125 private static MimetypeRegistry getMimetypeService() { 126 if (mimetypeService == null) { 127 mimetypeService = Framework.getService(MimetypeRegistry.class); 128 } 129 return mimetypeService; 130 } 131 132 private static TypeManager getTypeManager() { 133 if (typeManagerService == null) { 134 typeManagerService = Framework.getService(TypeManager.class); 135 } 136 return typeManagerService; 137 } 138 139 private static String getDefaultView(DocumentModel doc) { 140 String docType = doc.getType(); 141 142 if (defaultViewCache.containsKey(docType)) { 143 return defaultViewCache.get(docType); 144 } else { 145 org.nuxeo.ecm.platform.types.Type type = getTypeManager().getType(docType); 146 if (type == null) { 147 return null; 148 } 149 String defaultView = type.getDefaultView(); 150 defaultViewCache.put(docType, defaultView); 151 return defaultView; 152 } 153 } 154 155 private static LifeCycleService geLifeCycleService() { 156 if (lifeCycleService == null) { 157 lifeCycleService = NXCore.getLifeCycleService(); 158 if (lifeCycleService == null) { 159 log.error("No Life Cycle service registered"); 160 } 161 } 162 return lifeCycleService; 163 } 164 165 public static TypeInfo typeInfo(DocumentModel document) { 166 if (document != null) { 167 return document.getAdapter(TypeInfo.class); 168 } else { 169 return null; 170 } 171 } 172 173 public static String typeLabel(DocumentModel document) { 174 String label = ""; 175 if (document != null) { 176 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 177 if (typeInfo != null) { 178 label = typeInfo.getLabel(); 179 } 180 } 181 return label; 182 } 183 184 public static String typeView(DocumentModel document, String viewId) { 185 String viewValue = ""; 186 if (document != null) { 187 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 188 if (typeInfo != null) { 189 viewValue = typeInfo.getView(viewId); 190 } 191 } 192 return viewValue; 193 } 194 195 public static String iconPath(DocumentModel document) { 196 String iconPath = ""; 197 if (document != null) { 198 try { 199 iconPath = (String) document.getProperty("common", "icon"); 200 } catch (PropertyException e) { 201 iconPath = null; 202 } 203 if (iconPath == null || iconPath.length() == 0 || document.getType().equals("Workspace")) { 204 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 205 if (typeInfo != null) { 206 iconPath = typeInfo.getIcon(); 207 } 208 } 209 } 210 return iconPath; 211 } 212 213 public static String iconExpandedPath(DocumentModel document) { 214 String iconPath = ""; 215 if (document != null) { 216 try { 217 iconPath = (String) document.getProperty("common", "icon-expanded"); 218 } catch (PropertyException e) { 219 iconPath = null; 220 } 221 if (iconPath == null || iconPath.length() == 0) { 222 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 223 if (typeInfo != null) { 224 iconPath = typeInfo.getIconExpanded(); 225 // Set to default icon if expanded is not set 226 if (iconPath == null || iconPath.equals("")) { 227 iconPath = iconPath(document); 228 } 229 } 230 231 } 232 } 233 return iconPath; 234 } 235 236 public static String bigIconPath(DocumentModel document) { 237 String iconPath = ""; 238 if (document != null) { 239 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 240 if (typeInfo != null) { 241 iconPath = typeInfo.getBigIcon(); 242 } 243 } 244 return iconPath; 245 } 246 247 public static String bigIconExpandedPath(DocumentModel document) { 248 String iconPath = ""; 249 if (document != null) { 250 TypeInfo typeInfo = document.getAdapter(TypeInfo.class); 251 if (typeInfo != null) { 252 iconPath = typeInfo.getIconExpanded(); 253 // Set to default icon if expanded is not set 254 if (iconPath == null || iconPath.equals("")) { 255 iconPath = bigIconPath(document); 256 } 257 } 258 } 259 return iconPath; 260 } 261 262 public static String fileIconPath(Blob blob) { 263 String iconPath = ""; 264 if (blob != null) { 265 MimetypeEntry mimeEntry = getMimetypeService().getMimetypeEntryByMimeType(blob.getMimeType()); 266 if (mimeEntry != null) { 267 if (mimeEntry.getIconPath() != null) { 268 // FIXME: above Context should find it 269 iconPath = "/icons/" + mimeEntry.getIconPath(); 270 } 271 } 272 } 273 return iconPath; 274 } 275 276 public static String titleOrId(DocumentModel document) { 277 String title = null; 278 if (document != null) { 279 try { 280 title = (String) document.getProperty("dublincore", "title"); 281 } catch (PropertyException e) { 282 title = null; 283 } 284 if (title == null || title.length() == 0) { 285 // handle root document title 286 if ("/".equals(document.getPathAsString())) { 287 title = "/"; 288 } else { 289 title = document.getId(); 290 } 291 } 292 } 293 294 if (title == null) { 295 title = "<Unknown>"; 296 } 297 298 if (title.startsWith(i18n_prefix)) { 299 String i18nTitle = title.substring(i18n_prefix.length()); 300 FacesContext context = FacesContext.getCurrentInstance(); 301 title = ComponentUtils.translate(context, i18nTitle); 302 } 303 return title; 304 } 305 306 /** 307 * @since 6.0 308 */ 309 public static String titleFromId(final String documentId) { 310 final CoreSession coreSession = (CoreSession) Component.getInstance("documentManager"); 311 if (StringUtils.isNotBlank(documentId)) { 312 try { 313 DocumentModel doc = coreSession.getDocument(new IdRef(documentId)); 314 if (doc != null) { 315 return titleOrId(doc); 316 } 317 } catch (DocumentNotFoundException e) { 318 log.info(String.format("Could not find document with id %s", documentId)); 319 } 320 } 321 return documentId; 322 } 323 324 public static boolean isDocumentModel(Object value) { 325 return value instanceof DocumentModel; 326 } 327 328 public static boolean isDirty(DocumentModel doc) { 329 if (doc == null) { 330 return false; 331 } 332 for (DocumentPart part : doc.getParts()) { 333 if (part.isDirty()) { 334 // check if dirty properties are not empty 335 Iterator<Property> props = part.getDirtyChildren(); 336 if (props != null) { 337 while (props.hasNext()) { 338 Property prop = props.next(); 339 Serializable value = prop.getValue(); 340 if (value != null) { 341 if (isPropertyValueDirty(value)) { 342 return true; 343 } 344 } 345 } 346 } 347 } 348 } 349 return false; 350 } 351 352 @SuppressWarnings("rawtypes") 353 protected static boolean isPropertyValueDirty(Object value) { 354 if (value instanceof String) { 355 if (!StringUtils.isBlank((String) value)) { 356 return true; 357 } 358 } else if (value instanceof List) { 359 List<?> list = (List) value; 360 if (!list.isEmpty()) { 361 return true; 362 } 363 } else if (value instanceof Collection) { 364 Collection<?> col = (Collection) value; 365 if (!col.isEmpty()) { 366 return true; 367 } 368 } else if (value instanceof Object[]) { 369 Object[] col = (Object[]) value; 370 if (col.length > 0) { 371 return true; 372 } 373 } else if (value instanceof Map) { 374 Map<?, ?> map = (Map) value; 375 if (map.isEmpty()) { 376 return true; 377 } 378 for (Object mapItem : map.values()) { 379 if (isPropertyValueDirty(mapItem)) { 380 return true; 381 } 382 } 383 } else if (value != null) { 384 // in any other case, test if object is null (int, calendar, 385 // etc...) 386 return true; 387 } 388 return false; 389 } 390 391 public static boolean hasPermission(DocumentModel document, String permission) { 392 if (document == null) { 393 return false; 394 } 395 CoreSession session = document.getCoreSession(); 396 if (session == null) { 397 session = (CoreSession) Component.getInstance("documentManager", ScopeType.CONVERSATION); 398 } 399 if (session == null) { 400 log.error("Cannot retrieve CoreSession for " + document); 401 return false; 402 } 403 boolean granted = session.hasPermission(document.getRef(), permission); 404 return granted; 405 } 406 407 /** 408 * Returns true if document can be modified. 409 * <p> 410 * A document can be modified if current user has 'Write' permission on it and document is mutable (no archived 411 * version). 412 * 413 * @param document 414 * @return true if document can be modified. 415 */ 416 public static boolean canModify(DocumentModel document) { 417 if (document == null) { 418 return false; 419 } 420 return hasPermission(document, SecurityConstants.WRITE) && !document.hasFacet(FacetNames.IMMUTABLE); 421 } 422 423 /** 424 * Returns the default value for given schema field. 425 * 426 * @param schemaName the schema name 427 * @param fieldName the field name 428 * @return the default value. 429 * @deprecated use defaultValue(propertyName) instead 430 */ 431 @Deprecated 432 public static Object defaultValue(String schemaName, String fieldName) { 433 Object value = null; 434 SchemaManager tm = Framework.getService(SchemaManager.class); 435 Schema schema = tm.getSchema(schemaName); 436 Field field = schema.getField(fieldName); 437 Type type = field.getType(); 438 if (type.isListType()) { 439 Type itemType = ((ListType) type).getFieldType(); 440 value = itemType.newInstance(); 441 } 442 return value; 443 } 444 445 /** 446 * Returns the default value for given property name. 447 * 448 * @param propertyName as xpath 449 * @return the default value. 450 */ 451 public static Object defaultValue(String propertyName) { 452 SchemaManager tm = Framework.getService(SchemaManager.class); 453 Field field = tm.getField(propertyName); 454 Object value = null; 455 if (field != null) { 456 Type type = field.getType(); 457 if (type.isListType()) { 458 Type itemType = ((ListType) type).getFieldType(); 459 value = itemType.newInstance(); 460 } else if (type.isComplexType()) { 461 value = type.newInstance(); 462 } else { 463 value = field.getDefaultValue(); 464 } 465 } 466 return value; 467 } 468 469 /** 470 * @since 6.0 471 */ 472 public static String fileUrl(String baseURL, String patternName, DocumentModel doc, String blobPropertyName, 473 String filename) { 474 if (doc == null) { 475 return null; 476 } 477 DocumentLocation docLoc = new DocumentLocationImpl(doc); 478 Map<String, String> params = new HashMap<String, String>(); 479 params.put(DocumentFileCodec.FILE_PROPERTY_PATH_KEY, blobPropertyName); 480 params.put(DocumentFileCodec.FILENAME_KEY, filename); 481 DocumentView docView = new DocumentViewImpl(docLoc, null, params); 482 483 // generate url 484 URLPolicyService service = Framework.getService(URLPolicyService.class); 485 if (patternName == null) { 486 patternName = service.getDefaultPatternName(); 487 } 488 return service.getUrlFromDocumentView(patternName, docView, baseURL); 489 } 490 491 public static String fileUrl(String patternName, DocumentModel doc, String blobPropertyName, String filename) { 492 return fileUrl(BaseURL.getBaseURL(), patternName, doc, blobPropertyName, filename); 493 } 494 495 public static String bigFileUrl(DocumentModel doc, String blobPropertyName, String filename) { 496 if (doc == null) { 497 return null; 498 } 499 DownloadService downloadService = Framework.getService(DownloadService.class); 500 return BaseURL.getBaseURL() + downloadService.getDownloadUrl(doc, blobPropertyName, filename); 501 } 502 503 public static String fileDescription(DocumentModel document, String blobPropertyName, String filePropertyName, 504 String filename) { 505 String fileInfo = ""; 506 if (document != null) { 507 Long blobLength = null; 508 try { 509 Blob blob = (Blob) document.getPropertyValue(blobPropertyName); 510 if (blob != null) { 511 blobLength = blob.getLength(); 512 } 513 } catch (PropertyException e) { 514 // no prop by that name with that type 515 } 516 if (filename == null && filePropertyName != null) { 517 try { 518 filename = (String) document.getPropertyValue(filePropertyName); 519 } catch (PropertyException e) { 520 // no prop by that name with that type 521 } 522 } 523 if (blobLength != null && filename != null) { 524 fileInfo = String.format("%s [%s]", filename, Functions.printFileSize(String.valueOf(blobLength))); 525 } else if (blobLength != null) { 526 fileInfo = String.format("[%s]", Functions.printFileSize(String.valueOf(blobLength))); 527 } else if (filename != null) { 528 fileInfo = filename; 529 } 530 } 531 return fileInfo; 532 } 533 534 /** 535 * Convenient method to get the REST URL of a blob inside the <code>Files</code> schema. 536 * 537 * @param patternName 538 * @param doc The document model. 539 * @param index index of the element containing the blob. <code>index</code> starts at 0. 540 * @param filename The filename of the blob. 541 * @return the REST URL for the blob, or <code>null</code> if an error occurred. 542 */ 543 public static String complexFileUrl(String patternName, DocumentModel doc, int index, String filename) { 544 return complexFileUrl(patternName, doc, "files:files", index, DEFAULT_SUB_BLOB_FIELD, filename); 545 } 546 547 /** 548 * Get the REST URL for a blob inside a list of complex type. For instance, 549 * <code>http://localhost/nuxeo/nxfile/server/docId/files:files%5B0%5D/file/image.png</code> for the blob property 550 * 'file' of the first element inside the 'files:files' list. 551 * 552 * @param patternName 553 * @param doc The document model. 554 * @param listElement Element containing a list of complex type. 555 * @param index Index of the element containing the blob inside the list. <code>index</code> starts at 0. 556 * @param blobPropertyName The property containing the blob. 557 * @param filename Filename of the blob. 558 * @return the REST URL for the blob, or <code>null</code> if an error occurred. 559 */ 560 public static String complexFileUrl(String patternName, DocumentModel doc, String listElement, int index, 561 String blobPropertyName, String filename) { 562 DocumentLocation docLoc = new DocumentLocationImpl(doc); 563 Map<String, String> params = new HashMap<String, String>(); 564 565 String fileProperty = getPropertyPath(listElement, index, blobPropertyName); 566 567 params.put(DocumentFileCodec.FILE_PROPERTY_PATH_KEY, fileProperty); 568 params.put(DocumentFileCodec.FILENAME_KEY, filename); 569 DocumentView docView = new DocumentViewImpl(docLoc, null, params); 570 571 // generate url 572 URLPolicyService service = Framework.getService(URLPolicyService.class); 573 if (patternName == null) { 574 patternName = service.getDefaultPatternName(); 575 } 576 return service.getUrlFromDocumentView(patternName, docView, BaseURL.getBaseURL()); 577 } 578 579 // TODO: add method accepting filename property name (used for edit online) 580 581 public static String documentUrl(DocumentModel doc, HttpServletRequest req) { 582 return documentUrl(null, doc, null, null, false); 583 } 584 585 public static String documentUrl(DocumentModel doc) { 586 return documentUrl(null, doc, null, null, false); 587 } 588 589 /** 590 * @deprecated since 7.3, use {@link #documentUrl(String, DocumentLocation, String, Map, boolean, String)} instead. 591 */ 592 public static String documentUrl(String patternName, DocumentModel doc, String viewId, 593 Map<String, String> parameters, boolean newConversation) { 594 return documentUrl(patternName, doc, viewId, parameters, newConversation, (HttpServletRequest) null); 595 } 596 597 /** 598 * @deprecated since 7.3, use {@link #documentUrl(String, DocumentLocation, String, Map, boolean, String)} instead. 599 */ 600 public static String documentUrl(String patternName, DocumentModel doc, String viewId, 601 Map<String, String> parameters, boolean newConversation, HttpServletRequest req) { 602 String baseURL = null; 603 if (req == null) { 604 baseURL = BaseURL.getBaseURL(); 605 } else { 606 baseURL = BaseURL.getBaseURL(req); 607 } 608 return documentUrl(patternName, doc, viewId, parameters, newConversation, baseURL); 609 } 610 611 /** 612 * Computes a document URL. 613 * 614 * @since 7.3 615 */ 616 public static String documentUrl(String patternName, DocumentModel doc, String viewId, 617 Map<String, String> parameters, boolean newConversation, String baseURL) { 618 DocumentLocation docLoc = new DocumentLocationImpl(doc); 619 if (viewId == null || viewId.length() == 0) { 620 viewId = getDefaultView(doc); 621 } 622 parameters = parameters == null ? new HashMap<String, String>() : parameters; 623 624 if (doc.isVersion()) { 625 parameters.put("version", "true"); 626 } 627 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, baseURL); 628 } 629 630 /** 631 * @since 5.7 632 */ 633 public static String documentUrl(String patternName, DocumentLocation docLoc, String viewId, 634 Map<String, String> parameters, boolean newConversation, HttpServletRequest req) { 635 String baseURL = null; 636 if (req == null) { 637 baseURL = BaseURL.getBaseURL(); 638 } else { 639 baseURL = BaseURL.getBaseURL(req); 640 } 641 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, baseURL); 642 } 643 644 /** 645 * Computes a document URL. 646 * 647 * @since 7.3 648 */ 649 public static String documentUrl(String patternName, DocumentLocation docLoc, String viewId, 650 Map<String, String> parameters, boolean newConversation, String baseURL) { 651 DocumentView docView = new DocumentViewImpl(docLoc, viewId, parameters); 652 653 // generate url 654 URLPolicyService service = Framework.getService(URLPolicyService.class); 655 if (patternName == null || patternName.length() == 0) { 656 patternName = service.getDefaultPatternName(); 657 } 658 659 String url = service.getUrlFromDocumentView(patternName, docView, baseURL); 660 661 // pass conversation info if needed 662 if (!newConversation && url != null) { 663 url = RestHelper.addCurrentConversationParameters(url); 664 } 665 666 return url; 667 } 668 669 /** 670 * Computes an URL for a {@code repositoryName} only. 671 * 672 * @since 5.7 673 * @deprecated since 7.3, use {@link #repositoryUrl(String, String, String, Map, boolean, String)} instead. 674 */ 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 public static String repositoryUrl(String patternName, String repositoryName, String viewId, 688 Map<String, String> parameters, boolean newConversation, HttpServletRequest req) { 689 DocumentLocation docLoc = new DocumentLocationImpl(repositoryName, null); 690 parameters = parameters == null ? new HashMap<String, String>() : parameters; 691 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, req); 692 } 693 694 /** 695 * Computes an URL for a {@code repositoryName} only. 696 * 697 * @since 7.3 698 */ 699 public static String repositoryUrl(String patternName, String repositoryName, String viewId, 700 Map<String, String> parameters, boolean newConversation, String baseURL) { 701 DocumentLocation docLoc = new DocumentLocationImpl(repositoryName, null); 702 parameters = parameters == null ? new HashMap<String, String>() : parameters; 703 return documentUrl(patternName, docLoc, viewId, parameters, newConversation, baseURL); 704 } 705 706 protected static void addQueryParameter(StringBuilder sb, String name, String value, boolean isFirst) 707 { 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(String.format("could not encode URL parameter: %s=%s", 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 * @throws DirectoryException 903 * @deprecated use {@link DirectoryFunctions#getDirectoryEntry(String, String)} 904 */ 905 @Deprecated 906 public static String getLabelFromId(String directoryName, String id) throws DirectoryException { 907 if (id == null) { 908 return ""; 909 } 910 try (Session directory = getDirectoryService().open(directoryName)) { 911 // XXX hack, directory entries have only one datamodel 912 DocumentModel documentModel = directory.getEntry(id); 913 String schemaName = documentModel.getSchemas()[0]; 914 return (String) documentModel.getProperty(schemaName, "label"); 915 } catch (PropertyException e) { 916 return ""; 917 } 918 } 919 920 public static String getPropertyPath(String listPropertyName, int index, String subPropertyName) { 921 return String.format("%s/%s/%s", listPropertyName, index, subPropertyName); 922 } 923 924 /** 925 * Returns all the available transitions given the current state. 926 * 927 * @param lifeCycleName the Life Cycle name 928 * @param currentState the state from which the transitions should start 929 * @since 5.4.2 930 */ 931 public static Collection<String> getAvailableLifeCycleTransitions(String lifeCycleName, String currentState) 932 throws LifeCycleException { 933 LifeCycle lf = geLifeCycleService().getLifeCycleByName(lifeCycleName); 934 return lf.getAllowedStateTransitionsFrom(currentState); 935 } 936 937 /** 938 * Reset default view cache. 939 * 940 * @since 5.8 941 */ 942 public static void resetDefaultViewCache() { 943 defaultViewCache.clear(); 944 } 945 946}