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