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