diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java index 50fff6228fce..0d2ae242954a 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java @@ -86,8 +86,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; -import static org.fao.geonet.resources.Resources.DEFAULT_LOGO_EXTENSION; - public class BaseMetadataIndexer implements IMetadataIndexer, ApplicationEventPublisherAware { @Autowired @@ -456,7 +454,9 @@ public void indexMetadata(final String metadataId, } } - String logoUUID = null; + // Group logo are in the harvester folder and contains extension in file name + boolean groupLogoAdded = false; + if (groupOwner != null) { final Optional groupOpt = groupRepository.findById(groupOwner); if (groupOpt.isPresent()) { @@ -466,37 +466,29 @@ public void indexMetadata(final String metadataId, if (group.getWebsite() != null && !group.getWebsite().isEmpty() && preferGroup) { fields.put(Geonet.IndexFieldNames.GROUP_WEBSITE, group.getWebsite()); } - if (group.getLogo() != null && preferGroup) { - logoUUID = group.getLogo(); - } - } - } - - // Group logo are in the harvester folder and contains extension in file name - boolean added = false; - if (StringUtils.isNotEmpty(logoUUID)) { - final Path harvesterLogosDir = resources.locateHarvesterLogosDir(getServiceContext()); - try (Resources.ResourceHolder logo = resources.getImage(getServiceContext(), logoUUID, harvesterLogosDir)) { - if (logo != null) { - added = true; - fields.put(Geonet.IndexFieldNames.LOGO, - "/images/harvesting/" + logo.getPath().getFileName()); + if (preferGroup && StringUtils.isNotEmpty(group.getLogo())) { + final Path harvesterLogosDir = resources.locateHarvesterLogosDir(getServiceContext()); + try (Resources.ResourceHolder logo = resources.getImage(getServiceContext(), group.getLogo(), harvesterLogosDir)) { + if (logo != null) { + groupLogoAdded = true; + fields.put(Geonet.IndexFieldNames.LOGO, + "/srv/api/groups/" + group.getId() + "/logo"); + } + } } } } - // If not available, use the local catalog logo - if (!added) { + // If not available, use the local catalog or the harvester logo + if (!groupLogoAdded) { Source sourceCatalogue = sourceRepository.findOneByUuid(source); - logoUUID = - sourceCatalogue != null - && StringUtils.isNotEmpty(sourceCatalogue.getLogo()) - ? sourceCatalogue.getLogo() : source + DEFAULT_LOGO_EXTENSION; final Path logosDir = resources.locateLogosDir(getServiceContext()); - try (Resources.ResourceHolder image = resources.getImage(getServiceContext(), logoUUID, logosDir)) { - if (image != null) { - fields.put(Geonet.IndexFieldNames.LOGO, - "/images/logos/" + logoUUID); + if (sourceCatalogue != null && StringUtils.isNotEmpty(sourceCatalogue.getLogo())) { + try (Resources.ResourceHolder image = resources.getImage(getServiceContext(), sourceCatalogue.getLogo(), logosDir)) { + if (image != null) { + fields.put(Geonet.IndexFieldNames.LOGO, + "/srv/api/sources/" + sourceCatalogue.getUuid() + "/logo"); + } } } } diff --git a/core/src/main/java/org/fao/geonet/resources/FileResources.java b/core/src/main/java/org/fao/geonet/resources/FileResources.java index 5119f936d50d..3e647c84d5bf 100644 --- a/core/src/main/java/org/fao/geonet/resources/FileResources.java +++ b/core/src/main/java/org/fao/geonet/resources/FileResources.java @@ -36,7 +36,7 @@ public class FileResources extends Resources { try (DirectoryStream possibleLogos = Files.newDirectoryStream(logosDir, imageName + ".*")) { for (final Path next: possibleLogos) { String ext = FilenameUtils.getExtension(next.getFileName().toString()); - if (IMAGE_EXTENSIONS.contains(ext.toLowerCase())) { + if (IMAGE_EXTENSIONS.contains(ext.toLowerCase()) || "svg".equalsIgnoreCase(ext)) { return next; } } diff --git a/core/src/main/java/org/fao/geonet/resources/ResourceFilter.java b/core/src/main/java/org/fao/geonet/resources/ResourceFilter.java index d89883aa0e20..7f74e9c1f3f1 100644 --- a/core/src/main/java/org/fao/geonet/resources/ResourceFilter.java +++ b/core/src/main/java/org/fao/geonet/resources/ResourceFilter.java @@ -28,8 +28,10 @@ import jeeves.config.springutil.JeevesDelegatingFilterProxy; import org.fao.geonet.NodeInfo; import org.fao.geonet.domain.Pair; +import org.fao.geonet.domain.Source; import org.fao.geonet.kernel.GeonetworkDataDirectory; import org.fao.geonet.kernel.setting.SettingManager; +import org.fao.geonet.repository.SourceRepository; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.MediaType; import org.springframework.web.context.request.ServletWebRequest; @@ -40,8 +42,11 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.attribute.FileTime; +import java.util.Optional; import java.util.concurrent.ConcurrentMap; +import static com.google.common.net.MediaType.SVG_UTF_8; + /** * Servlet for serving up resources located in GeoNetwork data directory. For example, this solves a * largely historical issue because logos are hardcoded across the application to be in @@ -52,8 +57,10 @@ * User: jeichar Date: 1/17/12 Time: 4:03 PM */ public class ResourceFilter implements Filter { + public static final String DEFAULT_LOGO = "GN3.png"; private static final int FIVE_DAYS = 60 * 60 * 24 * 5; private static final int SIX_HOURS = 60 * 60 * 6; + public static final String IMAGES_LOGOS_FOLDER = "images/logos/"; private FilterConfig config; private ServletContext servletContext; private volatile Pair defaultImage; @@ -94,13 +101,17 @@ public Instance(ServletRequest request, ServletResponse response) throws IOExcep this.siteId = applicationContext.getBean(SettingManager.class).getSiteId(); this.resourcesDir = resources.locateResourcesDir(servletContext, applicationContext); this.schemaPublicationDir = applicationContext.getBean(GeonetworkDataDirectory.class).getSchemaPublicationDir(); + SourceRepository sourceRepository = applicationContext.getBean(SourceRepository.class); + this.nodeId = applicationContext.getBean(NodeInfo.class).getId(); if (defaultImage == null) { - defaultImage = resources.loadResource(resourcesDir, servletContext, appPath, "images/logos/" + siteId + ".png", new byte[0], -1); + Optional catalogues = sourceRepository.findById(this.nodeId); + String defaultImageName = IMAGES_LOGOS_FOLDER + ( + catalogues.isPresent() ? catalogues.get().getLogo() : DEFAULT_LOGO); + defaultImage = resources.loadResource(resourcesDir, servletContext, appPath, defaultImageName, new byte[0], -1); } - this.nodeId = applicationContext.getBean(NodeInfo.class).getId(); if (!faviconMap.containsKey(nodeId)) { final byte[] defaultImageBytes = defaultImage.one(); - addFavIcon(nodeId, resources.loadResource(resourcesDir, servletContext, appPath, "images/logos/" + siteId + ".ico", + addFavIcon(nodeId, resources.loadResource(resourcesDir, servletContext, appPath, IMAGES_LOGOS_FOLDER + siteId + ".ico", defaultImageBytes, -1)); } @@ -136,8 +147,8 @@ public void execute() throws IOException { httpServletResponse.setContentType(contentType); httpServletResponse.addHeader("Cache-Control", "max-age=" + SIX_HOURS + ", public"); - if (filename.equals("images/logos/" + siteId + ".ico")) { - favicon = resources.loadResource(resourcesDir, servletContext, appPath, "images/logos/" + siteId + ".ico", favicon.one(), + if (filename.equals(IMAGES_LOGOS_FOLDER + siteId + ".ico")) { + favicon = resources.loadResource(resourcesDir, servletContext, appPath, IMAGES_LOGOS_FOLDER + siteId + ".ico", favicon.one(), favicon.two()); addFavIcon(nodeId, favicon); @@ -186,6 +197,8 @@ private String extensionToMediaType(String ext) { contentType = MediaType.APPLICATION_XML_VALUE; } else if(ext.equals("txt")) { contentType = MediaType.TEXT_PLAIN_VALUE; + } else if (ext.equals("svg")) { + contentType = SVG_UTF_8.toString(); } else { contentType = "image/" + ext; } diff --git a/core/src/main/java/org/fao/geonet/resources/Resources.java b/core/src/main/java/org/fao/geonet/resources/Resources.java index 157877c9ee2c..1bec918fbcc5 100644 --- a/core/src/main/java/org/fao/geonet/resources/Resources.java +++ b/core/src/main/java/org/fao/geonet/resources/Resources.java @@ -62,7 +62,6 @@ */ public abstract class Resources { public static final String BLANK_LOGO = "blank.png"; - public static final String DEFAULT_LOGO_EXTENSION = ".png"; protected final static Set IMAGE_READ_SUFFIXES; protected final static Set IMAGE_WRITE_SUFFIXES; diff --git a/schemas/iso19115-3.2018/src/test/resources/UpperRhineCastles-ISO19115-3-full-view.html b/schemas/iso19115-3.2018/src/test/resources/UpperRhineCastles-ISO19115-3-full-view.html index b57a8a60484a..ccccbbcffd95 100644 --- a/schemas/iso19115-3.2018/src/test/resources/UpperRhineCastles-ISO19115-3-full-view.html +++ b/schemas/iso19115-3.2018/src/test/resources/UpperRhineCastles-ISO19115-3-full-view.html @@ -848,7 +848,7 @@

