001/* 002 * (C) Copyright 2014 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 * <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a> 016 */ 017package org.nuxeo.ecm.platform.tag.web; 018 019import static org.jboss.seam.ScopeType.EVENT; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028 029import net.sf.json.JSONArray; 030import net.sf.json.JSONObject; 031 032import org.apache.commons.lang.StringUtils; 033import org.jboss.seam.Component; 034import org.jboss.seam.annotations.Factory; 035import org.jboss.seam.annotations.In; 036import org.jboss.seam.annotations.Name; 037import org.jboss.seam.annotations.Scope; 038import org.jboss.seam.contexts.Contexts; 039import org.jboss.seam.faces.FacesMessages; 040import org.jboss.seam.international.StatusMessage; 041import org.nuxeo.ecm.core.api.CoreSession; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.DocumentRef; 044import org.nuxeo.ecm.core.api.IdRef; 045import org.nuxeo.ecm.platform.tag.Tag; 046import org.nuxeo.ecm.platform.tag.TagService; 047import org.nuxeo.ecm.platform.ui.select2.common.Select2Common; 048import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 049import org.nuxeo.runtime.api.Framework; 050 051/** 052 * Helper component for tagging widget relying on select2. 053 * 054 * @since 6.0 055 */ 056@Name("tagSelect2Support") 057@Scope(EVENT) 058public class TagSelect2Support { 059 060 @In(create = true) 061 protected NavigationContext navigationContext; 062 063 @In(create = true, required = false) 064 protected transient CoreSession documentManager; 065 066 @In(create = true, required = false) 067 protected transient FacesMessages facesMessages; 068 069 @In(create = true) 070 protected Map<String, String> messages; 071 072 @In(create = true, value = "currentDocumentTags") 073 protected List<Tag> currentDocumentTags; 074 075 protected String label; 076 077 @Factory(value = "resolveDocumentTags", scope = EVENT) 078 public String resolveDocumentTags() { 079 if (currentDocumentTags == null || currentDocumentTags.isEmpty()) { 080 return "[]"; 081 } else { 082 JSONArray result = new JSONArray(); 083 for (Tag tag : currentDocumentTags) { 084 JSONObject obj = new JSONObject(); 085 obj.element(Select2Common.ID, tag.getLabel()); 086 obj.element(Select2Common.LABEL, tag.getLabel()); 087 result.add(obj); 088 } 089 return result.toString(); 090 } 091 } 092 093 public String resolveTags(final List<String> list) { 094 return Select2Common.resolveDefaultEntries(list); 095 } 096 097 public String resolveTags(final String[] array) { 098 if (array == null || array.length == 0) { 099 return Select2Common.resolveDefaultEntries(null); 100 } 101 return Select2Common.resolveDefaultEntries(Arrays.asList(array)); 102 } 103 104 @Factory(value = "documentTagIds", scope = EVENT) 105 public List<String> getDocumentTagStrings() { 106 if (currentDocumentTags == null || currentDocumentTags.isEmpty()) { 107 return null; 108 } else { 109 List<String> result = new ArrayList<String>(); 110 for (Tag tag : currentDocumentTags) { 111 result.add(tag.getLabel()); 112 } 113 return result; 114 } 115 } 116 117 /** 118 * Performs the tagging on the current document. 119 */ 120 public String addTagging() { 121 String messageKey; 122 if (StringUtils.isBlank(label)) { 123 messageKey = "message.add.new.tagging.not.empty"; 124 } else { 125 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 126 String docId = currentDocument.getId(); 127 128 TagService tagService = getTagService(); 129 tagService.tag(documentManager, docId, label, null); 130 if (currentDocument.isVersion()) { 131 DocumentModel liveDocument = documentManager.getSourceDocument(currentDocument.getRef()); 132 if (!liveDocument.isCheckedOut()) { 133 tagService.tag(documentManager, liveDocument.getId(), label, null); 134 } 135 } else if (!currentDocument.isCheckedOut()) { 136 DocumentRef ref = documentManager.getBaseVersion(currentDocument.getRef()); 137 if (ref instanceof IdRef) { 138 tagService.tag(documentManager, ref.toString(), label, null); 139 } 140 } 141 messageKey = "message.add.new.tagging"; 142 // force invalidation 143 Contexts.getEventContext().remove("resolveDocumentTags"); 144 } 145 facesMessages.add(StatusMessage.Severity.INFO, messages.get(messageKey), label); 146 reset(); 147 return null; 148 } 149 150 public String removeTagging() { 151 DocumentModel currentDocument = navigationContext.getCurrentDocument(); 152 String docId = currentDocument.getId(); 153 154 TagService tagService = getTagService(); 155 tagService.untag(documentManager, docId, label, null); 156 157 if (currentDocument.isVersion()) { 158 DocumentModel liveDocument = documentManager.getSourceDocument(currentDocument.getRef()); 159 if (!liveDocument.isCheckedOut()) { 160 tagService.untag(documentManager, liveDocument.getId(), label, null); 161 } 162 } else if (!currentDocument.isCheckedOut()) { 163 DocumentRef ref = documentManager.getBaseVersion(currentDocument.getRef()); 164 if (ref instanceof IdRef) { 165 tagService.untag(documentManager, ref.toString(), label, null); 166 } 167 } 168 // force invalidation 169 Contexts.getEventContext().remove("currentDocumentTags"); 170 facesMessages.add(StatusMessage.Severity.INFO, messages.get("message.remove.tagging"), label); 171 reset(); 172 return null; 173 } 174 175 protected void reset() { 176 label = null; 177 } 178 179 protected TagService getTagService() { 180 TagService tagService = Framework.getService(TagService.class); 181 return tagService.isEnabled() ? tagService : null; 182 } 183 184 public String encodeParameters(final Map<String, Serializable> widgetProperties) { 185 return encodeCommonParameters(widgetProperties).toString(); 186 } 187 188 public String encodeParametersForCurrentDocument(final Map<String, Serializable> widgetProperties) { 189 Map<String, String> parameters = new HashMap<String, String>(); 190 parameters.put("onAddEntryHandler", "addTagHandler"); 191 parameters.put("onRemoveEntryHandler", "removeTagHandler"); 192 parameters.put("containerCssClass", "s2tagContainerCssClass"); 193 parameters.put("dropdownCssClass", "s2tagDropdownCssClass"); 194 parameters.put("createSearchChoice", "createNewTag"); 195 if (widgetProperties.containsKey("canSelectNewTag") 196 && !Boolean.parseBoolean((String) widgetProperties.get("canSelectNewTag"))) { 197 parameters.remove("createSearchChoice"); 198 } 199 return encodeCommonParameters(widgetProperties, parameters).toString(); 200 } 201 202 protected JSONObject encodeCommonParameters(final Map<String, Serializable> widgetProperties) { 203 return encodeCommonParameters(widgetProperties, null); 204 } 205 206 protected JSONObject encodeCommonParameters(final Map<String, Serializable> widgetProperties, 207 final Map<String, String> additionalParameters) { 208 JSONObject obj = new JSONObject(); 209 obj.put("multiple", "true"); 210 obj.put(Select2Common.MIN_CHARS, "1"); 211 obj.put(Select2Common.READ_ONLY_PARAM, "false"); 212 if (widgetProperties.containsKey("canSelectNewTag") 213 && Boolean.parseBoolean((String) widgetProperties.get("canSelectNewTag"))) { 214 obj.put("createSearchChoice", "createNewTag"); 215 } 216 obj.put(Select2Common.OPERATION_ID, "Tag.Suggestion"); 217 obj.put(Select2Common.WIDTH, "300px"); 218 obj.put(Select2Common.SELECTION_FORMATTER, "formatSelectedTags"); 219 obj.put(Select2Common.SUGGESTION_FORMATTER, "formatSuggestedTags"); 220 JSONArray tokenSeparator = new JSONArray(); 221 tokenSeparator.add(","); 222 tokenSeparator.add(" "); 223 obj.put("tokenSeparators", tokenSeparator); 224 if (additionalParameters != null) { 225 for (Entry<String, String> entry : additionalParameters.entrySet()) { 226 obj.put(entry.getKey(), entry.getValue().toString()); 227 } 228 } 229 for (Entry<String, Serializable> entry : widgetProperties.entrySet()) { 230 obj.put(entry.getKey(), entry.getValue().toString()); 231 } 232 return obj; 233 } 234 235 /** 236 * @since 7.1 237 */ 238 public void listDocumentsForTag() { 239 final TagActionsBean tagActionsBean = (TagActionsBean) Component.getInstance(TagActionsBean.class); 240 tagActionsBean.setListLabel(label); 241 } 242 243 public String getLabel() { 244 return label; 245 } 246 247 public void setLabel(String label) { 248 this.label = label; 249 } 250}