org.fao.geonet.api.cssstyle.CssStyleSettingsService.java Source code

Java tutorial

Introduction

Here is the source code for org.fao.geonet.api.cssstyle.CssStyleSettingsService.java

Source

/*
 * Copyright (C) 2001-2016 Food and Agriculture Organization of the
 * United Nations (FAO-UN), United Nations World Food Programme (WFP)
 * and United Nations Environment Programme (UNEP)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
 * Rome - Italy. email: geonetwork@osgeo.org
 */
package org.fao.geonet.api.cssstyle;

import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.LOWER_HYPHEN;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.api.API;
import org.fao.geonet.api.cssstyle.service.ICssStyleSettingService;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.domain.CssStyleSetting;
import org.fao.geonet.kernel.GeonetworkDataDirectory;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import springfox.documentation.annotations.ApiIgnore;

/**
 * The Class CssStyleSettingsService.
 */
@RequestMapping(value = { "/api/customstyle", "/api/" + API.VERSION_0_1 + "/customstyle" })
@Api(value = "customstyle", tags = "customstyle", description = "Functionalities for custom styling")
@Controller("stylesheet")
public class CssStyleSettingsService {

    /**
     * Save css style.
     *
     * @param request the request
     * @param response the response
     * @param jsonVariables the json variables
     * @return the response entity
     * @throws Exception the exception
     */
    @ApiOperation(value = "Saves custom style variables.", notes = "Saves custom style variables.", nickname = "saveCustomStyle")
    @RequestMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
    @PreAuthorize("hasRole('Administrator')")
    @ResponseStatus(HttpStatus.CREATED)
    @ResponseBody
    public ResponseEntity<String> saveCssStyle(@ApiIgnore HttpServletRequest request,
            @ApiIgnore HttpServletResponse response,
            @ApiParam(name = "gnCssStyle") @RequestBody String jsonVariables) throws Exception {

        try {
            checkJSONFile(jsonVariables);
            final String cleanedVariables = convertFormNamesToLessVariablesNames(jsonVariables);
            saveLessVariablesOnDB(cleanedVariables);
            saveLessVariablesInDataFolder(cleanedVariables);
            return new ResponseEntity<String>(HttpStatus.CREATED);

        } catch (final Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * Gets the css style.
     *
     * @param request the request
     * @param response the response
     * @return the css style
     * @throws Exception the exception
     */
    @ApiOperation(value = "Get CssStyleSettings", notes = "This returns a map with all Less variables.", nickname = "getCssStyle")
    @RequestMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public Map<String, String> getCssStyle(@ApiIgnore HttpServletRequest request,
            @ApiIgnore HttpServletResponse response) throws Exception {

        final ICssStyleSettingService cssStyleSettingService = (ICssStyleSettingService) ApplicationContextHolder
                .get().getBean("cssStyleSettingService");

        final List<CssStyleSetting> currentCssStyleSettings = cssStyleSettingService.getCustomCssSettings();

        if (currentCssStyleSettings == null) {
            return null;
        } else {
            final Map<String, String> map = convertLessListToAngularFormat(currentCssStyleSettings);
            return map;
        }
    }

    /**
     * Convert less list to map.
     *
     * @param currentCssStyleSettings the current css style settings
     * @return the map
     * @throws JSONException the JSON exception
     */
    private Map<String, String> convertLessListToAngularFormat(List<CssStyleSetting> currentCssStyleSettings)
            throws JSONException {
        final Map<String, String> map = new HashMap<>();

        for (final CssStyleSetting cssStyleSetting : currentCssStyleSettings) {
            // Checking the url variable (removing quotes)
            if (cssStyleSetting.getVariable().equals("gn-background-image") && cssStyleSetting.getValue() != null
                    && cssStyleSetting.getValue().startsWith("'")) {
                map.put(fromDashToCamel(cssStyleSetting.getVariable()),
                        cssStyleSetting.getValue().substring(1, cssStyleSetting.getValue().length() - 1));
            } else {
                map.put(fromDashToCamel(cssStyleSetting.getVariable()), cssStyleSetting.getValue());
            }
        }
        return map;
    }

    /**
     * Save less variables on DB.
     *
     * @param jsonLessVariables the json less variables
     * @throws JSONException the JSON exception
     */

    private void saveLessVariablesOnDB(String jsonLessVariables) throws JSONException {
        final List<CssStyleSetting> listLessVariables = convertToListOfVariables(jsonLessVariables);
        storeListOnDB(listLessVariables);
    }

    /**
     * Save less variables in data folder.
     *
     * @param cssStyleSettings the css style settings
     * @throws IOException Signals that an I/O exception has occurred.
     */

    private void saveLessVariablesInDataFolder(String cssStyleSettings) throws IOException {
        final String dataFolderPath = getDataFolder();
        final Path lessPath = initializeLessFileInDataFolder(dataFolderPath);

        // Write properties to file
        final Charset charset = StandardCharsets.UTF_8;
        Files.write(lessPath, cssStyleSettings.getBytes(charset));
    }

    /**
     * Gets the data folder.
     *
     * @return the data folder
     */

    private String getDataFolder() {
        final GeonetworkDataDirectory geonetworkDataDirectory = ApplicationContextHolder.get()
                .getBean(GeonetworkDataDirectory.class);
        final String path = geonetworkDataDirectory.getSystemDataDir().resolve(Geonet.Config.NODE_LESS_DIR)
                .toString();
        return path;
    }

    /**
     * Initialize less file in data folder.
     *
     * @param path the path
     * @return the path
     * @throws IOException Signals that an I/O exception has occurred.
     */

    private Path initializeLessFileInDataFolder(String path) throws IOException {
        final Path lessPath = Paths.get(path + "/gn_dynamic_style.json");
        try {
            Files.createFile(lessPath);
        } catch (final FileAlreadyExistsException e1) {
            Files.delete(lessPath);
            Files.createFile(lessPath);
        } catch (final NoSuchFileException e2) {
            final Path tmp = lessPath.getParent();
            Files.createDirectories(tmp);
            Files.createFile(lessPath);
        }
        return lessPath;
    }

    /**
     * Store list on DB.
     *
     * @param cssVariables the css variables
     */

    private void storeListOnDB(List<CssStyleSetting> cssVariables) {
        final ICssStyleSettingService cssStyleSettingService = (ICssStyleSettingService) ApplicationContextHolder
                .get().getBean("cssStyleSettingService");
        cssStyleSettingService.saveSettings(cssVariables);
    }

    /**
     * Convert to list of variables.
     *
     * @param jsonLessVariables the json less variables
     * @return the list
     * @throws JSONException the JSON exception
     */

    private List<CssStyleSetting> convertToListOfVariables(String jsonLessVariables) throws JSONException {
        final List<CssStyleSetting> cssVariables = new ArrayList<>();

        final JSONObject jObject = new JSONObject(jsonLessVariables);
        final Iterator<String> iter = jObject.keys();
        while (iter.hasNext()) {
            final String key = iter.next();
            if (jObject.getString(key) != null) {
                cssVariables.add(new CssStyleSetting(key, jObject.getString(key)));
            }
        }
        return cssVariables;
    }

    /**
     * Convert form names to less variables names.
     *
     * @param jsonLessVariables the json less variables
     * @return the string
     * @throws JSONException the JSON exception
     */
    private String convertFormNamesToLessVariablesNames(String jsonLessVariables) throws JSONException {

        final JSONObject input = new JSONObject(jsonLessVariables);
        final JSONObject output = new JSONObject();
        final Iterator<String> iter = input.keys();
        while (iter.hasNext()) {
            final String key = iter.next();
            if (input.getString(key) != null) {

                // For the url is necessary to add quotes
                if (key.equals("gnBackgroundImage") && input.getString(key) != null
                        && !input.getString(key).startsWith("'")) {
                    output.put(fromCamelToDash(key), "'" + input.getString(key) + "'");
                } else {
                    output.put(fromCamelToDash(key), input.getString(key));
                }
            }
        }
        return output.toString();
    }

    /**
     * Check the JSON file and Less variables values.
     *
     * @param jsonLessVariables the json less variables
     * @return true if the file is a valid JSON
     *  and the properties are correct
     * @throws JSONException the JSON exception
     */
    private void checkJSONFile(String jsonLessVariables) throws JSONException {

        final JSONObject input = new JSONObject(jsonLessVariables);
        final Iterator<String> iter = input.keys();
        while (iter.hasNext()) {
            final String key = iter.next();
            if (input.getString(key) != null) {

                if (!key.equals("gnBackgroundImage") && !StringUtils.isEmpty(input.getString(key))) {
                    Pattern pattern = Pattern.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
                    Matcher matcher = pattern.matcher(input.getString(key));
                    if (!matcher.matches()) {
                        throw new JSONException("Invalid color value. It must be in the format #RRGGBB.");
                    }
                } else if (key.equals("gnBackgroundImage") && !StringUtils.isEmpty(input.getString(key))) {
                    Pattern pattern = Pattern
                            .compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
                    Matcher matcher = pattern.matcher(input.getString(key));
                    if (!matcher.matches()) {
                        throw new JSONException("Invalid URL in 'gnBackgroundImage' property");
                    }
                }

            }
        }
    }

    /**
     * Get a string in dashed format and convert to camel format abc-def -> abcDef
     *
     * @param dashFormat the dash format
     * @return the string
     */
    private String fromDashToCamel(String dashFormat) {
        final String upperFormat = LOWER_HYPHEN.to(LOWER_CAMEL, dashFormat);

        return upperFormat;
    }

    /**
     * Get a string in camel format and convert to dashed format abcDef -> abc-def
     *
     * @param upperFormat the upper format
     * @return the string
     */
    private String fromCamelToDash(String upperFormat) {
        final String dashFormat = LOWER_CAMEL.to(LOWER_HYPHEN, upperFormat);

        return dashFormat;
    }

}