- +
diff --git a/schemas/iso19115-3.2018/src/test/resources/two-lineages-and-constraints-ISO19115-3-full-view.html b/schemas/iso19115-3.2018/src/test/resources/two-lineages-and-constraints-ISO19115-3-full-view.html index 08eec6f455cb..0d6dcd085fc4 100644 --- a/schemas/iso19115-3.2018/src/test/resources/two-lineages-and-constraints-ISO19115-3-full-view.html +++ b/schemas/iso19115-3.2018/src/test/resources/two-lineages-and-constraints-ISO19115-3-full-view.html @@ -268,7 +268,7 @@

- +
diff --git a/services/src/main/java/org/fao/geonet/api/LogoUtils.java b/services/src/main/java/org/fao/geonet/api/LogoUtils.java new file mode 100644 index 000000000000..58c736cb1232 --- /dev/null +++ b/services/src/main/java/org/fao/geonet/api/LogoUtils.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2001-2026 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; + +import jeeves.server.context.ServiceContext; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.fao.geonet.api.records.attachments.AttachmentsApi; +import org.fao.geonet.resources.Resources; +import org.springframework.web.context.request.WebRequest; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; + +public final class LogoUtils { + public static final String API_GET_LOGO_NOTE = "If last-modified header " + + "is present it is used to check if the logo has been modified since " + + "the header date. If it hasn't been modified returns an empty 304 Not" + + " Modified response. If modified returns the image. If there is " + + "no logo then returns a transparent 1x1 px PNG image."; + + private static final int SIX_HOURS = 60 * 60 * 6; + + private static final String TRANSPARENT_1_X_1_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII"; + + private static final byte[] TRANSPARENT_1_X_1_PNG = org.apache.commons.codec.binary.Base64.decodeBase64(TRANSPARENT_1_X_1_PNG_BASE64); + + private LogoUtils() { + } + + public static Resources.ResourceHolder getImage(Resources resources, + ServiceContext serviceContext, + String logoRef) throws IOException { + final Path logosDir = resources.locateLogosDir(serviceContext); + final Path harvesterLogosDir = resources.locateHarvesterLogosDir(serviceContext); + Resources.ResourceHolder image = null; + if (isLocalLogoRef(logoRef)) { + image = resources.getImage(serviceContext, logoRef, logosDir); + if (image == null) { + image = resources.getImage(serviceContext, logoRef, harvesterLogosDir); + } + } + return image; + } + + public static void writeImageOrTransparentLogo(WebRequest webRequest, + HttpServletResponse response, + Resources.ResourceHolder image) throws IOException { + if (image != null) { + FileTime lastModifiedTime = image.getLastModifiedTime(); + response.setDateHeader("Expires", System.currentTimeMillis() + SIX_HOURS * 1000L); + if (webRequest.checkNotModified(lastModifiedTime.toMillis())) { + return; + } + response.setContentType(AttachmentsApi.getFileContentType(image.getPath().getFileName().toString())); + response.setContentLength((int) Files.size(image.getPath())); + response.addHeader("Cache-Control", "max-age=" + SIX_HOURS + ", public"); + FileUtils.copyFile(image.getPath().toFile(), response.getOutputStream()); + return; + } + + if (webRequest.checkNotModified(0L)) { + return; + } + response.setContentType("image/png"); + response.setContentLength(TRANSPARENT_1_X_1_PNG.length); + response.addHeader("Cache-Control", "max-age=" + SIX_HOURS + ", public"); + response.getOutputStream().write(TRANSPARENT_1_X_1_PNG); + } + + private static boolean isLocalLogoRef(String logoRef) { + return StringUtils.isNotBlank(logoRef) + && !logoRef.startsWith("http://") + && !logoRef.startsWith("https://") + && !logoRef.startsWith("https//"); + } +} diff --git a/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java b/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java index 0add43272b26..4f96d8961c3d 100644 --- a/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java +++ b/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java @@ -31,19 +31,16 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.regex.Pattern; import jeeves.server.UserSession; import jeeves.server.context.ServiceContext; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.api.API; import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.ApiUtils; +import org.fao.geonet.api.LogoUtils; import org.fao.geonet.api.exception.NotAllowedException; import org.fao.geonet.api.exception.ResourceNotFoundException; -import org.fao.geonet.api.records.attachments.AttachmentsApi; import org.fao.geonet.api.tools.i18n.LanguageUtils; import org.fao.geonet.api.tools.i18n.TranslationPackBuilder; import org.fao.geonet.constants.Geonet; @@ -77,14 +74,13 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; import java.sql.SQLException; import java.util.*; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.fao.geonet.api.LogoUtils.API_GET_LOGO_NOTE; import static org.springframework.data.jpa.domain.Specification.where; @RequestMapping(value = { @@ -111,27 +107,6 @@ public class GroupsApi { public static final int GROUPNAME_MAX_LENGHT = 32; - /** - * API logo note. - */ - private static final String API_GET_LOGO_NOTE = "If last-modified header " - + "is present it is used to check if the logo has been modified since " - + "the header date. If it hasn't been modified returns an empty 304 Not" - + " Modified response. If modified returns the image. If the group has " - + "no logo then returns a transparent 1x1 px PNG image."; - /** - * Six hours in seconds. - */ - private static final int SIX_HOURS = 60 * 60 * 6; - /** - * Transparent 1x1 px PNG encoded in Base64. - */ - private static final String TRANSPARENT_1_X_1_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR" - + "42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=="; - /** - * Transparent 1x1 px PNG. - */ - private static final byte[] TRANSPARENT_1_X_1_PNG = org.apache.commons.codec.binary.Base64.decodeBase64(TRANSPARENT_1_X_1_PNG_BASE64); /** * Message source. */ @@ -169,20 +144,6 @@ public class GroupsApi { @Autowired private PageRepository pageRepository; - private static Resources.ResourceHolder getImage(Resources resources, ServiceContext serviceContext, Group group) throws IOException { - final Path logosDir = resources.locateLogosDir(serviceContext); - final Path harvesterLogosDir = resources.locateHarvesterLogosDir(serviceContext); - final String logoUUID = group.getLogo(); - Resources.ResourceHolder image = null; - if (StringUtils.isNotBlank(logoUUID) && !logoUUID.startsWith("http://") && !logoUUID.startsWith("https//")) { - image = resources.getImage(serviceContext, logoUUID, logosDir); - if (image == null) { - image = resources.getImage(serviceContext, logoUUID, harvesterLogosDir); - } - } - return image; - } - /** * Writes the group logo image to the response. If no image is found, it * writes a 1x1 transparent PNG. If the request contains cache-related @@ -220,35 +181,9 @@ public void getGroupLogo( } try { final Resources resources = context.getBean(Resources.class); - final String logoUUID = group.get().getLogo(); - if (StringUtils.isNotBlank(logoUUID) && !logoUUID.startsWith("http://") && !logoUUID.startsWith("https//")) { - try (Resources.ResourceHolder image = getImage(resources, serviceContext, group.get())) { - if (image != null) { - FileTime lastModifiedTime = image.getLastModifiedTime(); - response.setDateHeader("Expires", System.currentTimeMillis() + SIX_HOURS * 1000L); - if (webRequest.checkNotModified(lastModifiedTime.toMillis())) { - // webRequest.checkNotModified sets the right HTTP headers - return; - } - response.setContentType(AttachmentsApi.getFileContentType(image.getPath().getFileName().toString())); - response.setContentLength((int) Files.size(image.getPath())); - response.addHeader("Cache-Control", "max-age=" + SIX_HOURS + ", public"); - FileUtils.copyFile(image.getPath().toFile(), response.getOutputStream()); - return; - } - } - } - - // no logo image found. Return a transparent 1x1 png - FileTime lastModifiedTime = FileTime.fromMillis(0); - if (webRequest.checkNotModified(lastModifiedTime.toMillis())) { - return; + try (Resources.ResourceHolder image = LogoUtils.getImage(resources, serviceContext, group.get().getLogo())) { + LogoUtils.writeImageOrTransparentLogo(webRequest, response, image); } - response.setContentType("image/png"); - response.setContentLength(TRANSPARENT_1_X_1_PNG.length); - response.addHeader("Cache-Control", "max-age=" + SIX_HOURS + ", public"); - response.getOutputStream().write(TRANSPARENT_1_X_1_PNG); - } catch (IOException e) { Log.error(LOGGER, String.format("There was an error accessing the logo of the group with id '%d'", groupId)); diff --git a/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java b/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java index 58d434445f9f..de72d0c13e26 100644 --- a/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java +++ b/services/src/main/java/org/fao/geonet/api/harvesting/HarvestersApi.java @@ -34,14 +34,10 @@ import org.fao.geonet.api.ApiUtils; import org.fao.geonet.api.exception.NoResultsFoundException; import org.fao.geonet.api.exception.ResourceNotFoundException; -import org.fao.geonet.domain.AbstractMetadata; -import org.fao.geonet.domain.HarvestHistory; -import org.fao.geonet.domain.ISODate; -import org.fao.geonet.domain.Source; +import org.fao.geonet.domain.*; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; -import org.fao.geonet.kernel.harvest.Common; import org.fao.geonet.kernel.harvest.HarvestManager; import org.fao.geonet.kernel.harvest.harvester.AbstractHarvester; import org.fao.geonet.repository.HarvestHistoryRepository; @@ -54,14 +50,11 @@ import org.springframework.http.ResponseEntity; 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 org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -173,6 +166,34 @@ public HttpEntity assignHarvestedRecordToSource( return new HttpEntity<>(HttpStatus.NO_CONTENT); } + @io.swagger.v3.oas.annotations.Operation( + summary = "Get harvester logo image.", + description = "Redirect to source logo endpoint." + ) + @GetMapping( + value = "/{harvesterUuid}/logo" + ) + @ResponseStatus(value = HttpStatus.FOUND) + public void getHarvesterLogo( + @PathVariable + String portal, + @Parameter( + description = "The harvester UUID" + ) + @PathVariable + String harvesterUuid, + HttpServletResponse response + ) throws ResourceNotFoundException, IOException { + Source source = sourceRepository.findOneByUuid(harvesterUuid); + if (source == null || source.getType() != SourceType.harvester) { + throw new ResourceNotFoundException(String.format( + "Harvester with UUID '%s' not found.", + harvesterUuid)); + } + + response.sendRedirect("/" + portal + "/api/sources/" + harvesterUuid + "/logo"); + } + @io.swagger.v3.oas.annotations.Operation( summary = "Reindexes all records of an harvester", diff --git a/services/src/main/java/org/fao/geonet/api/site/LogosApi.java b/services/src/main/java/org/fao/geonet/api/site/LogosApi.java index 872751d99e48..d3941354a31f 100644 --- a/services/src/main/java/org/fao/geonet/api/site/LogosApi.java +++ b/services/src/main/java/org/fao/geonet/api/site/LogosApi.java @@ -37,7 +37,6 @@ import org.fao.geonet.api.exception.ResourceAlreadyExistException; import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.domain.Group; -import org.fao.geonet.kernel.GeonetworkDataDirectory; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.repository.GroupRepository; import org.fao.geonet.resources.Resources; @@ -71,10 +70,8 @@ description = "Logos operations") @Controller("siteLogos") public class LogosApi { - private static final String[] iconExt = {".gif", ".png", ".jpg", ".jpeg"}; + private static final String[] iconExt = {".gif", ".png", ".jpg", ".jpeg", ".svg"}; - @Autowired - GeonetworkDataDirectory dataDirectory; @Autowired SettingManager settingManager; diff --git a/services/src/main/java/org/fao/geonet/api/site/SiteApi.java b/services/src/main/java/org/fao/geonet/api/site/SiteApi.java index ecb23ec77945..67b446c35a82 100644 --- a/services/src/main/java/org/fao/geonet/api/site/SiteApi.java +++ b/services/src/main/java/org/fao/geonet/api/site/SiteApi.java @@ -41,18 +41,24 @@ import jeeves.server.context.ServiceContext; import jeeves.xlink.Processor; import org.apache.commons.lang3.StringUtils; -import org.fao.geonet.*; +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.ApiParams; import org.fao.geonet.api.ApiUtils; +import org.fao.geonet.api.LogoUtils; import org.fao.geonet.api.OpenApiConfig; import org.fao.geonet.api.exception.FeatureNotEnabledException; import org.fao.geonet.api.exception.NotAllowedException; +import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.api.site.model.SettingSet; import org.fao.geonet.api.site.model.SettingsListResponse; import org.fao.geonet.api.tools.i18n.LanguageUtils; import org.fao.geonet.api.users.recaptcha.RecaptchaChecker; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.*; +import org.fao.geonet.exceptions.BadParameterEx; import org.fao.geonet.exceptions.OperationAbortedEx; import org.fao.geonet.index.Status; import org.fao.geonet.index.es.EsRestClient; @@ -88,16 +94,15 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.WebRequest; -import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.awt.image.BufferedImage; import java.io.*; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.*; import java.util.stream.Collectors; @@ -813,9 +818,9 @@ public ProxyConfiguration getProxyConfiguration( @io.swagger.v3.oas.annotations.Operation( summary = "Set catalog logo", description = "Logos are stored in the data directory " + - "resources/images/harvesting as PNG or GIF images. " + + "`resources/images/harvesting` images. " + "When a logo is assigned to the catalog, a new " + - "image is created in images/logos/.png.") + "image is created in `images/logos/.`.") @RequestMapping( path = "/logo", produces = MediaType.APPLICATION_JSON_VALUE, @@ -852,51 +857,85 @@ public void setLogo( String nodeUuid = settingManager.getSiteId(); - Resources.ResourceHolder holder = resources.getImage(serviceContext, file, logoDirectory); - final Path resourcesDir = - resources.locateResourcesDir(request.getServletContext(), serviceContext.getApplicationContext()); - if (holder == null || holder.getPath() == null) { - holder = resources.getImage(serviceContext, "images/harvesting/" + file, resourcesDir); + Optional siteSourceOpt = sourceRepository.findById(nodeUuid); + if (siteSourceOpt.isEmpty()) { + throw new ResourceNotFoundException("Unable to find main catalog in source table with uuid '" + nodeUuid + "'."); } - try { - try (InputStream inputStream = Files.newInputStream(holder.getPath())) { - BufferedImage source = ImageIO.read(inputStream); + Source siteSource = siteSourceOpt.get(); + if (siteSource.getType() != SourceType.portal) { + throw new BadParameterEx("Source with uuid '" + nodeUuid + "' is not of the type portal (main catalogue)."); + } + boolean isSvg = file.endsWith(".svg"); + + if (asFavicon) { + if (isSvg) { + throw new IllegalArgumentException("SVG logo can not be used to generate favicon."); + } + + Resources.ResourceHolder holder = resources.getImage(serviceContext, file, logoDirectory); + final Path resourcesDir = + resources.locateResourcesDir(request.getServletContext(), serviceContext.getApplicationContext()); - if (asFavicon) { + if (holder == null || holder.getPath() == null) { + holder = resources.getImage(serviceContext, "images/harvesting/" + file, resourcesDir); + } + + if (holder == null || holder.getPath() == null) { + throw new FileNotFoundException("Logo file '" + file + "' not found in resources."); + } + + try { + + try (InputStream inputStream = Files.newInputStream(holder.getPath())) { + java.awt.image.BufferedImage source = javax.imageio.ImageIO.read(inputStream); try (Resources.ResourceHolder favicon = resources.getWritableImage(serviceContext, "images/logos/favicon.png", resourcesDir)) { ApiUtils.createFavicon(source, favicon.getPath()); } - } else { - try (Resources.ResourceHolder logo = - resources.getWritableImage(serviceContext, - "images/logos/" + nodeUuid + ".png", - resourcesDir); - Resources.ResourceHolder defaultLogo = - resources.getWritableImage(serviceContext, - "images/logo.png", resourcesDir)) { - if (!file.endsWith(".png")) { - try ( - OutputStream logoOut = Files.newOutputStream(logo.getPath()); - OutputStream defLogoOut = Files.newOutputStream(defaultLogo.getPath()) - ) { - ImageIO.write(source, "png", logoOut); - ImageIO.write(source, "png", defLogoOut); - } - } else { - Files.copy(holder.getPath(), logo.getPath(), StandardCopyOption.REPLACE_EXISTING); - Files.copy(holder.getPath(), defaultLogo.getPath(), - StandardCopyOption.REPLACE_EXISTING); - } - } } + } finally { + holder.close(); } - } catch (Exception e) { - throw new Exception( - "Unable to move uploaded thumbnail to destination directory. Error: " + e.getMessage()); - } finally { - holder.close(); + } else { + String actualLogo = siteSource.getLogo(); + if (StringUtils.isNotEmpty(actualLogo)) { + resources.deleteImageIfExists(actualLogo, dataDirectory.getResourcesDir().resolve("images").resolve("logos")); + } + String logoFile = resources.copyLogo(serviceContext, + "images" + File.separator + "harvesting" + File.separator + file, nodeUuid); + + siteSource.setLogo(logoFile); + sourceRepository.save(siteSource); + } + } + + @io.swagger.v3.oas.annotations.Operation( + summary = "Get catalog logo image.", + description = LogoUtils.API_GET_LOGO_NOTE) + @GetMapping( + path = "/logo") + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public void getLogo( + @Parameter(hidden = true) WebRequest webRequest, + HttpServletRequest request, + HttpServletResponse response + ) { + ServiceContext serviceContext = ApiUtils.createServiceContext(request); + final Resources resources = serviceContext.getBean(Resources.class); + final String siteUuid = settingManager.getSiteId(); + final String logoRef = sourceRepository.findById(siteUuid) + .map(Source::getLogo) + .filter(StringUtils::isNotBlank) + .orElse(siteUuid); + + try (Resources.ResourceHolder image = LogoUtils.getImage(resources, serviceContext, logoRef)) { + LogoUtils.writeImageOrTransparentLogo(webRequest, response, image); + } catch (IOException e) { + Log.error(Geonet.GEONETWORK + ".api.site", + String.format("There was an error accessing the logo for site uuid '%s'", siteUuid)); + throw new RuntimeException(e); } } diff --git a/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java b/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java index 9edd5a11f274..2a374f7f2fd6 100644 --- a/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java +++ b/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java @@ -37,13 +37,16 @@ import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.ApiUtils; import org.fao.geonet.api.exception.ResourceNotFoundException; +import org.fao.geonet.api.LogoUtils; import org.fao.geonet.api.tools.i18n.TranslationPackBuilder; +import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.*; import org.fao.geonet.guiapi.search.XsltResponseWriter; import org.fao.geonet.repository.LanguageRepository; import org.fao.geonet.repository.SortUtils; import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.resources.Resources; +import org.fao.geonet.utils.Log; import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; @@ -53,6 +56,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.WebRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -73,6 +77,7 @@ description = "Source catalogue operations") @Controller("sources") public class SourcesApi { + private static final String LOGGER = Geonet.GEONETWORK + ".api.sources"; @Autowired SourceRepository sourceRepository; @@ -133,7 +138,7 @@ public ResponseEntity getSources( if (sources == null) { return ResponseEntity.notFound().build(); } - String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT))?MediaType.APPLICATION_JSON_VALUE:request.getHeader(HttpHeaders.ACCEPT); + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT)) ? MediaType.APPLICATION_JSON_VALUE : request.getHeader(HttpHeaders.ACCEPT); if (acceptHeader.contains(MediaType.TEXT_HTML_VALUE)) { return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(getSourcesAsHtml(sources)); } else { @@ -145,12 +150,12 @@ private String getSourcesAsHtml(List sources) throws Exception { Element sourcesList = new Element("sources"); sources.stream().map(GeonetEntity::asXml).forEach(sourcesList::addContent); return new XsltResponseWriter(null, "portal") - .withJson("catalog/locales/en-core.json") - .withJson("catalog/locales/en-search.json") - .withXml(sourcesList) - .withParam("cssClass", "gn-portal") - .withXsl("xslt/ui-search/portal-list.xsl") - .asHtml(); + .withJson("catalog/locales/en-core.json") + .withJson("catalog/locales/en-search.json") + .withXml(sourcesList) + .withParam("cssClass", "gn-portal") + .withXsl("xslt/ui-search/portal-list.xsl") + .asHtml(); } @io.swagger.v3.oas.annotations.Operation( @@ -185,6 +190,49 @@ public Source getSource( } } + @io.swagger.v3.oas.annotations.Operation( + summary = "Get source logo image.", + description = LogoUtils.API_GET_LOGO_NOTE) + @GetMapping( + value = "/{sourceIdentifier}/logo") + @ResponseStatus(HttpStatus.OK) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Logo returned."), + @ApiResponse(responseCode = "404", description = "Source not found.", + content = @Content(schema = @Schema(implementation = ApiError.class))) + }) + @ResponseBody + public void getSourceLogo( + @Parameter( + description = "Source identifier", + required = true + ) + @PathVariable + String sourceIdentifier, + @Parameter(hidden = true) WebRequest webRequest, + HttpServletRequest request, + HttpServletResponse response + ) throws ResourceNotFoundException { + Optional source = sourceRepository.findById(sourceIdentifier); + if (source.isEmpty()) { + throw new ResourceNotFoundException(String.format( + "Source with uuid '%s' does not exist.", + sourceIdentifier + )); + } + + ServiceContext serviceContext = ApiUtils.createServiceContext(request); + Resources resources = serviceContext.getBean(Resources.class); + String logoRef = getLogoReference(source.get()); + try (Resources.ResourceHolder image = LogoUtils.getImage(resources, serviceContext, logoRef)) { + LogoUtils.writeImageOrTransparentLogo(webRequest, response, image); + } catch (IOException e) { + Log.error(LOGGER, String.format("There was an error accessing the logo of the source with id '%s'", + sourceIdentifier)); + throw new RuntimeException(e); + } + } + @io.swagger.v3.oas.annotations.Operation( summary = "Add a source", description = "") @@ -366,4 +414,13 @@ private void updateSource(String sourceIdentifier, }); } + + private String getLogoReference(Source source) { + if (source.getType() == SourceType.portal + || source.getType() == SourceType.subportal + || source.getType() == SourceType.harvester) { + return source.getLogo(); + } + return StringUtils.defaultIfBlank(source.getLogo(), source.getUuid()); + } } diff --git a/services/src/main/java/org/fao/geonet/util/FileMimetypeChecker.java b/services/src/main/java/org/fao/geonet/util/FileMimetypeChecker.java index 8a1a0674690f..00af11b3b0a2 100644 --- a/services/src/main/java/org/fao/geonet/util/FileMimetypeChecker.java +++ b/services/src/main/java/org/fao/geonet/util/FileMimetypeChecker.java @@ -32,7 +32,7 @@ @Component public class FileMimetypeChecker { - private static final String[] validImagesMimeTypes = {"image/gif", "image/png", "image/jpeg"}; + private static final String[] validImagesMimeTypes = {"image/gif", "image/png", "image/jpeg", "image/svg+xml"}; // application/xml --> SDMX files, application/rdf+xml --> RDF, OWL files private static final String[] validThesaurusMimeTypes = {"application/xml", "application/rdf+xml"}; diff --git a/services/src/test/java/org/fao/geonet/api/harvesting/HarvestersApiTest.java b/services/src/test/java/org/fao/geonet/api/harvesting/HarvestersApiTest.java new file mode 100644 index 000000000000..1df53941dcc5 --- /dev/null +++ b/services/src/test/java/org/fao/geonet/api/harvesting/HarvestersApiTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2001-2026 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.harvesting; + +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.Source; +import org.fao.geonet.domain.SourceType; +import org.fao.geonet.repository.SourceRepository; +import org.fao.geonet.services.AbstractServiceIntegrationTest; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.junit.Assert.assertNotNull; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class HarvestersApiTest extends AbstractServiceIntegrationTest { + @Autowired + private WebApplicationContext wac; + + @Autowired + private SourceRepository sourceRepo; + + private MockMvc mockMvc; + + @Before + public void setUp() { + createTestData(); + } + + @Test + public void getHarvesterLogoRedirectsToSourceLogo() throws Exception { + Source source = sourceRepo.findOneByName("harvester-source"); + assertNotNull(source); + + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + this.mockMvc.perform(get("/srv/api/harvesters/" + source.getUuid() + "/logo")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/srv/api/sources/" + source.getUuid() + "/logo")); + } + + @Test + public void getHarvesterLogoWithNonExistingHarvester() throws Exception { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + this.mockMvc.perform(get("/srv/api/harvesters/not-an-existing-harvester/logo")) + .andExpect(status().isNotFound()); + } + + private void createTestData() { + Source source = new Source(); + source.setName("harvester-source"); + source.setUuid("harvester-source-uuid"); + source.setCreationDate(new ISODate()); + source.setType(SourceType.harvester); + sourceRepo.save(source); + } +} diff --git a/services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java b/services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java index 73130398d92f..b489f5bbbaa5 100644 --- a/services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java @@ -84,7 +84,6 @@ public void whenNotGeneratingPdfWithPropertySetSiteLogoIsUsed() throws Exception MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); MockHttpSession mockHttpSession = loginAsAdmin(); settingManager.setValue("metadata/pdfReport/headerLogoFileName", "pdf_test_banner_to_use.png"); - String siteId = settingManager.getValue("system/site/siteId"); String url = "/srv/api/records/" + metadata.getUuid() + "/formatters/xsl-view?language=fre"; mockMvc.perform(get(url) @@ -96,7 +95,7 @@ public void whenNotGeneratingPdfWithPropertySetSiteLogoIsUsed() throws Exception ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); Mockito.verify(responseWriterSpy).writeOutResponse(any(ServiceContext.class), any(String.class), any(String.class), any(HttpServletResponse.class), any(FormatType.class), captor.capture()); assertFalse(new String(captor.getValue(), StandardCharsets.UTF_8).contains("pdf_test_banner_to_use.png")); - assertTrue(new String(captor.getValue(), StandardCharsets.UTF_8).contains("images/logos/" + siteId + ".png")); + assertTrue(new String(captor.getValue(), StandardCharsets.UTF_8).contains("srv/api/sources/" + metadata.getSourceInfo().getSourceId() + "/logo")); } @Test @@ -105,7 +104,6 @@ public void whenGeneratingPdfWithPropertyNotSetSiteLogoIsUsed() throws Exception MockHttpSession mockHttpSession = loginAsAdmin(); Optional se = settingRepository.findById("metadata/pdfReport/headerLogoFileName"); se.ifPresent(settingRepository::delete); - String siteId = settingManager.getValue("system/site/siteId"); String url = "/srv/api/records/" + metadata.getUuid() + "/formatters/xsl-view?output=pdf&language=fre"; mockMvc.perform(get(url) @@ -117,6 +115,6 @@ public void whenGeneratingPdfWithPropertyNotSetSiteLogoIsUsed() throws Exception ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); Mockito.verify(responseWriterSpy).writeOutResponse(any(ServiceContext.class), any(String.class), any(String.class), any(HttpServletResponse.class), any(FormatType.class), captor.capture()); assertFalse(new String(captor.getValue(), StandardCharsets.UTF_8).contains("pdf_test_banner_to_use.png")); - assertTrue(new String(captor.getValue(), StandardCharsets.UTF_8).contains("images/logos/" + siteId + ".png")); + assertTrue(new String(captor.getValue(), StandardCharsets.UTF_8).contains("srv/api/sources/" + metadata.getSourceInfo().getSourceId() + "/logo")); } } diff --git a/services/src/test/java/org/fao/geonet/api/sources/SourcesApiTest.java b/services/src/test/java/org/fao/geonet/api/sources/SourcesApiTest.java index 9e7ca4b79775..21c7f189766b 100644 --- a/services/src/test/java/org/fao/geonet/api/sources/SourcesApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/sources/SourcesApiTest.java @@ -24,7 +24,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import junit.framework.Assert; import org.fao.geonet.api.FieldNameExclusionStrategy; import org.fao.geonet.api.JsonFieldNamingStrategy; import org.fao.geonet.domain.ISODate; @@ -44,6 +43,8 @@ import java.util.UUID; import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -73,7 +74,7 @@ public void setUp() throws Exception { @Test public void getSource() throws Exception { Source source = sourceRepo.findOneByName("source-test"); - Assert.assertNotNull(source); + assertNotNull(source); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -86,10 +87,28 @@ public void getSource() throws Exception { .andExpect(content().contentType(API_JSON_EXPECTED_ENCODING)); } + @Test + public void getSourceLogo() throws Exception { + Source source = sourceRepo.findOneByName("source-test"); + assertNotNull(source); + + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + this.mockMvc.perform(get("/srv/api/sources/" + source.getUuid() + "/logo")) + .andExpect(status().isOk()) + .andExpect(content().contentType(API_PNG_EXPECTED_ENCODING)); + } + + @Test + public void getNonExistingSourceLogo() throws Exception { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + this.mockMvc.perform(get("/srv/api/sources/source-test-2/logo")) + .andExpect(status().is(404)); + } + @Test public void getNonExistingSource() throws Exception { Source sourceToUpdate = sourceRepo.findOneByName("source-test-2"); - Assert.assertNull(sourceToUpdate); + assertNull(sourceToUpdate); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -117,7 +136,7 @@ public void getSources() throws Exception { @Test public void updateSource() throws Exception { Source source = sourceRepo.findOneByName("source-test"); - Assert.assertNotNull(source); + assertNotNull(source); source.setName("source-test-updated"); @@ -142,7 +161,7 @@ public void updateSource() throws Exception { @Test public void updateNonExistingSource() throws Exception { Source sourceToUpdate = sourceRepo.findOneByName("source-test-2"); - Assert.assertNull(sourceToUpdate); + assertNull(sourceToUpdate); sourceToUpdate = new Source(); sourceToUpdate.setName("source-test-2"); diff --git a/web-ui/src/main/resources/catalog/components/admin/harvester/HarvesterDirective.js b/web-ui/src/main/resources/catalog/components/admin/harvester/HarvesterDirective.js index 26ee2d40a6aa..fac053d5795f 100644 --- a/web-ui/src/main/resources/catalog/components/admin/harvester/HarvesterDirective.js +++ b/web-ui/src/main/resources/catalog/components/admin/harvester/HarvesterDirective.js @@ -61,11 +61,9 @@ } $("#translationModal").modal("show"); }; - $http - .get("admin.harvester.info?type=icons&_content_type=json", { cache: true }) - .then(function (response) { - scope.icons = response.data[0]; - }); + $http.get("../api/logos", { cache: true }).then(function (response) { + scope.icons = response.data; + }); $http .get("info?_content_type=json&type=languages", { cache: true }) .then(function (response) { @@ -338,11 +336,9 @@ "../../catalog/components/admin/harvester/partials/" + "logopicker.html", link: function (scope, element, attrs) { - $http - .get("admin.harvester.info?type=icons&_content_type=json", { cache: true }) - .then(function (response) { - scope.icons = response.data[0]; - }); + $http.get("../api/logos", { cache: true }).then(function (response) { + scope.icons = response.data; + }); /** * Set the icon diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/partials/metadatagroupupdater.html b/web-ui/src/main/resources/catalog/components/metadataactions/partials/metadatagroupupdater.html index 0864bdb6e1db..837c899d9678 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/partials/metadatagroupupdater.html +++ b/web-ui/src/main/resources/catalog/components/metadataactions/partials/metadatagroupupdater.html @@ -34,7 +34,7 @@ {{g.label[lang]}} diff --git a/web-ui/src/main/resources/catalog/components/toolbar/partials/menu-sitename.html b/web-ui/src/main/resources/catalog/components/toolbar/partials/menu-sitename.html index 35c0e52d428f..de40ee7a77f0 100644 --- a/web-ui/src/main/resources/catalog/components/toolbar/partials/menu-sitename.html +++ b/web-ui/src/main/resources/catalog/components/toolbar/partials/menu-sitename.html @@ -1,10 +1,13 @@
diff --git a/web/src/main/webapp/xslt/services/pdf/metadata-fop.xsl b/web/src/main/webapp/xslt/services/pdf/metadata-fop.xsl index de3a89c56c3a..10f14be50485 100644 --- a/web/src/main/webapp/xslt/services/pdf/metadata-fop.xsl +++ b/web/src/main/webapp/xslt/services/pdf/metadata-fop.xsl @@ -323,7 +323,7 @@ url('')" diff --git a/web/src/main/webapp/xslt/skin/default/skin.xsl b/web/src/main/webapp/xslt/skin/default/skin.xsl index 0d7bd763a79b..a0c5646be6c5 100644 --- a/web/src/main/webapp/xslt/skin/default/skin.xsl +++ b/web/src/main/webapp/xslt/skin/default/skin.xsl @@ -84,7 +84,7 @@ + src="{/root/gui/nodeUrl}api/site/logo"/> diff --git a/web/src/main/webapp/xslt/ui-search/portal-list.xsl b/web/src/main/webapp/xslt/ui-search/portal-list.xsl index a08619389bf5..f2b452eab87b 100644 --- a/web/src/main/webapp/xslt/ui-search/portal-list.xsl +++ b/web/src/main/webapp/xslt/ui-search/portal-list.xsl @@ -48,7 +48,7 @@