001/* 002 * (C) Copyright 2015 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-2.1.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 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 016 */ 017 018package org.nuxeo.ecm.directory.io; 019 020import static java.util.Locale.ENGLISH; 021import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; 022import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON; 023import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE; 024 025import java.io.Closeable; 026import java.io.IOException; 027import java.util.Locale; 028import java.util.MissingResourceException; 029import java.util.Set; 030 031import javax.inject.Inject; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.codehaus.jackson.JsonGenerator; 036import org.nuxeo.common.utils.i18n.I18NUtils; 037import org.nuxeo.ecm.core.api.DocumentModel; 038import org.nuxeo.ecm.core.api.model.Property; 039import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter; 040import org.nuxeo.ecm.core.io.marshallers.json.OutputStreamWithJsonWriter; 041import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentPropertiesJsonReader; 042import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher; 043import org.nuxeo.ecm.core.io.registry.Writer; 044import org.nuxeo.ecm.core.io.registry.context.MaxDepthReachedException; 045import org.nuxeo.ecm.core.io.registry.reflect.Setup; 046import org.nuxeo.ecm.core.schema.SchemaManager; 047import org.nuxeo.ecm.core.schema.types.Field; 048import org.nuxeo.ecm.core.schema.types.QName; 049import org.nuxeo.ecm.core.schema.types.Schema; 050import org.nuxeo.ecm.directory.Session; 051import org.nuxeo.ecm.directory.api.DirectoryEntry; 052import org.nuxeo.ecm.directory.api.DirectoryService; 053 054import com.thoughtworks.xstream.io.json.JsonWriter; 055 056/** 057 * Convert {@link DirectoryEntry} to Json. 058 * <p> 059 * This marshaller is enrichable: register class implementing {@link AbstractJsonEnricher} and managing 060 * {@link DirectoryEntry}. 061 * </p> 062 * <p> 063 * This marshaller is also extensible: extend it and simply override 064 * {@link ExtensibleEntityJsonWriter#extend(DirectoryEntry, JsonWriter)}. 065 * </p> 066 * <p> 067 * Format is: 068 * 069 * <pre> 070 * { 071 * "entity-type": "directoryEntry", 072 * "directoryName": "DIRECTORY_NAME", <- use it to update an existing document 073 * "properties": { 074 * <- entry properties depending on the directory schema (password fields are hidden) 075 * <- format is managed by {@link DocumentPropertiesJsonReader} 076 * } 077 * <-- contextParameters if there are enrichers activated 078 * <-- additional property provided by extend() method 079 * } 080 * </pre> 081 * 082 * </p> 083 * 084 * @since 7.2 085 */ 086@Setup(mode = SINGLETON, priority = REFERENCE) 087public class DirectoryEntryJsonWriter extends ExtensibleEntityJsonWriter<DirectoryEntry> { 088 089 public static final String ENTITY_TYPE = "directoryEntry"; 090 091 private static final String MESSAGES_BUNDLE = "messages"; 092 093 private static final Log log = LogFactory.getLog(DirectoryEntryJsonWriter.class); 094 095 @Inject 096 private SchemaManager schemaManager; 097 098 @Inject 099 private DirectoryService directoryService; 100 101 public DirectoryEntryJsonWriter() { 102 super(ENTITY_TYPE, DirectoryEntry.class); 103 } 104 105 @Override 106 protected void writeEntityBody(DirectoryEntry entry, JsonGenerator jg) throws IOException { 107 String directoryName = entry.getDirectoryName(); 108 DocumentModel document = entry.getDocumentModel(); 109 String schemaName = directoryService.getDirectorySchema(directoryName); 110 String passwordField = directoryService.getDirectoryPasswordField(directoryName); 111 jg.writeStringField("directoryName", directoryName); 112 Schema schema = schemaManager.getSchema(schemaName); 113 Writer<Property> propertyWriter = registry.getWriter(ctx, Property.class, APPLICATION_JSON_TYPE); 114 // for each properties, fetch it 115 jg.writeObjectFieldStart("properties"); 116 Set<String> translated = ctx.getTranslated(ENTITY_TYPE); 117 Set<String> fetched = ctx.getFetched(ENTITY_TYPE); 118 for (Field field : schema.getFields()) { 119 QName fieldName = field.getName(); 120 String key = fieldName.getLocalName(); 121 jg.writeFieldName(key); 122 if (key.equals(passwordField)) { 123 jg.writeString(""); 124 } else { 125 Property property = document.getProperty(fieldName.getPrefixedName()); 126 boolean managed = false; 127 Object value = property.getValue(); 128 if (value != null && value instanceof String) { 129 String valueString = (String) value; 130 if (fetched.contains(fieldName.getLocalName())) { 131 // try to fetch a referenced entry (parent for example) 132 try (Closeable resource = ctx.wrap().controlDepth().open()) { 133 managed = writeFetchedValue(jg, directoryName, fieldName.getLocalName(), valueString); 134 } catch (MaxDepthReachedException e) { 135 managed = false; 136 } 137 } else if (translated.contains(fieldName.getLocalName())) { 138 // try to fetch a translated property 139 managed = writeTranslatedValue(jg, fieldName.getLocalName(), valueString); 140 } 141 } 142 if (!managed) { 143 propertyWriter.write(property, Property.class, Property.class, APPLICATION_JSON_TYPE, 144 new OutputStreamWithJsonWriter(jg)); 145 } 146 } 147 } 148 jg.writeEndObject(); 149 } 150 151 protected boolean writeFetchedValue(JsonGenerator jg, String directoryName, String fieldName, String value) 152 throws IOException { 153 try (Session session = directoryService.open(directoryName)) { 154 DocumentModel entryModel = session.getEntry(value); 155 if (entryModel != null) { 156 DirectoryEntry entry = new DirectoryEntry(directoryName, entryModel); 157 writeEntity(entry, jg); 158 return true; 159 } 160 } 161 return false; 162 } 163 164 protected boolean writeTranslatedValue(JsonGenerator jg, String fieldName, String value) throws IOException { 165 Locale locale = ctx.getLocale(); 166 String msg = getMessageString(value, new Object[0], locale); 167 if (msg == null && locale != ENGLISH) { 168 msg = getMessageString(value, new Object[0], ENGLISH); 169 } 170 if (msg != null && !msg.equals(value)) { 171 jg.writeString(msg); 172 return true; 173 } 174 return false; 175 } 176 177 public static String getMessageString(String key, Object[] params, Locale locale) { 178 try { 179 return I18NUtils.getMessageString(MESSAGES_BUNDLE, key, params, locale); 180 } catch (MissingResourceException e) { 181 log.trace("No bundle found", e); 182 return null; 183 } 184 } 185 186}