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