org.fao.geonet.api.site.SiteApi.java Source code

Java tutorial

Introduction

Here is the source code for org.fao.geonet.api.site.SiteApi.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.site;

import static org.apache.commons.fileupload.util.Streams.checkFileName;
import static org.fao.geonet.api.ApiParams.API_CLASS_CATALOG_TAG;

import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.GeonetContext;
import org.fao.geonet.NodeInfo;
import org.fao.geonet.SystemInfo;
import org.fao.geonet.api.API;
import org.fao.geonet.api.ApiParams;
import org.fao.geonet.api.ApiUtils;
import org.fao.geonet.api.site.model.SettingSet;
import org.fao.geonet.api.site.model.SettingsListResponse;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.doi.client.DoiManager;
import org.fao.geonet.domain.Metadata;
import org.fao.geonet.domain.MetadataSourceInfo_;
import org.fao.geonet.domain.Metadata_;
import org.fao.geonet.domain.Profile;
import org.fao.geonet.domain.Setting;
import org.fao.geonet.domain.SettingDataType;
import org.fao.geonet.domain.Source;
import org.fao.geonet.exceptions.OperationAbortedEx;
import org.fao.geonet.kernel.DataManager;
import org.fao.geonet.kernel.GeonetworkDataDirectory;
import org.fao.geonet.kernel.datamanager.IMetadataManager;
import org.fao.geonet.kernel.search.EsSearchManager;
import org.fao.geonet.kernel.search.SearchManager;
import org.fao.geonet.kernel.setting.SettingInfo;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.kernel.setting.Settings;
import org.fao.geonet.lib.Lib;
import org.fao.geonet.repository.PathSpec;
import org.fao.geonet.repository.SettingRepository;
import org.fao.geonet.repository.SourceRepository;
import org.fao.geonet.repository.specification.MetadataSpecs;
import org.fao.geonet.resources.Resources;
import org.fao.geonet.utils.FilePathChecker;
import org.fao.geonet.utils.IO;
import org.fao.geonet.utils.ProxyInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
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 io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import jeeves.component.ProfileManager;
import jeeves.config.springutil.ServerBeanPropertyUpdater;
import jeeves.server.JeevesProxyInfo;
import jeeves.server.UserSession;
import jeeves.server.context.ServiceContext;
import springfox.documentation.annotations.ApiIgnore;

/**
 *
 */

@RequestMapping(value = { "/api/site", "/api/" + API.VERSION_0_1 + "/site" })
@Api(value = API_CLASS_CATALOG_TAG, tags = API_CLASS_CATALOG_TAG, description = ApiParams.API_CLASS_CATALOG_OPS)
@Controller("site")
public class SiteApi {

