001/* 002 * (C) Copyright 2013 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 * <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 018 * <a href="mailto:grenard@nuxeo.com">Guillaume</a> 019 */ 020 021package org.nuxeo.ecm.platform.ui.select2; 022 023import java.io.BufferedOutputStream; 024import java.io.IOException; 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Locale; 030import java.util.Map; 031import java.util.Map.Entry; 032 033import javax.faces.context.FacesContext; 034import javax.faces.event.PhaseId; 035import javax.servlet.http.HttpServletRequest; 036 037import net.sf.json.JSONArray; 038import net.sf.json.JSONObject; 039 040import org.apache.commons.io.output.ByteArrayOutputStream; 041import org.apache.commons.lang.StringUtils; 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044import org.codehaus.jackson.JsonGenerator; 045import org.jboss.seam.ScopeType; 046import org.jboss.seam.annotations.Destroy; 047import org.jboss.seam.annotations.In; 048import org.jboss.seam.annotations.Name; 049import org.jboss.seam.annotations.Scope; 050import org.jboss.seam.contexts.FacesLifecycle; 051import org.nuxeo.ecm.automation.AutomationService; 052import org.nuxeo.ecm.automation.InvalidChainException; 053import org.nuxeo.ecm.automation.OperationContext; 054import org.nuxeo.ecm.automation.OperationException; 055import org.nuxeo.ecm.automation.core.operations.services.DocumentPageProviderOperation; 056import org.nuxeo.ecm.automation.core.operations.services.directory.SuggestDirectoryEntries; 057import org.nuxeo.ecm.automation.core.operations.users.SuggestUserEntries; 058import org.nuxeo.ecm.automation.features.SuggestConstants; 059import org.nuxeo.ecm.automation.jaxrs.io.JsonHelper; 060import org.nuxeo.ecm.core.api.CoreInstance; 061import org.nuxeo.ecm.core.api.CoreSession; 062import org.nuxeo.ecm.core.api.DocumentModel; 063import org.nuxeo.ecm.core.api.DocumentModelList; 064import org.nuxeo.ecm.core.api.DocumentRef; 065import org.nuxeo.ecm.core.api.IdRef; 066import org.nuxeo.ecm.core.api.NuxeoGroup; 067import org.nuxeo.ecm.core.api.NuxeoPrincipal; 068import org.nuxeo.ecm.core.api.PathRef; 069import org.nuxeo.ecm.core.api.PropertyException; 070import org.nuxeo.ecm.core.api.repository.RepositoryManager; 071import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter; 072import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 073import org.nuxeo.ecm.core.io.registry.context.RenderingContext; 074import org.nuxeo.ecm.core.schema.SchemaManager; 075import org.nuxeo.ecm.core.schema.types.Field; 076import org.nuxeo.ecm.core.schema.types.QName; 077import org.nuxeo.ecm.core.schema.types.Schema; 078import org.nuxeo.ecm.directory.Directory; 079import org.nuxeo.ecm.directory.DirectoryException; 080import org.nuxeo.ecm.directory.Session; 081import org.nuxeo.ecm.directory.api.DirectoryService; 082import org.nuxeo.ecm.platform.forms.layout.api.Widget; 083import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeConfiguration; 084import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeDefinition; 085import org.nuxeo.ecm.platform.forms.layout.api.service.LayoutStore; 086import org.nuxeo.ecm.platform.ui.select2.common.Select2Common; 087import org.nuxeo.ecm.platform.url.DocumentViewImpl; 088import org.nuxeo.ecm.platform.url.codec.DocumentIdCodec; 089import org.nuxeo.ecm.platform.usermanager.UserManager; 090import org.nuxeo.ecm.webengine.jaxrs.coreiodelegate.RenderingContextWebUtils; 091import org.nuxeo.runtime.api.Framework; 092 093/** 094 * Initialization for Select2. 095 * 096 * @since 5.7.3 097 */ 098@Name("select2Actions") 099@Scope(ScopeType.EVENT) 100public class Select2ActionsBean implements Serializable { 101 102 private static final long serialVersionUID = 1L; 103 104 private static final Log log = LogFactory.getLog(Select2ActionsBean.class); 105 106 protected static final String SELECT2_RESOURCES_MARKER = "SELECT2_RESOURCES_MARKER"; 107 108 private static List<String> formatList(JSONArray array) { 109 List<String> result = new ArrayList<String>(); 110 if (array != null) { 111 for (int i = 0; i < array.size(); i++) { 112 result.add(array.getJSONObject(i).getString(Select2Common.LABEL)); 113 } 114 } 115 return result; 116 } 117 118 @In(create = true) 119 protected Map<String, String> messages; 120 121 @In(create = true, required = false) 122 protected transient CoreSession documentManager; 123 124 protected transient CoreSession dedicatedSession = null; 125 126 @Destroy 127 public void destroy() { 128 if (dedicatedSession != null) { 129 dedicatedSession.close(); 130 } 131 } 132 133 private static Map<String, String> getDefaultFormattersMap(final String suggestionFormatterName, 134 String selectionFormatterName) { 135 Map<String, String> result = new HashMap<String, String>(); 136 result.put(Select2Common.SUGGESTION_FORMATTER, suggestionFormatterName); 137 result.put(Select2Common.SELECTION_FORMATTER, selectionFormatterName); 138 return result; 139 } 140 141 /** 142 * @since 5.9.3 143 */ 144 protected static Map<String, String> getContextParameter(final DocumentModel doc) { 145 DocumentIdCodec documentIdCodec = new DocumentIdCodec(); 146 Map<String, String> contextParameters = new HashMap<String, String>(); 147 contextParameters.put("documentURL", documentIdCodec.getUrlFromDocumentView(new DocumentViewImpl(doc))); 148 return contextParameters; 149 } 150 151 public String encodeParametersForUserSuggestion(final Widget widget, 152 final Map<String, Serializable> resolvedWidgetProperties) { 153 Map<String, String> params = getDefaultFormattersMap(Select2Common.USER_DEFAULT_SUGGESTION_FORMATTER, 154 Select2Common.USER_DEFAULT_SELECTION_FORMATTER); 155 params.put(Select2Common.OPERATION_ID, SuggestUserEntries.ID); 156 return encodeParameters(widget, params, resolvedWidgetProperties); 157 } 158 159 public String encodeParametersForDirectory(final Widget widget, 160 final Map<String, Serializable> resolvedWidgetProperties) { 161 Map<String, String> params = getDefaultFormattersMap(Select2Common.DIR_DEFAULT_SUGGESTION_FORMATTER, 162 Select2Common.DIR_DEFAULT_SELECTION_FORMATTER); 163 params.put(Select2Common.OPERATION_ID, SuggestDirectoryEntries.ID); 164 return encodeParameters(widget, params, resolvedWidgetProperties); 165 } 166 167 public String encodeParameters(final Widget widget) { 168 return encodeParameters(widget, null); 169 } 170 171 public String encodeParameters(final Widget widget, final Map<String, Serializable> resolvedWidgetProperties) { 172 Map<String, String> params = getDefaultFormattersMap(Select2Common.DOC_DEFAULT_SUGGESTION_FORMATTER, 173 Select2Common.DOC_DEFAULT_SELECTION_FORMATTER); 174 params.put(Select2Common.OPERATION_ID, DocumentPageProviderOperation.ID); 175 return encodeParameters(widget, params, resolvedWidgetProperties); 176 } 177 178 /** 179 * Encode widget properties and parameters that Select2 pick them up in a hidden input. 180 * 181 * @param widget the widget 182 * @return encoded 183 * @since 5.7.3 184 */ 185 public String encodeParameters(final Widget widget, final Map<String, String> defaultParams, 186 final Map<String, Serializable> resolvedWidgetProperties) { 187 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 188 BufferedOutputStream out = new BufferedOutputStream(baos); 189 190 JsonGenerator jg; 191 try { 192 jg = JsonHelper.createJsonGenerator(out); 193 194 jg.writeStartObject(); 195 196 // multiple is not in properties and we must add it because Select2 197 // needs to know. 198 jg.writeStringField("multiple", "" + isMultiSelection(widget)); 199 200 final boolean isTranslated = widget.isTranslated(); 201 202 // Are we writing or reading 203 boolean readonly = !widget.getMode().equals("edit") && !widget.getMode().equals("create"); 204 jg.writeStringField(Select2Common.READ_ONLY_PARAM, Boolean.toString(readonly)); 205 206 Map<String, Serializable> propertySet = null; 207 if (resolvedWidgetProperties != null) { 208 propertySet = resolvedWidgetProperties; 209 } else { 210 propertySet = widget.getProperties(); 211 } 212 213 boolean hasPlaceholder = false; 214 boolean hasAjaxReRender = false; 215 boolean hasWidth = false; 216 boolean hasMinChars = false; 217 218 for (Entry<String, Serializable> entry : propertySet.entrySet()) { 219 220 if (entry.getValue() == null) { 221 continue; 222 } 223 224 if (defaultParams != null) { 225 // Widget properties have priority on default properties 226 defaultParams.remove(entry.getKey()); 227 } 228 229 String value = entry.getValue().toString(); 230 231 if (entry.getKey().equals(Select2Common.PLACEHOLDER)) { 232 hasPlaceholder = true; 233 // placeholder can be translated 234 if (isTranslated || Boolean.parseBoolean((String) propertySet.get("translatePlaceholder"))) { 235 value = messages.get(entry.getValue().toString()); 236 } 237 } else if (entry.getKey().equals(Select2Common.AJAX_RERENDER)) { 238 hasAjaxReRender = true; 239 } else if (entry.getKey().equals(Select2Common.WIDTH)) { 240 hasWidth = true; 241 } else if (entry.getKey().equals(Select2Common.MIN_CHARS)) { 242 hasMinChars = true; 243 } 244 245 jg.writeStringField(entry.getKey(), value); 246 247 } 248 249 if (defaultParams != null) { 250 // All default params which are not in widget properties 251 for (Entry<String, String> e : defaultParams.entrySet()) { 252 jg.writeStringField(e.getKey(), e.getValue()); 253 } 254 } 255 256 if (!hasPlaceholder) { 257 // No placeholder provider and Select2 requires one to enable 258 // the 259 // reset button. 260 jg.writeStringField(Select2Common.PLACEHOLDER, messages.get("label.vocabulary.selectValue")); 261 } 262 263 if (!hasWidth) { 264 jg.writeStringField(Select2Common.WIDTH, Select2Common.DEFAULT_WIDTH); 265 } 266 267 if (!hasMinChars) { 268 jg.writeNumberField(Select2Common.MIN_CHARS, Select2Common.DEFAULT_MIN_CHARS); 269 } 270 271 if (hasAjaxReRender) { 272 jg.writeStringField(Select2Common.RERENDER_JS_FUNCTION_NAME, widget.getId() + "_reRender"); 273 } 274 275 jg.writeEndObject(); 276 jg.flush(); 277 out.flush(); 278 return new String(baos.toByteArray(), "UTF-8"); 279 } catch (IOException e) { 280 log.error("Could not encode parameters", e); 281 return null; 282 } 283 } 284 285 protected LayoutStore getLayoutStore() { 286 return Framework.getService(LayoutStore.class); 287 } 288 289 @SuppressWarnings("rawtypes") 290 protected JSONArray getMultipleDirectoryEntries(final Object value, final String directoryName, 291 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 292 JSONArray result = new JSONArray(); 293 if (value == null) { 294 return result; 295 } 296 297 List<String> storedRefs = new ArrayList<>(); 298 if (value instanceof List) { 299 for (Object v : (List) value) { 300 storedRefs.add(v.toString()); 301 } 302 } else if (value instanceof Object[]) { 303 for (Object v : (Object[]) value) { 304 storedRefs.add(v.toString()); 305 } 306 } 307 308 DirectoryService directoryService = Framework.getLocalService(DirectoryService.class); 309 try { 310 Directory directory = directoryService.getDirectory(directoryName); 311 if (directory == null) { 312 log.error("Could not find directory with name " + directoryName); 313 return null; 314 } 315 316 try (Session session = directory.getSession()) { 317 String schemaName = directory.getSchema(); 318 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 319 Schema schema = schemaManager.getSchema(schemaName); 320 final Locale locale = org.jboss.seam.core.Locale.instance(); 321 final String label = SuggestConstants.getLabelFieldName(schema, dbl10n, labelFieldName, 322 locale.getLanguage()); 323 324 for (String ref : storedRefs) { 325 JSONObject obj = resolveDirectoryEntry(ref, keySeparator, session, schema, label, localize, dbl10n); 326 if (obj != null) { 327 result.add(obj); 328 } 329 } 330 return result; 331 } 332 } catch (DirectoryException de) { 333 log.error("An error occured while obtaining directory " + directoryName, de); 334 return result; 335 } 336 } 337 338 @SuppressWarnings("rawtypes") 339 protected JSONArray getMultipleUserReference(final Object value, final boolean prefixed, 340 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 341 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 342 final boolean displayEmailInSuggestion, final boolean hideIcon) { 343 if (value == null) { 344 return null; 345 } 346 JSONArray result = new JSONArray(); 347 List<String> storedRefs = new ArrayList<>(); 348 if (value instanceof List) { 349 for (Object v : (List) value) { 350 storedRefs.add(v.toString()); 351 } 352 } else if (value instanceof Object[]) { 353 for (Object v : (Object[]) value) { 354 storedRefs.add(v.toString()); 355 } 356 } 357 358 for (String ref : storedRefs) { 359 JSONObject resolved = getSingleUserReference(ref, prefixed, firstLabelField, secondLabelField, 360 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, 361 hideIcon); 362 if (resolved != null && !resolved.isEmpty()) { 363 result.add(resolved); 364 } 365 } 366 return result; 367 } 368 369 protected CoreSession getRepositorySession(String repoName) { 370 371 if (repoName == null || repoName.isEmpty()) { 372 RepositoryManager rm = Framework.getLocalService(RepositoryManager.class); 373 repoName = rm.getDefaultRepositoryName(); 374 } 375 376 if (documentManager != null && documentManager.getRepositoryName().equals(repoName)) { 377 return documentManager; 378 } 379 380 dedicatedSession = CoreInstance.openCoreSession(repoName); 381 return dedicatedSession; 382 } 383 384 protected JSONObject getSingleDirectoryEntry(final String storedReference, final String directoryName, 385 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 386 387 if (storedReference == null || storedReference.isEmpty()) { 388 return null; 389 } 390 391 DirectoryService directoryService = Framework.getLocalService(DirectoryService.class); 392 try { 393 Directory directory = directoryService.getDirectory(directoryName); 394 if (directory == null) { 395 log.error("Could not find directory with name " + directoryName); 396 return null; 397 } 398 399 try (Session session = directory.getSession()) { 400 String schemaName = directory.getSchema(); 401 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 402 Schema schema = schemaManager.getSchema(schemaName); 403 404 final Locale locale = org.jboss.seam.core.Locale.instance(); 405 final String label = SuggestConstants.getLabelFieldName(schema, dbl10n, labelFieldName, locale.getLanguage()); 406 407 JSONObject obj = resolveDirectoryEntry(storedReference, keySeparator, session, schema, label, localize, 408 dbl10n); 409 410 return obj; 411 } 412 } catch (DirectoryException de) { 413 log.error("An error occured while obtaining directory " + directoryName, de); 414 return null; 415 } 416 } 417 418 protected JSONObject getSingleUserReference(final String storedReference, final boolean prefixed, 419 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 420 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 421 final boolean displayEmailInSuggestion, final boolean hideIcon) { 422 UserManager userManager = Framework.getLocalService(UserManager.class); 423 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 424 DirectoryService dirService = Framework.getLocalService(DirectoryService.class); 425 JSONObject obj = new JSONObject(); 426 if (storedReference == null || storedReference.isEmpty()) { 427 return null; 428 } 429 DocumentModel user = null; 430 DocumentModel group = null; 431 Directory userDir = dirService.getDirectory(userManager.getUserDirectoryName()); 432 if (prefixed) { 433 if (storedReference.startsWith(NuxeoPrincipal.PREFIX)) { 434 user = userManager.getUserModel(storedReference.substring(NuxeoPrincipal.PREFIX.length())); 435 } else if (storedReference.startsWith(NuxeoGroup.PREFIX)) { 436 group = userManager.getGroupModel(storedReference.substring(NuxeoGroup.PREFIX.length())); 437 } else { 438 log.warn("User reference is prefixed but prefix was not found on reference: " + storedReference); 439 return createNotFoundEntry(storedReference); 440 } 441 } else { 442 user = userManager.getUserModel(storedReference); 443 if (user == null) { 444 group = userManager.getGroupModel(storedReference); 445 } 446 } 447 if (user != null) { 448 Schema schema = schemaManager.getSchema(userManager.getUserSchemaName()); 449 for (Field field : schema.getFields()) { 450 QName fieldName = field.getName(); 451 String key = fieldName.getLocalName(); 452 Serializable value = user.getPropertyValue(fieldName.getPrefixedName()); 453 if (key.equals(userDir.getPasswordField())) { 454 continue; 455 } 456 obj.element(key, value); 457 } 458 String userId = user.getId(); 459 obj.put(SuggestConstants.ID, userId); 460 obj.put(SuggestConstants.TYPE_KEY_NAME, SuggestConstants.USER_TYPE); 461 obj.put(SuggestConstants.PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + userId); 462 SuggestConstants.computeUserLabel(obj, firstLabelField, secondLabelField, thirdLabelField, hideFirstLabel, 463 hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, userId); 464 SuggestConstants.computeUserGroupIcon(obj, hideIcon); 465 } else if (group != null) { 466 Schema schema = schemaManager.getSchema(userManager.getGroupSchemaName()); 467 for (Field field : schema.getFields()) { 468 QName fieldName = field.getName(); 469 String key = fieldName.getLocalName(); 470 Serializable value = group.getPropertyValue(fieldName.getPrefixedName()); 471 obj.element(key, value); 472 } 473 // If the group hasn't an label, let's put the groupid 474 String groupId = group.getId(); 475 SuggestConstants.computeGroupLabel(obj, groupId, userManager.getGroupLabelField(), hideFirstLabel); 476 obj.put(SuggestConstants.ID, groupId); 477 obj.put(SuggestConstants.TYPE_KEY_NAME, SuggestConstants.GROUP_TYPE); 478 obj.put(SuggestConstants.PREFIXED_ID_KEY_NAME, NuxeoGroup.PREFIX + groupId); 479 SuggestConstants.computeUserGroupIcon(obj, hideIcon); 480 } else { 481 log.warn("Could not resolve user or group reference: " + storedReference); 482 return createNotFoundEntry(storedReference); 483 } 484 return obj; 485 } 486 487 public boolean isMultiSelection(final Widget widget) { 488 String wtCat = widget.getTypeCategory(); 489 if (StringUtils.isBlank(wtCat)) { 490 wtCat = "jsf"; 491 } 492 WidgetTypeDefinition wtDef = getLayoutStore().getWidgetTypeDefinition(wtCat, widget.getType()); 493 if (wtDef != null) { 494 WidgetTypeConfiguration conf = wtDef.getConfiguration(); 495 if (conf != null) { 496 return conf.isList(); 497 } 498 } 499 return false; 500 } 501 502 /** 503 * @deprecated since 7.10: JSF resources mechanism allows to detect resources already included in the page natively. 504 */ 505 @Deprecated 506 public boolean mustIncludeResources() { 507 FacesContext facesContext = FacesContext.getCurrentInstance(); 508 if (facesContext != null) { 509 PhaseId currentPhaseId = FacesLifecycle.getPhaseId(); 510 if (currentPhaseId.equals(PhaseId.RENDER_RESPONSE)) { 511 HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest(); 512 513 if (request.getAttribute(SELECT2_RESOURCES_MARKER) != null) { 514 return false; 515 } else { 516 request.setAttribute(SELECT2_RESOURCES_MARKER, "done"); 517 return true; 518 } 519 } 520 } 521 return false; 522 } 523 524 protected JSONObject createNotFoundEntry(final String id) { 525 return createEntryWithWarnMessage(id, "entry not found"); 526 } 527 528 protected JSONObject createEntryWithWarnMessage(final String label, final String warnMessage) { 529 JSONObject obj = new JSONObject(); 530 obj.put(SuggestConstants.ID, label); 531 obj.put(SuggestConstants.ABSOLUTE_LABEL, label); 532 obj.put(SuggestConstants.LABEL, label); 533 obj.put(SuggestConstants.WARN_MESSAGE_LABEL, warnMessage); 534 return obj; 535 } 536 537 protected JSONObject resolveDirectoryEntry(final String storedReference, String keySeparator, 538 final Session session, final Schema schema, final String label, final boolean localize, final boolean dbl10n) { 539 if (storedReference == null || storedReference.isEmpty()) { 540 log.trace("No reference provided "); 541 return null; 542 } 543 544 if (keySeparator == null || keySeparator.isEmpty()) { 545 keySeparator = SuggestConstants.DEFAULT_KEY_SEPARATOR; 546 } 547 548 String entryId = storedReference.substring(storedReference.lastIndexOf(keySeparator) + 1, 549 storedReference.length()); 550 551 DocumentModel result = session.getEntry(entryId); 552 if (result == null) { 553 log.warn("Unable to resolve entry " + storedReference); 554 return createNotFoundEntry(storedReference); 555 } 556 557 JSONObject obj = new JSONObject(); 558 for (Field field : schema.getFields()) { 559 QName fieldName = field.getName(); 560 String key = fieldName.getLocalName(); 561 Serializable value = result.getPropertyValue(fieldName.getPrefixedName()); 562 if (label.equals(key)) { 563 if (localize && !dbl10n) { 564 value = messages.get(value); 565 } 566 obj.element(SuggestConstants.LABEL, value); 567 obj.element(SuggestConstants.ABSOLUTE_LABEL, 568 getParentAbsoluteLabel(storedReference, keySeparator, session, fieldName, localize, dbl10n)); 569 } else { 570 obj.element(key, value); 571 } 572 } 573 574 // Add a warning message if the entity is obsolete 575 if (obj.containsKey(SuggestConstants.OBSOLETE_FIELD_ID) && obj.getInt(SuggestConstants.OBSOLETE_FIELD_ID) > 0) { 576 obj.element(SuggestConstants.WARN_MESSAGE_LABEL, messages.get("label.vocabulary.entry.obsolete")); 577 } 578 579 obj.element(SuggestConstants.COMPUTED_ID, storedReference); 580 return obj; 581 } 582 583 /** 584 * @since 5.9.3 585 */ 586 protected String getParentAbsoluteLabel(final String entryId, final String keySeparator, final Session session, 587 final QName labelFieldName, final boolean localize, final boolean dbl10n) throws PropertyException { 588 String[] split = entryId.split(keySeparator); 589 String result = ""; 590 for (int i = 0; i < split.length; i++) { 591 DocumentModel entry = session.getEntry(split[i]); 592 if (entry != null) { 593 Serializable value = entry.getPropertyValue(labelFieldName.getPrefixedName()); 594 if (localize && !dbl10n) { 595 value = messages.get(value); 596 } 597 result += (i > 0 ? "/" : "") + value; 598 } 599 } 600 601 return result; 602 } 603 604 public String resolveMultipleDirectoryEntries(final Object value, final String directoryName, 605 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 606 JSONArray result = getMultipleDirectoryEntries(value, directoryName, localize, keySeparator, dbl10n, 607 labelFieldName); 608 if (result != null) { 609 return result.toString(); 610 } else { 611 return "[]"; 612 } 613 } 614 615 public List<String> resolveMultipleDirectoryEntryLabels(final Object value, final String directoryName, 616 final boolean localize, final String keySeparator, final boolean dbl10n, final String labelFieldName) { 617 return formatList(getMultipleDirectoryEntries(value, directoryName, localize, keySeparator, dbl10n, 618 labelFieldName)); 619 } 620 621 @SuppressWarnings("rawtypes") 622 public List<String> resolveMultipleReferenceLabels(final Object value, final String repo, 623 final String operationName, final String idProperty, final String label) { 624 625 List<String> result = new ArrayList<>(); 626 627 if (value == null) { 628 return result; 629 } 630 631 List<String> storedRefs = new ArrayList<>(); 632 if (value instanceof List) { 633 for (Object v : (List) value) { 634 storedRefs.add(v.toString()); 635 } 636 } else if (value instanceof Object[]) { 637 for (Object v : (Object[]) value) { 638 storedRefs.add(v.toString()); 639 } 640 } 641 642 for (String ref : storedRefs) { 643 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 644 if (doc != null) { 645 if (label != null && !label.isEmpty()) { 646 Object val = doc.getPropertyValue(label); 647 if (val == null) { 648 result.add(""); 649 } else { 650 result.add(val.toString()); 651 } 652 } else { 653 result.add(doc.getTitle()); 654 } 655 } else { 656 result.add(messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + ref + ")"); 657 } 658 } 659 return result; 660 } 661 662 @SuppressWarnings("rawtypes") 663 public String resolveMultipleReferences(final Object value, final String repo, final String operationName, 664 final String idProperty, final String schemaNames) throws IOException { 665 666 if (value == null) { 667 return "[]"; 668 } 669 670 List<String> storedRefs = new ArrayList<>(); 671 if (value instanceof List) { 672 for (Object v : (List) value) { 673 storedRefs.add(v.toString()); 674 } 675 } else if (value instanceof Object[]) { 676 for (Object v : (Object[]) value) { 677 storedRefs.add(v.toString()); 678 } 679 } 680 if (storedRefs.isEmpty()) { 681 return "[]"; 682 } 683 684 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 685 BufferedOutputStream out = new BufferedOutputStream(baos); 686 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 687 jg.writeStartArray(); 688 689 DocumentModelJsonWriter writer = getDocumentModelWriter(schemaNames); 690 691 for (String ref : storedRefs) { 692 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 693 if (doc == null) { 694 processDocumentNotFound(ref, jg); 695 } else { 696 writer.write(doc, jg); 697 jg.flush(); 698 } 699 } 700 701 jg.writeEndArray(); 702 out.flush(); 703 String json = new String(baos.toByteArray(), "UTF-8"); 704 705 if (json.isEmpty()) { 706 return "[]"; 707 } 708 if (json.startsWith("[") && !json.endsWith("]")) { 709 // XXX !!! 710 // AT: what's this for? 711 json = json + "]"; 712 } 713 714 return json; 715 } 716 717 @SuppressWarnings("rawtypes") 718 public String resolveMultipleUserReference(final Object value, final boolean prefixed, 719 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 720 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 721 final boolean displayEmailInSuggestion, final boolean hideIcon) { 722 if (value == null) { 723 return "[]"; 724 } 725 JSONArray result = new JSONArray(); 726 List<String> storedRefs = new ArrayList<>(); 727 if (value instanceof List) { 728 for (Object v : (List) value) { 729 storedRefs.add(v.toString()); 730 } 731 } else if (value instanceof Object[]) { 732 for (Object v : (Object[]) value) { 733 storedRefs.add(v.toString()); 734 } 735 } 736 737 for (String ref : storedRefs) { 738 String resolved = resolveSingleUserReference(ref, prefixed, firstLabelField, secondLabelField, 739 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, 740 hideIcon); 741 if (resolved != null && !resolved.isEmpty()) { 742 result.add(resolved); 743 } 744 } 745 return result.toString(); 746 } 747 748 public List<String> resolveMultipleUserReferenceLabels(final Object value, final boolean prefixed, 749 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 750 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 751 final boolean displayEmailInSuggestion, final boolean hideIcon) { 752 return formatList(getMultipleUserReference(value, prefixed, firstLabelField, secondLabelField, thirdLabelField, 753 hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon)); 754 } 755 756 protected DocumentModel resolveReference(final String repo, final String storedReference, 757 final String operationName, final String idProperty) { 758 759 if (storedReference == null || storedReference.isEmpty()) { 760 log.trace("No reference provided "); 761 return null; 762 } 763 DocumentModel doc = null; 764 CoreSession session; 765 try { 766 session = getRepositorySession(repo); 767 if (session == null) { 768 log.error("Unable to get CoreSession for repo " + repo); 769 return null; 770 } 771 if (operationName == null || operationName.isEmpty()) { 772 DocumentRef ref = null; 773 774 if (idProperty != null && !idProperty.isEmpty()) { 775 String query = " select * from Document where " + idProperty + "='" + storedReference + "'"; 776 DocumentModelList docs = session.query(query); 777 if (docs.size() > 0) { 778 return docs.get(0); 779 } else { 780 log.warn("Unable to resolve doc using property " + idProperty + " and value " + storedReference); 781 return null; 782 } 783 } else { 784 if (storedReference.startsWith("/")) { 785 ref = new PathRef(storedReference); 786 } else { 787 ref = new IdRef(storedReference); 788 } 789 if (session.exists(ref)) { 790 doc = session.getDocument(ref); 791 } else { 792 log.warn("Unable to resolve reference on " + ref); 793 } 794 } 795 } else { 796 AutomationService as = Framework.getLocalService(AutomationService.class); 797 try (OperationContext ctx = new OperationContext(session)) { 798 Map<String, Object> params = new HashMap<String, Object>(); 799 800 params.put("value", storedReference); 801 params.put("xpath", idProperty); 802 params.put("lang", org.jboss.seam.core.Locale.instance().getLanguage()); 803 Object result = as.run(ctx, operationName, params); 804 805 if (result == null) { 806 log.warn("Unable to resolve reference " + storedReference + " using property " + idProperty 807 + " and operation" + operationName); 808 doc = null; 809 } else if (result instanceof DocumentModel) { 810 doc = (DocumentModel) result; 811 } else if (result instanceof DocumentModelList) { 812 DocumentModelList docs = (DocumentModelList) result; 813 if (docs.size() > 0) { 814 doc = docs.get(0); 815 } else { 816 log.warn("No document found"); 817 } 818 } 819 } 820 } 821 return doc; 822 } catch (InvalidChainException e) { 823 log.error("Unable to resolve reference", e); 824 } catch (OperationException e) { 825 log.error("Unable to resolve reference", e); 826 } 827 return doc; 828 } 829 830 protected void processDocumentNotFound(String id, JsonGenerator jg) { 831 if (StringUtils.isEmpty(id)) { 832 return; 833 } 834 try { 835 jg.writeStartObject(); 836 jg.writeStringField(SuggestConstants.ID, id); 837 jg.writeStringField(Select2Common.TITLE, messages.get("label.documentSuggestion.docNotFoundOrNotVisible")); 838 jg.writeStringField(SuggestConstants.WARN_MESSAGE_LABEL, id); 839 jg.writeEndObject(); 840 jg.flush(); 841 } catch (IOException e) { 842 log.error("Error while writing not found message ", e); 843 } 844 845 } 846 847 public String resolveSingleDirectoryEntry(final String storedReference, final String directoryName, 848 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 849 JSONObject result = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 850 labelFieldName); 851 if (result != null) { 852 return result.toString(); 853 } else { 854 return ""; 855 } 856 } 857 858 public String resolveSingleDirectoryEntryLabel(final String storedReference, final String directoryName, 859 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 860 JSONObject obj = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 861 labelFieldName); 862 if (obj == null) { 863 return ""; 864 } 865 return obj.optString(SuggestConstants.LABEL); 866 } 867 868 public String resolveSingleReference(final String storedReference, final String repo, final String operationName, 869 final String idProperty, final String schemaNames) throws IOException { 870 871 DocumentModel doc; 872 doc = resolveReference(repo, storedReference, operationName, idProperty); 873 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 874 BufferedOutputStream out = new BufferedOutputStream(baos); 875 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 876 if (doc == null) { 877 processDocumentNotFound(storedReference, jg); 878 } else { 879 getDocumentModelWriter(schemaNames).write(doc, jg); 880 } 881 jg.flush(); 882 return new String(baos.toByteArray(), "UTF-8"); 883 884 } 885 886 public String resolveSingleReferenceLabel(final String storedReference, final String repo, 887 final String operationName, final String idProperty, final String label) { 888 DocumentModel doc = resolveReference(repo, storedReference, operationName, idProperty); 889 if (doc == null) { 890 return messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + doc + ")"; 891 } 892 893 if (label != null && !label.isEmpty()) { 894 Object val = doc.getPropertyValue(label); 895 if (val == null) { 896 return ""; 897 } else { 898 return val.toString(); 899 } 900 } 901 return doc.getTitle(); 902 } 903 904 public String resolveSingleUserReference(final String storedReference, final boolean prefixed, 905 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 906 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 907 final boolean displayEmailInSuggestion, final boolean hideIcon) { 908 JSONObject result = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 909 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 910 if (result != null) { 911 return result.toString(); 912 } else { 913 return ""; 914 } 915 } 916 917 public String resolveUserReferenceLabel(final String storedReference, final boolean prefixed, 918 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 919 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 920 final boolean displayEmailInSuggestion, final boolean hideIcon) { 921 JSONObject obj = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 922 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 923 if (obj == null) { 924 return ""; 925 } 926 return obj.optString(SuggestConstants.LABEL); 927 } 928 929 protected DocumentModelJsonWriter getDocumentModelWriter(final String schemaNames) { 930 MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); 931 String[] schemas = Select2Common.getSchemas(schemaNames); 932 HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance() 933 .getExternalContext() 934 .getRequest(); 935 RenderingContext ctx = RenderingContextWebUtils.getBuilder(request) 936 .properties(schemas) 937 .enrichDoc("documentURL") 938 .get(); 939 return registry.getInstance(ctx, DocumentModelJsonWriter.class); 940 } 941 942}