001/* 002 * (C) Copyright 2006-2010 Nuxeo SAS (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 * Nuxeo - initial API and implementation 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.theme.bank; 021 022import java.io.File; 023import java.io.IOException; 024import java.security.Principal; 025import java.util.Date; 026import java.util.List; 027import java.util.Properties; 028 029import javax.ws.rs.GET; 030import javax.ws.rs.POST; 031import javax.ws.rs.Path; 032import javax.ws.rs.PathParam; 033import javax.ws.rs.Produces; 034import javax.ws.rs.WebApplicationException; 035import javax.ws.rs.core.MediaType; 036import javax.ws.rs.core.Response; 037 038import org.apache.commons.logging.Log; 039import org.apache.commons.logging.LogFactory; 040import org.nuxeo.common.utils.FileUtils; 041import org.nuxeo.ecm.core.api.NuxeoPrincipal; 042import org.nuxeo.ecm.webengine.WebException; 043import org.nuxeo.ecm.webengine.model.WebObject; 044import org.nuxeo.ecm.webengine.model.exceptions.WebSecurityException; 045import org.nuxeo.ecm.webengine.model.impl.ModuleRoot; 046import org.nuxeo.theme.presets.PaletteParser; 047import org.nuxeo.theme.resources.BankManager; 048import org.nuxeo.theme.resources.BankUtils; 049 050import com.sun.jersey.api.NotFoundException; 051 052@Path("/theme-banks") 053@WebObject(type = "theme-banks") 054@Produces(MediaType.TEXT_HTML) 055public class Main extends ModuleRoot { 056 057 private static final Log log = LogFactory.getLog(Main.class); 058 059 private static final String SERVER_ID = "Nuxeo/ThemeBank-1.0"; 060 061 @GET 062 public Object getIndex() { 063 return getTemplate("index.ftl"); 064 } 065 066 public boolean isAdministrator() { 067 Principal principal = ctx.getPrincipal(); 068 if (principal == null) { 069 return false; 070 } 071 return ((NuxeoPrincipal) principal).isAdministrator(); 072 } 073 074 /* 075 * Management mode 076 */ 077 @Path("{bank}/manage") 078 public Object getManagement(@PathParam("bank") String bank) { 079 return newObject("Management", bank); 080 } 081 082 /* 083 * Banks 084 */ 085 @GET 086 @Path("{bank}") 087 public Object displayBank(@PathParam("bank") String bank) { 088 return getTemplate("index.ftl").arg("bank", bank); 089 } 090 091 @GET 092 @Path("{bank}/view") 093 public Object displayBankView(@PathParam("bank") String bank) { 094 return getTemplate("bank.ftl").arg("bank", bank); 095 } 096 097 @GET 098 @Path("{bank}/status") 099 public Object getBankStatus(@PathParam("bank") String bank) { 100 return "OK"; 101 } 102 103 @GET 104 @Path("{bank}/logo") 105 public Object displayBankLogo(@PathParam("bank") String bank) { 106 File file; 107 try { 108 file = BankManager.getBankLogoFile(bank); 109 } catch (IOException e) { 110 throw new ThemeBankException(e.getMessage(), e); 111 } 112 if (file == null || !file.exists()) { 113 return noPreview(); 114 } 115 String ext = FileUtils.getFileExtension(path); 116 String mimeType = ctx.getEngine().getMimeType(ext); 117 if (mimeType == null) { 118 mimeType = "application/octet-stream"; 119 } 120 return Response.ok().entity(Utils.streamFile(file)).lastModified(new Date(file.lastModified())).header( 121 "Cache-Control", "public").header("Server", SERVER_ID).type(mimeType).build(); 122 } 123 124 /* 125 * UI 126 */ 127 @GET 128 @Path("navtree") 129 public Object getNavtreeView() { 130 return getTemplate("navtree.ftl"); 131 } 132 133 @GET 134 @Path("actionbar") 135 public Object getActionBarView() { 136 return getTemplate("actionbar.ftl"); 137 } 138 139 @GET 140 @Path("banks") 141 public Object getBanksView() { 142 return getTemplate("banks.ftl"); 143 } 144 145 @GET 146 @Path("session/login") 147 public Object getSessionView() { 148 return getTemplate("session.ftl"); 149 } 150 151 @GET 152 @Path("session") 153 public Object doSession() { 154 Object failed = ctx.getProperty("failed"); 155 if (failed == null) { 156 return getIndex(); 157 } 158 return getSessionView(); 159 } 160 161 @POST 162 @Path("session/@@login") 163 public Object login() { 164 return getIndex(); 165 } 166 167 /* 168 * Styles 169 */ 170 @GET 171 @Produces(MediaType.APPLICATION_JSON) 172 @Path("{bank}/json/styles") 173 public String listBankStyles(@PathParam("bank") String bankName) { 174 try { 175 return Utils.listBankStyles(bankName); 176 } catch (IOException e) { 177 throw new ThemeBankException(e.getMessage(), e); 178 } 179 } 180 181 @GET 182 @Produces(MediaType.APPLICATION_JSON) 183 @Path("{bank}/json/skins") 184 public String listBankSkins(@PathParam("bank") String bankName) { 185 try { 186 return Utils.listBankSkins(bankName); 187 } catch (IOException e) { 188 throw new ThemeBankException(e.getMessage(), e); 189 } 190 } 191 192 @GET 193 @Produces(MediaType.APPLICATION_JSON) 194 @Path("{bank}/json/presets") 195 public String listBankPresets(@PathParam("bank") String bankName) { 196 try { 197 return Utils.listBankPresets(bankName); 198 } catch (IOException e) { 199 throw new ThemeBankException(e.getMessage(), e); 200 } 201 } 202 203 @GET 204 @Path("{bank}/{collection}/view") 205 public Object getCollectionView(@PathParam("bank") String bank, @PathParam("collection") String collection) { 206 return getTemplate("collection.ftl").arg("collection", collection).arg("bank", bank); 207 } 208 209 @GET 210 @Path("{bank}/{collection}/{type}/info") 211 public Object getPresetCollectionInfo(@PathParam("bank") String bank, @PathParam("collection") String collection, 212 @PathParam("type") String typeName) { 213 try { 214 return BankManager.getInfoFile(bank, collection, typeName); 215 } catch (IOException e) { 216 throw new ThemeBankException(e.getMessage(), e); 217 } 218 } 219 220 @GET 221 @Path("{bank}/{collection}/style/view") 222 public Object getStyleCollectionsView(@PathParam("bank") String bank, @PathParam("collection") String collection) { 223 return getStyleCollections(bank, collection, false); 224 } 225 226 @GET 227 @Path("{bank}/{collection}/skin/view") 228 public Object getSkinsCollectionsView(@PathParam("bank") String bank, @PathParam("collection") String collection) { 229 return getStyleCollections(bank, collection, true); 230 } 231 232 public Object getStyleCollections(String bank, String collection, Boolean skins_only) { 233 return getTemplate("styleCollection.ftl").arg("styles", getItemsInCollection(bank, collection, "style")).arg( 234 "skins", listSkinsInCollection(bank, collection)).arg("collection", collection).arg("bank", bank).arg( 235 "skins_only", skins_only); 236 } 237 238 @GET 239 @Produces("text/css") 240 @Path("{bank}/{collection}/style/{resource}") 241 public Response getStyle(@PathParam("bank") String bank, @PathParam("collection") String collection, 242 @PathParam("resource") String resource) { 243 File file; 244 try { 245 file = BankManager.getStyleFile(bank, collection, resource); 246 } catch (IOException e) { 247 throw new ThemeBankException(e.getMessage(), e); 248 } 249 return Response.ok().entity(Utils.streamFile(file)).lastModified(new Date(file.lastModified())).header( 250 "Cache-Control", "public").header("Server", SERVER_ID).build(); 251 } 252 253 @GET 254 @Path("{bank}/{collection}/style/{resource}/{action}") 255 public Object renderStyle(@PathParam("bank") String bank, @PathParam("collection") String collection, 256 @PathParam("resource") String resource, @PathParam("action") String action) { 257 return getTemplate("style.ftl").arg("content", getStyleContent(bank, collection, resource)).arg("bank", bank).arg( 258 "resource", resource).arg("collection", collection).arg("action", action).arg("is_skin", true); 259 } 260 261 @GET 262 @Path("{bank}/{collection}/skin/{resource}/{action}") 263 public Object renderSkin(@PathParam("bank") String bank, @PathParam("collection") String collection, 264 @PathParam("resource") String resource, @PathParam("action") String action) { 265 return getTemplate("style.ftl").arg("content", getStyleContent(bank, collection, resource)).arg("bank", bank).arg( 266 "resource", resource).arg("collection", collection).arg("action", action).arg("is_skin", true); 267 } 268 269 @GET 270 @Path("{bank}/{collection}/style/{resource}/preview") 271 public Object displayStylePreview(@PathParam("bank") String bank, @PathParam("collection") String collection, 272 @PathParam("resource") String resource) { 273 File file; 274 try { 275 file = BankManager.getStylePreviewFile(bank, collection, resource); 276 } catch (IOException e) { 277 return noPreview(); 278 } 279 String ext = FileUtils.getFileExtension(path); 280 String mimeType = ctx.getEngine().getMimeType(ext); 281 if (mimeType == null) { 282 mimeType = "application/octet-stream"; 283 } 284 return Response.ok().entity(Utils.streamFile(file)).lastModified(new Date(file.lastModified())).header( 285 "Cache-Control", "public").header("Server", SERVER_ID).type(mimeType).build(); 286 } 287 288 public String getStyleContent(String bank, String collection, String resource) { 289 File file; 290 try { 291 file = BankManager.getStyleFile(bank, collection, resource); 292 } catch (IOException e) { 293 throw new ThemeBankException(e.getMessage(), e); 294 } 295 String content; 296 try { 297 content = BankUtils.getFileContent(file); 298 } catch (IOException e) { 299 throw new ThemeBankException(e.getMessage(), e); 300 } 301 return content; 302 } 303 304 /* 305 * Presets 306 */ 307 308 @GET 309 @Path("{bank}/{collection}/preset/view") 310 public Object getPresetCollectionView(@PathParam("bank") String bank, @PathParam("collection") String collection) { 311 return getTemplate("presetCollection.ftl").arg("presets", getItemsInCollection(bank, collection, "preset")).arg( 312 "collection", collection).arg("bank", bank); 313 } 314 315 @GET 316 @Produces(MediaType.TEXT_PLAIN) 317 @Path("{bank}/{collection}/preset/{category}") 318 public Response getPreset(@PathParam("bank") String bank, @PathParam("collection") String collection, 319 @PathParam("category") String category) { 320 String path = String.format("%s/%s/preset/%s", bank, collection, category); 321 File file; 322 try { 323 file = BankManager.getFile(path); 324 } catch (IOException e) { 325 throw new ThemeBankException(e.getMessage(), e); 326 } 327 String content = ""; 328 if (!file.exists()) { 329 return Response.status(404).build(); 330 } 331 332 StringBuilder sb = new StringBuilder(); 333 for (File f : file.listFiles()) { 334 try { 335 content = BankUtils.getFileContent(f); 336 } catch (IOException e) { 337 log.warn("Could not read file: " + f.getAbsolutePath()); 338 continue; 339 } 340 content = PaletteParser.renderPaletteAsCsv(content.getBytes(), f.getName()); 341 sb.append(content); 342 } 343 content = sb.toString(); 344 return Response.ok(content).lastModified(new Date(file.lastModified())).header("Cache-Control", "public").header( 345 "Server", SERVER_ID).build(); 346 } 347 348 @GET 349 @Path("{bank}/{collection}/preset/{category}/view") 350 public Object getPresetView(@PathParam("bank") String bank, @PathParam("collection") String collection, 351 @PathParam("category") String category) { 352 Properties properties = Utils.getPresetProperties(bank, collection, category); 353 return getTemplate("preset.ftl").arg("properties", properties).arg("bank", bank).arg("collection", collection).arg( 354 "category", category); 355 } 356 357 /* 358 * Images 359 */ 360 361 @GET 362 @Path("{bank}/{collection}/image/view") 363 public Object getImageCollectionView(@PathParam("bank") String bank, @PathParam("collection") String collection) { 364 return getTemplate("imageCollection.ftl").arg("images", getItemsInCollection(bank, collection, "image")).arg( 365 "collection", collection).arg("bank", bank); 366 } 367 368 @GET 369 @Path("{bank}/{collection}/image/{resource}") 370 public Response getImage(@PathParam("bank") String bank, @PathParam("collection") String collection, 371 @PathParam("resource") String resource) { 372 File file; 373 try { 374 file = BankManager.getImageFile(bank, collection, resource); 375 } catch (IOException e) { 376 throw new ThemeBankException(e.getMessage(), e); 377 } 378 String ext = FileUtils.getFileExtension(file.getPath()); 379 String mimeType = ctx.getEngine().getMimeType(ext); 380 if (mimeType == null) { 381 mimeType = "application/octet-stream"; 382 } 383 return Response.ok().entity(Utils.streamFile(file)).lastModified(new Date(file.lastModified())).header( 384 "Cache-Control", "public").header("Server", SERVER_ID).type(mimeType).build(); 385 } 386 387 @GET 388 @Path("{bank}/{collection}/image/{resource}/view") 389 public Object getImageView(@PathParam("bank") String bank, @PathParam("collection") String collection, 390 @PathParam("resource") String resource) { 391 return getTemplate("image.ftl").arg("bank", bank).arg("resource", resource).arg("collection", collection); 392 } 393 394 @GET 395 @Produces(MediaType.APPLICATION_JSON) 396 @Path("{bank}/json/images") 397 public String listImages(@PathParam("bank") String bank) { 398 try { 399 return Utils.listImages(bank); 400 } catch (IOException e) { 401 throw new ThemeBankException(e.getMessage(), e); 402 } 403 } 404 405 @GET 406 @Produces(MediaType.APPLICATION_JSON) 407 @Path("{bank}/json/collections") 408 public String listCollections(@PathParam("bank") String bank) { 409 try { 410 return Utils.listCollections(bank); 411 } catch (IOException e) { 412 throw new ThemeBankException(e.getMessage(), e); 413 } 414 } 415 416 @GET 417 @Produces(MediaType.APPLICATION_JSON) 418 @Path("json/tree") 419 public String getTree() throws IOException { 420 return Utils.getNavTree(); 421 } 422 423 private Object noPreview() { 424 return redirect(ctx.getModulePath() + "/skin/img/no-preview.png"); 425 } 426 427 /* 428 * API 429 */ 430 public static List<String> getBankNames() { 431 return BankManager.getBankNames(); 432 } 433 434 public static List<String> getCollections(String bankName) { 435 try { 436 return Utils.getCollections(bankName); 437 } catch (IOException e) { 438 throw new ThemeBankException(e.getMessage(), e); 439 } 440 } 441 442 public List<String> listSkinsInCollection(String bankName, String collection) { 443 try { 444 return Utils.listSkinsInCollection(bankName, collection); 445 } catch (IOException e) { 446 throw new ThemeBankException(e.getMessage(), e); 447 } 448 } 449 450 public static List<String> getItemsInCollection(String bankName, String collection, String typeName) { 451 try { 452 return Utils.getItemsInCollection(bankName, collection, typeName); 453 } catch (IOException e) { 454 throw new ThemeBankException(e.getMessage(), e); 455 } 456 } 457 458 // handle errors 459 @Override 460 public Object handleError(WebApplicationException e) { 461 if (e instanceof WebSecurityException) { 462 return Response.status(401).entity(getTemplate("session.ftl").arg("redirect_url", ctx.getUrlPath())).type( 463 "text/html").build(); 464 } else if (e instanceof NotFoundException) { 465 return Response.status(404).entity(getTemplate("not_found.ftl")).type("text/html").build(); 466 } else if (e instanceof WebException) { 467 return Response.status(500).entity( 468 getTemplate("error.ftl").arg("stacktrace", ((WebException) e).getStackTraceString())).type( 469 "text/html").build(); 470 } else { 471 472 return super.handleError(e); 473 } 474 } 475 476}