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 org.apache.commons.io.output.ByteArrayOutputStream; 038import org.apache.commons.lang.StringUtils; 039import org.apache.commons.logging.Log; 040import org.apache.commons.logging.LogFactory; 041import org.codehaus.jackson.JsonGenerator; 042import org.jboss.seam.ScopeType; 043import org.jboss.seam.annotations.Destroy; 044import org.jboss.seam.annotations.In; 045import org.jboss.seam.annotations.Name; 046import org.jboss.seam.annotations.Scope; 047import org.jboss.seam.contexts.FacesLifecycle; 048import org.nuxeo.ecm.automation.AutomationService; 049import org.nuxeo.ecm.automation.InvalidChainException; 050import org.nuxeo.ecm.automation.OperationContext; 051import org.nuxeo.ecm.automation.OperationException; 052import org.nuxeo.ecm.automation.core.operations.services.DocumentPageProviderOperation; 053import org.nuxeo.ecm.automation.core.operations.services.directory.SuggestDirectoryEntries; 054import org.nuxeo.ecm.automation.core.operations.users.SuggestUserEntries; 055import org.nuxeo.ecm.automation.features.SuggestConstants; 056import org.nuxeo.ecm.automation.jaxrs.io.JsonHelper; 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 net.sf.json.JSONArray; 088import net.sf.json.JSONObject; 089 090/** 091 * Initialization for Select2. 092 * 093 * @since 5.7.3 094 */ 095@Name("select2Actions") 096@Scope(ScopeType.EVENT) 097public class Select2ActionsBean implements Serializable { 098 099 private static final long serialVersionUID = 1L; 100 101 private static final Log log = LogFactory.getLog(Select2ActionsBean.class); 102 103 protected static final String SELECT2_RESOURCES_MARKER = "SELECT2_RESOURCES_MARKER"; 104 105 private static List<String> formatList(JSONArray array) { 106 List<String> result = new ArrayList<String>(); 107 if (array != null) { 108 for (int i = 0; i < array.size(); i++) { 109 result.add(array.getJSONObject(i).getString(Select2Common.LABEL)); 110 } 111 } 112 return result; 113 } 114 115 @In(create = true) 116 protected Map<String, String> messages; 117 118 @In(create = true, required = false) 119 protected transient CoreSession documentManager; 120 121 protected transient CoreSession dedicatedSession = null; 122 123 @Destroy 124 public void destroy() { 125 if (dedicatedSession != null) { 126 dedicatedSession.close(); 127 } 128 } 129 130 private static Map<String, String> getDefaultFormattersMap(final String suggestionFormatterName, 131 String selectionFormatterName) { 132 Map<String, String> result = new HashMap<String, String>(); 133 result.put(Select2Common.SUGGESTION_FORMATTER, suggestionFormatterName); 134 result.put(Select2Common.SELECTION_FORMATTER, selectionFormatterName); 135 return result; 136 } 137 138 /** 139 * @since 5.9.3 140 */ 141 protected static Map<String, String> getContextParameter(final DocumentModel doc) { 142 DocumentIdCodec documentIdCodec = new DocumentIdCodec(); 143 Map<String, String> contextParameters = new HashMap<String, String>(); 144 contextParameters.put("documentURL", documentIdCodec.getUrlFromDocumentView(new DocumentViewImpl(doc))); 145 return contextParameters; 146 } 147 148 public String encodeParametersForUserSuggestion(final Widget widget, 149 final Map<String, Serializable> resolvedWidgetProperties) { 150 Map<String, String> params = getDefaultFormattersMap(Select2Common.USER_DEFAULT_SUGGESTION_FORMATTER, 151 Select2Common.USER_DEFAULT_SELECTION_FORMATTER); 152 params.put(Select2Common.OPERATION_ID, SuggestUserEntries.ID); 153 return encodeParameters(widget, params, resolvedWidgetProperties); 154 } 155 156 public String encodeParametersForDirectory(final Widget widget, 157 final Map<String, Serializable> resolvedWidgetProperties) { 158 Map<String, String> params = getDefaultFormattersMap(Select2Common.DIR_DEFAULT_SUGGESTION_FORMATTER, 159 Select2Common.DIR_DEFAULT_SELECTION_FORMATTER); 160 params.put(Select2Common.OPERATION_ID, SuggestDirectoryEntries.ID); 161 return encodeParameters(widget, params, resolvedWidgetProperties); 162 } 163 164 public String encodeParameters(final Widget widget) { 165 return encodeParameters(widget, null); 166 } 167 168 public String encodeParameters(final Widget widget, final Map<String, Serializable> resolvedWidgetProperties) { 169 Map<String, String> params = getDefaultFormattersMap(Select2Common.DOC_DEFAULT_SUGGESTION_FORMATTER, 170 Select2Common.DOC_DEFAULT_SELECTION_FORMATTER); 171 params.put(Select2Common.OPERATION_ID, DocumentPageProviderOperation.ID); 172 return encodeParameters(widget, params, resolvedWidgetProperties); 173 } 174 175 /** 176 * Encode widget properties and parameters that Select2 pick them up in a hidden input. 177 * 178 * @param widget the widget 179 * @return encoded 180 * @since 5.7.3 181 */ 182 public String encodeParameters(final Widget widget, final Map<String, String> defaultParams, 183 final Map<String, Serializable> resolvedWidgetProperties) { 184 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 185 BufferedOutputStream out = new BufferedOutputStream(baos); 186 187 JsonGenerator jg; 188 try { 189 jg = JsonHelper.createJsonGenerator(out); 190 191 jg.writeStartObject(); 192 193 // multiple is not in properties and we must add it because Select2 194 // needs to know. 195 jg.writeStringField("multiple", "" + isMultiSelection(widget)); 196 197 final boolean isTranslated = widget.isTranslated(); 198 199 // Are we writing or reading 200 boolean readonly = !widget.getMode().equals("edit") && !widget.getMode().equals("create"); 201 jg.writeStringField(Select2Common.READ_ONLY_PARAM, Boolean.toString(readonly)); 202 203 Map<String, Serializable> propertySet = null; 204 if (resolvedWidgetProperties != null) { 205 propertySet = resolvedWidgetProperties; 206 } else { 207 propertySet = widget.getProperties(); 208 } 209 210 boolean hasPlaceholder = false; 211 boolean hasAjaxReRender = false; 212 boolean hasWidth = false; 213 boolean hasMinChars = false; 214 215 for (Entry<String, Serializable> entry : propertySet.entrySet()) { 216 217 if (entry.getValue() == null) { 218 continue; 219 } 220 221 if (defaultParams != null) { 222 // Widget properties have priority on default properties 223 defaultParams.remove(entry.getKey()); 224 } 225 226 String value = entry.getValue().toString(); 227 228 if (entry.getKey().equals(Select2Common.PLACEHOLDER)) { 229 hasPlaceholder = true; 230 // placeholder can be translated 231 if (isTranslated || Boolean.parseBoolean((String) propertySet.get("translatePlaceholder"))) { 232 value = messages.get(entry.getValue().toString()); 233 } 234 } else if (entry.getKey().equals(Select2Common.AJAX_RERENDER)) { 235 hasAjaxReRender = true; 236 } else if (entry.getKey().equals(Select2Common.WIDTH)) { 237 hasWidth = true; 238 } else if (entry.getKey().equals(Select2Common.MIN_CHARS)) { 239 hasMinChars = true; 240 } 241 242 jg.writeStringField(entry.getKey(), value); 243 244 } 245 246 if (defaultParams != null) { 247 // All default params which are not in widget properties 248 for (Entry<String, String> e : defaultParams.entrySet()) { 249 jg.writeStringField(e.getKey(), e.getValue()); 250 } 251 } 252 253 if (!hasPlaceholder) { 254 // No placeholder provider and Select2 requires one to enable 255 // the 256 // reset button. 257 jg.writeStringField(Select2Common.PLACEHOLDER, messages.get("label.vocabulary.selectValue")); 258 } 259 260 if (!hasWidth) { 261 jg.writeStringField(Select2Common.WIDTH, Select2Common.DEFAULT_WIDTH); 262 } 263 264 if (!hasMinChars) { 265 jg.writeNumberField(Select2Common.MIN_CHARS, Select2Common.DEFAULT_MIN_CHARS); 266 } 267 268 if (hasAjaxReRender) { 269 jg.writeStringField(Select2Common.RERENDER_JS_FUNCTION_NAME, widget.getId() + "_reRender"); 270 } 271 272 jg.writeEndObject(); 273 jg.flush(); 274 out.flush(); 275 return new String(baos.toByteArray(), "UTF-8"); 276 } catch (IOException e) { 277 log.error("Could not encode parameters", e); 278 return null; 279 } 280 } 281 282 protected LayoutStore getLayoutStore() { 283 return Framework.getService(LayoutStore.class); 284 } 285 286 @SuppressWarnings("rawtypes") 287 protected JSONArray getMultipleDirectoryEntries(final Object value, final String directoryName, 288 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 289 JSONArray result = new JSONArray(); 290 if (value == null) { 291 return result; 292 } 293 294 List<String> storedRefs = new ArrayList<>(); 295 if (value instanceof List) { 296 for (Object v : (List) value) { 297 storedRefs.add(v.toString()); 298 } 299 } else if (value instanceof Object[]) { 300 for (Object v : (Object[]) value) { 301 storedRefs.add(v.toString()); 302 } 303 } 304 305 DirectoryService directoryService = Framework.getService(DirectoryService.class); 306 try { 307 Directory directory = directoryService.getDirectory(directoryName); 308 if (directory == null) { 309 log.error("Could not find directory with name " + directoryName); 310 return null; 311 } 312 313 try (Session session = directory.getSession()) { 314 String schemaName = directory.getSchema(); 315 SchemaManager schemaManager = Framework.getService(SchemaManager.class); 316 Schema schema = schemaManager.getSchema(schemaName); 317 final Locale locale = org.jboss.seam.core.Locale.instance(); 318 final String label = SuggestConstants.getLabelFieldName(schema, dbl10n, labelFieldName, 319 locale.getLanguage()); 320 321 for (String ref : storedRefs) { 322 JSONObject obj = resolveDirectoryEntry(ref, keySeparator, session, schema, label, localize, dbl10n); 323 if (obj != null) { 324 result.add(obj); 325 } 326 } 327 return result; 328 } 329 } catch (DirectoryException de) { 330 log.error("An error occured while obtaining directory " + directoryName, de); 331 return result; 332 } 333 } 334 335 @SuppressWarnings("rawtypes") 336 protected JSONArray getMultipleUserReference(final Object value, final boolean prefixed, 337 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 338 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 339 final boolean displayEmailInSuggestion, final boolean hideIcon) { 340 if (value == null) { 341 return null; 342 } 343 JSONArray result = new JSONArray(); 344 List<String> storedRefs = new ArrayList<>(); 345 if (value instanceof List) { 346 for (Object v : (List) value) { 347 storedRefs.add(v.toString()); 348 } 349 } else if (value instanceof Object[]) { 350 for (Object v : (Object[]) value) { 351 storedRefs.add(v.toString()); 352 } 353 } 354 355 for (String ref : storedRefs) { 356 JSONObject resolved = getSingleUserReference(ref, prefixed, firstLabelField, secondLabelField, 357 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, 358 hideIcon); 359 if (resolved != null && !resolved.isEmpty()) { 360 result.add(resolved); 361 } 362 } 363 return result; 364 } 365 366 protected CoreSession getRepositorySession(String repoName) { 367 368 if (repoName == null || repoName.isEmpty()) { 369 RepositoryManager rm = Framework.getService(RepositoryManager.class); 370 repoName = rm.getDefaultRepositoryName(); 371 } 372 373 if (documentManager != null && documentManager.getRepositoryName().equals(repoName)) { 374 return documentManager; 375 } 376 377 dedicatedSession = CoreInstance.openCoreSession(repoName); 378 return dedicatedSession; 379 } 380 381 protected JSONObject getSingleDirectoryEntry(final String storedReference, final String directoryName, 382 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 383 384 if (storedReference == null || storedReference.isEmpty()) { 385 return null; 386 } 387 388 DirectoryService directoryService = Framework.getService(DirectoryService.class); 389 try { 390 Directory directory = directoryService.getDirectory(directoryName); 391 if (directory == null) { 392 log.error("Could not find directory with name " + directoryName); 393 return null; 394 } 395 396 try (Session session = directory.getSession()) { 397 String schemaName = directory.getSchema(); 398 SchemaManager schemaManager = Framework.getService(SchemaManager.class); 399 Schema schema = schemaManager.getSchema(schemaName); 400 401 final Locale locale = org.jboss.seam.core.Locale.instance(); 402 final String label = SuggestConstants.getLabelFieldName(schema, dbl10n, labelFieldName, 403 locale.getLanguage()); 404 405 JSONObject obj = resolveDirectoryEntry(storedReference, keySeparator, session, schema, label, localize, 406 dbl10n); 407 408 return obj; 409 } 410 } catch (DirectoryException de) { 411 log.error("An error occured while obtaining directory " + directoryName, de); 412 return null; 413 } 414 } 415 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 } 618 619 @SuppressWarnings("rawtypes") 620 public List<String> resolveMultipleReferenceLabels(final Object value, final String repo, 621 final String operationName, final String idProperty, final String label) { 622 623 List<String> result = new ArrayList<>(); 624 625 if (value == null) { 626 return result; 627 } 628 629 List<String> storedRefs = new ArrayList<>(); 630 if (value instanceof List) { 631 for (Object v : (List) value) { 632 storedRefs.add(v.toString()); 633 } 634 } else if (value instanceof Object[]) { 635 for (Object v : (Object[]) value) { 636 storedRefs.add(v.toString()); 637 } 638 } 639 640 for (String ref : storedRefs) { 641 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 642 if (doc != null) { 643 if (label != null && !label.isEmpty()) { 644 Object val = doc.getPropertyValue(label); 645 if (val == null) { 646 result.add(""); 647 } else { 648 result.add(val.toString()); 649 } 650 } else { 651 result.add(doc.getTitle()); 652 } 653 } else { 654 result.add(messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + ref + ")"); 655 } 656 } 657 return result; 658 } 659 660 @SuppressWarnings("rawtypes") 661 public String resolveMultipleReferences(final Object value, final String repo, final String operationName, 662 final String idProperty, final String schemaNames) throws IOException { 663 664 if (value == null) { 665 return "[]"; 666 } 667 668 List<String> storedRefs = new ArrayList<>(); 669 if (value instanceof List) { 670 for (Object v : (List) value) { 671 storedRefs.add(v.toString()); 672 } 673 } else if (value instanceof Object[]) { 674 for (Object v : (Object[]) value) { 675 storedRefs.add(v.toString()); 676 } 677 } 678 if (storedRefs.isEmpty()) { 679 return "[]"; 680 } 681 682 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 683 BufferedOutputStream out = new BufferedOutputStream(baos); 684 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 685 jg.writeStartArray(); 686 687 DocumentModelJsonWriter writer = getDocumentModelWriter(schemaNames); 688 689 for (String ref : storedRefs) { 690 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 691 if (doc == null) { 692 processDocumentNotFound(ref, jg); 693 } else { 694 writer.write(doc, jg); 695 jg.flush(); 696 } 697 } 698 699 jg.writeEndArray(); 700 out.flush(); 701 String json = new String(baos.toByteArray(), "UTF-8"); 702 703 if (json.isEmpty()) { 704 return "[]"; 705 } 706 if (json.startsWith("[") && !json.endsWith("]")) { 707 // XXX !!! 708 // AT: what's this for? 709 json = json + "]"; 710 } 711 712 return json; 713 } 714 715 @SuppressWarnings("rawtypes") 716 public String resolveMultipleUserReference(final Object value, final boolean prefixed, final String firstLabelField, 717 final String secondLabelField, final String thirdLabelField, final boolean hideFirstLabel, 718 final boolean hideSecondLabel, final boolean hideThirdLabel, final boolean displayEmailInSuggestion, 719 final boolean hideIcon) { 720 if (value == null) { 721 return "[]"; 722 } 723 JSONArray result = new JSONArray(); 724 List<String> storedRefs = new ArrayList<>(); 725 if (value instanceof List) { 726 for (Object v : (List) value) { 727 storedRefs.add(v.toString()); 728 } 729 } else if (value instanceof Object[]) { 730 for (Object v : (Object[]) value) { 731 storedRefs.add(v.toString()); 732 } 733 } 734 735 for (String ref : storedRefs) { 736 String resolved = resolveSingleUserReference(ref, prefixed, firstLabelField, secondLabelField, 737 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, 738 hideIcon); 739 if (resolved != null && !resolved.isEmpty()) { 740 result.add(resolved); 741 } 742 } 743 return result.toString(); 744 } 745 746 public List<String> resolveMultipleUserReferenceLabels(final Object value, final boolean prefixed, 747 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 748 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 749 final boolean displayEmailInSuggestion, final boolean hideIcon) { 750 return formatList(getMultipleUserReference(value, prefixed, firstLabelField, secondLabelField, thirdLabelField, 751 hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon)); 752 } 753 754 protected DocumentModel resolveReference(final String repo, final String storedReference, 755 final String operationName, final String idProperty) { 756 757 if (storedReference == null || storedReference.isEmpty()) { 758 log.trace("No reference provided "); 759 return null; 760 } 761 DocumentModel doc = null; 762 CoreSession session; 763 try { 764 session = getRepositorySession(repo); 765 if (session == null) { 766 log.error("Unable to get CoreSession for repo " + repo); 767 return null; 768 } 769 if (operationName == null || operationName.isEmpty()) { 770 doc = Select2Common.resolveReference(idProperty, storedReference, session); 771 } else { 772 AutomationService as = Framework.getService(AutomationService.class); 773 try (OperationContext ctx = new OperationContext(session)) { 774 Map<String, Object> params = new HashMap<String, Object>(); 775 776 params.put("value", storedReference); 777 params.put("xpath", idProperty); 778 params.put("lang", org.jboss.seam.core.Locale.instance().getLanguage()); 779 Object result = as.run(ctx, operationName, params); 780 781 if (result == null) { 782 log.warn("Unable to resolve reference " + storedReference + " using property " + idProperty 783 + " and operation" + operationName); 784 doc = null; 785 } else if (result instanceof DocumentModel) { 786 doc = (DocumentModel) result; 787 } else if (result instanceof DocumentModelList) { 788 DocumentModelList docs = (DocumentModelList) result; 789 if (docs.size() > 0) { 790 doc = docs.get(0); 791 } else { 792 log.warn("No document found"); 793 } 794 } 795 } 796 } 797 return doc; 798 } catch (InvalidChainException e) { 799 log.error("Unable to resolve reference", e); 800 } catch (OperationException e) { 801 log.error("Unable to resolve reference", e); 802 } 803 return doc; 804 } 805 806 protected void processDocumentNotFound(String id, JsonGenerator jg) { 807 if (StringUtils.isEmpty(id)) { 808 return; 809 } 810 try { 811 jg.writeStartObject(); 812 jg.writeStringField(SuggestConstants.ID, id); 813 jg.writeStringField(Select2Common.TITLE, messages.get("label.documentSuggestion.docNotFoundOrNotVisible")); 814 jg.writeStringField(SuggestConstants.WARN_MESSAGE_LABEL, id); 815 jg.writeEndObject(); 816 jg.flush(); 817 } catch (IOException e) { 818 log.error("Error while writing not found message ", e); 819 } 820 821 } 822 823 public String resolveSingleDirectoryEntry(final String storedReference, final String directoryName, 824 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 825 JSONObject result = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 826 labelFieldName); 827 if (result != null) { 828 return result.toString(); 829 } else { 830 return ""; 831 } 832 } 833 834 public String resolveSingleDirectoryEntryLabel(final String storedReference, final String directoryName, 835 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 836 JSONObject obj = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 837 labelFieldName); 838 if (obj == null) { 839 return ""; 840 } 841 return obj.optString(SuggestConstants.LABEL); 842 } 843 844 public String resolveSingleReference(final String storedReference, final String repo, final String operationName, 845 final String idProperty, final String schemaNames) throws IOException { 846 847 DocumentModel doc; 848 doc = resolveReference(repo, storedReference, operationName, idProperty); 849 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 850 BufferedOutputStream out = new BufferedOutputStream(baos); 851 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 852 if (doc == null) { 853 processDocumentNotFound(storedReference, jg); 854 } else { 855 getDocumentModelWriter(schemaNames).write(doc, jg); 856 } 857 jg.flush(); 858 return new String(baos.toByteArray(), "UTF-8"); 859 860 } 861 862 public String resolveSingleReferenceLabel(final String storedReference, final String repo, 863 final String operationName, final String idProperty, final String label) { 864 DocumentModel doc = resolveReference(repo, storedReference, operationName, idProperty); 865 if (doc == null) { 866 return messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + doc + ")"; 867 } 868 869 if (label != null && !label.isEmpty()) { 870 Object val = doc.getPropertyValue(label); 871 if (val == null) { 872 return ""; 873 } else { 874 return val.toString(); 875 } 876 } 877 return doc.getTitle(); 878 } 879 880 public String resolveSingleUserReference(final String storedReference, final boolean prefixed, 881 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 882 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 883 final boolean displayEmailInSuggestion, final boolean hideIcon) { 884 JSONObject result = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 885 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 886 if (result != null) { 887 return result.toString(); 888 } else { 889 return ""; 890 } 891 } 892 893 public String resolveUserReferenceLabel(final String storedReference, final boolean prefixed, 894 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 895 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 896 final boolean displayEmailInSuggestion, final boolean hideIcon) { 897 JSONObject obj = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 898 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 899 if (obj == null) { 900 return ""; 901 } 902 return obj.optString(SuggestConstants.LABEL); 903 } 904 905 protected DocumentModelJsonWriter getDocumentModelWriter(final String schemaNames) { 906 MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); 907 String[] schemas = Select2Common.getSchemas(schemaNames); 908 HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance() 909 .getExternalContext() 910 .getRequest(); 911 RenderingContext ctx = RenderingContextWebUtils.getBuilder(request) 912 .properties(schemas) 913 .enrichDoc("documentURL") 914 .get(); 915 return registry.getInstance(ctx, DocumentModelJsonWriter.class); 916 } 917 918}