001/* 002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 016 * <a href="mailto:grenard@nuxeo.com">Guillaume</a> 017 */ 018 019package org.nuxeo.ecm.platform.ui.select2; 020 021import java.io.BufferedOutputStream; 022import java.io.IOException; 023import java.io.Serializable; 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Locale; 028import java.util.Map; 029import java.util.Map.Entry; 030 031import javax.faces.context.FacesContext; 032import javax.faces.event.PhaseId; 033import javax.servlet.http.HttpServletRequest; 034 035import net.sf.json.JSONArray; 036import net.sf.json.JSONObject; 037 038import org.apache.commons.io.output.ByteArrayOutputStream; 039import org.apache.commons.lang.StringUtils; 040import org.apache.commons.logging.Log; 041import org.apache.commons.logging.LogFactory; 042import org.codehaus.jackson.JsonGenerator; 043import org.jboss.seam.ScopeType; 044import org.jboss.seam.annotations.Destroy; 045import org.jboss.seam.annotations.In; 046import org.jboss.seam.annotations.Name; 047import org.jboss.seam.annotations.Scope; 048import org.jboss.seam.contexts.FacesLifecycle; 049import org.nuxeo.ecm.automation.AutomationService; 050import org.nuxeo.ecm.automation.InvalidChainException; 051import org.nuxeo.ecm.automation.OperationContext; 052import org.nuxeo.ecm.automation.OperationException; 053import org.nuxeo.ecm.automation.core.operations.services.DocumentPageProviderOperation; 054import org.nuxeo.ecm.automation.jaxrs.io.JsonHelper; 055import org.nuxeo.ecm.core.api.CoreInstance; 056import org.nuxeo.ecm.core.api.CoreSession; 057import org.nuxeo.ecm.core.api.DocumentModel; 058import org.nuxeo.ecm.core.api.DocumentModelList; 059import org.nuxeo.ecm.core.api.DocumentRef; 060import org.nuxeo.ecm.core.api.IdRef; 061import org.nuxeo.ecm.core.api.NuxeoGroup; 062import org.nuxeo.ecm.core.api.NuxeoPrincipal; 063import org.nuxeo.ecm.core.api.PathRef; 064import org.nuxeo.ecm.core.api.PropertyException; 065import org.nuxeo.ecm.core.api.repository.RepositoryManager; 066import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter; 067import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 068import org.nuxeo.ecm.core.io.registry.context.RenderingContext; 069import org.nuxeo.ecm.core.schema.SchemaManager; 070import org.nuxeo.ecm.core.schema.types.Field; 071import org.nuxeo.ecm.core.schema.types.QName; 072import org.nuxeo.ecm.core.schema.types.Schema; 073import org.nuxeo.ecm.directory.Directory; 074import org.nuxeo.ecm.directory.DirectoryException; 075import org.nuxeo.ecm.directory.Session; 076import org.nuxeo.ecm.directory.api.DirectoryService; 077import org.nuxeo.ecm.platform.forms.layout.api.Widget; 078import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeConfiguration; 079import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeDefinition; 080import org.nuxeo.ecm.platform.forms.layout.api.service.LayoutStore; 081import org.nuxeo.ecm.platform.ui.select2.automation.SuggestDirectoryEntries; 082import org.nuxeo.ecm.platform.ui.select2.automation.SuggestUserEntries; 083import org.nuxeo.ecm.platform.ui.select2.common.Select2Common; 084import org.nuxeo.ecm.platform.url.DocumentViewImpl; 085import org.nuxeo.ecm.platform.url.codec.DocumentIdCodec; 086import org.nuxeo.ecm.platform.usermanager.UserManager; 087import org.nuxeo.ecm.webengine.jaxrs.coreiodelegate.RenderingContextWebUtils; 088import org.nuxeo.runtime.api.Framework; 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.getLocalService(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.getLocalService(SchemaManager.class); 316 Schema schema = schemaManager.getSchema(schemaName); 317 final Locale locale = org.jboss.seam.core.Locale.instance(); 318 final String label = Select2Common.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.getLocalService(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.getLocalService(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.getLocalService(SchemaManager.class); 399 Schema schema = schemaManager.getSchema(schemaName); 400 401 final Locale locale = org.jboss.seam.core.Locale.instance(); 402 final String label = Select2Common.getLabelFieldName(schema, dbl10n, labelFieldName, 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 protected JSONObject getSingleUserReference(final String storedReference, final boolean prefixed, 416 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 417 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 418 final boolean displayEmailInSuggestion, final boolean hideIcon) { 419 UserManager userManager = Framework.getLocalService(UserManager.class); 420 SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); 421 DirectoryService dirService = Framework.getLocalService(DirectoryService.class); 422 JSONObject obj = new JSONObject(); 423 if (storedReference == null || storedReference.isEmpty()) { 424 return null; 425 } 426 DocumentModel user = null; 427 DocumentModel group = null; 428 Directory userDir = dirService.getDirectory(userManager.getUserDirectoryName()); 429 if (prefixed) { 430 if (storedReference.startsWith(NuxeoPrincipal.PREFIX)) { 431 user = userManager.getUserModel(storedReference.substring(NuxeoPrincipal.PREFIX.length())); 432 } else if (storedReference.startsWith(NuxeoGroup.PREFIX)) { 433 group = userManager.getGroupModel(storedReference.substring(NuxeoGroup.PREFIX.length())); 434 } else { 435 log.warn("User reference is prefixed but prefix was not found on reference: " + storedReference); 436 return createNotFoundEntry(storedReference); 437 } 438 } else { 439 user = userManager.getUserModel(storedReference); 440 if (user == null) { 441 group = userManager.getGroupModel(storedReference); 442 } 443 } 444 if (user != null) { 445 Schema schema = schemaManager.getSchema(userManager.getUserSchemaName()); 446 for (Field field : schema.getFields()) { 447 QName fieldName = field.getName(); 448 String key = fieldName.getLocalName(); 449 Serializable value = user.getPropertyValue(fieldName.getPrefixedName()); 450 if (key.equals(userDir.getPasswordField())) { 451 continue; 452 } 453 obj.element(key, value); 454 } 455 String userId = user.getId(); 456 obj.put(Select2Common.ID, userId); 457 obj.put(Select2Common.TYPE_KEY_NAME, Select2Common.USER_TYPE); 458 obj.put(Select2Common.PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + userId); 459 Select2Common.computeUserLabel(obj, firstLabelField, secondLabelField, thirdLabelField, hideFirstLabel, 460 hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, userId); 461 Select2Common.computeUserGroupIcon(obj, hideIcon); 462 } else if (group != null) { 463 Schema schema = schemaManager.getSchema(userManager.getGroupSchemaName()); 464 for (Field field : schema.getFields()) { 465 QName fieldName = field.getName(); 466 String key = fieldName.getLocalName(); 467 Serializable value = group.getPropertyValue(fieldName.getPrefixedName()); 468 obj.element(key, value); 469 } 470 // If the group hasn't an label, let's put the groupid 471 String groupId = group.getId(); 472 Select2Common.computeGroupLabel(obj, groupId, userManager.getGroupLabelField(), hideFirstLabel); 473 obj.put(Select2Common.ID, groupId); 474 obj.put(Select2Common.TYPE_KEY_NAME, Select2Common.GROUP_TYPE); 475 obj.put(Select2Common.PREFIXED_ID_KEY_NAME, NuxeoGroup.PREFIX + groupId); 476 Select2Common.computeUserGroupIcon(obj, hideIcon); 477 } else { 478 log.warn("Could not resolve user or group reference: " + storedReference); 479 return createNotFoundEntry(storedReference); 480 } 481 return obj; 482 } 483 484 public boolean isMultiSelection(final Widget widget) { 485 String wtCat = widget.getTypeCategory(); 486 if (StringUtils.isBlank(wtCat)) { 487 wtCat = "jsf"; 488 } 489 WidgetTypeDefinition wtDef = getLayoutStore().getWidgetTypeDefinition(wtCat, widget.getType()); 490 if (wtDef != null) { 491 WidgetTypeConfiguration conf = wtDef.getConfiguration(); 492 if (conf != null) { 493 return conf.isList(); 494 } 495 } 496 return false; 497 } 498 499 /** 500 * @deprecated since 7.10: JSF resources mechanism allows to detect resources already included in the page natively. 501 */ 502 public boolean mustIncludeResources() { 503 FacesContext facesContext = FacesContext.getCurrentInstance(); 504 if (facesContext != null) { 505 PhaseId currentPhaseId = FacesLifecycle.getPhaseId(); 506 if (currentPhaseId.equals(PhaseId.RENDER_RESPONSE)) { 507 HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest(); 508 509 if (request.getAttribute(SELECT2_RESOURCES_MARKER) != null) { 510 return false; 511 } else { 512 request.setAttribute(SELECT2_RESOURCES_MARKER, "done"); 513 return true; 514 } 515 } 516 } 517 return false; 518 } 519 520 protected JSONObject createNotFoundEntry(final String id) { 521 return createEntryWithWarnMessage(id, "entry not found"); 522 } 523 524 protected JSONObject createEntryWithWarnMessage(final String label, final String warnMessage) { 525 JSONObject obj = new JSONObject(); 526 obj.put(Select2Common.ID, label); 527 obj.put(Select2Common.ABSOLUTE_LABEL, label); 528 obj.put(Select2Common.LABEL, label); 529 obj.put(Select2Common.WARN_MESSAGE_LABEL, warnMessage); 530 return obj; 531 } 532 533 protected JSONObject resolveDirectoryEntry(final String storedReference, String keySeparator, 534 final Session session, final Schema schema, final String label, final boolean localize, final boolean dbl10n) { 535 if (storedReference == null || storedReference.isEmpty()) { 536 log.trace("No reference provided "); 537 return null; 538 } 539 540 if (keySeparator == null || keySeparator.isEmpty()) { 541 keySeparator = Select2Common.DEFAULT_KEY_SEPARATOR; 542 } 543 544 String entryId = storedReference.substring(storedReference.lastIndexOf(keySeparator) + 1, 545 storedReference.length()); 546 547 DocumentModel result = session.getEntry(entryId); 548 if (result == null) { 549 log.warn("Unable to resolve entry " + storedReference); 550 return createNotFoundEntry(storedReference); 551 } 552 553 JSONObject obj = new JSONObject(); 554 for (Field field : schema.getFields()) { 555 QName fieldName = field.getName(); 556 String key = fieldName.getLocalName(); 557 Serializable value = result.getPropertyValue(fieldName.getPrefixedName()); 558 if (label.equals(key)) { 559 if (localize && !dbl10n) { 560 value = messages.get(value); 561 } 562 obj.element(Select2Common.LABEL, value); 563 obj.element(Select2Common.ABSOLUTE_LABEL, 564 getParentAbsoluteLabel(storedReference, keySeparator, session, fieldName, localize, dbl10n)); 565 } else { 566 obj.element(key, value); 567 } 568 } 569 570 // Add a warning message if the entity is obsolete 571 if (obj.containsKey(Select2Common.OBSOLETE_FIELD_ID) && obj.getInt(Select2Common.OBSOLETE_FIELD_ID) > 0) { 572 obj.element(Select2Common.WARN_MESSAGE_LABEL, messages.get("label.vocabulary.entry.obsolete")); 573 } 574 575 obj.element(Select2Common.COMPUTED_ID, storedReference); 576 return obj; 577 } 578 579 /** 580 * @since 5.9.3 581 */ 582 protected String getParentAbsoluteLabel(final String entryId, final String keySeparator, final Session session, 583 final QName labelFieldName, final boolean localize, final boolean dbl10n) throws PropertyException { 584 String[] split = entryId.split(keySeparator); 585 String result = ""; 586 for (int i = 0; i < split.length; i++) { 587 DocumentModel entry = session.getEntry(split[i]); 588 if (entry != null) { 589 Serializable value = entry.getPropertyValue(labelFieldName.getPrefixedName()); 590 if (localize && !dbl10n) { 591 value = messages.get(value); 592 } 593 result += (i > 0 ? "/" : "") + value; 594 } 595 } 596 597 return result; 598 } 599 600 public String resolveMultipleDirectoryEntries(final Object value, final String directoryName, 601 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 602 JSONArray result = getMultipleDirectoryEntries(value, directoryName, localize, keySeparator, dbl10n, 603 labelFieldName); 604 if (result != null) { 605 return result.toString(); 606 } else { 607 return "[]"; 608 } 609 } 610 611 public List<String> resolveMultipleDirectoryEntryLabels(final Object value, final String directoryName, 612 final boolean localize, final String keySeparator, final boolean dbl10n, final String labelFieldName) { 613 return formatList(getMultipleDirectoryEntries(value, directoryName, localize, keySeparator, dbl10n, 614 labelFieldName)); 615 } 616 617 @SuppressWarnings("rawtypes") 618 public List<String> resolveMultipleReferenceLabels(final Object value, final String repo, 619 final String operationName, final String idProperty, final String label) { 620 621 List<String> result = new ArrayList<>(); 622 623 if (value == null) { 624 return result; 625 } 626 627 List<String> storedRefs = new ArrayList<>(); 628 if (value instanceof List) { 629 for (Object v : (List) value) { 630 storedRefs.add(v.toString()); 631 } 632 } else if (value instanceof Object[]) { 633 for (Object v : (Object[]) value) { 634 storedRefs.add(v.toString()); 635 } 636 } 637 638 for (String ref : storedRefs) { 639 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 640 if (doc != null) { 641 if (label != null && !label.isEmpty()) { 642 Object val = doc.getPropertyValue(label); 643 if (val == null) { 644 result.add(""); 645 } else { 646 result.add(val.toString()); 647 } 648 } else { 649 result.add(doc.getTitle()); 650 } 651 } else { 652 result.add(messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + ref + ")"); 653 } 654 } 655 return result; 656 } 657 658 @SuppressWarnings("rawtypes") 659 public String resolveMultipleReferences(final Object value, final String repo, final String operationName, 660 final String idProperty, final String schemaNames) throws IOException { 661 662 if (value == null) { 663 return "[]"; 664 } 665 666 List<String> storedRefs = new ArrayList<>(); 667 if (value instanceof List) { 668 for (Object v : (List) value) { 669 storedRefs.add(v.toString()); 670 } 671 } else if (value instanceof Object[]) { 672 for (Object v : (Object[]) value) { 673 storedRefs.add(v.toString()); 674 } 675 } 676 if (storedRefs.isEmpty()) { 677 return "[]"; 678 } 679 680 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 681 BufferedOutputStream out = new BufferedOutputStream(baos); 682 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 683 jg.writeStartArray(); 684 685 DocumentModelJsonWriter writer = getDocumentModelWriter(schemaNames); 686 687 for (String ref : storedRefs) { 688 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 689 if (doc == null) { 690 processDocumentNotFound(ref, jg); 691 } else { 692 writer.write(doc, jg); 693 jg.flush(); 694 } 695 } 696 697 jg.writeEndArray(); 698 out.flush(); 699 String json = new String(baos.toByteArray(), "UTF-8"); 700 701 if (json.isEmpty()) { 702 return "[]"; 703 } 704 if (json.startsWith("[") && !json.endsWith("]")) { 705 // XXX !!! 706 // AT: what's this for? 707 json = json + "]"; 708 } 709 710 return json; 711 } 712 713 @SuppressWarnings("rawtypes") 714 public String resolveMultipleUserReference(final Object value, final boolean prefixed, 715 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 716 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 717 final boolean displayEmailInSuggestion, final boolean hideIcon) { 718 if (value == null) { 719 return "[]"; 720 } 721 JSONArray result = new JSONArray(); 722 List<String> storedRefs = new ArrayList<>(); 723 if (value instanceof List) { 724 for (Object v : (List) value) { 725 storedRefs.add(v.toString()); 726 } 727 } else if (value instanceof Object[]) { 728 for (Object v : (Object[]) value) { 729 storedRefs.add(v.toString()); 730 } 731 } 732 733 for (String ref : storedRefs) { 734 String resolved = resolveSingleUserReference(ref, prefixed, firstLabelField, secondLabelField, 735 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, 736 hideIcon); 737 if (resolved != null && !resolved.isEmpty()) { 738 result.add(resolved); 739 } 740 } 741 return result.toString(); 742 } 743 744 public List<String> resolveMultipleUserReferenceLabels(final Object value, final boolean prefixed, 745 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 746 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 747 final boolean displayEmailInSuggestion, final boolean hideIcon) { 748 return formatList(getMultipleUserReference(value, prefixed, firstLabelField, secondLabelField, thirdLabelField, 749 hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon)); 750 } 751 752 protected DocumentModel resolveReference(final String repo, final String storedReference, 753 final String operationName, final String idProperty) { 754 755 if (storedReference == null || storedReference.isEmpty()) { 756 log.trace("No reference provided "); 757 return null; 758 } 759 DocumentModel doc = null; 760 CoreSession session; 761 try { 762 session = getRepositorySession(repo); 763 if (session == null) { 764 log.error("Unable to get CoreSession for repo " + repo); 765 return null; 766 } 767 if (operationName == null || operationName.isEmpty()) { 768 DocumentRef ref = null; 769 770 if (idProperty != null && !idProperty.isEmpty()) { 771 String query = " select * from Document where " + idProperty + "='" + storedReference + "'"; 772 DocumentModelList docs = session.query(query); 773 if (docs.size() > 0) { 774 return docs.get(0); 775 } else { 776 log.warn("Unable to resolve doc using property " + idProperty + " and value " + storedReference); 777 return null; 778 } 779 } else { 780 if (storedReference.startsWith("/")) { 781 ref = new PathRef(storedReference); 782 } else { 783 ref = new IdRef(storedReference); 784 } 785 if (session.exists(ref)) { 786 doc = session.getDocument(ref); 787 } else { 788 log.warn("Unable to resolve reference on " + ref); 789 } 790 } 791 } else { 792 AutomationService as = Framework.getLocalService(AutomationService.class); 793 OperationContext ctx = new OperationContext(session); 794 Map<String, Object> params = new HashMap<String, Object>(); 795 796 params.put("value", storedReference); 797 params.put("xpath", idProperty); 798 params.put("lang", org.jboss.seam.core.Locale.instance().getLanguage()); 799 Object result = as.run(ctx, operationName, params); 800 801 if (result == null) { 802 log.warn("Unable to resolve reference " + storedReference + " using property " + idProperty 803 + " and operation" + operationName); 804 doc = null; 805 } else if (result instanceof DocumentModel) { 806 doc = (DocumentModel) result; 807 } else if (result instanceof DocumentModelList) { 808 DocumentModelList docs = (DocumentModelList) result; 809 if (docs.size() > 0) { 810 doc = docs.get(0); 811 } else { 812 log.warn("No document found"); 813 } 814 } 815 } 816 return doc; 817 } catch (InvalidChainException e) { 818 log.error("Unable to resolve reference", e); 819 } catch (OperationException e) { 820 log.error("Unable to resolve reference", e); 821 } 822 return doc; 823 } 824 825 protected void processDocumentNotFound(String id, JsonGenerator jg) { 826 if (StringUtils.isEmpty(id)) { 827 return; 828 } 829 try { 830 jg.writeStartObject(); 831 jg.writeStringField(Select2Common.ID, id); 832 jg.writeStringField(Select2Common.TITLE, messages.get("label.documentSuggestion.docNotFoundOrNotVisible")); 833 jg.writeStringField(Select2Common.WARN_MESSAGE_LABEL, id); 834 jg.writeEndObject(); 835 jg.flush(); 836 } catch (IOException e) { 837 log.error("Error while writing not found message ", e); 838 } 839 840 } 841 842 public String resolveSingleDirectoryEntry(final String storedReference, final String directoryName, 843 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 844 JSONObject result = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 845 labelFieldName); 846 if (result != null) { 847 return result.toString(); 848 } else { 849 return ""; 850 } 851 } 852 853 public String resolveSingleDirectoryEntryLabel(final String storedReference, final String directoryName, 854 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 855 JSONObject obj = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 856 labelFieldName); 857 if (obj == null) { 858 return ""; 859 } 860 return obj.optString(Select2Common.LABEL); 861 } 862 863 public String resolveSingleReference(final String storedReference, final String repo, final String operationName, 864 final String idProperty, final String schemaNames) throws IOException { 865 866 DocumentModel doc; 867 doc = resolveReference(repo, storedReference, operationName, idProperty); 868 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 869 BufferedOutputStream out = new BufferedOutputStream(baos); 870 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 871 if (doc == null) { 872 processDocumentNotFound(storedReference, jg); 873 } else { 874 getDocumentModelWriter(schemaNames).write(doc, jg); 875 } 876 jg.flush(); 877 return new String(baos.toByteArray(), "UTF-8"); 878 879 } 880 881 public String resolveSingleReferenceLabel(final String storedReference, final String repo, 882 final String operationName, final String idProperty, final String label) { 883 DocumentModel doc = resolveReference(repo, storedReference, operationName, idProperty); 884 if (doc == null) { 885 return messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + doc + ")"; 886 } 887 888 if (label != null && !label.isEmpty()) { 889 Object val = doc.getPropertyValue(label); 890 if (val == null) { 891 return ""; 892 } else { 893 return val.toString(); 894 } 895 } 896 return doc.getTitle(); 897 } 898 899 public String resolveSingleUserReference(final String storedReference, final boolean prefixed, 900 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 901 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 902 final boolean displayEmailInSuggestion, final boolean hideIcon) { 903 JSONObject result = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 904 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 905 if (result != null) { 906 return result.toString(); 907 } else { 908 return ""; 909 } 910 } 911 912 public String resolveUserReferenceLabel(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 obj = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 917 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 918 if (obj == null) { 919 return ""; 920 } 921 return obj.optString(Select2Common.LABEL); 922 } 923 924 protected DocumentModelJsonWriter getDocumentModelWriter(final String schemaNames) { 925 MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); 926 String[] schemas = Select2Common.getSchemas(schemaNames); 927 HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance() 928 .getExternalContext() 929 .getRequest(); 930 RenderingContext ctx = RenderingContextWebUtils.getBuilder(request) 931 .properties(schemas) 932 .enrichDoc("documentURL") 933 .get(); 934 return registry.getInstance(ctx, DocumentModelJsonWriter.class); 935 } 936 937}