Skip to content

Commit 208a7f5

Browse files
authored
Merge branch 'main' into public_permalink_to_private_md
2 parents 57295d3 + 5f6571e commit 208a7f5

File tree

37 files changed

+1123
-129
lines changed

37 files changed

+1123
-129
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (C) 2001-2026 Food and Agriculture Organization of the
3+
* United Nations (FAO-UN), United Nations World Food Programme (WFP)
4+
* and United Nations Environment Programme (UNEP)
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or (at
9+
* your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but
12+
* WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19+
*
20+
* Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
21+
* Rome - Italy. email: geonetwork@osgeo.org
22+
*/
23+
24+
package org.fao.geonet.api.exception;
25+
26+
import java.util.Locale;
27+
28+
import org.fao.geonet.exceptions.LocalizedException;
29+
30+
/**
31+
* Exception thrown when the total size of attachments exceeds the configured limit
32+
* during metadata export operations.
33+
*/
34+
public class AttachmentsExportLimitExceededException extends LocalizedException {
35+
36+
public AttachmentsExportLimitExceededException() {
37+
super();
38+
}
39+
40+
public AttachmentsExportLimitExceededException(String message) {
41+
super(message);
42+
}
43+
44+
public AttachmentsExportLimitExceededException(String message, Throwable cause) {
45+
super(message, cause);
46+
}
47+
48+
public AttachmentsExportLimitExceededException(Throwable cause) {
49+
super(cause);
50+
}
51+
52+
protected String getResourceBundleBeanQualifier() {
53+
return "apiMessages";
54+
}
55+
56+
@Override
57+
public AttachmentsExportLimitExceededException withMessageKey(String messageKey) {
58+
super.withMessageKey(messageKey);
59+
return this;
60+
}
61+
62+
@Override
63+
public AttachmentsExportLimitExceededException withMessageKey(String messageKey, Object[] messageKeyArgs) {
64+
super.withMessageKey(messageKey, messageKeyArgs);
65+
return this;
66+
}
67+
68+
@Override
69+
public AttachmentsExportLimitExceededException withDescriptionKey(String descriptionKey) {
70+
super.withDescriptionKey(descriptionKey);
71+
return this;
72+
}
73+
74+
@Override
75+
public AttachmentsExportLimitExceededException withDescriptionKey(String descriptionKey, Object[] descriptionKeyArgs) {
76+
super.withDescriptionKey(descriptionKey, descriptionKeyArgs);
77+
return this;
78+
}
79+
80+
@Override
81+
public AttachmentsExportLimitExceededException withLocale(Locale locale) {
82+
super.withLocale(locale);
83+
return this;
84+
}
85+
}
86+

core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public String apply(@Nullable Metadata input) {
142142
boolean removeXlinkAttribute = false;
143143
boolean skipOnError = true;
144144
srcFile = MEFLib.doMEF2Export(serviceContext, new HashSet<>(uuids), format, false, stylePath,
145-
resolveXlink, removeXlinkAttribute, skipOnError, true, true);
145+
resolveXlink, removeXlinkAttribute, skipOnError, true, true, true);
146146

147147
Path backupDir = dataDirectory.getBackupDir().resolve(BACKUP_DIR);
148148
String today = new SimpleDateFormat("-yyyy-MM-dd-HH:mm").format(new Date());

core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ public Multimap<String, Object> indexMetadataFileStore(AbstractMetadata fullMd)
709709

710710
if (metadataResources != null && !metadataResources.isEmpty()) {
711711
JsonNode jsonNode = indexObjectMapper.valueToTree(metadataResources);
712-
indexMetadataFileStoreFields.put("fileStore", jsonNode);
712+
indexMetadataFileStoreFields.put(IndexFields.FILESTORE, jsonNode);
713713
}
714714
} catch (Exception e) {
715715
Log.warning(Geonet.INDEX_ENGINE, String.format(

core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,29 @@ class MEF2Exporter {
6565
*
6666
* @param uuids List of records to export.
6767
* @param format {@link Format} to export.
68+
* @param includeAttachments If true, include attachments according to the export format and permissions.
69+
* If false, no attachments are included.
6870
* @return MEF2 File
6971
*/
7072
public static Path doExport(ServiceContext context, Set<String> uuids,
7173
Format format, boolean skipUUID, Path stylePath, boolean resolveXlink,
72-
boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation) throws Exception {
73-
return doExport(context, uuids, format, skipUUID, stylePath, resolveXlink, removeXlinkAttribute, skipError, addSchemaLocation, false);
74+
boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation, boolean includeAttachments) throws Exception {
75+
return doExport(context, uuids, format, skipUUID, stylePath, resolveXlink, removeXlinkAttribute, skipError, addSchemaLocation, false, includeAttachments);
7476
}
7577

7678
/**
7779
* Create a MEF2 file in ZIP format.
7880
*
7981
* @param uuids List of records to export.
8082
* @param format {@link Format} to export.
83+
* @param includeAttachments If true, include attachments according to the export format and permissions.
84+
* If false, no attachments are included.
8185
* @return MEF2 File
8286
*/
8387
public static Path doExport(ServiceContext context, Set<String> uuids,
8488
Format format, boolean skipUUID, Path stylePath, boolean resolveXlink,
8589
boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation,
86-
boolean approved) throws Exception {
90+
boolean approved, boolean includeAttachments) throws Exception {
8791

8892
Path file = Files.createTempFile("mef-", ".mef");
8993
EsSearchManager searchManager = context.getBean(EsSearchManager.class);
@@ -180,7 +184,7 @@ public static Path doExport(ServiceContext context, Set<String> uuids,
180184
)));
181185

182186
createMetadataFolder(context, md, zipFs, skipUUID, stylePath,
183-
format, resolveXlink, removeXlinkAttribute, addSchemaLocation);
187+
format, resolveXlink, removeXlinkAttribute, addSchemaLocation, includeAttachments);
184188
}
185189
Files.write(zipFs.getPath("/index.csv"), csvBuilder.toString().getBytes(Constants.CHARSET));
186190
Files.write(zipFs.getPath("/index.html"), Xml.getString(html).getBytes(Constants.CHARSET));
@@ -211,12 +215,14 @@ private static String cleanForCsv(String csvColumnText) {
211215
* files are included in MEF file. Export relevant information according to format parameter.
212216
*
213217
* @param zipFs Zip file to add new record
218+
* @param includeAttachments If true, include attachments according to the export format and permissions.
219+
* If false, no attachments are included.
214220
*/
215221
private static void createMetadataFolder(ServiceContext context,
216222
AbstractMetadata metadata, FileSystem zipFs, boolean skipUUID,
217223
Path stylePath, Format format, boolean resolveXlink,
218224
boolean removeXlinkAttribute,
219-
boolean addSchemaLocation) throws Exception {
225+
boolean addSchemaLocation, boolean includeAttachments) throws Exception {
220226

221227
final Path metadataRootDir = zipFs.getPath(metadata.getUuid());
222228
Files.createDirectories(metadataRootDir);
@@ -250,24 +256,35 @@ private static void createMetadataFolder(ServiceContext context,
250256
}
251257

252258
final Store store = context.getBean("resourceStore", Store.class);
253-
final List<MetadataResource> publicResources = store.getResources(context, metadata.getUuid(),
254-
MetadataResourceVisibility.PUBLIC, null, true);
255259

256-
// --- save thumbnails and maps
257-
258-
if (format == Format.PARTIAL || format == Format.FULL) {
259-
StoreUtils.extract(context, metadata.getUuid(), publicResources, metadataRootDir.resolve("public"), true);
260-
}
260+
// Get the paths to the public and private resources directories
261+
Path publicResourcesPath = metadataRootDir.resolve("public");
262+
Path privateResourcesPath = metadataRootDir.resolve("private");
263+
264+
// Create the resources directories
265+
Files.createDirectories(publicResourcesPath);
266+
Files.createDirectories(privateResourcesPath);
267+
268+
// Add the resources if the specified format allows it
269+
List<MetadataResource> publicResources = List.of();
270+
List<MetadataResource> privateResources = List.of();
271+
if (includeAttachments) {
272+
if (format == Format.PARTIAL || format == Format.FULL) {
273+
// Include public resources only for PARTIAL and FULL formats so the info file matches the MEF contents.
274+
publicResources = store.getResources(context, metadata.getUuid(),
275+
MetadataResourceVisibility.PUBLIC, null, true);
276+
StoreUtils.extract(context, metadata.getUuid(), publicResources, publicResourcesPath, true);
277+
}
261278

262-
List<MetadataResource> privateResources = null;
263-
if (format == Format.FULL) {
264-
try {
265-
Lib.resource.checkPrivilege(context, id, ReservedOperation.download);
266-
privateResources = store.getResources(context, metadata.getUuid(),
267-
MetadataResourceVisibility.PRIVATE, null, true);
268-
StoreUtils.extract(context, metadata.getUuid(), privateResources, metadataRootDir.resolve("private"), true);
269-
} catch (Exception e) {
270-
// Current user could not download private data
279+
if (format == Format.FULL) {
280+
try {
281+
Lib.resource.checkPrivilege(context, id, ReservedOperation.download);
282+
privateResources = store.getResources(context, metadata.getUuid(),
283+
MetadataResourceVisibility.PRIVATE, null, true);
284+
StoreUtils.extract(context, metadata.getUuid(), privateResources, privateResourcesPath, true);
285+
} catch (Exception e) {
286+
// Current user could not download private data
287+
}
271288
}
272289
}
273290

core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class MEFExporter {
6363
*/
6464
public static Path doExport(ServiceContext context, String uuid, Format format, boolean skipUUID,
6565
boolean resolveXlink, boolean removeXlinkAttribute, boolean addSchemaLocation,
66-
boolean approved) throws Exception {
66+
boolean approved, boolean includeAttachments) throws Exception {
6767

6868
//Search by ID, not by UUID
6969
final int id;
@@ -76,7 +76,7 @@ public static Path doExport(ServiceContext context, String uuid, Format format,
7676
}
7777
Pair<AbstractMetadata, String> recordAndMetadata = MEFLib.retrieveMetadata(context, id, resolveXlink,
7878
removeXlinkAttribute, addSchemaLocation);
79-
return export(context, approved, format, skipUUID, recordAndMetadata);
79+
return export(context, approved, format, skipUUID, recordAndMetadata, includeAttachments);
8080
}
8181

8282
/**
@@ -88,17 +88,32 @@ public static Path doExport(ServiceContext context, String uuid, Format format,
8888
*
8989
* @param id unique ID of the metadata record to export.
9090
* @param format {@link org.fao.geonet.kernel.mef.MEFLib.Format}
91+
* @param includeAttachments If true, include attachments according to the export format and permissions.
92+
* If false, no attachments are included.
9193
* @return the path of the generated MEF file.
9294
*/
9395
public static Path doExport(ServiceContext context, Integer id, Format format, boolean skipUUID,
94-
boolean resolveXlink, boolean removeXlinkAttribute, boolean addSchemaLocation) throws Exception {
96+
boolean resolveXlink, boolean removeXlinkAttribute, boolean addSchemaLocation, boolean includeAttachments) throws Exception {
9597
Pair<AbstractMetadata, String> recordAndMetadata = MEFLib.retrieveMetadata(context, id, resolveXlink,
9698
removeXlinkAttribute, addSchemaLocation);
97-
return export(context, true/* TODO: not sure*/, format, skipUUID, recordAndMetadata);
99+
return export(context, true/* TODO: not sure*/, format, skipUUID, recordAndMetadata, includeAttachments);
98100
}
99101

102+
/**
103+
* Create a metadata folder according to MEF {@link Version} 1 specification and
104+
* return file path.
105+
* <p>
106+
* Template or subtemplate could not be exported in MEF format. Use XML export
107+
* instead.
108+
*
109+
* @param recordAndMetadata Pair of AbstractMetadata and its XML representation.
110+
* @param format {@link org.fao.geonet.kernel.mef.MEFLib.Format}
111+
* @param includeAttachments If true, include attachments according to the export format and permissions.
112+
* If false, no attachments are included.
113+
* @return the path of the generated MEF file.
114+
*/
100115
private static Path export(ServiceContext context, boolean approved, Format format, boolean skipUUID,
101-
Pair<AbstractMetadata, String> recordAndMetadata)
116+
Pair<AbstractMetadata, String> recordAndMetadata, boolean includeAttachments)
102117
throws Exception {
103118
AbstractMetadata record = recordAndMetadata.one();
104119
String xmlDocumentAsString = recordAndMetadata.two();
@@ -116,32 +131,41 @@ private static Path export(ServiceContext context, boolean approved, Format form
116131
byte[] binData = xmlDocumentAsString.getBytes(Constants.ENCODING);
117132
Files.write(zipFs.getPath(FILE_METADATA), binData);
118133

119-
final List<MetadataResource> publicResources = store.getResources(context, record.getUuid(), MetadataResourceVisibility.PUBLIC, null,
120-
approved);
121-
final List<MetadataResource> privateResources = store.getResources(context, record.getUuid(), MetadataResourceVisibility.PRIVATE, null,
122-
approved);
134+
// Get the paths to the public and private resources directories
135+
Path publicResourcesPath = zipFs.getPath("public");
136+
Path privateResourcesPath = zipFs.getPath("private");
137+
138+
// Create the resources directories
139+
Files.createDirectories(publicResourcesPath);
140+
Files.createDirectories(privateResourcesPath);
141+
142+
// Add the resources if the specified format allows it
143+
List<MetadataResource> publicResources = List.of();
144+
List<MetadataResource> privateResources = List.of();
145+
if (includeAttachments) {
146+
if (format == Format.PARTIAL || format == Format.FULL) {
147+
publicResources = store.getResources(context, record.getUuid(), MetadataResourceVisibility.PUBLIC, null, approved);
148+
StoreUtils.extract(context, record.getUuid(), publicResources, publicResourcesPath, approved);
149+
}
150+
151+
if (format == Format.FULL) {
152+
privateResources = store.getResources(context, record.getUuid(), MetadataResourceVisibility.PRIVATE, null, approved);
153+
154+
try {
155+
Lib.resource.checkPrivilege(context, "" + record.getId(), ReservedOperation.download);
156+
StoreUtils.extract(context, record.getUuid(), privateResources, privateResourcesPath, approved);
157+
} catch (Exception e) {
158+
// Current user could not download private data
159+
Log.warning(Geonet.MEF,
160+
"Error encountered while trying to import private resources of MEF file. MEF ID: " + record.getId(), e);
161+
}
162+
}
163+
}
123164

124165
// --- save info file
125166
binData = MEFLib.buildInfoFile(context, record, format, publicResources, privateResources,
126167
skipUUID).getBytes(Constants.ENCODING);
127168
Files.write(zipFs.getPath(FILE_INFO), binData);
128-
129-
130-
if (format == Format.PARTIAL || format == Format.FULL && !publicResources.isEmpty()) {
131-
StoreUtils.extract(context, record.getUuid(), publicResources, zipFs.getPath("public"), approved);
132-
}
133-
134-
if (format == Format.FULL && !privateResources.isEmpty()) {
135-
try {
136-
Lib.resource.checkPrivilege(context, "" + record.getId(), ReservedOperation.download);
137-
StoreUtils.extract(context, record.getUuid(), privateResources, zipFs.getPath("private"), approved);
138-
} catch (Exception e) {
139-
// Current user could not download private data
140-
Log.warning(Geonet.MEF,
141-
"Error encountered while trying to import private resources of MEF file. MEF ID: " + record.getId(), e);
142-
143-
}
144-
}
145169
} catch (Exception e) {
146170
FileUtils.deleteQuietly(file.toFile());
147171
throw e;

0 commit comments

Comments
 (0)