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.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.DocumentRef; 062import org.nuxeo.ecm.core.api.IdRef; 063import org.nuxeo.ecm.core.api.NuxeoGroup; 064import org.nuxeo.ecm.core.api.NuxeoPrincipal; 065import org.nuxeo.ecm.core.api.PathRef; 066import org.nuxeo.ecm.core.api.PropertyException; 067import org.nuxeo.ecm.core.api.repository.RepositoryManager; 068import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter; 069import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 070import org.nuxeo.ecm.core.io.registry.context.RenderingContext; 071import org.nuxeo.ecm.core.schema.SchemaManager; 072import org.nuxeo.ecm.core.schema.types.Field; 073import org.nuxeo.ecm.core.schema.types.QName; 074import org.nuxeo.ecm.core.schema.types.Schema; 075import org.nuxeo.ecm.directory.Directory; 076import org.nuxeo.ecm.directory.DirectoryException; 077import org.nuxeo.ecm.directory.Session; 078import org.nuxeo.ecm.directory.api.DirectoryService; 079import org.nuxeo.ecm.platform.forms.layout.api.Widget; 080import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeConfiguration; 081import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeDefinition; 082import org.nuxeo.ecm.platform.forms.layout.api.service.LayoutStore; 083import org.nuxeo.ecm.platform.ui.select2.automation.SuggestDirectoryEntries; 084import org.nuxeo.ecm.platform.ui.select2.automation.SuggestUserEntries; 085import org.nuxeo.ecm.platform.ui.select2.common.Select2Common; 086import org.nuxeo.ecm.platform.url.DocumentViewImpl; 087import org.nuxeo.ecm.platform.url.codec.DocumentIdCodec; 088import org.nuxeo.ecm.platform.usermanager.UserManager; 089import org.nuxeo.ecm.webengine.jaxrs.coreiodelegate.RenderingContextWebUtils; 090import org.nuxeo.runtime.api.Framework; 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) { 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(Select2Common.LABEL)); 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 CoreSession 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 JsonGenerator jg; 190 try { 191 jg = JsonHelper.createJsonGenerator(out); 192 193 jg.writeStartObject(); 194 195 // multiple is not in properties and we must add it because Select2 196 // needs to know. 197 jg.writeStringField("multiple", "" + isMultiSelection(widget)); 198 199 final boolean isTranslated = widget.isTranslated(); 200 201 // Are we writing or reading 202 boolean readonly = !widget.getMode().equals("edit") && !widget.getMode().equals("create"); 203 jg.writeStringField(Select2Common.READ_ONLY_PARAM, Boolean.toString(readonly)); 204 205 Map<String, Serializable> propertySet = null; 206 if (resolvedWidgetProperties != null) { 207 propertySet = resolvedWidgetProperties; 208 } else { 209 propertySet = widget.getProperties(); 210 } 211 212 boolean hasPlaceholder = false; 213 boolean hasAjaxReRender = false; 214 boolean hasWidth = false; 215 boolean hasMinChars = false; 216 217 for (Entry<String, Serializable> entry : propertySet.entrySet()) { 218 219 if (entry.getValue() == null) { 220 continue; 221 } 222 223 if (defaultParams != null) { 224 // Widget properties have priority on default properties 225 defaultParams.remove(entry.getKey()); 226 } 227 228 String value = entry.getValue().toString(); 229 230 if (entry.getKey().equals(Select2Common.PLACEHOLDER)) { 231 hasPlaceholder = true; 232 // placeholder can be translated 233 if (isTranslated || Boolean.parseBoolean((String) propertySet.get("translatePlaceholder"))) { 234 value = messages.get(entry.getValue().toString()); 235 } 236 } else if (entry.getKey().equals(Select2Common.AJAX_RERENDER)) { 237 hasAjaxReRender = true; 238 } else if (entry.getKey().equals(Select2Common.WIDTH)) { 239 hasWidth = true; 240 } else if (entry.getKey().equals(Select2Common.MIN_CHARS)) { 241 hasMinChars = true; 242 } 243 244 jg.writeStringField(entry.getKey(), value); 245 246 } 247 248 if (defaultParams != null) { 249 // All default params which are not in widget properties 250 for (Entry<String, String> e : defaultParams.entrySet()) { 251 jg.writeStringField(e.getKey(), e.getValue()); 252 } 253 } 254 255 if (!hasPlaceholder) { 256 // No placeholder provider and Select2 requires one to enable 257 // the 258 // reset button. 259 jg.writeStringField(Select2Common.PLACEHOLDER, messages.get("label.vocabulary.selectValue")); 260 } 261 262 if (!hasWidth) { 263 jg.writeStringField(Select2Common.WIDTH, Select2Common.DEFAULT_WIDTH); 264 } 265 266 if (!hasMinChars) { 267 jg.writeNumberField(Select2Common.MIN_CHARS, Select2Common.DEFAULT_MIN_CHARS); 268 } 269 270 if (hasAjaxReRender) { 271 jg.writeStringField(Select2Common.RERENDER_JS_FUNCTION_NAME, widget.getId() + "_reRender"); 272 } 273 274 jg.writeEndObject(); 275 jg.flush(); 276 out.flush(); 277 return new String(baos.toByteArray(), "UTF-8"); 278 } catch (IOException e) { 279 log.error("Could not encode parameters", e); 280 return null; 281 } 282 } 283 284 protected LayoutStore getLayoutStore() { 285 return Framework.getService(LayoutStore.class); 286 } 287 288 @SuppressWarnings("rawtypes") 289 protected JSONArray getMultipleDirectoryEntries(final Object value, final String directoryName, 290 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 291 JSONArray result = new JSONArray(); 292 if (value == null) { 293 return result; 294 } 295 296 List<String> storedRefs = new ArrayList<>(); 297 if (value instanceof List) { 298 for (Object v : (List) value) { 299 storedRefs.add(v.toString()); 300 } 301 } else if (value instanceof Object[]) { 302 for (Object v : (Object[]) value) { 303 storedRefs.add(v.toString()); 304 } 305 } 306 307 DirectoryService directoryService = Framework.getLocalService(DirectoryService.class); 308 try { 309 Directory directory = directoryService.getDirectory(directoryName); 310 if (directory == null) { 311 log.error("Could not find directory with name " + directoryName); 312 return null; 313 } 314 315 try (Session session = directory.getSession()) { 316 String schemaName = directory.getSchema(); 317 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 318 Schema schema = schemaManager.getSchema(schemaName); 319 final Locale locale = org.jboss.seam.core.Locale.instance(); 320 final String label = Select2Common.getLabelFieldName(schema, dbl10n, labelFieldName, 321 locale.getLanguage()); 322 323 for (String ref : storedRefs) { 324 JSONObject obj = resolveDirectoryEntry(ref, keySeparator, session, schema, label, localize, dbl10n); 325 if (obj != null) { 326 result.add(obj); 327 } 328 } 329 return result; 330 } 331 } catch (DirectoryException de) { 332 log.error("An error occured while obtaining directory " + directoryName, de); 333 return result; 334 } 335 } 336 337 @SuppressWarnings("rawtypes") 338 protected JSONArray getMultipleUserReference(final Object value, final boolean prefixed, 339 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 340 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 341 final boolean displayEmailInSuggestion, final boolean hideIcon) { 342 if (value == null) { 343 return null; 344 } 345 JSONArray result = new JSONArray(); 346 List<String> storedRefs = new ArrayList<>(); 347 if (value instanceof List) { 348 for (Object v : (List) value) { 349 storedRefs.add(v.toString()); 350 } 351 } else if (value instanceof Object[]) { 352 for (Object v : (Object[]) value) { 353 storedRefs.add(v.toString()); 354 } 355 } 356 357 for (String ref : storedRefs) { 358 JSONObject resolved = getSingleUserReference(ref, prefixed, firstLabelField, secondLabelField, 359 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, 360 hideIcon); 361 if (resolved != null && !resolved.isEmpty()) { 362 result.add(resolved); 363 } 364 } 365 return result; 366 } 367 368 protected CoreSession getRepositorySession(String repoName) { 369 370 if (repoName == null || repoName.isEmpty()) { 371 RepositoryManager rm = Framework.getLocalService(RepositoryManager.class); 372 repoName = rm.getDefaultRepositoryName(); 373 } 374 375 if (documentManager != null && documentManager.getRepositoryName().equals(repoName)) { 376 return documentManager; 377 } 378 379 dedicatedSession = CoreInstance.openCoreSession(repoName); 380 return dedicatedSession; 381 } 382 383 protected JSONObject getSingleDirectoryEntry(final String storedReference, final String directoryName, 384 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 385 386 if (storedReference == null || storedReference.isEmpty()) { 387 return null; 388 } 389 390 DirectoryService directoryService = Framework.getLocalService(DirectoryService.class); 391 try { 392 Directory directory = directoryService.getDirectory(directoryName); 393 if (directory == null) { 394 log.error("Could not find directory with name " + directoryName); 395 return null; 396 } 397 398 try (Session session = directory.getSession()) { 399 String schemaName = directory.getSchema(); 400 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 401 Schema schema = schemaManager.getSchema(schemaName); 402 403 final Locale locale = org.jboss.seam.core.Locale.instance(); 404 final String label = Select2Common.getLabelFieldName(schema, dbl10n, labelFieldName, locale.getLanguage()); 405 406 JSONObject obj = resolveDirectoryEntry(storedReference, keySeparator, session, schema, label, localize, 407 dbl10n); 408 409 return obj; 410 } 411 } catch (DirectoryException de) { 412 log.error("An error occured while obtaining directory " + directoryName, de); 413 return null; 414 } 415 } 416 417 protected JSONObject getSingleUserReference(final String storedReference, final boolean prefixed, 418 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 419 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 420 final boolean displayEmailInSuggestion, final boolean hideIcon) { 421 UserManager userManager = Framework.getLocalService(UserManager.class); 422 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 423 DirectoryService dirService = Framework.getLocalService(DirectoryService.class); 424 JSONObject obj = new JSONObject(); 425 if (storedReference == null || storedReference.isEmpty()) { 426 return null; 427 } 428 DocumentModel user = null; 429 DocumentModel group = null; 430 Directory userDir = dirService.getDirectory(userManager.getUserDirectoryName()); 431 if (prefixed) { 432 if (storedReference.startsWith(NuxeoPrincipal.PREFIX)) { 433 user = userManager.getUserModel(storedReference.substring(NuxeoPrincipal.PREFIX.length())); 434 } else if (storedReference.startsWith(NuxeoGroup.PREFIX)) { 435 group = userManager.getGroupModel(storedReference.substring(NuxeoGroup.PREFIX.length())); 436 } else { 437 log.warn("User reference is prefixed but prefix was not found on reference: " + storedReference); 438 return createNotFoundEntry(storedReference); 439 } 440 } else { 441 user = userManager.getUserModel(storedReference); 442 if (user == null) { 443 group = userManager.getGroupModel(storedReference); 444 } 445 } 446 if (user != null) { 447 Schema schema = schemaManager.getSchema(userManager.getUserSchemaName()); 448 for (Field field : schema.getFields()) { 449 QName fieldName = field.getName(); 450 String key = fieldName.getLocalName(); 451 Serializable value = user.getPropertyValue(fieldName.getPrefixedName()); 452 if (key.equals(userDir.getPasswordField())) { 453 continue; 454 } 455 obj.element(key, value); 456 } 457 String userId = user.getId(); 458 obj.put(Select2Common.ID, userId); 459 obj.put(Select2Common.TYPE_KEY_NAME, Select2Common.USER_TYPE); 460 obj.put(Select2Common.PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + userId); 461 Select2Common.computeUserLabel(obj, firstLabelField, secondLabelField, thirdLabelField, hideFirstLabel, 462 hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, userId); 463 Select2Common.computeUserGroupIcon(obj, hideIcon); 464 } else if (group != null) { 465 Schema schema = schemaManager.getSchema(userManager.getGroupSchemaName()); 466 for (Field field : schema.getFields()) { 467 QName fieldName = field.getName(); 468 String key = fieldName.getLocalName(); 469 Serializable value = group.getPropertyValue(fieldName.getPrefixedName()); 470 obj.element(key, value); 471 } 472 // If the group hasn't an label, let's put the groupid 473 String groupId = group.getId(); 474 Select2Common.computeGroupLabel(obj, groupId, userManager.getGroupLabelField(), hideFirstLabel); 475 obj.put(Select2Common.ID, groupId); 476 obj.put(Select2Common.TYPE_KEY_NAME, Select2Common.GROUP_TYPE); 477 obj.put(Select2Common.PREFIXED_ID_KEY_NAME, NuxeoGroup.PREFIX + groupId); 478 Select2Common.computeUserGroupIcon(obj, hideIcon); 479 } else { 480 log.warn("Could not resolve user or group reference: " + storedReference); 481 return createNotFoundEntry(storedReference); 482 } 483 return obj; 484 } 485 486 public boolean isMultiSelection(final Widget widget) { 487 String wtCat = widget.getTypeCategory(); 488 if (StringUtils.isBlank(wtCat)) { 489 wtCat = "jsf"; 490 } 491 WidgetTypeDefinition wtDef = getLayoutStore().getWidgetTypeDefinition(wtCat, widget.getType()); 492 if (wtDef != null) { 493 WidgetTypeConfiguration conf = wtDef.getConfiguration(); 494 if (conf != null) { 495 return conf.isList(); 496 } 497 } 498 return false; 499 } 500 501 /** 502 * @deprecated since 7.10: JSF resources mechanism allows to detect resources already included in the page natively. 503 */ 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(Select2Common.ID, label); 529 obj.put(Select2Common.ABSOLUTE_LABEL, label); 530 obj.put(Select2Common.LABEL, label); 531 obj.put(Select2Common.WARN_MESSAGE_LABEL, warnMessage); 532 return obj; 533 } 534 535 protected JSONObject resolveDirectoryEntry(final String storedReference, String keySeparator, 536 final Session session, 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 = Select2Common.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(Select2Common.LABEL, value); 565 obj.element(Select2Common.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(Select2Common.OBSOLETE_FIELD_ID) && obj.getInt(Select2Common.OBSOLETE_FIELD_ID) > 0) { 574 obj.element(Select2Common.WARN_MESSAGE_LABEL, messages.get("label.vocabulary.entry.obsolete")); 575 } 576 577 obj.element(Select2Common.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(getMultipleDirectoryEntries(value, directoryName, localize, keySeparator, dbl10n, 616 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, 717 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 718 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 719 final boolean displayEmailInSuggestion, 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 DocumentRef ref = null; 771 772 if (idProperty != null && !idProperty.isEmpty()) { 773 String query = " select * from Document where " + idProperty + "='" + storedReference + "'"; 774 DocumentModelList docs = session.query(query); 775 if (docs.size() > 0) { 776 return docs.get(0); 777 } else { 778 log.warn("Unable to resolve doc using property " + idProperty + " and value " + storedReference); 779 return null; 780 } 781 } else { 782 if (storedReference.startsWith("/")) { 783 ref = new PathRef(storedReference); 784 } else { 785 ref = new IdRef(storedReference); 786 } 787 if (session.exists(ref)) { 788 doc = session.getDocument(ref); 789 } else { 790 log.warn("Unable to resolve reference on " + ref); 791 } 792 } 793 } else { 794 AutomationService as = Framework.getLocalService(AutomationService.class); 795 OperationContext ctx = new OperationContext(session); 796 Map<String, Object> params = new HashMap<String, Object>(); 797 798 params.put("value", storedReference); 799 params.put("xpath", idProperty); 800 params.put("lang", org.jboss.seam.core.Locale.instance().getLanguage()); 801 Object result = as.run(ctx, operationName, params); 802 803 if (result == null) { 804 log.warn("Unable to resolve reference " + storedReference + " using property " + idProperty 805 + " and operation" + operationName); 806 doc = null; 807 } else if (result instanceof DocumentModel) { 808 doc = (DocumentModel) result; 809 } else if (result instanceof DocumentModelList) { 810 DocumentModelList docs = (DocumentModelList) result; 811 if (docs.size() > 0) { 812 doc = docs.get(0); 813 } else { 814 log.warn("No document found"); 815 } 816 } 817 } 818 return doc; 819 } catch (InvalidChainException e) { 820 log.error("Unable to resolve reference", e); 821 } catch (OperationException e) { 822 log.error("Unable to resolve reference", e); 823 } 824 return doc; 825 } 826 827 protected void processDocumentNotFound(String id, JsonGenerator jg) { 828 if (StringUtils.isEmpty(id)) { 829 return; 830 } 831 try { 832 jg.writeStartObject(); 833 jg.writeStringField(Select2Common.ID, id); 834 jg.writeStringField(Select2Common.TITLE, messages.get("label.documentSuggestion.docNotFoundOrNotVisible")); 835 jg.writeStringField(Select2Common.WARN_MESSAGE_LABEL, id); 836 jg.writeEndObject(); 837 jg.flush(); 838 } catch (IOException e) { 839 log.error("Error while writing not found message ", e); 840 } 841 842 } 843 844 public String resolveSingleDirectoryEntry(final String storedReference, final String directoryName, 845 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 846 JSONObject result = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 847 labelFieldName); 848 if (result != null) { 849 return result.toString(); 850 } else { 851 return ""; 852 } 853 } 854 855 public String resolveSingleDirectoryEntryLabel(final String storedReference, final String directoryName, 856 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 857 JSONObject obj = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 858 labelFieldName); 859 if (obj == null) { 860 return ""; 861 } 862 return obj.optString(Select2Common.LABEL); 863 } 864 865 public String resolveSingleReference(final String storedReference, final String repo, final String operationName, 866 final String idProperty, final String schemaNames) throws IOException { 867 868 DocumentModel doc; 869 doc = resolveReference(repo, storedReference, operationName, idProperty); 870 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 871 BufferedOutputStream out = new BufferedOutputStream(baos); 872 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 873 if (doc == null) { 874 processDocumentNotFound(storedReference, jg); 875 } else { 876 getDocumentModelWriter(schemaNames).write(doc, jg); 877 } 878 jg.flush(); 879 return new String(baos.toByteArray(), "UTF-8"); 880 881 } 882 883 public String resolveSingleReferenceLabel(final String storedReference, final String repo, 884 final String operationName, final String idProperty, final String label) { 885 DocumentModel doc = resolveReference(repo, storedReference, operationName, idProperty); 886 if (doc == null) { 887 return messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + doc + ")"; 888 } 889 890 if (label != null && !label.isEmpty()) { 891 Object val = doc.getPropertyValue(label); 892 if (val == null) { 893 return ""; 894 } else { 895 return val.toString(); 896 } 897 } 898 return doc.getTitle(); 899 } 900 901 public String resolveSingleUserReference(final String storedReference, final boolean prefixed, 902 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 903 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 904 final boolean displayEmailInSuggestion, final boolean hideIcon) { 905 JSONObject result = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 906 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 907 if (result != null) { 908 return result.toString(); 909 } else { 910 return ""; 911 } 912 } 913 914 public String resolveUserReferenceLabel(final String storedReference, final boolean prefixed, 915 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 916 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 917 final boolean displayEmailInSuggestion, final boolean hideIcon) { 918 JSONObject obj = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 919 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 920 if (obj == null) { 921 return ""; 922 } 923 return obj.optString(Select2Common.LABEL); 924 } 925 926 protected DocumentModelJsonWriter getDocumentModelWriter(final String schemaNames) { 927 MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); 928 String[] schemas = Select2Common.getSchemas(schemaNames); 929 HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance() 930 .getExternalContext() 931 .getRequest(); 932 RenderingContext ctx = RenderingContextWebUtils.getBuilder(request) 933 .properties(schemas) 934 .enrichDoc("documentURL") 935 .get(); 936 return registry.getInstance(ctx, DocumentModelJsonWriter.class); 937 } 938 939}