001/* 002 * (C) Copyright 2010 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 * Anahide Tchertchian 016 */ 017package org.nuxeo.ecm.platform.forms.layout.export; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.LinkedHashMap; 024import java.util.List; 025import java.util.Map; 026 027import javax.servlet.http.HttpServletRequest; 028import javax.ws.rs.GET; 029import javax.ws.rs.Path; 030import javax.ws.rs.PathParam; 031import javax.ws.rs.QueryParam; 032import javax.ws.rs.core.Context; 033import javax.ws.rs.core.Response; 034import javax.ws.rs.core.UriInfo; 035 036import org.apache.commons.lang.StringUtils; 037import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeConfiguration; 038import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeDefinition; 039import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetTypeDefinitionComparator; 040import org.nuxeo.ecm.platform.forms.layout.api.service.LayoutStore; 041import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException; 042import org.nuxeo.ecm.webengine.model.view.TemplateView; 043import org.nuxeo.runtime.api.Framework; 044 045/** 046 * Exports and presents documentation about widget type definitions 047 * 048 * @author Anahide Tchertchian 049 * @since 5.4 050 */ 051public class WidgetTypeResource { 052 053 protected final String category; 054 055 protected LayoutStore service; 056 057 protected final List<WidgetTypeDefinition> widgetTypes; 058 059 protected final Map<String, List<WidgetTypeDefinition>> widgetTypesByCat; 060 061 public WidgetTypeResource(String category) { 062 this.category = category; 063 service = Framework.getService(LayoutStore.class); 064 widgetTypes = service.getWidgetTypeDefinitions(category); 065 // sort so that order is deterministic 066 Collections.sort(widgetTypes, new WidgetTypeDefinitionComparator(true)); 067 widgetTypesByCat = getWidgetTypesByCategory(); 068 } 069 070 protected Map<String, List<WidgetTypeDefinition>> getWidgetTypesByCategory() { 071 Map<String, List<WidgetTypeDefinition>> cats = new HashMap<String, List<WidgetTypeDefinition>>(); 072 List<WidgetTypeDefinition> unknownCatWidgets = new ArrayList<WidgetTypeDefinition>(); 073 for (WidgetTypeDefinition wTypeDef : widgetTypes) { 074 List<String> categories = null; 075 WidgetTypeConfiguration conf = wTypeDef.getConfiguration(); 076 if (conf != null) { 077 categories = conf.getCategories(); 078 } 079 boolean added = false; 080 if (categories != null) { 081 for (String cat : categories) { 082 List<WidgetTypeDefinition> list = cats.get(cat); 083 if (list == null) { 084 list = new ArrayList<WidgetTypeDefinition>(); 085 } 086 list.add(wTypeDef); 087 cats.put(cat, list); 088 added = true; 089 } 090 } 091 if (!added) { 092 unknownCatWidgets.add(wTypeDef); 093 } 094 } 095 if (!unknownCatWidgets.isEmpty()) { 096 cats.put("unknown", unknownCatWidgets); 097 } 098 // sort by category key 099 List<String> sortedKeys = new ArrayList<String>(cats.keySet()); 100 Collections.sort(sortedKeys); 101 Map<String, List<WidgetTypeDefinition>> res = new LinkedHashMap<String, List<WidgetTypeDefinition>>(); 102 for (String key : sortedKeys) { 103 res.put(key, cats.get(key)); 104 } 105 return res; 106 } 107 108 /** 109 * Returns widget types definitions for given categories 110 * <p> 111 * If the category is null, the filter does not check the category. Widget types without a configuration are 112 * included if boolean 'all' is set to true. Mutliple categories are extracted from the query parameter by splitting 113 * on the space character. 114 * <p> 115 * If not null, the version parameter will exclude all widget types that did not exist before this version. 116 */ 117 @GET 118 @Path("widgetTypes") 119 public Object getWidgetTypeDefinitions(@Context HttpServletRequest request, 120 @QueryParam("categories") String categories, @QueryParam("version") String version, 121 @QueryParam("all") Boolean all) { 122 // TODO: refactor so that's cached 123 List<String> catsList = new ArrayList<String>(); 124 if (categories != null) { 125 for (String cat : categories.split(" ")) { 126 catsList.add(cat); 127 } 128 } 129 WidgetTypeDefinitions res = new WidgetTypeDefinitions(); 130 for (WidgetTypeDefinition def : widgetTypes) { 131 WidgetTypeConfiguration conf = def.getConfiguration(); 132 if (!Boolean.TRUE.equals(all) && conf == null) { 133 continue; 134 } 135 if (version != null && conf != null) { 136 String confVersion = conf.getSinceVersion(); 137 if (isStriclyBeforeVersion(version, confVersion)) { 138 continue; 139 } 140 } 141 if (catsList != null && !catsList.isEmpty()) { 142 boolean hasCats = false; 143 if (conf != null) { 144 // filter on category 145 List<String> confCats = conf.getCategories(); 146 if (confCats != null) { 147 hasCats = true; 148 if (confCats.containsAll(catsList)) { 149 res.add(def); 150 } 151 } 152 } 153 if (!hasCats && catsList.size() == 1 && catsList.contains("unknown")) { 154 res.add(def); 155 } 156 } else { 157 if (conf == null && !Boolean.TRUE.equals(all)) { 158 continue; 159 } 160 res.add(def); 161 } 162 } 163 return res; 164 } 165 166 protected boolean isStriclyBeforeVersion(String ref, String version) { 167 if (version == null || version.trim().length() == 0) { 168 return true; 169 } 170 171 String[] components1 = ref.split("\\."); 172 String[] components2 = version.split("\\."); 173 int length = Math.min(components1.length, components2.length); 174 for(int i = 0; i < length; i++) { 175 int result = Integer.compare(Integer.valueOf(components1[i]), Integer.valueOf(components2[i])); 176 if (result != 0) { 177 return result < 0; 178 } 179 } 180 return components1.length < components2.length; 181 } 182 183 /** 184 * Returns widget types definitions for given category. 185 * <p> 186 * If the category is null, the filter does not check the category. Widget types without a configuration are 187 * included if boolean 'all' is set to true. 188 * <p> 189 * If not null, the version parameter will exclude all widget types that did not exist before this version. 190 */ 191 @GET 192 @Path("widgetTypes/{category}") 193 public Object getWidgetTypeDefinitionsForCategory(@Context HttpServletRequest request, 194 @PathParam("category") String category, @QueryParam("version") String version, 195 @QueryParam("all") Boolean all) { 196 return getWidgetTypeDefinitions(request, category, version, all); 197 } 198 199 @GET 200 @Path("widgetType/{name}") 201 public Object getWidgetTypeDefinition(@Context HttpServletRequest request, @PathParam("name") String name) { 202 WidgetTypeDefinition def = service.getWidgetTypeDefinition(category, name); 203 if (def != null) { 204 return def; 205 } else { 206 return Response.status(401).build(); 207 } 208 } 209 210 public TemplateView getTemplate(@Context UriInfo uriInfo) { 211 return getTemplate("widget-types.ftl", uriInfo); 212 } 213 214 @GET 215 @Path("wiki") 216 public Object getWikiDocumentation(@Context UriInfo uriInfo) { 217 return getTemplate("widget-types-wiki.ftl", uriInfo); 218 } 219 220 protected List<String> getNuxeoVersions() { 221 if ("jsf".equals(category) || "jsfAction".equals(category)) { 222 return Arrays.asList("5.8", "6.0", "7.10"); 223 } 224 return Collections.emptyList(); 225 } 226 227 protected TemplateView getTemplate(String name, UriInfo uriInfo) { 228 String baseURL = uriInfo.getAbsolutePath().toString(); 229 if (!baseURL.endsWith("/")) { 230 baseURL += "/"; 231 } 232 TemplateView tv = new TemplateView(this, name); 233 tv.arg("categories", widgetTypesByCat); 234 tv.arg("nuxeoVersions", getNuxeoVersions()); 235 tv.arg("widgetTypeCategory", category); 236 tv.arg("widgetTypes", widgetTypes); 237 tv.arg("baseURL", baseURL); 238 return tv; 239 } 240 241 @GET 242 public Object doGet(@QueryParam("widgetType") String widgetTypeName, @Context UriInfo uriInfo) { 243 if (widgetTypeName == null) { 244 return getTemplate(uriInfo); 245 } else { 246 WidgetTypeDefinition wType = service.getWidgetTypeDefinition(category, widgetTypeName); 247 if (wType == null) { 248 throw new WebResourceNotFoundException("No widget type found with name: " + widgetTypeName); 249 } 250 TemplateView tpl = getTemplate(uriInfo); 251 tpl.arg("widgetType", wType); 252 return tpl; 253 } 254 } 255 256 public String getWidgetTypeLabel(WidgetTypeDefinition wTypeDef) { 257 if (wTypeDef != null) { 258 WidgetTypeConfiguration conf = wTypeDef.getConfiguration(); 259 if (conf != null) { 260 return conf.getTitle(); 261 } 262 return wTypeDef.getName(); 263 } 264 return null; 265 } 266 267 public String getWidgetTypeDescription(WidgetTypeDefinition wTypeDef) { 268 if (wTypeDef != null) { 269 WidgetTypeConfiguration conf = wTypeDef.getConfiguration(); 270 if (conf != null) { 271 return conf.getDescription(); 272 } 273 } 274 return null; 275 } 276 277 public List<String> getWidgetTypeCategories(WidgetTypeDefinition wTypeDef) { 278 if (wTypeDef != null) { 279 WidgetTypeConfiguration conf = wTypeDef.getConfiguration(); 280 if (conf != null) { 281 return conf.getCategories(); 282 } 283 } 284 return null; 285 } 286 287 public String getWidgetTypeCategoriesAsString(WidgetTypeDefinition wTypeDef) { 288 List<String> categories = getWidgetTypeCategories(wTypeDef); 289 if (categories == null) { 290 return ""; 291 } else { 292 return StringUtils.join(categories, ", "); 293 } 294 } 295 296}