/** * Copyright (C) 2010-2017 Structr GmbH * * This file is part of Structr <http://structr.org>. * * Structr is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Structr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Structr. If not, see <http://www.gnu.org/licenses/>. */ package org.structr.websocket.command; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.structr.api.config.Settings; import org.structr.common.error.FrameworkException; import org.structr.core.GraphObject; import org.structr.core.GraphObjectMap; import org.structr.core.app.App; import org.structr.core.app.StructrApp; import org.structr.core.graph.Tx; import org.structr.core.property.GenericProperty; import org.structr.core.property.Property; import org.structr.websocket.StructrWebSocket; import org.structr.websocket.message.MessageBuilder; import org.structr.websocket.message.WebSocketMessage; //~--- classes ---------------------------------------------------------------- /** * * */ public class LayoutsCommand extends AbstractCommand { private static final Logger logger = LoggerFactory.getLogger(LayoutsCommand.class.getName()); private static final Property<List<String>> layoutsProperty = new GenericProperty<>("layouts"); static { StructrWebSocket.addCommand(LayoutsCommand.class); } @Override public void processMessage(final WebSocketMessage webSocketData) { final Map<String, Object> data = webSocketData.getNodeData(); final String mode = (String)data.get("mode"); final String name = (String)data.get("name"); if (mode != null) { final List<GraphObject> result = new LinkedList<>(); switch (mode) { case "list": final List<String> layouts = LayoutsCommand.listLayouts(); if (layouts != null) { final GraphObjectMap layoutContainer = new GraphObjectMap(); layoutContainer.put(layoutsProperty, layouts); result.add(layoutContainer); webSocketData.setResult(result); webSocketData.setRawResultCount(1); getWebSocket().send(webSocketData, true); } break; case "get": try { final String content = new String(Files.readAllBytes(locateFile(name, false).toPath())); getWebSocket().send(MessageBuilder.finished().callback(callback).data("schemaLayout", content).build(), true); } catch (IOException | FrameworkException ex) { logger.error("", ex); } break; case "add": final String positions = (String)data.get("schemaLayout"); try { final File layoutFile = locateFile(name, false); if (layoutFile.exists()) { getWebSocket().send(MessageBuilder.status().code(422).message("Layout already exists!").build(), true); } else { createLayout(name, positions); } getWebSocket().send(MessageBuilder.finished().callback(callback).build(), true); } catch (FrameworkException ex) { logger.error("", ex); } break; case "delete": try { deleteLayout(name); getWebSocket().send(MessageBuilder.finished().callback(callback).build(), true); } catch (FrameworkException ex) { logger.error("", ex); } break; default: getWebSocket().send(MessageBuilder.status().code(422).message("Mode must be one of list, get, add or delete.").build(), true); } } else { getWebSocket().send(MessageBuilder.status().code(422).message("Mode must be one of list, get, add or delete.").build(), true); } } @Override public String getCommand() { return "LAYOUTS"; } // ----- private methods ----- private void createLayout(final String name, final String positions) throws FrameworkException { // we want to create a sorted, human-readble, diffable representation of the schema final App app = StructrApp.getInstance(); // isolate write output try (final Tx tx = app.tx()) { final File layoutFile = locateFile(name, false); try (final Writer writer = new FileWriter(layoutFile)) { writer.append(positions); writer.append("\n"); // useful newline writer.flush(); } tx.success(); } catch (IOException ioex) { logger.warn("", ioex); } } private void deleteLayout(final String fileName) throws FrameworkException { if (fileName != null) { final File layoutFile = locateFile(fileName, false); layoutFile.delete(); } else { throw new FrameworkException(422, "Please supply schema name to import."); } } public static List<String> listLayouts() { final File baseDir = new File(getLayoutsPath()); final List<String> fileNames = new LinkedList<>(); if (baseDir.exists()) { final String[] names = baseDir.list((File dir, String name) -> (name.endsWith(".json"))); if (names != null) { fileNames.addAll(Arrays.asList(names)); } } Collections.sort(fileNames, String.CASE_INSENSITIVE_ORDER); return fileNames; } public static File locateFile(final String name, final boolean addTimestamp) throws FrameworkException { String fileName = name; if (StringUtils.isBlank(fileName)) { // create default value fileName = "schema.json"; } if (fileName.contains(System.getProperty("dir.separator", "/"))) { throw new FrameworkException(422, "Only relative file names are allowed, please use the snapshot.path configuration setting to supply a custom path for snapshots."); } if (addTimestamp) { final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-HHmmss"); fileName = format.format(System.currentTimeMillis()) + "-" + fileName; } // append JSON extension if (!fileName.endsWith(".json")) { fileName = fileName + ".json"; } // create final File path = new File(getLayoutsPath() + fileName); final File parent = path.getParentFile(); if (!parent.exists()) { parent.mkdirs(); } return path; } public static String getLayoutsPath() { return Settings.getFullSettingPath(Settings.LayoutsPath); } }