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 public boolean mustIncludeResources() { 500 FacesContext facesContext = FacesContext.getCurrentInstance(); 501 if (facesContext != null) { 502 PhaseId currentPhaseId = FacesLifecycle.getPhaseId(); 503 if (currentPhaseId.equals(PhaseId.RENDER_RESPONSE)) { 504 HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest(); 505 506 if (request.getAttribute(SELECT2_RESOURCES_MARKER) != null) { 507 return false; 508 } else { 509 request.setAttribute(SELECT2_RESOURCES_MARKER, "done"); 510 return true; 511 } 512 } 513 } 514 return false; 515 } 516 517 protected JSONObject createNotFoundEntry(final String id) { 518 return createEntryWithWarnMessage(id, "entry not found"); 519 } 520 521 protected JSONObject createEntryWithWarnMessage(final String label, final String warnMessage) { 522 JSONObject obj = new JSONObject(); 523 obj.put(Select2Common.ID, label); 524 obj.put(Select2Common.ABSOLUTE_LABEL, label); 525 obj.put(Select2Common.LABEL, label); 526 obj.put(Select2Common.WARN_MESSAGE_LABEL, warnMessage); 527 return obj; 528 } 529 530 protected JSONObject resolveDirectoryEntry(final String storedReference, String keySeparator, 531 final Session session, final Schema schema, final String label, final boolean localize, final boolean dbl10n) { 532 if (storedReference == null || storedReference.isEmpty()) { 533 log.trace("No reference provided "); 534 return null; 535 } 536 537 if (keySeparator == null || keySeparator.isEmpty()) { 538 keySeparator = Select2Common.DEFAULT_KEY_SEPARATOR; 539 } 540 541 String entryId = storedReference.substring(storedReference.lastIndexOf(keySeparator) + 1, 542 storedReference.length()); 543 544 DocumentModel result = session.getEntry(entryId); 545 if (result == null) { 546 log.warn("Unable to resolve entry " + storedReference); 547 return createNotFoundEntry(storedReference); 548 } 549 550 JSONObject obj = new JSONObject(); 551 for (Field field : schema.getFields()) { 552 QName fieldName = field.getName(); 553 String key = fieldName.getLocalName(); 554 Serializable value = result.getPropertyValue(fieldName.getPrefixedName()); 555 if (label.equals(key)) { 556 if (localize && !dbl10n) { 557 value = messages.get(value); 558 } 559 obj.element(Select2Common.LABEL, value); 560 obj.element(Select2Common.ABSOLUTE_LABEL, 561 getParentAbsoluteLabel(storedReference, keySeparator, session, fieldName, localize, dbl10n)); 562 } else { 563 obj.element(key, value); 564 } 565 } 566 567 // Add a warning message if the entity is obsolete 568 if (obj.containsKey(Select2Common.OBSOLETE_FIELD_ID) && obj.getInt(Select2Common.OBSOLETE_FIELD_ID) > 0) { 569 obj.element(Select2Common.WARN_MESSAGE_LABEL, messages.get("label.vocabulary.entry.obsolete")); 570 } 571 572 obj.element(Select2Common.COMPUTED_ID, storedReference); 573 return obj; 574 } 575 576 /** 577 * @since 5.9.3 578 */ 579 protected String getParentAbsoluteLabel(final String entryId, final String keySeparator, final Session session, 580 final QName labelFieldName, final boolean localize, final boolean dbl10n) throws PropertyException { 581 String[] split = entryId.split(keySeparator); 582 String result = ""; 583 for (int i = 0; i < split.length; i++) { 584 DocumentModel entry = session.getEntry(split[i]); 585 if (entry != null) { 586 Serializable value = entry.getPropertyValue(labelFieldName.getPrefixedName()); 587 if (localize && !dbl10n) { 588 value = messages.get(value); 589 } 590 result += (i > 0 ? "/" : "") + value; 591 } 592 } 593 594 return result; 595 } 596 597 public String resolveMultipleDirectoryEntries(final Object value, final String directoryName, 598 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 599 JSONArray result = getMultipleDirectoryEntries(value, directoryName, localize, keySeparator, dbl10n, 600 labelFieldName); 601 if (result != null) { 602 return result.toString(); 603 } else { 604 return "[]"; 605 } 606 } 607 608 public List<String> resolveMultipleDirectoryEntryLabels(final Object value, final String directoryName, 609 final boolean localize, final String keySeparator, final boolean dbl10n, final String labelFieldName) { 610 return formatList(getMultipleDirectoryEntries(value, directoryName, localize, keySeparator, dbl10n, 611 labelFieldName)); 612 } 613 614 @SuppressWarnings("rawtypes") 615 public List<String> resolveMultipleReferenceLabels(final Object value, final String repo, 616 final String operationName, final String idProperty, final String label) { 617 618 List<String> result = new ArrayList<>(); 619 620 if (value == null) { 621 return result; 622 } 623 624 List<String> storedRefs = new ArrayList<>(); 625 if (value instanceof List) { 626 for (Object v : (List) value) { 627 storedRefs.add(v.toString()); 628 } 629 } else if (value instanceof Object[]) { 630 for (Object v : (Object[]) value) { 631 storedRefs.add(v.toString()); 632 } 633 } 634 635 for (String ref : storedRefs) { 636 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 637 if (doc != null) { 638 if (label != null && !label.isEmpty()) { 639 Object val = doc.getPropertyValue(label); 640 if (val == null) { 641 result.add(""); 642 } else { 643 result.add(val.toString()); 644 } 645 } else { 646 result.add(doc.getTitle()); 647 } 648 } else { 649 result.add(messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + ref + ")"); 650 } 651 } 652 return result; 653 } 654 655 @SuppressWarnings("rawtypes") 656 public String resolveMultipleReferences(final Object value, final String repo, final String operationName, 657 final String idProperty, final String schemaNames) throws IOException { 658 659 if (value == null) { 660 return "[]"; 661 } 662 663 List<String> storedRefs = new ArrayList<>(); 664 if (value instanceof List) { 665 for (Object v : (List) value) { 666 storedRefs.add(v.toString()); 667 } 668 } else if (value instanceof Object[]) { 669 for (Object v : (Object[]) value) { 670 storedRefs.add(v.toString()); 671 } 672 } 673 if (storedRefs.isEmpty()) { 674 return "[]"; 675 } 676 677 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 678 BufferedOutputStream out = new BufferedOutputStream(baos); 679 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 680 jg.writeStartArray(); 681 682 DocumentModelJsonWriter writer = getDocumentModelWriter(schemaNames); 683 684 for (String ref : storedRefs) { 685 DocumentModel doc = resolveReference(repo, ref, operationName, idProperty); 686 if (doc == null) { 687 processDocumentNotFound(ref, jg); 688 } else { 689 writer.write(doc, jg); 690 jg.flush(); 691 } 692 } 693 694 jg.writeEndArray(); 695 out.flush(); 696 String json = new String(baos.toByteArray(), "UTF-8"); 697 698 if (json.isEmpty()) { 699 return "[]"; 700 } 701 if (json.startsWith("[") && !json.endsWith("]")) { 702 // XXX !!! 703 // AT: what's this for? 704 json = json + "]"; 705 } 706 707 return json; 708 } 709 710 @SuppressWarnings("rawtypes") 711 public String resolveMultipleUserReference(final Object value, final boolean prefixed, 712 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 713 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 714 final boolean displayEmailInSuggestion, final boolean hideIcon) { 715 if (value == null) { 716 return "[]"; 717 } 718 JSONArray result = new JSONArray(); 719 List<String> storedRefs = new ArrayList<>(); 720 if (value instanceof List) { 721 for (Object v : (List) value) { 722 storedRefs.add(v.toString()); 723 } 724 } else if (value instanceof Object[]) { 725 for (Object v : (Object[]) value) { 726 storedRefs.add(v.toString()); 727 } 728 } 729 730 for (String ref : storedRefs) { 731 String resolved = resolveSingleUserReference(ref, prefixed, firstLabelField, secondLabelField, 732 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, 733 hideIcon); 734 if (resolved != null && !resolved.isEmpty()) { 735 result.add(resolved); 736 } 737 } 738 return result.toString(); 739 } 740 741 public List<String> resolveMultipleUserReferenceLabels(final Object value, final boolean prefixed, 742 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 743 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 744 final boolean displayEmailInSuggestion, final boolean hideIcon) { 745 return formatList(getMultipleUserReference(value, prefixed, firstLabelField, secondLabelField, thirdLabelField, 746 hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon)); 747 } 748 749 protected DocumentModel resolveReference(final String repo, final String storedReference, 750 final String operationName, final String idProperty) { 751 752 if (storedReference == null || storedReference.isEmpty()) { 753 log.trace("No reference provided "); 754 return null; 755 } 756 DocumentModel doc = null; 757 CoreSession session; 758 try { 759 session = getRepositorySession(repo); 760 if (session == null) { 761 log.error("Unable to get CoreSession for repo " + repo); 762 return null; 763 } 764 if (operationName == null || operationName.isEmpty()) { 765 DocumentRef ref = null; 766 767 if (idProperty != null && !idProperty.isEmpty()) { 768 String query = " select * from Document where " + idProperty + "='" + storedReference + "'"; 769 DocumentModelList docs = session.query(query); 770 if (docs.size() > 0) { 771 return docs.get(0); 772 } else { 773 log.warn("Unable to resolve doc using property " + idProperty + " and value " + storedReference); 774 return null; 775 } 776 } else { 777 if (storedReference.startsWith("/")) { 778 ref = new PathRef(storedReference); 779 } else { 780 ref = new IdRef(storedReference); 781 } 782 if (session.exists(ref)) { 783 doc = session.getDocument(ref); 784 } else { 785 log.warn("Unable to resolve reference on " + ref); 786 } 787 } 788 } else { 789 AutomationService as = Framework.getLocalService(AutomationService.class); 790 OperationContext ctx = new OperationContext(session); 791 Map<String, Object> params = new HashMap<String, Object>(); 792 793 params.put("value", storedReference); 794 params.put("xpath", idProperty); 795 params.put("lang", org.jboss.seam.core.Locale.instance().getLanguage()); 796 Object result = as.run(ctx, operationName, params); 797 798 if (result == null) { 799 log.warn("Unable to resolve reference " + storedReference + " using property " + idProperty 800 + " and operation" + operationName); 801 doc = null; 802 } else if (result instanceof DocumentModel) { 803 doc = (DocumentModel) result; 804 } else if (result instanceof DocumentModelList) { 805 DocumentModelList docs = (DocumentModelList) result; 806 if (docs.size() > 0) { 807 doc = docs.get(0); 808 } else { 809 log.warn("No document found"); 810 } 811 } 812 } 813 return doc; 814 } catch (InvalidChainException e) { 815 log.error("Unable to resolve reference", e); 816 } catch (OperationException e) { 817 log.error("Unable to resolve reference", e); 818 } 819 return doc; 820 } 821 822 protected void processDocumentNotFound(String id, JsonGenerator jg) { 823 if (StringUtils.isEmpty(id)) { 824 return; 825 } 826 try { 827 jg.writeStartObject(); 828 jg.writeStringField(Select2Common.ID, id); 829 jg.writeStringField(Select2Common.TITLE, messages.get("label.documentSuggestion.docNotFoundOrNotVisible")); 830 jg.writeStringField(Select2Common.WARN_MESSAGE_LABEL, id); 831 jg.writeEndObject(); 832 jg.flush(); 833 } catch (IOException e) { 834 log.error("Error while writing not found message ", e); 835 } 836 837 } 838 839 public String resolveSingleDirectoryEntry(final String storedReference, final String directoryName, 840 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 841 JSONObject result = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 842 labelFieldName); 843 if (result != null) { 844 return result.toString(); 845 } else { 846 return ""; 847 } 848 } 849 850 public String resolveSingleDirectoryEntryLabel(final String storedReference, final String directoryName, 851 final boolean localize, String keySeparator, final boolean dbl10n, final String labelFieldName) { 852 JSONObject obj = getSingleDirectoryEntry(storedReference, directoryName, localize, keySeparator, dbl10n, 853 labelFieldName); 854 if (obj == null) { 855 return ""; 856 } 857 return obj.optString(Select2Common.LABEL); 858 } 859 860 public String resolveSingleReference(final String storedReference, final String repo, final String operationName, 861 final String idProperty, final String schemaNames) throws IOException { 862 863 DocumentModel doc; 864 doc = resolveReference(repo, storedReference, operationName, idProperty); 865 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 866 BufferedOutputStream out = new BufferedOutputStream(baos); 867 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 868 if (doc == null) { 869 processDocumentNotFound(storedReference, jg); 870 } else { 871 getDocumentModelWriter(schemaNames).write(doc, jg); 872 } 873 jg.flush(); 874 return new String(baos.toByteArray(), "UTF-8"); 875 876 } 877 878 public String resolveSingleReferenceLabel(final String storedReference, final String repo, 879 final String operationName, final String idProperty, final String label) { 880 DocumentModel doc = resolveReference(repo, storedReference, operationName, idProperty); 881 if (doc == null) { 882 return messages.get("label.documentSuggestion.docNotFoundOrNotVisible") + "(" + doc + ")"; 883 } 884 885 if (label != null && !label.isEmpty()) { 886 Object val = doc.getPropertyValue(label); 887 if (val == null) { 888 return ""; 889 } else { 890 return val.toString(); 891 } 892 } 893 return doc.getTitle(); 894 } 895 896 public String resolveSingleUserReference(final String storedReference, final boolean prefixed, 897 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 898 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 899 final boolean displayEmailInSuggestion, final boolean hideIcon) { 900 JSONObject result = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 901 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 902 if (result != null) { 903 return result.toString(); 904 } else { 905 return ""; 906 } 907 } 908 909 public String resolveUserReferenceLabel(final String storedReference, final boolean prefixed, 910 final String firstLabelField, final String secondLabelField, final String thirdLabelField, 911 final boolean hideFirstLabel, final boolean hideSecondLabel, final boolean hideThirdLabel, 912 final boolean displayEmailInSuggestion, final boolean hideIcon) { 913 JSONObject obj = getSingleUserReference(storedReference, prefixed, firstLabelField, secondLabelField, 914 thirdLabelField, hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, hideIcon); 915 if (obj == null) { 916 return ""; 917 } 918 return obj.optString(Select2Common.LABEL); 919 } 920 921 protected DocumentModelJsonWriter getDocumentModelWriter(final String schemaNames) { 922 MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); 923 String[] schemas = Select2Common.getSchemas(schemaNames); 924 HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance() 925 .getExternalContext() 926 .getRequest(); 927 RenderingContext ctx = RenderingContextWebUtils.getBuilder(request) 928 .properties(schemas) 929 .enrichDoc("documentURL") 930 .get(); 931 return registry.getInstance(ctx, DocumentModelJsonWriter.class); 932 } 933 934}