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