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