    public static void reloadServices(ServiceContext context) throws Exception {
        GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
        DataManager dataMan = gc.getBean(DataManager.class);
        SettingManager settingMan = gc.getBean(SettingManager.class);
        SettingInfo si = context.getBean(SettingInfo.class);

        try {
            if (si.getLuceneIndexOptimizerSchedulerEnabled()) {
                dataMan.rescheduleOptimizer(si.getLuceneIndexOptimizerSchedulerAt(),
                        si.getLuceneIndexOptimizerSchedulerInterval());
            } else {
                dataMan.disableOptimizer();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new OperationAbortedEx(
                    "Parameters saved but cannot restart Lucene Index Optimizer: " + e.getMessage());
        }

        LogUtils.refreshLogConfiguration();

        try {
            // Load proxy information into Jeeves
            ProxyInfo pi = JeevesProxyInfo.getInstance();
            boolean useProxy = settingMan.getValueAsBool(Settings.SYSTEM_PROXY_USE, false);
            if (useProxy) {
                String proxyHost = settingMan.getValue(Settings.SYSTEM_PROXY_HOST);
                String proxyPort = settingMan.getValue(Settings.SYSTEM_PROXY_PORT);
                String username = settingMan.getValue(Settings.SYSTEM_PROXY_USERNAME);
                String password = settingMan.getValue(Settings.SYSTEM_PROXY_PASSWORD);
                pi.setProxyInfo(proxyHost, Integer.valueOf(proxyPort), username, password);
            } else {
                pi.setProxyInfo(null, -1, null, null);
            }

            // Update http.proxyHost, http.proxyPort and http.nonProxyHosts
            Lib.net.setupProxy(settingMan);
        } catch (Exception e) {
            e.printStackTrace();
            throw new OperationAbortedEx("Parameters saved but cannot set proxy information: " + e.getMessage());
        }
        DoiManager doiManager = gc.getBean(DoiManager.class);
        doiManager.loadConfig();
    }

    @ApiOperation(value = "Get site description", notes = "", nickname = "getDescription")
    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    @ApiResponses(value = { @ApiResponse(code = 200, message = "Site description.") })
    @ResponseBody
    public SettingsListResponse get() throws Exception {
        ApplicationContext appContext = ApplicationContextHolder.get();
        SettingManager sm = appContext.getBean(SettingManager.class);

        SettingsListResponse response = new SettingsListResponse();
        response.setSettings(sm.getSettings(new String[] { Settings.SYSTEM_SITE_NAME_PATH,
                Settings.SYSTEM_SITE_ORGANIZATION, Settings.SYSTEM_SITE_SITE_ID_PATH,
                Settings.SYSTEM_PLATFORM_VERSION, Settings.SYSTEM_PLATFORM_SUBVERSION }));
        return response;
    }

    @ApiOperation(value = "Get settings", notes = "Return public settings for anonymous users, internals are allowed for authenticated.", nickname = "getSettings")
    @RequestMapping(path = "/settings", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    @ApiResponses(value = { @ApiResponse(code = 200, message = "Settings.") })
    @ResponseBody
    public SettingsListResponse getSettingsSet(
            @ApiParam(value = "Setting set. A common set of settings to retrieve.", required = false) @RequestParam(required = false) SettingSet[] set,
            @ApiParam(value = "Setting key", required = false) @RequestParam(required = false) String[] key,
            HttpServletRequest request, @ApiIgnore @ApiParam(hidden = true) HttpSession httpSession)
            throws Exception {
        ConfigurableApplicationContext appContext = ApplicationContextHolder.get();
        SettingManager sm = appContext.getBean(SettingManager.class);
        UserSession session = ApiUtils.getUserSession(httpSession);
        Profile profile = session == null ? null : session.getProfile();

        List<String> settingList = new ArrayList<>();
        if (set == null && key == null) {
            final SettingRepository settingRepository = appContext.getBean(SettingRepository.class);
            final List<org.fao.geonet.domain.Setting> publicSettings = settingRepository.findAllByInternal(false);

            // Add virtual settings based on internal settings.
            // eg. if mail server is defined, allow email interactions ...
            String mailServer = sm.getValue(Settings.SYSTEM_FEEDBACK_MAILSERVER_HOST);
            publicSettings.add(new Setting()
                    .setName(Settings.SYSTEM_FEEDBACK_MAILSERVER_HOST + Settings.VIRTUAL_SETTINGS_SUFFIX_ISDEFINED)
                    .setDataType(SettingDataType.BOOLEAN).setValue(StringUtils.isNotEmpty(mailServer) + ""));

            SettingsListResponse response = new SettingsListResponse();
            response.setSettings(publicSettings);
            return response;
        } else {
            if (set != null && set.length > 0) {
                for (SettingSet s : set) {
                    String[] props = s.getListOfSettings();
                    if (props != null) {
                        Collections.addAll(settingList, props);
                    }
                }
            }
            if (key != null && key.length > 0) {
                Collections.addAll(settingList, key);
            }
            List<org.fao.geonet.domain.Setting> settings = sm.getSettings(settingList.toArray(new String[0]));
            ListIterator<org.fao.geonet.domain.Setting> iterator = settings.listIterator();

            // Cleanup internal settings for not authenticated users.
            while (iterator.hasNext()) {
                org.fao.geonet.domain.Setting s = iterator.next();
                if (s.isInternal() && profile == null) {
                    settings.remove(s);
                }
            }

            SettingsListResponse response = new SettingsListResponse();
            response.setSettings(settings);
            return response;
        }
    }

    @ApiOperation(value = "Get settings with details", notes = "Provides also setting properties.", nickname = "getSettingsDetails")
    @RequestMapping(path = "/settings/details", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    @ApiResponses(value = { @ApiResponse(code = 200, message = "Settings with details.") })
    @ResponseBody
    @PreAuthorize("hasRole('Administrator')")
    public List<Setting> getSettingsDetails(
            @ApiParam(value = "Setting set. A common set of settings to retrieve.", required = false) @RequestParam(required = false) SettingSet[] set,
            @ApiParam(value = "Setting key", required = false) @RequestParam(required = false) String[] key,
            HttpServletRequest request, @ApiIgnore @ApiParam(hidden = true) HttpSession httpSession)
            throws Exception {
        ConfigurableApplicationContext appContext = ApplicationContextHolder.get();
        SettingManager sm = appContext.getBean(SettingManager.class);
        UserSession session = ApiUtils.getUserSession(httpSession);
        Profile profile = session == null ? null : session.getProfile();

        List<String> settingList = new ArrayList<>();
        if (set == null && key == null) {
            return sm.getAll();
        } else {
            if (set != null && set.length > 0) {
                for (SettingSet s : set) {
                    String[] props = s.getListOfSettings();
                    if (props != null) {
                        Collections.addAll(settingList, props);
                    }
                }
            }
            if (key != null && key.length > 0) {
                Collections.addAll(settingList, key);
            }
            List<org.fao.geonet.domain.Setting> settings = sm.getSettings(settingList.toArray(new String[0]));
            ListIterator<org.fao.geonet.domain.Setting> iterator = settings.listIterator();

            // Cleanup internal settings for not authenticated users.
            while (iterator.hasNext()) {
                org.fao.geonet.domain.Setting s = iterator.next();
                if (s.isInternal() && profile == null) {
                    settings.remove(s);
                }
            }
            return settings;
        }
    }

    @ApiOperation(value = "Save settings", notes = "", nickname = "saveSettingsDetails")
    @RequestMapping(path = "/settings", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
    @PreAuthorize("hasRole('Administrator')")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @ApiResponses(value = { @ApiResponse(code = 204, message = "Settings saved."),
            @ApiResponse(code = 403, message = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_ADMIN) })
    public void saveSettings(
            @ApiIgnore @ApiParam(hidden = false) @RequestParam Map<String, String> allRequestParams,
            HttpServletRequest request, @ApiIgnore @ApiParam(hidden = true) HttpSession httpSession)
            throws Exception {
        ApplicationContext applicationContext = ApplicationContextHolder.get();
        SettingManager sm = applicationContext.getBean(SettingManager.class);
        String currentUuid = sm.getSiteId();
        String oldSiteName = sm.getSiteName();

        if (!sm.setValues(allRequestParams)) {
            throw new OperationAbortedEx("Cannot set all values");
        }

        String newSiteName = sm.getSiteName();
        // Update site source name/translations if the site name is updated
        if (!oldSiteName.equals(newSiteName)) {
            SourceRepository sourceRepository = applicationContext.getBean(SourceRepository.class);
            Source siteSource = sourceRepository.findOne(currentUuid);

            if (siteSource != null) {
                siteSource.setName(newSiteName);
                siteSource.getLabelTranslations()
                        .forEach((l, t) -> siteSource.getLabelTranslations().put(l, newSiteName));
                sourceRepository.save(siteSource);
            }
        }

        // And reload services
        String newUuid = allRequestParams.get(Settings.SYSTEM_SITE_SITE_ID_PATH);

        if (newUuid != null && !currentUuid.equals(newUuid)) {
            final IMetadataManager metadataRepository = applicationContext.getBean(IMetadataManager.class);
            final SourceRepository sourceRepository = applicationContext.getBean(SourceRepository.class);
            final Source source = sourceRepository.findOne(currentUuid);
            Source newSource = new Source(newUuid, source.getName(), source.getLabelTranslations(),
                    source.isLocal());
            sourceRepository.save(newSource);

            PathSpec<Metadata, String> servicesPath = new PathSpec<Metadata, String>() {
                @Override
                public javax.persistence.criteria.Path<String> getPath(Root<Metadata> root) {
                    return root.get(Metadata_.sourceInfo).get(MetadataSourceInfo_.sourceId);
                }
            };
            metadataRepository.createBatchUpdateQuery(servicesPath, newUuid, MetadataSpecs.isHarvested(false));
            sourceRepository.delete(source);
        }

        SettingInfo info = applicationContext.getBean(SettingInfo.class);
        ServiceContext context = ApiUtils.createServiceContext(request);
        ServerBeanPropertyUpdater.updateURL(info.getSiteUrl(true) + context.getBaseUrl(), applicationContext);

        // Reload services affected by updated settings
        reloadServices(context);
    }

    @ApiOperation(value = "Get site informations", notes = "", nickname = "getInformation")
    @RequestMapping(path = "/info", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    @ApiResponses(value = { @ApiResponse(code = 200, message = "Site information.") })
    @ResponseBody
    public SiteInformation getInformation(HttpServletRequest request) throws Exception {
        ServiceContext context = ApiUtils.createServiceContext(request);
        return new SiteInformation(context, (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME));
    }

    @ApiOperation(value = "Is CAS enabled?", notes = "", nickname = "isCasEnabled")
    @RequestMapping(path = "/info/isCasEnabled", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    // This service returns a boolean, not encapsulated in json
    // TODO: Review to return a valid JSON, check also similar methods
    public boolean isCasEnabled(HttpServletRequest request) throws Exception {
        ApiUtils.createServiceContext(request);
        return ProfileManager.isCasEnabled();
    }

    @Autowired
    private SystemInfo info;

    @ApiOperation(value = "Update staging profile", notes = "TODO: Needs doc", nickname = "updateStagingProfile")
    @RequestMapping(path = "/info/staging/{profile}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @ApiResponses(value = { @ApiResponse(code = 204, message = "Staging profile saved."),
            @ApiResponse(code = 403, message = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_ADMIN) })
    @PreAuthorize("hasRole('Administrator')")
    public void updateStagingProfile(@PathVariable SystemInfo.Staging profile) {
        this.info.setStagingProfile(profile.toString());
    }

    @ApiOperation(value = "Is in read-only mode?", notes = "", nickname = "isReadOnly")
    @RequestMapping(path = "/info/readonly", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public boolean isReadOnly() throws Exception {
        return ApplicationContextHolder.get().getBean(NodeInfo.class).isReadOnly();
    }

    @ApiOperation(value = "Is indexing?", notes = "", nickname = "isIndexing")
    @RequestMapping(path = "/indexing", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public boolean isIndexing(HttpServletRequest request) throws Exception {
        ApiUtils.createServiceContext(request);
        return ApplicationContextHolder.get().getBean(DataManager.class).isIndexing();
    }

    @ApiOperation(value = "Index", notes = "", nickname = "index")
    @RequestMapping(path = "/index", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT)
    @PreAuthorize("hasRole('Editor')")
    @ResponseBody
    public HttpEntity index(
            @ApiParam(value = "Drop and recreate index", required = false) @RequestParam(required = false, defaultValue = "true") boolean reset,
            @ApiParam(value = "Records having only XLinks", required = false) @RequestParam(required = false, defaultValue = "false") boolean havingXlinkOnly,
            //        @ApiParam(value = API_PARAM_RECORD_UUIDS_OR_SELECTION,
            //            required = false,
            //            example = "")
            //        @RequestParam(required = false)
            //        String[] uuids,
            @ApiParam(value = ApiParams.API_PARAM_BUCKET_NAME, required = false) @RequestParam(required = false) String bucket,
            HttpServletRequest request) throws Exception {
        ServiceContext context = ApiUtils.createServiceContext(request);
        SearchManager searchMan = ApplicationContextHolder.get().getBean(SearchManager.class);

        searchMan.rebuildIndex(context, havingXlinkOnly, reset, bucket);

        return new HttpEntity<>(HttpStatus.CREATED);
    }

    @ApiOperation(value = "Index in Elastic", notes = "", nickname = "indexes")
    @RequestMapping(path = "/index/es", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT)
    @PreAuthorize("hasRole('Editor')")
    @ResponseBody
    public HttpEntity indexEs(
            @ApiParam(value = "Drop and recreate index", required = false) @RequestParam(required = false, defaultValue = "true") boolean reset,
            @ApiParam(value = "Records having only XLinks", required = false) @RequestParam(required = false, defaultValue = "false") boolean havingXlinkOnly,
            //        @ApiParam(value = API_PARAM_RECORD_UUIDS_OR_SELECTION,
            //            required = false,
            //            example = "")
            //        @RequestParam(required = false)
            //        String[] uuids,
            @ApiParam(value = ApiParams.API_PARAM_BUCKET_NAME, required = false) @RequestParam(required = false) String bucket,
            HttpServletRequest request) throws Exception {
        ServiceContext context = ApiUtils.createServiceContext(request);
        EsSearchManager searchMan = ApplicationContextHolder.get().getBean(EsSearchManager.class);

        searchMan.rebuildIndex(context, havingXlinkOnly, reset, bucket);

        return new HttpEntity<>(HttpStatus.CREATED);
    }

    @ApiOperation(value = "Delete index in Elastic", notes = "", nickname = "deleteIndexes")
    @RequestMapping(path = "/index/es", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.DELETE)
    @PreAuthorize("hasRole('Editor')")
    @ResponseBody
    public HttpEntity deleteIndexEs() throws Exception {
        EsSearchManager searchMan = ApplicationContextHolder.get().getBean(EsSearchManager.class);
        searchMan.clearIndex();
        return new HttpEntity<>(HttpStatus.NO_CONTENT);
    }

    @ApiOperation(value = "Get build details", notes = "To know when and how this version of the application was built.", nickname = "getSystemInfo")
    @RequestMapping(path = "/info/build", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    @ApiResponses(value = { @ApiResponse(code = 200, message = "Build info.") })
    @ResponseBody
    public SystemInfo getSystemInfo() throws Exception {
        return ApplicationContextHolder.get().getBean(SystemInfo.class);
    }

    @ApiOperation(value = "Set catalog logo", notes = "Logos are stored in the data directory "
            + "resources/images/harvesting as PNG or GIF images. "
            + "When a logo is assigned to the catalog, a new "
            + "image is created in images/logos/<catalogUuid>.png.", nickname = "setLogo")
    @RequestMapping(path = "/logo", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT)
    @PreAuthorize("hasRole('UserAdmin')")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @ApiResponses(value = { @ApiResponse(code = 204, message = "Logo set."),
            @ApiResponse(code = 403, message = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_USER_ADMIN) })
    public void setLogo(@ApiParam(value = "Logo to use for the catalog") @RequestParam("file") String file,
            @ApiParam(value = "Create favicon too", required = false) @RequestParam(defaultValue = "false", required = false) boolean asFavicon

    ) throws Exception {
        ApplicationContext appContext = ApplicationContextHolder.get();
        Path logoDirectory = Resources.locateHarvesterLogosDirSMVC(appContext);

        checkFileName(file);
        FilePathChecker.verify(file);

        SettingManager settingMan = appContext.getBean(SettingManager.class);
        GeonetworkDataDirectory dataDirectory = appContext.getBean(GeonetworkDataDirectory.class);
        String nodeUuid = settingMan.getSiteId();

        try {
            Path logoFilePath = logoDirectory.resolve(file);
            Path nodeLogoDirectory = dataDirectory.getResourcesDir().resolve("images");
            if (!Files.exists(logoFilePath)) {
                logoFilePath = nodeLogoDirectory.resolve("harvesting").resolve(file);
            }
            try (InputStream inputStream = Files.newInputStream(logoFilePath)) {
                BufferedImage source = ImageIO.read(inputStream);

                if (asFavicon) {
                    ApiUtils.createFavicon(source, dataDirectory.getResourcesDir().resolve("images")
                            .resolve("logos").resolve("favicon.png"));
                } else {
                    Path logo = nodeLogoDirectory.resolve("logos").resolve(nodeUuid + ".png");
                    Path defaultLogo = nodeLogoDirectory.resolve("images").resolve("logo.png");

                    if (!file.endsWith(".png")) {
                        try (OutputStream logoOut = Files.newOutputStream(logo);
                                OutputStream defLogoOut = Files.newOutputStream(defaultLogo);) {
                            ImageIO.write(source, "png", logoOut);
                            ImageIO.write(source, "png", defLogoOut);
                        }
                    } else {
                        Files.deleteIfExists(logo);
                        IO.copyDirectoryOrFile(logoFilePath, logo, false);
                        Files.deleteIfExists(defaultLogo);
                        IO.copyDirectoryOrFile(logoFilePath, defaultLogo, false);
                    }
                }
            }
        } catch (Exception e) {
            throw new Exception(
                    "Unable to move uploaded thumbnail to destination directory. Error: " + e.getMessage());
        }
    }

    @ApiOperation(value = "Get XSL tranformations available", notes = "XSL transformations may be applied while importing or harvesting records.", nickname = "getXslTransformations")
    @RequestMapping(path = "/info/transforms", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    @ApiResponses(value = { @ApiResponse(code = 200, message = "XSLT available.") })
    @ResponseBody
    public List<String> getXslTransformations() throws Exception {
        ApplicationContext applicationContext = ApplicationContextHolder.get();
        GeonetworkDataDirectory dataDirectory = applicationContext.getBean(GeonetworkDataDirectory.class);

        try (DirectoryStream<Path> sheets = Files
                .newDirectoryStream(dataDirectory.getWebappDir().resolve(Geonet.Path.IMPORT_STYLESHEETS))) {
            List<String> list = new ArrayList<>();
            for (Path sheet : sheets) {
                String id = sheet.toString();
                if (id != null && id.endsWith(".xsl")) {
                    String name = com.google.common.io.Files
                            .getNameWithoutExtension(sheet.getFileName().toString());
                    list.add(name);
                }
            }
            return list;
        }
    }
}