diff --git a/.gitignore b/.gitignore index 2cd8767775d..4453cc62260 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,6 @@ schemas/*/doc/*/*.rst *.old web-ui-docs/package-lock.json release/jetty/* +chromedriver +node_modules +package-lock.json diff --git a/core/src/main/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigration.java b/core/src/main/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigration.java index ce12c6b8e65..67991017c6c 100644 --- a/core/src/main/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigration.java +++ b/core/src/main/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigration.java @@ -34,6 +34,7 @@ import org.fao.geonet.repository.specification.UserGroupSpecs; import org.fao.geonet.utils.Log; import org.springframework.context.ApplicationContext; +import org.springframework.data.jpa.domain.Specification; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -119,7 +120,7 @@ void transferMetadata(ApplicationContext applicationContext, List oldMetad User oldOwner = oldMetadataOwnerList.get(i); // Transfer metadata to user but keep old group - List metadataList = metadataRepository.findAll(MetadataSpecs.isOwnedByUser(oldOwner.getId())); + List metadataList = metadataRepository.findAll((Specification)MetadataSpecs.isOwnedByUser(oldOwner.getId())); for (Metadata metadata : metadataList) { dataManager.updateMetadataOwner(metadata.getId(), Integer.toString(newMetadataOwner.getId()), Integer.toString(metadata.getSourceInfo().getGroupOwner())); diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStore.java b/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStore.java index e82e4f0ccf7..4b357c817f2 100644 --- a/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStore.java +++ b/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStore.java @@ -36,8 +36,10 @@ import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.lib.Lib; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.utils.IO; import org.springframework.context.ApplicationContext; import org.springframework.web.multipart.MultipartFile; @@ -77,15 +79,22 @@ public FilesystemStore() { public List getResources(ServiceContext context, String metadataUuid, Sort sort, String filter) throws Exception { + return getResources(context, metadataUuid, sort, filter, true); + } + + @Override + public List getResources(ServiceContext context, String metadataUuid, + Sort sort, + String filter, Boolean approved) throws Exception { List resourceList = new ArrayList<>(); ApplicationContext _appContext = ApplicationContextHolder.get(); - String metadataId = getAndCheckMetadataId(metadataUuid); + String metadataId = getAndCheckMetadataId(metadataUuid, approved); AccessManager accessManager = _appContext.getBean(AccessManager.class); boolean canEdit = accessManager.canEdit(context, metadataId); - resourceList.addAll(getResources(context, metadataUuid, MetadataResourceVisibility.PUBLIC, filter)); + resourceList.addAll(getResources(context, metadataUuid, MetadataResourceVisibility.PUBLIC, filter, approved)); if (canEdit) { - resourceList.addAll(getResources(context, metadataUuid, MetadataResourceVisibility.PRIVATE, filter)); + resourceList.addAll(getResources(context, metadataUuid, MetadataResourceVisibility.PRIVATE, filter, approved)); } if (sort == Sort.name) { @@ -95,13 +104,20 @@ public List getResources(ServiceContext context, String metada return resourceList; } + @Override + @Deprecated + public List getResources(ServiceContext context, String metadataUuid, + MetadataResourceVisibility visibility, + String filter) throws Exception { + return getResources(context, metadataUuid, visibility, filter, true); + } @Override public List getResources(ServiceContext context, String metadataUuid, MetadataResourceVisibility visibility, - String filter) throws Exception { + String filter, Boolean approved) throws Exception { ApplicationContext _appContext = ApplicationContextHolder.get(); - String metadataId = getAndCheckMetadataId(metadataUuid); + String metadataId = getAndCheckMetadataId(metadataUuid, approved); GeonetworkDataDirectory dataDirectory = _appContext.getBean(GeonetworkDataDirectory.class); SettingManager settingManager = _appContext.getBean(SettingManager.class); @@ -140,10 +156,14 @@ public List getResources(ServiceContext context, String metada return resourceList; } - - @Override + @Deprecated public Path getResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception { + return getResource(context, metadataUuid, resourceId, true); + } + + @Override + public Path getResource(ServiceContext context, String metadataUuid, String resourceId, Boolean approved) throws Exception { // Those characters should not be allowed by URL structure if (resourceId.contains("..") || resourceId.startsWith("/") || @@ -155,7 +175,7 @@ public Path getResource(ServiceContext context, String metadataUuid, String reso ApplicationContext _appContext = ApplicationContextHolder.get(); AccessManager accessManager = _appContext.getBean(AccessManager.class); GeonetworkDataDirectory dataDirectory = _appContext.getBean(GeonetworkDataDirectory.class); - String metadataId = getAndCheckMetadataId(metadataUuid); + String metadataId = getAndCheckMetadataId(metadataUuid, approved); Path metadataDir = Lib.resource.getMetadataDir(dataDirectory, metadataId); Path resourceFile = null; @@ -174,6 +194,7 @@ public Path getResource(ServiceContext context, String metadataUuid, String reso } } + if (resourceFile != null && Files.exists(resourceFile)) { if (resourceFile.getParent().getFileName().toString().equals( MetadataResourceVisibility.PRIVATE.toString()) && !canDownload) { @@ -212,8 +233,17 @@ private MetadataResource getResourceDescription(String metadataUuid, MetadataRes public MetadataResource putResource(ServiceContext context, String metadataUuid, MultipartFile file, MetadataResourceVisibility visibility) throws Exception { + return putResource(context, metadataUuid, file, visibility, true); + } + + + @Override + public MetadataResource putResource(ServiceContext context, String metadataUuid, + MultipartFile file, + MetadataResourceVisibility visibility, + Boolean approved) throws Exception { canEdit(context, metadataUuid); - Path filePath = getPath(metadataUuid, visibility, file.getOriginalFilename()); + Path filePath = getPath(metadataUuid, visibility, file.getOriginalFilename(), approved); BufferedOutputStream stream = new BufferedOutputStream( @@ -228,34 +258,43 @@ public MetadataResource putResource(ServiceContext context, String metadataUuid, @Override public MetadataResource putResource(ServiceContext context, String metadataUuid, Path file, MetadataResourceVisibility visibility) throws Exception { + return putResource(context, metadataUuid, file, visibility, true); + } + + @Override + public MetadataResource putResource(ServiceContext context, String metadataUuid, Path file, MetadataResourceVisibility visibility, Boolean approved) throws Exception { canEdit(context, metadataUuid); - Path filePath = getPath(metadataUuid, visibility, file.getFileName().toString()); + Path filePath = getPath(metadataUuid, visibility, file.getFileName().toString(), approved); FileUtils.copyFile(file.toFile(), filePath.toFile()); return getResourceDescription(metadataUuid, visibility, filePath); } - @Override public MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, MetadataResourceVisibility visibility) throws Exception { + return putResource(context, metadataUuid, fileUrl, visibility, true); + } + + @Override + public MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, MetadataResourceVisibility visibility, Boolean approved) throws Exception { canEdit(context, metadataUuid); String fileName = FilenameUtils.getName(fileUrl.getPath()); if (fileName.contains("?")) { fileName = fileName.substring(0, fileName.indexOf("?")); } - Path filePath = getPath(metadataUuid, visibility, fileName); + Path filePath = getPath(metadataUuid, visibility, fileName, approved); Files.copy(fileUrl.openStream(), filePath); return getResourceDescription(metadataUuid, visibility, filePath); } - - private Path getPath(String metadataUuid, MetadataResourceVisibility visibility, String fileName) throws Exception { + + private Path getPath(String metadataUuid, MetadataResourceVisibility visibility, String fileName, Boolean approved) throws Exception { ApplicationContext _appContext = ApplicationContextHolder.get(); GeonetworkDataDirectory dataDirectory = _appContext.getBean(GeonetworkDataDirectory.class); - String metadataId = getAndCheckMetadataId(metadataUuid); + String metadataId = getAndCheckMetadataId(metadataUuid, approved); Path metadataDir = Lib.resource.getMetadataDir(dataDirectory, metadataId); Path folderPath = metadataDir.resolve(visibility.toString()); @@ -282,8 +321,13 @@ private Path getPath(String metadataUuid, MetadataResourceVisibility visibility, @Override public String delResource(ServiceContext context, String metadataUuid) throws Exception { + return delResource(context, metadataUuid, true); + } + + @Override + public String delResource(ServiceContext context, String metadataUuid, Boolean approved) throws Exception { ApplicationContext _appContext = ApplicationContextHolder.get(); - String metadataId = getAndCheckMetadataId(metadataUuid); + String metadataId = getAndCheckMetadataId(metadataUuid, approved); canEdit(context, metadataUuid); @@ -296,13 +340,17 @@ public String delResource(ServiceContext context, String metadataUuid) throws Ex return String.format("Unable to remove metadata '%s' directory.", metadataUuid); } } + @Override + public String delResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception { + return delResource(context, metadataUuid, resourceId, true); + } @Override - public String delResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception { + public String delResource(ServiceContext context, String metadataUuid, String resourceId, Boolean approved) throws Exception { canEdit(context, metadataUuid); - Path filePath = getResource(context, metadataUuid, resourceId); + Path filePath = getResource(context, metadataUuid, resourceId, approved); try { Files.deleteIfExists(filePath); @@ -317,12 +365,20 @@ public String delResource(ServiceContext context, String metadataUuid, String re public MetadataResource patchResourceStatus(ServiceContext context, String metadataUuid, String resourceId, MetadataResourceVisibility visibility) throws Exception { + return patchResourceStatus(context, metadataUuid, resourceId, visibility, true); + } + + @Override + public MetadataResource patchResourceStatus(ServiceContext context, String metadataUuid, + String resourceId, + MetadataResourceVisibility visibility, + Boolean approved) throws Exception { ApplicationContext _appContext = ApplicationContextHolder.get(); AccessManager accessManager = _appContext.getBean(AccessManager.class); - String metadataId = getAndCheckMetadataId(metadataUuid); + String metadataId = getAndCheckMetadataId(metadataUuid, approved); if (accessManager.canEdit(context, metadataId)) { - Path filePath = getResource(context, metadataUuid, resourceId); + Path filePath = getResource(context, metadataUuid, resourceId, approved); GeonetworkDataDirectory dataDirectory = _appContext.getBean(GeonetworkDataDirectory.class); Path metadataDir = Lib.resource.getMetadataDir(dataDirectory, metadataId); @@ -351,14 +407,19 @@ public MetadataResource patchResourceStatus(ServiceContext context, String metad /** * TODO: To be improve */ - private String getAndCheckMetadataId(String metadataUuid) throws Exception { + private String getAndCheckMetadataId(String metadataUuid, Boolean approved) throws Exception { ApplicationContext _appContext = ApplicationContextHolder.get(); - String metadataId = _appContext.getBean(DataManager.class).getMetadataId(metadataUuid); + String metadataId = String.valueOf(_appContext.getBean(IMetadataUtils.class).findOneByUuid(metadataUuid).getId()); if (metadataId == null) { throw new ResourceNotFoundException(String.format( "Metadata with UUID '%s' not found.", metadataUuid )); } + + if(approved) { + metadataId = String.valueOf(_appContext.getBean(MetadataRepository.class) + .findOneByUuid(metadataUuid).getId()); + } return metadataId; } @@ -369,7 +430,7 @@ private void canEdit(ServiceContext context, String metadataUuid) throws Excepti private void canEdit(ServiceContext context, String metadataUuid, MetadataResourceVisibility visibility) throws Exception { ApplicationContext _appContext = ApplicationContextHolder.get(); - String metadataId = getAndCheckMetadataId(metadataUuid); + String metadataId = getAndCheckMetadataId(metadataUuid, false); AccessManager accessManager = _appContext.getBean(AccessManager.class); boolean canEdit = accessManager.canEdit(context, metadataId); if ((visibility == null && !canEdit) || diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/ResourceLoggerStore.java b/core/src/main/java/org/fao/geonet/api/records/attachments/ResourceLoggerStore.java index ed7b8e4e92c..4e3f0392bab 100644 --- a/core/src/main/java/org/fao/geonet/api/records/attachments/ResourceLoggerStore.java +++ b/core/src/main/java/org/fao/geonet/api/records/attachments/ResourceLoggerStore.java @@ -33,6 +33,7 @@ import org.fao.geonet.domain.MetadataResource; import org.fao.geonet.domain.MetadataResourceVisibility; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.MetadataFileDownloadRepository; import org.fao.geonet.repository.MetadataFileUploadRepository; import org.fao.geonet.util.ThreadPool; @@ -66,25 +67,25 @@ public ResourceLoggerStore(Store decoratedStore) { } @Override - public List getResources(ServiceContext context, String metadataUuid, Sort sort, String filter) throws Exception { + public List getResources(ServiceContext context, String metadataUuid, Sort sort, String filter, Boolean approved) throws Exception { if (decoratedStore != null) { - return decoratedStore.getResources(context, metadataUuid, sort, filter); + return decoratedStore.getResources(context, metadataUuid, sort, filter, approved); } return null; } @Override - public List getResources(ServiceContext context, String metadataUuid, MetadataResourceVisibility metadataResourceVisibility, String filter) throws Exception { + public List getResources(ServiceContext context, String metadataUuid, MetadataResourceVisibility metadataResourceVisibility, String filter, Boolean approved) throws Exception { if (decoratedStore != null) { - return decoratedStore.getResources(context, metadataUuid, metadataResourceVisibility, filter); + return decoratedStore.getResources(context, metadataUuid, metadataResourceVisibility, filter, approved); } return null; } @Override - public Path getResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception { + public Path getResource(ServiceContext context, String metadataUuid, String resourceId, Boolean approved) throws Exception { if (decoratedStore != null) { - Path filePath = decoratedStore.getResource(context, metadataUuid, resourceId); + Path filePath = decoratedStore.getResource(context, metadataUuid, resourceId, approved); if (filePath != null) { // TODO: Add Requester details which may have been provided by a form ? storeGetRequest(context, metadataUuid, resourceId, @@ -97,9 +98,9 @@ public Path getResource(ServiceContext context, String metadataUuid, String reso } @Override - public MetadataResource putResource(ServiceContext context, String metadataUuid, MultipartFile file, MetadataResourceVisibility metadataResourceVisibility) throws Exception { + public MetadataResource putResource(ServiceContext context, String metadataUuid, MultipartFile file, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception { if (decoratedStore != null) { - MetadataResource resource = decoratedStore.putResource(context, metadataUuid, file, metadataResourceVisibility); + MetadataResource resource = decoratedStore.putResource(context, metadataUuid, file, metadataResourceVisibility, approved); if (resource != null) { storePutRequest(context, metadataUuid, resource.getId(), @@ -111,41 +112,41 @@ public MetadataResource putResource(ServiceContext context, String metadataUuid, } @Override - public MetadataResource putResource(ServiceContext context, String metadataUuid, Path filePath, MetadataResourceVisibility metadataResourceVisibility) throws Exception { + public MetadataResource putResource(ServiceContext context, String metadataUuid, Path filePath, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception { if (decoratedStore != null) { - return decoratedStore.putResource(context, metadataUuid, filePath, metadataResourceVisibility); + return decoratedStore.putResource(context, metadataUuid, filePath, metadataResourceVisibility, approved); } return null; } @Override - public MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, MetadataResourceVisibility metadataResourceVisibility) throws Exception { + public MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception { if (decoratedStore != null) { - return decoratedStore.putResource(context, metadataUuid, fileUrl, metadataResourceVisibility); + return decoratedStore.putResource(context, metadataUuid, fileUrl, metadataResourceVisibility, approved); } return null; } @Override - public MetadataResource patchResourceStatus(ServiceContext context, String metadataUuid, String resourceId, MetadataResourceVisibility metadataResourceVisibility) throws Exception { + public MetadataResource patchResourceStatus(ServiceContext context, String metadataUuid, String resourceId, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception { if (decoratedStore != null) { - return decoratedStore.patchResourceStatus(context, metadataUuid, resourceId, metadataResourceVisibility); + return decoratedStore.patchResourceStatus(context, metadataUuid, resourceId, metadataResourceVisibility, approved); } return null; } @Override - public String delResource(ServiceContext context, String metadataUuid) throws Exception { + public String delResource(ServiceContext context, String metadataUuid, Boolean approved) throws Exception { if (decoratedStore != null) { - return decoratedStore.delResource(context, metadataUuid); + return decoratedStore.delResource(context, metadataUuid, approved); } return null; } @Override - public String delResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception { + public String delResource(ServiceContext context, String metadataUuid, String resourceId, Boolean approved) throws Exception { if (decoratedStore != null) { - String response = decoratedStore.delResource(context, metadataUuid, resourceId); + String response = decoratedStore.delResource(context, metadataUuid, resourceId, approved); if (response != null) { storeDeleteRequest(metadataUuid, resourceId); } @@ -165,7 +166,7 @@ private void storeGetRequest(ServiceContext context, final String metadataUuid, final String requesterComments, final String downloadDate) throws Exception { final int metadataId = - Integer.valueOf(context.getBean(DataManager.class).getMetadataId(metadataUuid)); + Integer.valueOf(context.getBean(IMetadataUtils.class).getMetadataId(metadataUuid)); final MetadataFileUploadRepository uploadRepository = context.getBean(MetadataFileUploadRepository.class); final MetadataFileDownloadRepository repo = @@ -217,7 +218,7 @@ private void storeDeleteRequest(final String metadataUuid, final String fileName) throws Exception { final ConfigurableApplicationContext context = ApplicationContextHolder.get(); final int metadataId = - Integer.valueOf(context.getBean(DataManager.class).getMetadataId(metadataUuid)); + Integer.valueOf(context.getBean(IMetadataUtils.class).getMetadataId(metadataUuid)); MetadataFileUploadRepository repo = context.getBean(MetadataFileUploadRepository.class); @@ -242,7 +243,7 @@ private void storePutRequest(ServiceContext context, final String metadataUuid, final MetadataFileUploadRepository repo = context.getBean(MetadataFileUploadRepository.class); final int metadataId = - Integer.valueOf(context.getBean(DataManager.class).getMetadataId(metadataUuid)); + Integer.valueOf(context.getBean(IMetadataUtils.class).getMetadataId(metadataUuid)); MetadataFileUpload metadataFileUpload = new MetadataFileUpload(); @@ -254,4 +255,55 @@ private void storePutRequest(ServiceContext context, final String metadataUuid, repo.save(metadataFileUpload); } + + @Override + public List getResources(ServiceContext context, String metadataUuid, Sort sort, String filter) + throws Exception { + return getResources(context, metadataUuid, sort, filter, true); + } + + @Override + public List getResources(ServiceContext context, String metadataUuid, + MetadataResourceVisibility metadataResourceVisibility, String filter) throws Exception { + return getResources(context, metadataUuid, metadataResourceVisibility, filter, true); + } + + @Override + public Path getResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception { + return getResource(context, metadataUuid, resourceId, true); + } + + @Override + public MetadataResource putResource(ServiceContext context, String metadataUuid, MultipartFile file, + MetadataResourceVisibility metadataResourceVisibility) throws Exception { + return putResource(context, metadataUuid, file, metadataResourceVisibility, true); + } + + @Override + public MetadataResource putResource(ServiceContext context, String metadataUuid, Path filePath, + MetadataResourceVisibility metadataResourceVisibility) throws Exception { + return putResource(context, metadataUuid, filePath, metadataResourceVisibility, true); + } + + @Override + public MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, + MetadataResourceVisibility metadataResourceVisibility) throws Exception { + return putResource(context, metadataUuid, fileUrl, metadataResourceVisibility, true); + } + + @Override + public MetadataResource patchResourceStatus(ServiceContext context, String metadataUuid, String resourceId, + MetadataResourceVisibility metadataResourceVisibility) throws Exception { + return patchResourceStatus(context, metadataUuid, resourceId, metadataResourceVisibility, true); + } + + @Override + public String delResource(ServiceContext context, String metadataUuid) throws Exception { + return delResource(context, metadataUuid, true); + } + + @Override + public String delResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception { + return delResource(context, metadataUuid, resourceId, true); + } } diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/Store.java b/core/src/main/java/org/fao/geonet/api/records/attachments/Store.java index 9b20d1e0b7f..20f531e86b3 100644 --- a/core/src/main/java/org/fao/geonet/api/records/attachments/Store.java +++ b/core/src/main/java/org/fao/geonet/api/records/attachments/Store.java @@ -50,8 +50,24 @@ public interface Store { * to filter resources eg. *.{png|jpg} * @return A list of resources */ + @Deprecated List getResources(ServiceContext context, String metadataUuid, Sort sort, String filter) throws Exception; + /** + * Retrieve all resources for a metadata. The list of resources depends on current user + * privileges. + * + * + * @param context + * @param metadataUuid The metadata UUID + * @param sort Sort by resource name or sharing policy {@link Sort} + * @param filter a {@link java.nio.file.Files#newDirectoryStream(Path)} GLOB expression} + * to filter resources eg. *.{png|jpg} + * @param approved Return the approved version or not + * @return A list of resources + */ + List getResources(ServiceContext context, String metadataUuid, Sort sort, String filter, Boolean approved) throws Exception; + /** * Retrieve all resources for a metadata having a specific sharing policy * @@ -63,7 +79,21 @@ public interface Store { * expression} to filter resources eg. *.{png|jpg} * @return A list of resources */ + @Deprecated List getResources(ServiceContext context, String metadataUuid, MetadataResourceVisibility metadataResourceVisibility, String filter) throws Exception; + /** + * Retrieve all resources for a metadata having a specific sharing policy + * + * + * @param context + * @param metadataUuid The metadata UUID + * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} + * @param filter a {@link java.nio.file.Files#newDirectoryStream(Path) GLOB + * expression} to filter resources eg. *.{png|jpg} + * @param approved Return the approved version or not + * @return A list of resources + */ + List getResources(ServiceContext context, String metadataUuid, MetadataResourceVisibility metadataResourceVisibility, String filter, Boolean approved) throws Exception; /** * Retrieve a metadata resource path. @@ -74,8 +104,21 @@ public interface Store { * @param resourceId The resource identifier * @return The resource */ + @Deprecated Path getResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception; + /** + * Retrieve a metadata resource path. + * + * + * @param context + * @param metadataUuid The metadata UUID + * @param resourceId The resource identifier + * @param approved Return the approved version or not + * @return The resource + */ + Path getResource(ServiceContext context, String metadataUuid, String resourceId, Boolean approved) throws Exception; + /** * Add a new resource from a file. * @@ -86,8 +129,22 @@ public interface Store { * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} * @return The resource description */ + @Deprecated MetadataResource putResource(ServiceContext context, String metadataUuid, MultipartFile file, MetadataResourceVisibility metadataResourceVisibility) throws Exception; + /** + * Add a new resource from a file. + * + * + * @param context + * @param metadataUuid The metadata UUID + * @param file The resource file + * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} + * @param approved Return the approved version or not + * @return The resource description + */ + MetadataResource putResource(ServiceContext context, String metadataUuid, MultipartFile file, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception; + /** * Add a new resource from a local file path. * @@ -96,8 +153,22 @@ public interface Store { * @param metadataUuid The metadata UUID * @param filePath The resource local filepath * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} + * @param approved Return the approved version or not * @return The resource description */ + MetadataResource putResource(ServiceContext context, String metadataUuid, Path filePath, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception; + + /** + * Add a new resource from a local file path. + * + * + * @param context + * @param metadataUuid The metadata UUID + * @param filePath The resource local filepath + * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} + * @return The resource description + */ + @Deprecated MetadataResource putResource(ServiceContext context, String metadataUuid, Path filePath, MetadataResourceVisibility metadataResourceVisibility) throws Exception; /** @@ -110,8 +181,22 @@ public interface Store { * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} * @return The resource description */ + @Deprecated MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, MetadataResourceVisibility metadataResourceVisibility) throws Exception; + /** + * Add a new resource from a URL. + * + * + * @param context + * @param metadataUuid The metadata UUID + * @param fileUrl The resource file URL + * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} + * @param approved Return the approved version or not + * @return The resource description + */ + MetadataResource putResource(ServiceContext context, String metadataUuid, URL fileUrl, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception; + /** * Change the resource sharing policy * @@ -120,8 +205,20 @@ public interface Store { * @param resourceId The resource identifier * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} */ + @Deprecated MetadataResource patchResourceStatus(ServiceContext context, String metadataUuid, String resourceId, MetadataResourceVisibility metadataResourceVisibility) throws Exception; + /** + * Change the resource sharing policy + * + * @param context + * @param metadataUuid The metadata UUID + * @param resourceId The resource identifier + * @param metadataResourceVisibility The type of sharing policy {@link MetadataResourceVisibility} + * @param approved Return the approved version or not + */ + MetadataResource patchResourceStatus(ServiceContext context, String metadataUuid, String resourceId, MetadataResourceVisibility metadataResourceVisibility, Boolean approved) throws Exception; + /** * Delete all resources for a metadata @@ -129,8 +226,18 @@ public interface Store { * @param context * @param metadataUuid The metadata UUID */ + @Deprecated String delResource(ServiceContext context, String metadataUuid) throws Exception; + /** + * Delete all resources for a metadata + * + * @param context + * @param metadataUuid The metadata UUID + * @param approved Return the approved version or not + */ + String delResource(ServiceContext context, String metadataUuid, Boolean approved) throws Exception; + /** * Delete a resource from the metadata store * @@ -138,5 +245,15 @@ public interface Store { * @param metadataUuid The metadata UUID * @param resourceId The resource identifier */ + @Deprecated String delResource(ServiceContext context, String metadataUuid, String resourceId) throws Exception; + /** + * Delete a resource from the metadata store + * + * @param context + * @param metadataUuid The metadata UUID + * @param resourceId The resource identifier + * @param approved Return the approved version or not + */ + String delResource(ServiceContext context, String metadataUuid, String resourceId, Boolean approved) throws Exception; } diff --git a/core/src/main/java/org/fao/geonet/constants/Geonet.java b/core/src/main/java/org/fao/geonet/constants/Geonet.java index 9621ced1906..0ac6f140a97 100644 --- a/core/src/main/java/org/fao/geonet/constants/Geonet.java +++ b/core/src/main/java/org/fao/geonet/constants/Geonet.java @@ -672,5 +672,6 @@ public static class IndexFieldNames { public static final String LOCALE = "locale"; public static final String IS_PUBLISHED_TO_ALL = "_isPublishedToAll"; public static final String FEEDBACKCOUNT = "feedbackCount"; + public static final String DRAFT = "_draft"; } } diff --git a/core/src/main/java/org/fao/geonet/kernel/AccessManager.java b/core/src/main/java/org/fao/geonet/kernel/AccessManager.java index af6e9bd67f5..4b33bd456f4 100644 --- a/core/src/main/java/org/fao/geonet/kernel/AccessManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/AccessManager.java @@ -36,8 +36,8 @@ import org.apache.commons.lang.StringUtils; import org.fao.geonet.ApplicationContextHolder; -import org.fao.geonet.domain.Group; import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; import org.fao.geonet.domain.MetadataSourceInfo; import org.fao.geonet.domain.Operation; import org.fao.geonet.domain.OperationAllowed; @@ -49,10 +49,10 @@ import org.fao.geonet.domain.User; import org.fao.geonet.domain.UserGroup; import org.fao.geonet.domain.User_; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.setting.Settings; import org.fao.geonet.repository.GroupRepository; import org.fao.geonet.repository.GroupRepositoryCustom; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.OperationRepository; import org.fao.geonet.repository.SettingRepository; @@ -106,7 +106,7 @@ public Set getOperations(ServiceContext context, String mdId, String } UserSession us = context.getUserSession(); - if (us.isAuthenticated() && us.getProfile() == Profile.Editor) { + if (us.isAuthenticated() && us.getProfile() == Profile.Editor && us.getProfile() == Profile.Reviewer) { results.add(_opRepository.findReservedOperation(ReservedOperation.view)); } } @@ -241,6 +241,26 @@ public boolean canEdit(final ServiceContext context, final String id) throws Exc return isOwner(context, id) || hasEditPermission(context, id); } + /** + * Returns true if, and only if, at least one of these conditions is satisfied:
  • the + * user is owner (@see #isOwner)
  • the user has reviewing rights over the metadata
+ * + * @param id The metadata internal identifier + */ + public boolean canReview(final ServiceContext context, final String id) throws Exception { + return isOwner(context, id) || hasReviewPermission(context, id); + } + + /** + * Returns true if, and only if, at least one of these conditions is satisfied:
  • the + * user is owner (@see #isOwner)
  • the user has reviewing rights over owning group of the metadata
+ * + * @param id The metadata internal identifier + */ + public boolean canChangeStatus(final ServiceContext context, final String id) throws Exception { + return hasOnwershipReviewPermission(context, id) || hasReviewPermission(context, id); + } + /** * Return true if the current user is:
  • administrator
  • the metadata owner (the * user who created the record)
  • reviewer in the group the metadata was created
  • @@ -255,7 +275,7 @@ public boolean canEdit(final ServiceContext context, final String id) throws Exc public boolean isOwner(final ServiceContext context, final String id) throws Exception { //--- retrieve metadata info - AbstractMetadata info = context.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata info = context.getBean(IMetadataUtils.class).findOne(id); if (info == null) return false; @@ -347,7 +367,7 @@ public Element getContentReviewers(ServiceContext context, final Set me * @param metadataId the id of the metadata */ public boolean isVisibleToAll(final String metadataId) throws Exception { - AbstractMetadata metadata = ApplicationContextHolder.get().getBean(MetadataRepository.class).findOne(metadataId); + AbstractMetadata metadata = ApplicationContextHolder.get().getBean(IMetadataUtils.class).findOne(metadataId); if (metadata == null) { return false; } else { @@ -408,6 +428,28 @@ public boolean hasPermission(final AbstractMetadata metadata, final Group group, * @param id The metadata internal identifier */ public boolean hasEditPermission(final ServiceContext context, final String id) throws Exception { + return hasEditingPermissionWithProfile(context, id, Profile.Editor); + + } + + /** + * Check if current user can review the metadata according to the groups where the metadata is + * editable. + * + * @param id The metadata internal identifier + */ + public boolean hasReviewPermission(final ServiceContext context, final String id) throws Exception { + return hasEditingPermissionWithProfile(context, id, Profile.Reviewer); + } + + + /** + * Check if current user has permission for the metadata according to the groups where the metadata is + * editable and specific user profile. + * + * @param id The metadata internal identifier + */ + private boolean hasEditingPermissionWithProfile(final ServiceContext context, final String id, Profile profile) throws Exception { UserSession us = context.getUserSession(); if (us == null || !us.isAuthenticated()) return false; @@ -417,11 +459,12 @@ public boolean hasEditPermission(final ServiceContext context, final String id) UserGroupRepository userGroupRepository = context.getBean(UserGroupRepository.class); List allOpAlloweds = opAllowedRepository.findAll(where(hasMetadataId(id)).and(hasOperation(ReservedOperation .editing))); + if (allOpAlloweds.isEmpty()) { return false; } - Specifications spec = where(UserGroupSpecs.hasProfile(Profile.Editor)).and(UserGroupSpecs.hasUserId(us.getUserIdAsInt())); + Specifications spec = where(UserGroupSpecs.hasProfile(profile)).and(UserGroupSpecs.hasUserId(us.getUserIdAsInt())); List opAlloweds = new ArrayList(); for (OperationAllowed opAllowed : allOpAlloweds) { @@ -429,6 +472,32 @@ public boolean hasEditPermission(final ServiceContext context, final String id) } spec = spec.and(UserGroupSpecs.hasGroupIds(opAlloweds)); + return (!userGroupRepository.findAll(spec).isEmpty()); + + } + + /** + * Check if current user is reviewer of the owner group for this metadata + * + * @param id The metadata internal identifier + */ + public boolean hasOnwershipReviewPermission(final ServiceContext context, final String id) throws Exception { + UserSession us = context.getUserSession(); + if (us == null || !us.isAuthenticated()) + return false; + + + OperationAllowedRepository opAllowedRepository = context.getBean(OperationAllowedRepository.class); + UserGroupRepository userGroupRepository = context.getBean(UserGroupRepository.class); + IMetadataUtils metadataUtils = context.getBean(IMetadataUtils.class); + + Specifications spec = where(UserGroupSpecs.hasProfile(Profile.Reviewer)).and(UserGroupSpecs.hasUserId(us.getUserIdAsInt())); + + List opAlloweds = new ArrayList(); + opAlloweds.add(metadataUtils.findOne(id).getSourceInfo().getGroupOwner()); + + spec = spec.and(UserGroupSpecs.hasGroupIds(opAlloweds)); + return (!userGroupRepository.findAll(spec).isEmpty()); } diff --git a/core/src/main/java/org/fao/geonet/kernel/EditLib.java b/core/src/main/java/org/fao/geonet/kernel/EditLib.java index 1d386846d52..edb6a103ec9 100644 --- a/core/src/main/java/org/fao/geonet/kernel/EditLib.java +++ b/core/src/main/java/org/fao/geonet/kernel/EditLib.java @@ -27,10 +27,24 @@ package org.fao.geonet.kernel; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import static org.fao.geonet.constants.Edit.ChildElem.Attr.NAME; +import static org.fao.geonet.constants.Edit.ChildElem.Attr.NAMESPACE; +import static org.fao.geonet.constants.Edit.RootChild.CHILD; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.jxpath.ri.parser.Token; @@ -39,7 +53,11 @@ import org.fao.geonet.constants.Edit; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.Pair; -import org.fao.geonet.kernel.schema.*; +import org.fao.geonet.kernel.schema.ISOPlugin; +import org.fao.geonet.kernel.schema.MetadataAttribute; +import org.fao.geonet.kernel.schema.MetadataSchema; +import org.fao.geonet.kernel.schema.MetadataType; +import org.fao.geonet.kernel.schema.SchemaPlugin; import org.fao.geonet.utils.Xml; import org.jaxen.JaxenException; import org.jaxen.SimpleNamespaceContext; @@ -51,27 +69,13 @@ import org.jdom.Namespace; import org.jdom.Text; import org.jdom.filter.ElementFilter; - -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.fao.geonet.constants.Edit.ChildElem.Attr.NAME; -import static org.fao.geonet.constants.Edit.ChildElem.Attr.NAMESPACE; -import static org.fao.geonet.constants.Edit.RootChild.CHILD; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; public class EditLib { private static final Logger LOGGER = LoggerFactory.getLogger(Geonet.EDITOR); @@ -86,8 +90,8 @@ public class EditLib { private static final Joiner SLASH_STRING_JOINER = Joiner.on('/'); private SchemaManager scm; - private Hashtable htVersions = new Hashtable(1000); - + private static final Map htVersions = new ConcurrentHashMap(); + public EditLib(SchemaManager scm) { this.scm = scm; htVersions.clear(); diff --git a/core/src/main/java/org/fao/geonet/kernel/SvnManager.java b/core/src/main/java/org/fao/geonet/kernel/SvnManager.java index b46b8007ac3..0b70343a977 100644 --- a/core/src/main/java/org/fao/geonet/kernel/SvnManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/SvnManager.java @@ -44,8 +44,8 @@ import org.fao.geonet.Constants; import org.fao.geonet.constants.Geonet; import org.fao.geonet.constants.Params; -import org.fao.geonet.domain.Group; import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; import org.fao.geonet.domain.MetadataCategory; import org.fao.geonet.domain.MetadataStatus; import org.fao.geonet.domain.Operation; @@ -54,10 +54,10 @@ import org.fao.geonet.domain.OperationAllowedId_; import org.fao.geonet.domain.OperationAllowed_; import org.fao.geonet.domain.User; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.kernel.setting.Settings; import org.fao.geonet.repository.GroupRepository; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.OperationRepository; import org.fao.geonet.repository.SortUtils; @@ -697,7 +697,7 @@ private void commitMetadataOwner(ISVNEditor editor, String id) throws Exception // get owner from the database Set ids = new HashSet(); ids.add(Integer.valueOf(id)); - AbstractMetadata metadata = this.context.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata metadata = this.context.getBean(IMetadataUtils.class).findOne(id); User user = this.context.getBean(UserRepository.class).findOne(metadata.getSourceInfo().getOwner()); // Backwards compatibility. Format the metadata as XML in same format as previous versions. Element xml = new Element("results").addContent( diff --git a/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java b/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java index 27c1754a74a..3b2b119e0ea 100644 --- a/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java +++ b/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java @@ -159,9 +159,9 @@ public boolean isLoggingEmptyWithHeld() { * @param forEditing If true, then withheld elements are not removed. */ protected Element internalSelect(String id, boolean isIndexingTask, boolean forEditing) throws Exception { - IMetadataUtils _metadataRepository = ApplicationContextHolder.get().getBean(IMetadataUtils.class); + IMetadataUtils _metadataUtils = ApplicationContextHolder.get().getBean(IMetadataUtils.class); - AbstractMetadata metadata = _metadataRepository.findOne(Integer.parseInt(id)); + AbstractMetadata metadata = _metadataUtils.findOne(Integer.parseInt(id)); if (metadata == null) return null; @@ -241,7 +241,7 @@ protected void updateDb(final String id, final Element xml, final String changeD final String uuid) throws SQLException { if (resolveXLinks()) Processor.removeXLink(xml); - IMetadataManager _metadataRepository = ApplicationContextHolder.get().getBean(IMetadataManager.class); + IMetadataManager _metadataManager = ApplicationContextHolder.get().getBean(IMetadataManager.class); IMetadataUtils metadataUtils = ApplicationContextHolder.get().getBean(IMetadataUtils.class); int metadataId = Integer.valueOf(id); @@ -261,19 +261,20 @@ protected void updateDb(final String id, final Element xml, final String changeD md.setUuid(uuid); } - _metadataRepository.save(md); + _metadataManager.save(md); } /** * Deletes an xml element given its id. */ + protected void deleteDb(String id) throws Exception { - IMetadataManager _metadataRepository = ApplicationContextHolder.get().getBean(IMetadataManager.class); + IMetadataManager _metadataManager = ApplicationContextHolder.get().getBean(IMetadataManager.class); // TODO: Ultimately we want to remove any xlinks in this document // that aren't already in use from the xlink cache. For now we // rely on the admin clearing cache and reindexing regularly - _metadataRepository.delete(Integer.valueOf(id)); + _metadataManager.delete(Integer.valueOf(id)); // Assert.isTrue(!_metadataRepository.exists(Integer.valueOf(id)), "Metadata should have been deleted"); diff --git a/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java b/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java index 245a95b7a20..666b043f697 100644 --- a/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java +++ b/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java @@ -23,14 +23,20 @@ package org.fao.geonet.kernel.backup; -import com.google.common.base.Function; -import com.google.common.collect.Lists; -import com.vividsolutions.jts.util.Assert; -import jeeves.server.UserSession; -import jeeves.server.context.ServiceContext; -import jeeves.server.dispatchers.ServiceManager; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.annotation.Nullable; + import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataType; import org.fao.geonet.domain.Profile; @@ -56,15 +62,13 @@ import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Service; -import javax.annotation.Nullable; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import com.vividsolutions.jts.util.Assert; + +import jeeves.server.UserSession; +import jeeves.server.context.ServiceContext; +import jeeves.server.dispatchers.ServiceManager; @Service public class ArchiveAllMetadataJob extends QuartzJobBean { @@ -120,8 +124,8 @@ public void createBackup(ServiceContext serviceContext) throws Exception { final MetadataRepository metadataRepository = serviceContext.getBean(MetadataRepository.class); loginAsAdmin(serviceContext); - final Specification harvested = Specifications.where(MetadataSpecs.isHarvested(false)). - and(Specifications.not(MetadataSpecs.hasType(MetadataType.SUB_TEMPLATE))); + final Specification harvested = Specifications.where((Specification)MetadataSpecs.isHarvested(false)). + and((Specification)Specifications.not(MetadataSpecs.hasType(MetadataType.SUB_TEMPLATE))); List uuids = Lists.transform(metadataRepository.findAll(harvested), new Function() { @Nullable @@ -138,7 +142,7 @@ public String apply(@Nullable Metadata input) { boolean removeXlinkAttribute = false; boolean skipOnError = true; Path srcFile = MEFLib.doMEF2Export(serviceContext, new HashSet<>(uuids), format, false, stylePath, - resolveXlink, removeXlinkAttribute, skipOnError, true); + resolveXlink, removeXlinkAttribute, skipOnError, true, true); Path backupDir = dataDirectory.getBackupDir().resolve(BACKUP_DIR); String today = new SimpleDateFormat("-yyyy-MM-dd-HH:mm").format(new Date()); diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataCategory.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataCategory.java index 3668d59abdc..68f4ae89144 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataCategory.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataCategory.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; import java.util.Collection; @@ -39,9 +62,10 @@ public interface IMetadataCategory { * @param context * @param mdId * @param categId + * @return if the category was assigned * @throws Exception */ - void setCategory(ServiceContext context, String mdId, String categId) throws Exception; + boolean setCategory(ServiceContext context, String mdId, String categId) throws Exception; /** * Given a record id and a category id, unassign that category from the previous record @@ -49,9 +73,10 @@ public interface IMetadataCategory { * @param context * @param mdId * @param categId + * @return if the category was deassigned * @throws Exception */ - void unsetCategory(ServiceContext context, String mdId, int categId) throws Exception; + boolean unsetCategory(ServiceContext context, String mdId, int categId) throws Exception; /** * Given a record id, return the list of categories associated to that record diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java index 3f3b74830a7..de602f50f1d 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; import java.io.IOException; diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java index 126af79131d..caca288f3cc 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; import java.util.Map; @@ -7,6 +30,7 @@ import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataSourceInfo; import org.fao.geonet.kernel.EditLib; import org.fao.geonet.kernel.UpdateDatestamp; import org.fao.geonet.repository.BatchUpdateQuery; @@ -192,8 +216,8 @@ Element updateFixedInfo(String schema, Optional metadataId, String uuid EditLib getEditLib(); /** - * Saves an IMetadata into the database. Useful to avoid using the MetadataRepository classes directly, who may not know how to handle - * IMetadata types + * Saves an AbstractMetadata into the database. Useful to avoid using the MetadataRepository classes directly, who may not know how to handle + * AbstractMetadata types * * @param info */ @@ -236,5 +260,9 @@ Element updateFixedInfo(String schema, Optional metadataId, String uuid * @param The type of the attribute * @return a {@link BatchUpdateQuery} object to allow for updating multiple objects in a single query. */ - public void createBatchUpdateQuery(PathSpec servicesPath, String newUuid, Specification harvested); + public void createBatchUpdateQuery(PathSpec servicesPath, String newUuid, + Specification harvested); + + + public Map findAllSourceInfo(Specification specs); } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataOperations.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataOperations.java index 7985738da75..bfe2c085ce6 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataOperations.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataOperations.java @@ -1,5 +1,30 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; +import java.util.Collection; + import org.fao.geonet.domain.OperationAllowed; import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.repository.UserGroupRepository; @@ -28,8 +53,14 @@ public interface IMetadataOperations { /** * Removes all operations stored for a metadata. */ + @Deprecated void deleteMetadataOper(ServiceContext context, String metadataId, boolean skipAllReservedGroup) throws Exception; + /** + * Removes all operations stored for a metadata. + */ + void deleteMetadataOper(String metadataId, boolean skipAllReservedGroup) throws Exception; + /** * Adds a permission to a group. Metadata is not reindexed. */ @@ -85,6 +116,16 @@ public interface IMetadataOperations { */ boolean setOperation(ServiceContext context, int mdId, int grpId, int opId) throws Exception; + /** + * Set metadata privileges even if the user logged in does not have privileges + * + * @param mdId The metadata identifier + * @param grpId The group identifier + * @param opId The operation identifier + * @return true if the operation was set. + */ + boolean forceSetOperation(ServiceContext context, int mdId, int grpId, int opId) throws Exception; + /** * Check that the operation has not been added and if not that it can be added. *
      @@ -150,4 +191,12 @@ public interface IMetadataOperations { * @throws Exception */ boolean existsUser(ServiceContext context, int id) throws Exception; + + /** + * Return all operations related to one record + * + * @param id + * @return + */ + public Collection getAllOperations(int id); } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataSchemaUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataSchemaUtils.java index b33d4d2bc31..1d6819ac514 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataSchemaUtils.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataSchemaUtils.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; import java.nio.file.Path; diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java index 99728eb35c7..2a63cccaec6 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; import org.fao.geonet.domain.ISODate; diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java index 749c6f15578..97645e1e3d8 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java @@ -1,17 +1,44 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.fao.geonet.domain.AbstractMetadata; -import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.MetadataSourceInfo; import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.domain.Pair; import org.fao.geonet.repository.SimpleMetadata; import org.fao.geonet.repository.reports.MetadataReportsQueries; import org.jdom.Element; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; @@ -64,8 +91,9 @@ public interface IMetadataUtils { * * Note: Only the metadata record is stored in session. If the editing session upload new documents or thumbnails, those documents will * not be cancelled. This needs improvements. + * @return id of the record to edit */ - void startEditingSession(ServiceContext context, String id) throws Exception; + Integer startEditingSession(ServiceContext context, String id) throws Exception; /** * Rollback to the record in the state it was when the editing session started (See @@ -360,9 +388,20 @@ void setCreativeCommons(ServiceContext context, String id, String licenseurl, St * Find the record with the UUID uuid * * @param firstMetadataId + * + * @param uuid + * @return + */ + public AbstractMetadata findOneByUuid(String uuid); + + + /** + * Find all records with the UUID uuid + * + * @param uuid * @return */ - public AbstractMetadata findOneByUuid(String firstMetadataId); + public List findAllByUuid(String uuid); /** * Find the record that fits the specification @@ -370,7 +409,7 @@ void setCreativeCommons(ServiceContext context, String id, String licenseurl, St * @param spec * @return */ - public AbstractMetadata findOne(Specification spec); + public AbstractMetadata findOne(Specification spec); /** * Find the record that fits the id @@ -398,13 +437,23 @@ void setCreativeCommons(ServiceContext context, String id, String licenseurl, St */ public Iterable findAll(Set keySet); + /** + * Find all the metadata with the identifiers + * + * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) + * @param spec + * @param order + * @return + */ + public List findAll(Specification spec, Sort order); + /** * Returns all entities matching the given {@link Specification}. * * @param spec * @return */ - public List findAll(Specification hasHarvesterUuid); + public List findAll(Specification spec); /** * Load only the basic info for a metadata. Used in harvesters, mostly. @@ -472,4 +521,28 @@ void setCreativeCommons(ServiceContext context, String id, String licenseurl, St * @return An exception if another record is found, false otherwise */ boolean checkMetadataWithSameUuidExist(String uuid, int id); + + /** + * Find the list of Metadata Ids and changes dates for the metadata. + *

      + * When constructing sort objects use the MetaModel objects: + *

        + *
      • new Sort(Metadata_.id.getName())
      • + *
      • new Sort(Sort.Direction.ASC, Metadata_.id.getName())
      • + *
      + *

      + * + * @param pageable if non-null then control which subset of the results to return (and how to sort the results). + * @return List of <MetadataId, changeDate> + */ + @Nonnull + Page> findAllIdsAndChangeDates(@Nonnull Pageable pageable); + + /** + * Load the source info objects for all the metadata selected by the spec. + * + * @param spec the specification identifying the metadata of interest + * @return a map of metadataId -> SourceInfo + */ + Map findAllSourceInfo(Specification spec); } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java index 16b99882e22..85e7be2c35a 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager; import java.util.List; diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseErrorHandlerAttachingErrorToElem.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseErrorHandlerAttachingErrorToElem.java index 7bb4e4cfa0f..03449394e01 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseErrorHandlerAttachingErrorToElem.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseErrorHandlerAttachingErrorToElem.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import org.fao.geonet.utils.XmlErrorHandler; diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataCategory.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataCategory.java index 92d21071464..48e0076e8d8 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataCategory.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataCategory.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import java.util.Collection; @@ -38,9 +61,15 @@ public void init(ServiceContext context, Boolean force) throws Exception { /** * Adds a category to a metadata. Metadata is not reindexed. + * + * @return if the category was assigned */ @Override - public void setCategory(ServiceContext context, String mdId, String categId) throws Exception { + public boolean setCategory(ServiceContext context, String mdId, String categId) throws Exception { + + if (!getMetadataRepository().exists(Integer.valueOf(mdId))) { + return false; + } final MetadataCategory newCategory = metadataCategoryRepository.findOne(Integer.valueOf(categId)); final boolean[] changed = new boolean[1]; @@ -56,11 +85,13 @@ public void apply(@Nonnull Metadata entity) { if (getSvnManager() != null) { getSvnManager().setHistory(mdId, context); } + + return true; } + return false; } /** - * * @param mdId * @param categId * @return @@ -68,7 +99,7 @@ public void apply(@Nonnull Metadata entity) { */ @Override public boolean isCategorySet(final String mdId, final int categId) throws Exception { - Set categories = getMetadataUtils().findOne(mdId).getMetadataCategories(); + Set categories = getMetadataUtils().findOne(mdId).getCategories(); for (MetadataCategory category : categories) { if (category.getId() == categId) { return true; @@ -78,23 +109,23 @@ public boolean isCategorySet(final String mdId, final int categId) throws Except } /** - * * @param mdId * @param categId + * @return if the category was deassigned * @throws Exception */ @Override - public void unsetCategory(final ServiceContext context, final String mdId, final int categId) throws Exception { + public boolean unsetCategory(final ServiceContext context, final String mdId, final int categId) throws Exception { AbstractMetadata metadata = getMetadataUtils().findOne(mdId); if (metadata == null) { - return; + return false; } boolean changed = false; - for (MetadataCategory category : metadata.getMetadataCategories()) { + for (MetadataCategory category : metadata.getCategories()) { if (category.getId() == categId) { changed = true; - metadata.getMetadataCategories().remove(category); + metadata.getCategories().remove(category); break; } } @@ -105,10 +136,11 @@ public void unsetCategory(final ServiceContext context, final String mdId, final getSvnManager().setHistory(mdId + "", context); } } + + return changed; } /** - * * @param mdId * @return * @throws Exception @@ -120,7 +152,7 @@ public Collection getCategories(final String mdId) throws Exce throw new IllegalArgumentException("No metadata found with id: " + mdId); } - return metadata.getMetadataCategories(); + return metadata.getCategories(); } protected SvnManager getSvnManager() { @@ -132,11 +164,11 @@ protected IMetadataUtils getMetadataUtils() { return metadataUtils; } - + protected MetadataCategoryRepository getMetadataCategoryRepository() { return metadataCategoryRepository; } - + protected MetadataRepository getMetadataRepository() { return metadataRepository; } 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 8794113bc4b..00d7b09d02e 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 @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import java.io.IOException; @@ -50,6 +73,7 @@ import org.fao.geonet.kernel.datamanager.IMetadataIndexer; import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.kernel.datamanager.draft.DraftMetadataIndexer; import org.fao.geonet.kernel.search.ISearchManager; import org.fao.geonet.kernel.search.SearchManager; import org.fao.geonet.kernel.setting.SettingManager; @@ -87,546 +111,560 @@ public class BaseMetadataIndexer implements IMetadataIndexer, ApplicationEventPublisherAware { - Lock waitLoopLock = new ReentrantLock(); - Lock indexingLock = new ReentrantLock(); - - @Autowired - private SearchManager searchManager; - @Autowired - private GeonetworkDataDirectory geonetworkDataDirectory; - @Autowired - private MetadataStatusRepository statusRepository; - - private IMetadataUtils metadataUtils; - private IMetadataManager metadataManager; - @Autowired - private UserRepository userRepository; - @Autowired - private OperationAllowedRepository operationAllowedRepository; - @Autowired - private GroupRepository groupRepository; - @Autowired - private MetadataValidationRepository metadataValidationRepository; - @Autowired - private SchemaManager schemaManager; - @Autowired(required = false) - private SvnManager svnManager; - @Autowired - private InspireAtomFeedRepository inspireAtomFeedRepository; - @Autowired(required = false) - private XmlSerializer xmlSerializer; - @Autowired - @Lazy - private SettingManager settingManager; - @Autowired - private UserFeedbackRepository userFeedbackRepository; - - // FIXME remove when get rid of Jeeves - private ServiceContext servContext; - - private ApplicationEventPublisher publisher; - - public BaseMetadataIndexer() { - } - - public void init(ServiceContext context, Boolean force) throws Exception { - searchManager = context.getBean(SearchManager.class); - geonetworkDataDirectory = context.getBean(GeonetworkDataDirectory.class); - statusRepository = context.getBean(MetadataStatusRepository.class); - metadataUtils = context.getBean(IMetadataUtils.class); - metadataManager = context.getBean(IMetadataManager.class); - userRepository = context.getBean(UserRepository.class); - operationAllowedRepository = context.getBean(OperationAllowedRepository.class); - groupRepository = context.getBean(GroupRepository.class); - metadataValidationRepository = context.getBean(MetadataValidationRepository.class); - schemaManager = context.getBean(SchemaManager.class); - svnManager = context.getBean(SvnManager.class); - inspireAtomFeedRepository = context.getBean(InspireAtomFeedRepository.class); - xmlSerializer = context.getBean(XmlSerializer.class); - settingManager = context.getBean(SettingManager.class); - userFeedbackRepository = context.getBean(UserFeedbackRepository.class); - - servContext = context; - } - - @Override - public void setMetadataUtils(IMetadataUtils metadataUtils) { - this.metadataUtils = metadataUtils; - } - - @Override - public void setMetadataManager(IMetadataManager metadataManager) { - this.metadataManager = metadataManager; - } - - Set waitForIndexing = new HashSet(); - Set indexing = new HashSet(); - Set batchIndex = new ConcurrentHashSet(); - - @Override - public void forceIndexChanges() throws IOException { - searchManager.forceIndexChanges(); - } - - @Override - public int batchDeleteMetadataAndUpdateIndex(Specification specification) - throws Exception { - final List idsOfMetadataToDelete = metadataUtils.findAllIdsBy(specification); - - for (Integer id : idsOfMetadataToDelete) { - // --- remove metadata directory for each record - final Path metadataDataDir = geonetworkDataDirectory.getMetadataDataDir(); - Path pb = Lib.resource.getMetadataDir(metadataDataDir, id + ""); - IO.deleteFileOrDirectory(pb); - } - - // Remove records from the index - searchManager.delete(Lists.transform(idsOfMetadataToDelete, new Function() { - @Nullable - @Override - public String apply(@Nonnull Integer input) { - return input.toString(); - } - })); - - // Remove records from the database - metadataManager.deleteAll(specification); - - return idsOfMetadataToDelete.size(); - } - - @Override - /** - * Search for all records having XLinks (ie. indexed with _hasxlinks flag), - * clear the cache and reindex all records found. - */ - public synchronized void rebuildIndexXLinkedMetadata(final ServiceContext context) throws Exception { - - // get all metadata with XLinks - Set toIndex = searchManager.getDocsWithXLinks(); - - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) - Log.debug(Geonet.DATA_MANAGER, "Will index " + toIndex.size() + " records with XLinks"); - if (toIndex.size() > 0) { - // clean XLink Cache so that cache and index remain in sync - Processor.clearCache(); - - ArrayList stringIds = new ArrayList(); - for (Integer id : toIndex) { - stringIds.add(id.toString()); - } - // execute indexing operation - batchIndexInThreadPool(context, stringIds); - } - } - - /** - * Reindex all records in current selection. - */ - @Override - public synchronized void rebuildIndexForSelection(final ServiceContext context, String bucket, boolean clearXlink) - throws Exception { - - // get all metadata ids from selection - ArrayList listOfIdsToIndex = new ArrayList(); - UserSession session = context.getUserSession(); - SelectionManager sm = SelectionManager.getManager(session); - - synchronized (sm.getSelection(bucket)) { - for (Iterator iter = sm.getSelection(bucket).iterator(); iter.hasNext();) { - String uuid = (String) iter.next(); - String id = metadataUtils.getMetadataId(uuid); - if (id != null) { - listOfIdsToIndex.add(id); - } - } - } - - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) { - Log.debug(Geonet.DATA_MANAGER, "Will index " + listOfIdsToIndex.size() + " records from selection."); - } - - if (listOfIdsToIndex.size() > 0) { - // clean XLink Cache so that cache and index remain in sync - if (clearXlink) { - Processor.clearCache(); - } - - // execute indexing operation - batchIndexInThreadPool(context, listOfIdsToIndex); - } - } - - /** - * Index multiple metadata in a separate thread. Wait until the current - * transaction commits before starting threads (to make sure that all metadata - * are committed). - * - * @param context - * context object - * @param metadataIds - * the metadata ids to index - */ - @Override - public void batchIndexInThreadPool(ServiceContext context, List metadataIds) { - - TransactionStatus transactionStatus = null; - try { - transactionStatus = TransactionAspectSupport.currentTransactionStatus(); - } catch (NoTransactionException e) { - // not in a transaction so we can go ahead. - } - // split reindexing task according to number of processors we can assign - int threadCount = ThreadUtils.getNumberOfThreads(); - ExecutorService executor = Executors.newFixedThreadPool(threadCount); - - int perThread; - if (metadataIds.size() < threadCount) - perThread = metadataIds.size(); - else - perThread = metadataIds.size() / threadCount; - int index = 0; - if (Log.isDebugEnabled(Geonet.INDEX_ENGINE)) { - Log.debug(Geonet.INDEX_ENGINE, "Indexing " + metadataIds.size() + " records."); - Log.debug(Geonet.INDEX_ENGINE, metadataIds.toString()); - } - AtomicInteger numIndexedTracker = new AtomicInteger(); - while (index < metadataIds.size()) { - int start = index; - int count = Math.min(perThread, metadataIds.size() - start); - int nbRecords = start + count; - - if (Log.isDebugEnabled(Geonet.INDEX_ENGINE)) { - Log.debug(Geonet.INDEX_ENGINE, "Indexing records from " + start + " to " + nbRecords); - } - - List subList = metadataIds.subList(start, nbRecords); - - if (Log.isDebugEnabled(Geonet.INDEX_ENGINE)) { - Log.debug(Geonet.INDEX_ENGINE, subList.toString()); - } - - // create threads to process this chunk of ids - Runnable worker = new IndexMetadataTask(context, subList, batchIndex, transactionStatus, numIndexedTracker); - executor.execute(worker); - index += count; - } - - executor.shutdown(); - } - - @Override - public boolean isIndexing() { - indexingLock.lock(); - try { - return !indexing.isEmpty() || !batchIndex.isEmpty(); - } finally { - indexingLock.unlock(); - } - } - - @Override - public void indexMetadata(final List metadataIds) throws Exception { - for (String metadataId : metadataIds) { - indexMetadata(metadataId, false, null); - } - - searchManager.forceIndexChanges(); - } - - @Override - public void indexMetadata(final String metadataId, boolean forceRefreshReaders, ISearchManager searchManager) - throws Exception { - waitLoopLock.lock(); - try { - if (waitForIndexing.contains(metadataId)) { - return; - } - while (indexing.contains(metadataId)) { - try { - waitForIndexing.add(metadataId); - // don't index the same metadata 2x - synchronized (this) { - wait(200); - } - } catch (InterruptedException e) { - return; - } finally { - waitForIndexing.remove(metadataId); - } - } - indexingLock.lock(); - try { - indexing.add(metadataId); - } finally { - indexingLock.unlock(); - } - } finally { - waitLoopLock.unlock(); - } - AbstractMetadata fullMd; - - try { - Vector moreFields = new Vector(); - int id$ = Integer.parseInt(metadataId); - - // get metadata, extracting and indexing any xlinks - Element md = getXmlSerializer().selectNoXLinkResolver(metadataId, true, false); - if (getXmlSerializer().resolveXLinks()) { - List xlinks = Processor.getXLinks(md); - if (xlinks.size() > 0) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.HASXLINKS, "1", true, true)); - for (Attribute xlink : xlinks) { - moreFields.add( - SearchManager.makeField(Geonet.IndexFieldNames.XLINK, xlink.getValue(), true, true)); - } - Processor.detachXLink(md, getServiceContext()); - } else { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.HASXLINKS, "0", true, true)); - } - } else { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.HASXLINKS, "0", true, true)); - } - - fullMd = metadataUtils.findOne(id$); - - final String schema = fullMd.getDataInfo().getSchemaId(); - final String createDate = fullMd.getDataInfo().getCreateDate().getDateAndTime(); - final String changeDate = fullMd.getDataInfo().getChangeDate().getDateAndTime(); - final String source = fullMd.getSourceInfo().getSourceId(); - final MetadataType metadataType = fullMd.getDataInfo().getType(); - final String root = fullMd.getDataInfo().getRoot(); - final String uuid = fullMd.getUuid(); - final String extra = fullMd.getDataInfo().getExtra(); - final String isHarvested = String - .valueOf(Constants.toYN_EnabledChar(fullMd.getHarvestInfo().isHarvested())); - final String owner = String.valueOf(fullMd.getSourceInfo().getOwner()); - final Integer groupOwner = fullMd.getSourceInfo().getGroupOwner(); - final String popularity = String.valueOf(fullMd.getDataInfo().getPopularity()); - final String rating = String.valueOf(fullMd.getDataInfo().getRating()); - final String displayOrder = fullMd.getDataInfo().getDisplayOrder() == null ? null - : String.valueOf(fullMd.getDataInfo().getDisplayOrder()); - - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) { - Log.debug(Geonet.DATA_MANAGER, "record schema (" + schema + ")"); // DEBUG - Log.debug(Geonet.DATA_MANAGER, "record createDate (" + createDate + ")"); // DEBUG - } - - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.ROOT, root, true, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.SCHEMA, schema, true, true)); - moreFields - .add(SearchManager.makeField(Geonet.IndexFieldNames.DATABASE_CREATE_DATE, createDate, true, true)); - moreFields - .add(SearchManager.makeField(Geonet.IndexFieldNames.DATABASE_CHANGE_DATE, changeDate, true, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.SOURCE, source, true, true)); - moreFields.add( - SearchManager.makeField(Geonet.IndexFieldNames.IS_TEMPLATE, metadataType.codeString, true, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.UUID, uuid, true, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.IS_HARVESTED, isHarvested, true, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.OWNER, owner, true, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.DUMMY, "0", false, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.POPULARITY, popularity, true, true)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.RATING, rating, true, true)); - if (RatingsSetting.ADVANCED.equals(settingManager.getValue(Settings.SYSTEM_LOCALRATING_ENABLE))) { - int nbOfFeedback = userFeedbackRepository.findByMetadata_Uuid(uuid).size(); - moreFields.add( - SearchManager.makeField(Geonet.IndexFieldNames.FEEDBACKCOUNT, nbOfFeedback + "", true, true)); - } - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.DISPLAY_ORDER, displayOrder, true, false)); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.EXTRA, extra, false, true)); - - // If the metadata has an atom document, index related information - InspireAtomFeed feed = inspireAtomFeedRepository.findByMetadataId(id$); - - if ((feed != null) && StringUtils.isNotEmpty(feed.getAtom())) { - moreFields.add(SearchManager.makeField("has_atom", "y", true, true)); - moreFields.add(SearchManager.makeField("any", feed.getAtom(), false, true)); - } - - if (owner != null) { - User user = userRepository.findOne(fullMd.getSourceInfo().getOwner()); - if (user != null) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.USERINFO, user.getUsername() + "|" - + user.getSurname() + "|" + user.getName() + "|" + user.getProfile(), true, false)); - } - } - - String logoUUID = null; - if (groupOwner != null) { - final Group group = groupRepository.findOne(groupOwner); - if (group != null) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.GROUP_OWNER, - String.valueOf(groupOwner), true, true)); - final boolean preferGroup = settingManager.getValueAsBool(Settings.SYSTEM_PREFER_GROUP_LOGO, true); - if (group.getWebsite() != null && !group.getWebsite().isEmpty() && preferGroup) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.GROUP_WEBSITE, group.getWebsite(), - true, false)); - } - if (group.getLogo() != null && preferGroup) { - logoUUID = group.getLogo(); - } - } - } - - // Group logo are in the harvester folder and contains extension in file name - final Path harvesterLogosDir = Resources.locateHarvesterLogosDir(getServiceContext()); - boolean added = false; - if (StringUtils.isNotEmpty(logoUUID)) { - final Path logoPath = harvesterLogosDir.resolve(logoUUID); - if (Files.exists(logoPath)) { - added = true; - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.LOGO, - "/images/harvesting/" + logoPath.getFileName(), true, false)); - } - } - - // If not available, use the local catalog logo - if (!added) { - logoUUID = source + ".png"; - final Path logosDir = Resources.locateLogosDir(getServiceContext()); - final Path logoPath = logosDir.resolve(logoUUID); - if (Files.exists(logoPath)) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.LOGO, "/images/logos/" + logoUUID, - true, false)); - } - } - - // get privileges - List operationsAllowed = operationAllowedRepository.findAllById_MetadataId(id$); - - boolean isPublishedToAll = false; - - for (OperationAllowed operationAllowed : operationsAllowed) { - OperationAllowedId operationAllowedId = operationAllowed.getId(); - int groupId = operationAllowedId.getGroupId(); - int operationId = operationAllowedId.getOperationId(); - - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.OP_PREFIX + operationId, - String.valueOf(groupId), true, true)); - if (operationId == ReservedOperation.view.getId()) { - Group g = groupRepository.findOne(groupId); - if (g != null) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.GROUP_PUBLISHED, g.getName(), - true, true)); - if (g.getId() == ReservedGroup.all.getId()) { - isPublishedToAll = true; - } - } - } - } - - if (isPublishedToAll) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.IS_PUBLISHED_TO_ALL, "y", true, true)); - } else { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.IS_PUBLISHED_TO_ALL, "n", true, true)); - } - - for (MetadataCategory category : fullMd.getMetadataCategories()) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.CAT, category.getName(), true, true)); - } - - // get status - Sort statusSort = new Sort(Sort.Direction.DESC, - MetadataStatus_.id.getName() + "." + MetadataStatusId_.changeDate.getName()); - List statuses = statusRepository.findAllByIdAndByType(id$, StatusValueType.workflow, statusSort); - if (!statuses.isEmpty()) { - MetadataStatus stat = statuses.get(0); - String status = String.valueOf(stat.getId().getStatusId()); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.STATUS, status, true, true)); - String statusChangeDate = stat.getId().getChangeDate().getDateAndTime(); - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.STATUS_CHANGE_DATE, statusChangeDate, - true, true)); - } - - // getValidationInfo - // -1 : not evaluated - // 0 : invalid - // 1 : valid - List validationInfo = metadataValidationRepository.findAllById_MetadataId(id$); - if (validationInfo.isEmpty()) { - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.VALID, "-1", true, true)); - } else { - String isValid = "1"; - for (MetadataValidation vi : validationInfo) { - String type = vi.getId().getValidationType(); - MetadataValidationStatus status = vi.getStatus(); - if (status == MetadataValidationStatus.INVALID && vi.isRequired()) { - isValid = "0"; - } - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.VALID + "_" + type, status.getCode(), - true, true)); - } - moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.VALID, isValid, true, true)); - } - - if (searchManager == null) { - searchManager = servContext.getBean(SearchManager.class); - } - - searchManager.index(schemaManager.getSchemaDir(schema), md, metadataId, moreFields, metadataType, root, - forceRefreshReaders); - - } catch (Exception x) { - Log.error(Geonet.DATA_MANAGER, "The metadata document index with id=" + metadataId - + " is corrupt/invalid - ignoring it. Error: " + x.getMessage(), x); - fullMd = null; - } finally { - indexingLock.lock(); - try { - indexing.remove(metadataId); - } finally { - indexingLock.unlock(); - } - } - if (fullMd != null) { - this.publisher.publishEvent(new MetadataIndexCompleted(fullMd)); - } - } - - private XmlSerializer getXmlSerializer() { - return xmlSerializer; - } - - /** - * - * @param context - * @param id - * @param md - * @throws Exception - */ - @Override - public void versionMetadata(ServiceContext context, String id, Element md) throws Exception { - if (svnManager != null) { - svnManager.createMetadataDir(id, context, md); - } - } - - /** - * - * @param beginAt - * @param interval - * @throws Exception - */ - @Override - public void rescheduleOptimizer(Calendar beginAt, int interval) throws Exception { - searchManager.rescheduleOptimizer(beginAt, interval); - } - - /** - * - * @throws Exception - */ - @Override - public void disableOptimizer() throws Exception { - searchManager.disableOptimizer(); - } - - private ServiceContext getServiceContext() { - ServiceContext context = ServiceContext.get(); - return context == null ? servContext : context; - } - - public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { - this.publisher = publisher; - } + Lock waitLoopLock = new ReentrantLock(); + Lock indexingLock = new ReentrantLock(); + + @Autowired + private SearchManager searchManager; + @Autowired + private GeonetworkDataDirectory geonetworkDataDirectory; + @Autowired + private MetadataStatusRepository statusRepository; + + private IMetadataUtils metadataUtils; + private IMetadataManager metadataManager; + @Autowired + private UserRepository userRepository; + @Autowired + private OperationAllowedRepository operationAllowedRepository; + @Autowired + private GroupRepository groupRepository; + @Autowired + private MetadataValidationRepository metadataValidationRepository; + @Autowired + private SchemaManager schemaManager; + @Autowired(required = false) + private SvnManager svnManager; + @Autowired + private InspireAtomFeedRepository inspireAtomFeedRepository; + @Autowired(required = false) + private XmlSerializer xmlSerializer; + @Autowired + @Lazy + private SettingManager settingManager; + @Autowired + private UserFeedbackRepository userFeedbackRepository; + + // FIXME remove when get rid of Jeeves + private ServiceContext servContext; + + private ApplicationEventPublisher publisher; + + public BaseMetadataIndexer() { + } + + public void init(ServiceContext context, Boolean force) throws Exception { + searchManager = context.getBean(SearchManager.class); + geonetworkDataDirectory = context.getBean(GeonetworkDataDirectory.class); + statusRepository = context.getBean(MetadataStatusRepository.class); + metadataUtils = context.getBean(IMetadataUtils.class); + metadataManager = context.getBean(IMetadataManager.class); + userRepository = context.getBean(UserRepository.class); + operationAllowedRepository = context.getBean(OperationAllowedRepository.class); + groupRepository = context.getBean(GroupRepository.class); + metadataValidationRepository = context.getBean(MetadataValidationRepository.class); + schemaManager = context.getBean(SchemaManager.class); + svnManager = context.getBean(SvnManager.class); + inspireAtomFeedRepository = context.getBean(InspireAtomFeedRepository.class); + xmlSerializer = context.getBean(XmlSerializer.class); + settingManager = context.getBean(SettingManager.class); + userFeedbackRepository = context.getBean(UserFeedbackRepository.class); + + servContext = context; + } + + @Override + public void setMetadataUtils(IMetadataUtils metadataUtils) { + this.metadataUtils = metadataUtils; + } + + @Override + public void setMetadataManager(IMetadataManager metadataManager) { + this.metadataManager = metadataManager; + } + + Set waitForIndexing = new HashSet(); + Set indexing = new HashSet(); + Set batchIndex = new ConcurrentHashSet(); + + @Override + public void forceIndexChanges() throws IOException { + searchManager.forceIndexChanges(); + } + + @Override + public int batchDeleteMetadataAndUpdateIndex(Specification specification) + throws Exception { + final List idsOfMetadataToDelete = metadataUtils.findAllIdsBy(specification); + + for (Integer id : idsOfMetadataToDelete) { + // --- remove metadata directory for each record + final Path metadataDataDir = geonetworkDataDirectory.getMetadataDataDir(); + Path pb = Lib.resource.getMetadataDir(metadataDataDir, id + ""); + IO.deleteFileOrDirectory(pb); + } + + // Remove records from the index + searchManager.delete(Lists.transform(idsOfMetadataToDelete, new Function() { + @Nullable + @Override + public String apply(@Nonnull Integer input) { + return input.toString(); + } + })); + + // Remove records from the database + metadataManager.deleteAll(specification); + + return idsOfMetadataToDelete.size(); + } + + @Override + /** + * Search for all records having XLinks (ie. indexed with _hasxlinks flag), + * clear the cache and reindex all records found. + */ + public synchronized void rebuildIndexXLinkedMetadata(final ServiceContext context) throws Exception { + + // get all metadata with XLinks + Set toIndex = searchManager.getDocsWithXLinks(); + + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) + Log.debug(Geonet.DATA_MANAGER, "Will index " + toIndex.size() + " records with XLinks"); + if (toIndex.size() > 0) { + // clean XLink Cache so that cache and index remain in sync + Processor.clearCache(); + + ArrayList stringIds = new ArrayList(); + for (Integer id : toIndex) { + stringIds.add(id.toString()); + } + // execute indexing operation + batchIndexInThreadPool(context, stringIds); + } + } + + /** + * Reindex all records in current selection. + */ + @Override + public synchronized void rebuildIndexForSelection(final ServiceContext context, String bucket, boolean clearXlink) + throws Exception { + + // get all metadata ids from selection + ArrayList listOfIdsToIndex = new ArrayList(); + UserSession session = context.getUserSession(); + SelectionManager sm = SelectionManager.getManager(session); + + synchronized (sm.getSelection(bucket)) { + for (Iterator iter = sm.getSelection(bucket).iterator(); iter.hasNext(); ) { + String uuid = (String) iter.next(); + String id = metadataUtils.getMetadataId(uuid); + if (id != null) { + listOfIdsToIndex.add(id); + } + } + } + + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) { + Log.debug(Geonet.DATA_MANAGER, "Will index " + listOfIdsToIndex.size() + " records from selection."); + } + + if (listOfIdsToIndex.size() > 0) { + // clean XLink Cache so that cache and index remain in sync + if (clearXlink) { + Processor.clearCache(); + } + + // execute indexing operation + batchIndexInThreadPool(context, listOfIdsToIndex); + } + } + + /** + * Index multiple metadata in a separate thread. Wait until the current + * transaction commits before starting threads (to make sure that all metadata + * are committed). + * + * @param context context object + * @param metadataIds the metadata ids to index + */ + @Override + public void batchIndexInThreadPool(ServiceContext context, List metadataIds) { + + TransactionStatus transactionStatus = null; + try { + transactionStatus = TransactionAspectSupport.currentTransactionStatus(); + } catch (NoTransactionException e) { + // not in a transaction so we can go ahead. + } + // split reindexing task according to number of processors we can assign + int threadCount = ThreadUtils.getNumberOfThreads(); + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + + int perThread; + if (metadataIds.size() < threadCount) + perThread = metadataIds.size(); + else + perThread = metadataIds.size() / threadCount; + int index = 0; + if (Log.isDebugEnabled(Geonet.INDEX_ENGINE)) { + Log.debug(Geonet.INDEX_ENGINE, "Indexing " + metadataIds.size() + " records."); + Log.debug(Geonet.INDEX_ENGINE, metadataIds.toString()); + } + AtomicInteger numIndexedTracker = new AtomicInteger(); + while (index < metadataIds.size()) { + int start = index; + int count = Math.min(perThread, metadataIds.size() - start); + int nbRecords = start + count; + + if (Log.isDebugEnabled(Geonet.INDEX_ENGINE)) { + Log.debug(Geonet.INDEX_ENGINE, "Indexing records from " + start + " to " + nbRecords); + } + + List subList = metadataIds.subList(start, nbRecords); + + if (Log.isDebugEnabled(Geonet.INDEX_ENGINE)) { + Log.debug(Geonet.INDEX_ENGINE, subList.toString()); + } + + // create threads to process this chunk of ids + Runnable worker = new IndexMetadataTask(context, subList, batchIndex, transactionStatus, numIndexedTracker); + executor.execute(worker); + index += count; + } + + executor.shutdown(); + } + + @Override + public boolean isIndexing() { + indexingLock.lock(); + try { + return !indexing.isEmpty() || !batchIndex.isEmpty(); + } finally { + indexingLock.unlock(); + } + } + + @Override + public void indexMetadata(final List metadataIds) throws Exception { + for (String metadataId : metadataIds) { + indexMetadata(metadataId, false, null); + } + + searchManager.forceIndexChanges(); + } + + @Override + public void indexMetadata(final String metadataId, boolean forceRefreshReaders, ISearchManager searchManager) + throws Exception { + waitLoopLock.lock(); + try { + if (waitForIndexing.contains(metadataId)) { + return; + } + while (indexing.contains(metadataId)) { + try { + waitForIndexing.add(metadataId); + // don't index the same metadata 2x + synchronized (this) { + wait(200); + } + } catch (InterruptedException e) { + return; + } finally { + waitForIndexing.remove(metadataId); + } + } + indexingLock.lock(); + try { + indexing.add(metadataId); + } finally { + indexingLock.unlock(); + } + } finally { + waitLoopLock.unlock(); + } + AbstractMetadata fullMd; + + try { + Vector moreFields = new Vector(); + int id$ = Integer.parseInt(metadataId); + + // get metadata, extracting and indexing any xlinks + Element md = getXmlSerializer().selectNoXLinkResolver(metadataId, true, false); + if (getXmlSerializer().resolveXLinks()) { + List xlinks = Processor.getXLinks(md); + if (xlinks.size() > 0) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.HASXLINKS, "1", true, true)); + for (Attribute xlink : xlinks) { + moreFields.add( + SearchManager.makeField(Geonet.IndexFieldNames.XLINK, xlink.getValue(), true, true)); + } + Processor.detachXLink(md, getServiceContext()); + } else { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.HASXLINKS, "0", true, true)); + } + } else { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.HASXLINKS, "0", true, true)); + } + + fullMd = metadataUtils.findOne(id$); + + final String schema = fullMd.getDataInfo().getSchemaId(); + final String createDate = fullMd.getDataInfo().getCreateDate().getDateAndTime(); + final String changeDate = fullMd.getDataInfo().getChangeDate().getDateAndTime(); + final String source = fullMd.getSourceInfo().getSourceId(); + final MetadataType metadataType = fullMd.getDataInfo().getType(); + final String root = fullMd.getDataInfo().getRoot(); + final String uuid = fullMd.getUuid(); + final String extra = fullMd.getDataInfo().getExtra(); + final String isHarvested = String + .valueOf(Constants.toYN_EnabledChar(fullMd.getHarvestInfo().isHarvested())); + final String owner = String.valueOf(fullMd.getSourceInfo().getOwner()); + final Integer groupOwner = fullMd.getSourceInfo().getGroupOwner(); + final String popularity = String.valueOf(fullMd.getDataInfo().getPopularity()); + final String rating = String.valueOf(fullMd.getDataInfo().getRating()); + final String displayOrder = fullMd.getDataInfo().getDisplayOrder() == null ? null + : String.valueOf(fullMd.getDataInfo().getDisplayOrder()); + + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) { + Log.debug(Geonet.DATA_MANAGER, "record schema (" + schema + ")"); // DEBUG + Log.debug(Geonet.DATA_MANAGER, "record createDate (" + createDate + ")"); // DEBUG + } + + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.ROOT, root, true, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.SCHEMA, schema, true, true)); + moreFields + .add(SearchManager.makeField(Geonet.IndexFieldNames.DATABASE_CREATE_DATE, createDate, true, true)); + moreFields + .add(SearchManager.makeField(Geonet.IndexFieldNames.DATABASE_CHANGE_DATE, changeDate, true, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.SOURCE, source, true, true)); + moreFields.add( + SearchManager.makeField(Geonet.IndexFieldNames.IS_TEMPLATE, metadataType.codeString, true, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.UUID, uuid, true, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.IS_HARVESTED, isHarvested, true, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.OWNER, owner, true, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.DUMMY, "0", false, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.POPULARITY, popularity, true, true)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.RATING, rating, true, true)); + if (RatingsSetting.ADVANCED.equals(settingManager.getValue(Settings.SYSTEM_LOCALRATING_ENABLE))) { + int nbOfFeedback = userFeedbackRepository.findByMetadata_Uuid(uuid).size(); + moreFields.add( + SearchManager.makeField(Geonet.IndexFieldNames.FEEDBACKCOUNT, nbOfFeedback + "", true, true)); + } + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.DISPLAY_ORDER, displayOrder, true, false)); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.EXTRA, extra, false, true)); + + // If the metadata has an atom document, index related information + InspireAtomFeed feed = inspireAtomFeedRepository.findByMetadataId(id$); + + if ((feed != null) && StringUtils.isNotEmpty(feed.getAtom())) { + moreFields.add(SearchManager.makeField("has_atom", "y", true, true)); + moreFields.add(SearchManager.makeField("any", feed.getAtom(), false, true)); + } + + if (owner != null) { + User user = userRepository.findOne(fullMd.getSourceInfo().getOwner()); + if (user != null) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.USERINFO, user.getUsername() + "|" + + user.getSurname() + "|" + user.getName() + "|" + user.getProfile(), true, false)); + } + } + + String logoUUID = null; + if (groupOwner != null) { + final Group group = groupRepository.findOne(groupOwner); + if (group != null) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.GROUP_OWNER, + String.valueOf(groupOwner), true, true)); + final boolean preferGroup = settingManager.getValueAsBool(Settings.SYSTEM_PREFER_GROUP_LOGO, true); + if (group.getWebsite() != null && !group.getWebsite().isEmpty() && preferGroup) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.GROUP_WEBSITE, group.getWebsite(), + true, false)); + } + if (group.getLogo() != null && preferGroup) { + logoUUID = group.getLogo(); + } + } + } + + // Group logo are in the harvester folder and contains extension in file name + final Path harvesterLogosDir = Resources.locateHarvesterLogosDir(getServiceContext()); + boolean added = false; + if (StringUtils.isNotEmpty(logoUUID)) { + final Path logoPath = harvesterLogosDir.resolve(logoUUID); + if (Files.exists(logoPath)) { + added = true; + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.LOGO, + "/images/harvesting/" + logoPath.getFileName(), true, false)); + } + } + + // If not available, use the local catalog logo + if (!added) { + logoUUID = source + ".png"; + final Path logosDir = Resources.locateLogosDir(getServiceContext()); + final Path logoPath = logosDir.resolve(logoUUID); + if (Files.exists(logoPath)) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.LOGO, "/images/logos/" + logoUUID, + true, false)); + } + } + + // get privileges + List operationsAllowed = operationAllowedRepository.findAllById_MetadataId(id$); + + boolean isPublishedToAll = false; + + for (OperationAllowed operationAllowed : operationsAllowed) { + OperationAllowedId operationAllowedId = operationAllowed.getId(); + int groupId = operationAllowedId.getGroupId(); + int operationId = operationAllowedId.getOperationId(); + + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.OP_PREFIX + operationId, + String.valueOf(groupId), true, true)); + if (operationId == ReservedOperation.view.getId()) { + Group g = groupRepository.findOne(groupId); + if (g != null) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.GROUP_PUBLISHED, g.getName(), + true, true)); + if (g.getId() == ReservedGroup.all.getId()) { + isPublishedToAll = true; + } + } + } + } + + if (isPublishedToAll) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.IS_PUBLISHED_TO_ALL, "y", true, true)); + } else { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.IS_PUBLISHED_TO_ALL, "n", true, true)); + } + + for (MetadataCategory category : fullMd.getCategories()) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.CAT, category.getName(), true, true)); + } + + // get status + Sort statusSort = new Sort(Sort.Direction.DESC, + MetadataStatus_.id.getName() + "." + MetadataStatusId_.changeDate.getName()); + List statuses = statusRepository.findAllByIdAndByType(id$, StatusValueType.workflow, statusSort); + if (!statuses.isEmpty()) { + MetadataStatus stat = statuses.get(0); + String status = String.valueOf(stat.getId().getStatusId()); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.STATUS, status, true, true)); + String statusChangeDate = stat.getId().getChangeDate().getDateAndTime(); + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.STATUS_CHANGE_DATE, statusChangeDate, + true, true)); + } + + // getValidationInfo + // -1 : not evaluated + // 0 : invalid + // 1 : valid + List validationInfo = metadataValidationRepository.findAllById_MetadataId(id$); + if (validationInfo.isEmpty()) { + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.VALID, "-1", true, true)); + } else { + String isValid = "1"; + for (MetadataValidation vi : validationInfo) { + String type = vi.getId().getValidationType(); + MetadataValidationStatus status = vi.getStatus(); + if (status == MetadataValidationStatus.INVALID && vi.isRequired()) { + isValid = "0"; + } + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.VALID + "_" + type, status.getCode(), + true, true)); + } + moreFields.add(SearchManager.makeField(Geonet.IndexFieldNames.VALID, isValid, true, true)); + } + + //To inject extra fields from BaseMetadataIndexer inherited beans + addExtraFields(fullMd, moreFields); + + if (searchManager == null) { + searchManager = servContext.getBean(SearchManager.class); + } + + searchManager.index(schemaManager.getSchemaDir(schema), md, metadataId, moreFields, metadataType, root, + forceRefreshReaders); + + } catch (Exception x) { + Log.error(Geonet.DATA_MANAGER, "The metadata document index with id=" + metadataId + + " is corrupt/invalid - ignoring it. Error: " + x.getMessage(), x); + fullMd = null; + } finally { + indexingLock.lock(); + try { + indexing.remove(metadataId); + } finally { + indexingLock.unlock(); + } + } + if (fullMd != null) { + this.publisher.publishEvent(new MetadataIndexCompleted(fullMd)); + } + } + + + /** + * Function to be overrided by children to add extra fields cleanly. + * Don't forget to call always super.addExtraFields, just in case + * + * @param fullMd + * @param moreFields + */ + protected void addExtraFields(AbstractMetadata fullMd, Vector moreFields) { + // If we are not using draft utils, mark all as "no draft" + // needed to be compatible with UI searches that check draft existence + if (!DraftMetadataIndexer.class.isInstance(this)) { + moreFields.addElement(SearchManager.makeField(Geonet.IndexFieldNames.DRAFT, "n", true, true)); + } + } + + private XmlSerializer getXmlSerializer() { + return xmlSerializer; + } + + /** + * @param context + * @param id + * @param md + * @throws Exception + */ + @Override + public void versionMetadata(ServiceContext context, String id, Element md) throws Exception { + if (svnManager != null) { + svnManager.createMetadataDir(id, context, md); + } + } + + /** + * @param beginAt + * @param interval + * @throws Exception + */ + @Override + public void rescheduleOptimizer(Calendar beginAt, int interval) throws Exception { + searchManager.rescheduleOptimizer(beginAt, interval); + } + + /** + * @throws Exception + */ + @Override + public void disableOptimizer() throws Exception { + searchManager.disableOptimizer(); + } + + private ServiceContext getServiceContext() { + ServiceContext context = ServiceContext.get(); + return context == null ? servContext : context; + } + + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java index e133eeafe7b..55875188bbc 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java @@ -1,22 +1,54 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Maps; -import com.google.common.collect.SetMultimap; -import com.google.common.collect.Sets; -import jeeves.constants.Jeeves; -import jeeves.server.ServiceConfig; -import jeeves.server.UserSession; -import jeeves.server.context.ServiceContext; -import jeeves.transaction.TransactionManager; -import jeeves.transaction.TransactionTask; -import jeeves.xlink.Processor; -import org.apache.commons.lang.NotImplementedException; +import static org.springframework.data.jpa.domain.Specifications.where; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.criteria.Root; +import javax.transaction.Transactional; + import org.apache.commons.lang.StringUtils; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.constants.Edit; @@ -83,6 +115,7 @@ import org.fao.geonet.repository.specification.MetadataFileUploadSpecs; import org.fao.geonet.repository.specification.MetadataSpecs; import org.fao.geonet.repository.specification.OperationAllowedSpecs; +import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.jdom.Element; import org.jdom.JDOMException; @@ -99,398 +132,385 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.transaction.TransactionStatus; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.criteria.Root; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.UUID; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; -import static org.springframework.data.jpa.domain.Specifications.where; +import jeeves.constants.Jeeves; +import jeeves.server.ServiceConfig; +import jeeves.server.UserSession; +import jeeves.server.context.ServiceContext; +import jeeves.transaction.TransactionManager; +import jeeves.transaction.TransactionTask; +import jeeves.xlink.Processor; public class BaseMetadataManager implements IMetadataManager { private static final Logger LOGGER_DATA_MANAGER = LoggerFactory.getLogger(Geonet.DATA_MANAGER); @Autowired - private IMetadataUtils metadataUtils; - @Autowired - private IMetadataIndexer metadataIndexer; - @Autowired - private IMetadataValidator metadataValidator; - @Autowired - private IMetadataOperations metadataOperations; - @Autowired - private IMetadataSchemaUtils metadataSchemaUtils; - @Autowired - private GroupRepository groupRepository; - @Autowired - private MetadataStatusRepository metadataStatusRepository; - @Autowired - private MetadataValidationRepository metadataValidationRepository; - @Autowired - private MetadataRepository metadataRepository; - @Autowired - private SearchManager searchManager; - - private EditLib editLib; - @Autowired - private MetadataRatingByIpRepository metadataRatingByIpRepository; - @Autowired - private MetadataFileUploadRepository metadataFileUploadRepository; - @Autowired(required = false) - private XmlSerializer xmlSerializer; - @Autowired - @Lazy - private SettingManager settingManager; - @Autowired - private MetadataCategoryRepository metadataCategoryRepository; - @Autowired(required = false) - private HarvestInfoProvider harvestInfoProvider; - @Autowired - private UserRepository userRepository; - @Autowired - private SchemaManager schemaManager; - @Autowired - private ThesaurusManager thesaurusManager; - @Autowired - private AccessManager accessManager; - @Autowired - private UserSavedSelectionRepository userSavedSelectionRepository; - - private static final int METADATA_BATCH_PAGE_SIZE = 100000; - private String baseURL; - - @Autowired - private ApplicationContext _applicationContext; - @PersistenceContext - private EntityManager _entityManager; - - @Override - public EditLib getEditLib() { - return editLib; - } - - /** - * To avoid cyclic references on autowired - */ - @PostConstruct - public void init() { - editLib = new EditLib(schemaManager); - metadataValidator.setMetadataManager(this); - metadataUtils.setMetadataManager(this); - metadataIndexer.setMetadataManager(this); - } - - public void init(ServiceContext context, Boolean force) throws Exception { - metadataUtils = context.getBean(IMetadataUtils.class); - metadataIndexer = context.getBean(IMetadataIndexer.class); - metadataStatusRepository = context.getBean(MetadataStatusRepository.class); - metadataValidationRepository = context.getBean(MetadataValidationRepository.class); - metadataRepository = context.getBean(MetadataRepository.class); - metadataValidator = context.getBean(IMetadataValidator.class); - metadataSchemaUtils = context.getBean(IMetadataSchemaUtils.class); - searchManager = context.getBean(SearchManager.class); - metadataRatingByIpRepository = context.getBean(MetadataRatingByIpRepository.class); - metadataFileUploadRepository = context.getBean(MetadataFileUploadRepository.class); - groupRepository = context.getBean(GroupRepository.class); - xmlSerializer = context.getBean(XmlSerializer.class); - settingManager = context.getBean(SettingManager.class); - metadataCategoryRepository = context.getBean(MetadataCategoryRepository.class); - try { - harvestInfoProvider = context.getBean(HarvestInfoProvider.class); - } catch (Exception e) { - // If it doesn't exist, that's fine - } - userRepository = context.getBean(UserRepository.class); - schemaManager = context.getBean(SchemaManager.class); - thesaurusManager = context.getBean(ThesaurusManager.class); - accessManager = context.getBean(AccessManager.class); - - // From DataManager: - - // get lastchangedate of all metadata in index - Map docs = getSearchManager().getDocsChangeDate(); - - // set up results HashMap for post processing of records to be indexed - ArrayList toIndex = new ArrayList(); + protected IMetadataUtils metadataUtils; + @Autowired + private IMetadataIndexer metadataIndexer; + @Autowired + private IMetadataValidator metadataValidator; + @Autowired + private IMetadataOperations metadataOperations; + @Autowired + private IMetadataSchemaUtils metadataSchemaUtils; + @Autowired + private GroupRepository groupRepository; + @Autowired + private MetadataStatusRepository metadataStatusRepository; + @Autowired + private MetadataValidationRepository metadataValidationRepository; + @Autowired + private MetadataRepository metadataRepository; + @Autowired + private SearchManager searchManager; + + private EditLib editLib; + @Autowired + private MetadataRatingByIpRepository metadataRatingByIpRepository; + @Autowired + private MetadataFileUploadRepository metadataFileUploadRepository; + @Autowired(required = false) + private XmlSerializer xmlSerializer; + @Autowired + @Lazy + private SettingManager settingManager; + @Autowired + private MetadataCategoryRepository metadataCategoryRepository; + @Autowired(required = false) + private HarvestInfoProvider harvestInfoProvider; + @Autowired + private UserRepository userRepository; + @Autowired + private SchemaManager schemaManager; + @Autowired + private ThesaurusManager thesaurusManager; + @Autowired + protected AccessManager accessManager; + @Autowired + private UserSavedSelectionRepository userSavedSelectionRepository; + + private static final int METADATA_BATCH_PAGE_SIZE = 50000; + private String baseURL; + + @Autowired + private ApplicationContext _applicationContext; + @PersistenceContext + private EntityManager _entityManager; + + @Override + public EditLib getEditLib() { + return editLib; + } + + /** + * To avoid cyclic references on autowired + */ + @PostConstruct + public void init() { + editLib = new EditLib(schemaManager); + metadataValidator.setMetadataManager(this); + metadataUtils.setMetadataManager(this); + metadataIndexer.setMetadataManager(this); + } + + public void init(ServiceContext context, Boolean force) throws Exception { + metadataUtils = context.getBean(IMetadataUtils.class); + metadataIndexer = context.getBean(IMetadataIndexer.class); + metadataStatusRepository = context.getBean(MetadataStatusRepository.class); + metadataValidationRepository = context.getBean(MetadataValidationRepository.class); + metadataRepository = context.getBean(MetadataRepository.class); + metadataValidator = context.getBean(IMetadataValidator.class); + metadataSchemaUtils = context.getBean(IMetadataSchemaUtils.class); + searchManager = context.getBean(SearchManager.class); + metadataRatingByIpRepository = context.getBean(MetadataRatingByIpRepository.class); + metadataFileUploadRepository = context.getBean(MetadataFileUploadRepository.class); + groupRepository = context.getBean(GroupRepository.class); + xmlSerializer = context.getBean(XmlSerializer.class); + settingManager = context.getBean(SettingManager.class); + metadataCategoryRepository = context.getBean(MetadataCategoryRepository.class); + try { + harvestInfoProvider = context.getBean(HarvestInfoProvider.class); + } catch (Exception e) { + // If it doesn't exist, that's fine + } + userRepository = context.getBean(UserRepository.class); + schemaManager = context.getBean(SchemaManager.class); + thesaurusManager = context.getBean(ThesaurusManager.class); + accessManager = context.getBean(AccessManager.class); + + // From DataManager: + + // get lastchangedate of all metadata in index + Map docs = getSearchManager().getDocsChangeDate(); + + // set up results HashMap for post processing of records to be indexed + ArrayList toIndex = new ArrayList(); LOGGER_DATA_MANAGER.debug("INDEX CONTENT:"); - Sort sortByMetadataChangeDate = SortUtils.createSort(Metadata_.dataInfo, MetadataDataInfo_.changeDate); - int currentPage = 0; - Page> results = metadataRepository.findAllIdsAndChangeDates( - new PageRequest(currentPage, METADATA_BATCH_PAGE_SIZE, sortByMetadataChangeDate)); + Sort sortByMetadataChangeDate = SortUtils.createSort(Metadata_.dataInfo, MetadataDataInfo_.changeDate); + int currentPage = 0; + Page> results = metadataUtils.findAllIdsAndChangeDates( + new PageRequest(currentPage, METADATA_BATCH_PAGE_SIZE, sortByMetadataChangeDate)); - // index all metadata in DBMS if needed - while (results.getNumberOfElements() > 0) { - for (Pair result : results) { + // index all metadata in DBMS if needed + while (results.getNumberOfElements() > 0) { + for (Pair result : results) { - // get metadata - String id = String.valueOf(result.one()); + // get metadata + String id = String.valueOf(result.one()); LOGGER_DATA_MANAGER.debug("- record ({})", id); - String idxLastChange = docs.get(id); + String idxLastChange = docs.get(id); - // if metadata is not indexed index it - if (idxLastChange == null) { + // if metadata is not indexed index it + if (idxLastChange == null) { LOGGER_DATA_MANAGER.debug("- will be indexed"); - toIndex.add(id); + toIndex.add(id); - // else, if indexed version is not the latest index it - } else { - docs.remove(id); + // else, if indexed version is not the latest index it + } else { + docs.remove(id); - String lastChange = result.two().toString(); + String lastChange = result.two().toString(); LOGGER_DATA_MANAGER.debug("- lastChange: {}", lastChange); LOGGER_DATA_MANAGER.debug("- idxLastChange: {}", idxLastChange); - // date in index contains 't', date in DBMS contains 'T' - if (force || !idxLastChange.equalsIgnoreCase(lastChange)) { + // date in index contains 't', date in DBMS contains 'T' + if (force || !idxLastChange.equalsIgnoreCase(lastChange)) { LOGGER_DATA_MANAGER.debug("- will be indexed"); - toIndex.add(id); - } - } - } - - currentPage++; - results = metadataRepository.findAllIdsAndChangeDates( - new PageRequest(currentPage, METADATA_BATCH_PAGE_SIZE, sortByMetadataChangeDate)); - } - - // if anything to index then schedule it to be done after servlet is - // up so that any links to local fragments are resolvable - if (toIndex.size() > 0) { - metadataIndexer.batchIndexInThreadPool(context, toIndex); - } - - if (docs.size() > 0) { // anything left? + toIndex.add(id); + } + } + } + + currentPage++; + results = metadataRepository.findAllIdsAndChangeDates( + new PageRequest(currentPage, METADATA_BATCH_PAGE_SIZE, sortByMetadataChangeDate)); + } + + // if anything to index then schedule it to be done after servlet is + // up so that any links to local fragments are resolvable + if (toIndex.size() > 0) { + metadataIndexer.batchIndexInThreadPool(context, toIndex); + } + + if (docs.size() > 0) { // anything left? LOGGER_DATA_MANAGER.debug("INDEX HAS RECORDS THAT ARE NOT IN DB:"); - } + } - // remove from index metadata not in DBMS - for (String id : docs.keySet()) { - getSearchManager().delete(id); + // remove from index metadata not in DBMS + for (String id : docs.keySet()) { + getSearchManager().delete(id); LOGGER_DATA_MANAGER.debug("- removed record ({}) from index", id); - } - } - - private SearchManager getSearchManager() { - return searchManager; - } - - /** - * You should not use a direct flush. If you need to use this to properly run - * your code, you are missing something. Check the transaction annotations and - * try to comply to Spring/Hibernate - */ - @Override - @Deprecated - public void flush() { - TransactionManager.runInTransaction("DataManager flush()", getApplicationContext(), - TransactionManager.TransactionRequirement.CREATE_ONLY_WHEN_NEEDED, - TransactionManager.CommitBehavior.ALWAYS_COMMIT, false, new TransactionTask() { - @Override - public Object doInTransaction(TransactionStatus transaction) throws Throwable { - _entityManager.flush(); - return null; - } - }); - - } - - private ApplicationContext getApplicationContext() { - final ConfigurableApplicationContext applicationContext = ApplicationContextHolder.get(); - return applicationContext == null ? _applicationContext : applicationContext; - } - - private void deleteMetadataFromDB(ServiceContext context, String id) throws Exception { - AbstractMetadata metadata = metadataUtils.findOne(Integer.valueOf(id)); - if (!settingManager.getValueAsBool(Settings.SYSTEM_XLINK_ALLOW_REFERENCED_DELETION) - && metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE) { - MetaSearcher searcher = searcherForReferencingMetadata(context, metadata); - Map result = ((LuceneSearcher) searcher).getAllMdInfo(context, 1); - if (result.size() > 0) { - throw new Exception("this template is referenced."); - } - } - - // --- remove operations - metadataOperations.deleteMetadataOper(context, id, false); - - int intId = Integer.parseInt(id); - metadataRatingByIpRepository.deleteAllById_MetadataId(intId); - metadataValidationRepository.deleteAllById_MetadataId(intId); - metadataStatusRepository.deleteAllById_MetadataId(intId); - userSavedSelectionRepository.deleteAllByUuid(metadataUtils.getMetadataUuid(id)); - - // Logical delete for metadata file uploads - PathSpec deletedDatePathSpec = new PathSpec() { - @Override - public javax.persistence.criteria.Path getPath(Root root) { - return root.get(MetadataFileUpload_.deletedDate); - } - }; - - metadataFileUploadRepository.createBatchUpdateQuery(deletedDatePathSpec, new ISODate().toString(), - MetadataFileUploadSpecs.isNotDeletedForMetadata(intId)); - - // --- remove metadata - getXmlSerializer().delete(id, context); - } - - - private MetaSearcher searcherForReferencingMetadata(ServiceContext context, AbstractMetadata metadata) - throws Exception { - MetaSearcher searcher = context.getBean(SearchManager.class).newSearcher(SearcherType.LUCENE, - Geonet.File.SEARCH_LUCENE); - Element parameters = new Element(Jeeves.Elem.REQUEST); - parameters.addContent(new Element(Geonet.IndexFieldNames.XLINK).addContent("*" + metadata.getUuid() + "*")); - parameters.addContent(new Element(Geonet.SearchResult.BUILD_SUMMARY).setText("false")); - parameters.addContent(new Element(SearchParameter.ISADMIN).addContent("true")); - parameters.addContent(new Element(SearchParameter.ISTEMPLATE).addContent("y or n")); - ServiceConfig config = new ServiceConfig(); - searcher.search(context, parameters, config); - return searcher; - } - - // -------------------------------------------------------------------------- - // --- - // --- Metadata thumbnail API - // --- - // -------------------------------------------------------------------------- - - private XmlSerializer getXmlSerializer() { - return xmlSerializer; - } - - /** - * Removes a metadata. - */ - @Override - public synchronized void deleteMetadata(ServiceContext context, String metadataId) throws Exception { - String uuid = metadataUtils.getMetadataUuid(metadataId); - AbstractMetadata findOne = metadataUtils.findOne(metadataId); - if (findOne != null) { - boolean isMetadata = findOne.getDataInfo().getType() == MetadataType.METADATA; - - deleteMetadataFromDB(context, metadataId); - - // Notifies the metadata change to metatada notifier service - if (isMetadata) { - context.getBean(MetadataNotifierManager.class).deleteMetadata(metadataId, uuid, context); - } - } - - // --- update search criteria - getSearchManager().delete(metadataId + ""); - // _entityManager.flush(); - // _entityManager.clear(); - } - - /** - * - * @param context - * @param metadataId - * @throws Exception - */ - @Override - public synchronized void deleteMetadataGroup(ServiceContext context, String metadataId) throws Exception { - deleteMetadataFromDB(context, metadataId); - getSearchManager().delete(metadataId); - } - - /** - * Creates a new metadata duplicating an existing template creating a random - * uuid. - * - * @param isTemplate - * @param fullRightsForGroup - */ - @Override - public String createMetadata(ServiceContext context, String templateId, String groupOwner, String source, int owner, - String parentUuid, String isTemplate, boolean fullRightsForGroup) throws Exception { - - return createMetadata(context, templateId, groupOwner, source, owner, parentUuid, isTemplate, - fullRightsForGroup, UUID.randomUUID().toString()); - } - - /** - * Creates a new metadata duplicating an existing template with an specified - * uuid. - * - * @param isTemplate - * @param fullRightsForGroup - */ - @Override - public String createMetadata(ServiceContext context, String templateId, String groupOwner, String source, int owner, - String parentUuid, String isTemplate, boolean fullRightsForGroup, String uuid) throws Exception { - AbstractMetadata templateMetadata = metadataUtils.findOne(templateId); - if (templateMetadata == null) { - throw new IllegalArgumentException("Template id not found : " + templateId); - } - - String schema = templateMetadata.getDataInfo().getSchemaId(); - String data = templateMetadata.getData(); - Element xml = Xml.loadString(data, false); - boolean isMetadata = templateMetadata.getDataInfo().getType() == MetadataType.METADATA; - setMetadataTitle(schema, xml, context.getLanguage(), !isMetadata); - if (isMetadata) { - xml = updateFixedInfo(schema, Optional.absent(), uuid, xml, parentUuid, UpdateDatestamp.NO, - context); - } - final Metadata newMetadata = new Metadata(); - newMetadata.setUuid(uuid); - newMetadata.getDataInfo().setChangeDate(new ISODate()).setCreateDate(new ISODate()).setSchemaId(schema) - .setType(MetadataType.lookup(isTemplate)); - newMetadata.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)).setOwner(owner).setSourceId(source); - - // If there is a default category for the group, use it: - Group group = groupRepository.findOne(Integer.valueOf(groupOwner)); - if (group.getDefaultCategory() != null) { - newMetadata.getMetadataCategories().add(group.getDefaultCategory()); - } - Collection filteredCategories = Collections2.filter(templateMetadata.getMetadataCategories(), - new Predicate() { - @Override - public boolean apply(@Nullable MetadataCategory input) { - return input != null; - } - }); - - newMetadata.getMetadataCategories().addAll(filteredCategories); - - int finalId = insertMetadata(context, newMetadata, xml, false, true, true, UpdateDatestamp.YES, - fullRightsForGroup, true).getId(); - - return String.valueOf(finalId); - } + } + } + + protected SearchManager getSearchManager() { + return searchManager; + } + + /** + * You should not use a direct flush. If you need to use this to properly run + * your code, you are missing something. Check the transaction annotations and + * try to comply to Spring/Hibernate + */ + @Override + @Deprecated + public void flush() { + TransactionManager.runInTransaction("DataManager flush()", getApplicationContext(), + TransactionManager.TransactionRequirement.CREATE_ONLY_WHEN_NEEDED, + TransactionManager.CommitBehavior.ALWAYS_COMMIT, false, new TransactionTask() { + @Override + public Object doInTransaction(TransactionStatus transaction) throws Throwable { + _entityManager.flush(); + return null; + } + }); + + } + + private ApplicationContext getApplicationContext() { + final ConfigurableApplicationContext applicationContext = ApplicationContextHolder.get(); + return applicationContext == null ? _applicationContext : applicationContext; + } + + protected void deleteMetadataFromDB(ServiceContext context, String id) throws Exception { + AbstractMetadata metadata = metadataUtils.findOne(Integer.valueOf(id)); + if (!settingManager.getValueAsBool(Settings.SYSTEM_XLINK_ALLOW_REFERENCED_DELETION) + && metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE) { + MetaSearcher searcher = searcherForReferencingMetadata(context, metadata); + Map result = ((LuceneSearcher) searcher).getAllMdInfo(context, 1); + if (result.size() > 0) { + throw new Exception("this template is referenced."); + } + } + + // --- remove operations + metadataOperations.deleteMetadataOper(context, id, false); + + int intId = Integer.parseInt(id); + metadataRatingByIpRepository.deleteAllById_MetadataId(intId); + metadataValidationRepository.deleteAllById_MetadataId(intId); + metadataStatusRepository.deleteAllById_MetadataId(intId); + userSavedSelectionRepository.deleteAllByUuid(metadataUtils.getMetadataUuid(id)); + + // Logical delete for metadata file uploads + PathSpec deletedDatePathSpec = new PathSpec() { + @Override + public javax.persistence.criteria.Path getPath(Root root) { + return root.get(MetadataFileUpload_.deletedDate); + } + }; + + metadataFileUploadRepository.createBatchUpdateQuery(deletedDatePathSpec, new ISODate().toString(), + MetadataFileUploadSpecs.isNotDeletedForMetadata(intId)); + + // --- remove metadata + getXmlSerializer().delete(id, context); + } + + private MetaSearcher searcherForReferencingMetadata(ServiceContext context, AbstractMetadata metadata) + throws Exception { + MetaSearcher searcher = context.getBean(SearchManager.class).newSearcher(SearcherType.LUCENE, + Geonet.File.SEARCH_LUCENE); + Element parameters = new Element(Jeeves.Elem.REQUEST); + parameters.addContent(new Element(Geonet.IndexFieldNames.XLINK).addContent("*" + metadata.getUuid() + "*")); + parameters.addContent(new Element(Geonet.SearchResult.BUILD_SUMMARY).setText("false")); + parameters.addContent(new Element(SearchParameter.ISADMIN).addContent("true")); + parameters.addContent(new Element(SearchParameter.ISTEMPLATE).addContent("y or n")); + ServiceConfig config = new ServiceConfig(); + searcher.search(context, parameters, config); + return searcher; + } + + // -------------------------------------------------------------------------- + // --- + // --- Metadata thumbnail API + // --- + // -------------------------------------------------------------------------- + + private XmlSerializer getXmlSerializer() { + return xmlSerializer; + } + + /** + * Removes a metadata. + */ + @Override + public void deleteMetadata(ServiceContext context, String metadataId) throws Exception { + AbstractMetadata findOne = metadataUtils.findOne(metadataId); + if (findOne != null) { + boolean isMetadata = findOne.getDataInfo().getType() == MetadataType.METADATA; + + deleteMetadataFromDB(context, metadataId); + + // Notifies the metadata change to metatada notifier service + if (isMetadata) { + context.getBean(MetadataNotifierManager.class).deleteMetadata(metadataId, findOne.getUuid(), context); + } + } + + // --- update search criteria + getSearchManager().delete(metadataId + ""); + // _entityManager.flush(); + // _entityManager.clear(); + } /** - * Update XML document title as defined by schema plugin - * in xpathTitle property. + * @param context + * @param metadataId + * @throws Exception + */ + @Override + public void deleteMetadataGroup(ServiceContext context, String metadataId) throws Exception { + deleteMetadataFromDB(context, metadataId); + getSearchManager().delete(metadataId); + } + + /** + * Creates a new metadata duplicating an existing template creating a random + * uuid. + * + * @param isTemplate + * @param fullRightsForGroup + */ + @Override + public String createMetadata(ServiceContext context, String templateId, String groupOwner, String source, int owner, + String parentUuid, String isTemplate, boolean fullRightsForGroup) throws Exception { + + return createMetadata(context, templateId, groupOwner, source, owner, parentUuid, isTemplate, + fullRightsForGroup, UUID.randomUUID().toString()); + } + + /** + * Creates a new metadata duplicating an existing template with an specified + * uuid. + * + * @param isTemplate + * @param fullRightsForGroup + */ + @Override + public String createMetadata(ServiceContext context, String templateId, String groupOwner, String source, int owner, + String parentUuid, String isTemplate, boolean fullRightsForGroup, String uuid) throws Exception { + AbstractMetadata templateMetadata = metadataUtils.findOne(templateId); + if (templateMetadata == null) { + throw new IllegalArgumentException("Template id not found : " + templateId); + } + + String schema = templateMetadata.getDataInfo().getSchemaId(); + String data = templateMetadata.getData(); + Element xml = Xml.loadString(data, false); + boolean isMetadata = templateMetadata.getDataInfo().getType() == MetadataType.METADATA; + setMetadataTitle(schema, xml, context.getLanguage(), !isMetadata); + if (isMetadata) { + xml = updateFixedInfo(schema, Optional.absent(), uuid, xml, parentUuid, UpdateDatestamp.NO, + context); + } + final Metadata newMetadata = new Metadata(); + newMetadata.setUuid(uuid); + newMetadata.getDataInfo().setChangeDate(new ISODate()).setCreateDate(new ISODate()).setSchemaId(schema) + .setType(MetadataType.lookup(isTemplate)); + newMetadata.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)).setOwner(owner).setSourceId(source); + + // If there is a default category for the group, use it: + Group group = groupRepository.findOne(Integer.valueOf(groupOwner)); + if (group.getDefaultCategory() != null) { + newMetadata.getMetadataCategories().add(group.getDefaultCategory()); + } + Collection filteredCategories = Collections2.filter(templateMetadata.getCategories(), + new Predicate() { + @Override + public boolean apply(@Nullable MetadataCategory input) { + return input != null; + } + }); + + newMetadata.getMetadataCategories().addAll(filteredCategories); + + int finalId = insertMetadata(context, newMetadata, xml, false, true, true, UpdateDatestamp.YES, + fullRightsForGroup, true).getId(); + + return String.valueOf(finalId); + } + + /** + * Update XML document title as defined by schema plugin in xpathTitle property. */ private void setMetadataTitle(String schema, Element xml, String language, boolean fromTemplate) { - ResourceBundle messages = ResourceBundle.getBundle( - "org.fao.geonet.api.Messages", - new Locale(language)); + ResourceBundle messages = ResourceBundle.getBundle("org.fao.geonet.api.Messages", new Locale(language)); SchemaPlugin schemaPlugin = SchemaManager.getSchemaPlugin(schema); List xpathTitle = schemaPlugin.getXpathTitle(); @@ -498,203 +518,182 @@ private void setMetadataTitle(String schema, Element xml, String language, boole xpathTitle.forEach(path -> { List titleNodes = null; try { - titleNodes = Xml.selectNodes( - xml, - path, - new ArrayList(schemaPlugin.getNamespaces())); + titleNodes = Xml.selectNodes(xml, path, new ArrayList(schemaPlugin.getNamespaces())); for (Object o : titleNodes) { if (o instanceof Element) { Element title = (Element) o; title.setText(String.format( messages.getString( - "metadata.title.createdFrom" + (fromTemplate ? "Template" : "Record")), + "metadata.title.createdFrom" + (fromTemplate ? "Template" : "Record")), title.getTextTrim(), - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) - )); + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); } } } catch (JDOMException e) { - LOGGER_DATA_MANAGER.debug("Check xpath '{}' for schema plugin '{}'. Error is '{}'.", new Object[] { - path, schema, e.getMessage()}); + LOGGER_DATA_MANAGER.debug("Check xpath '{}' for schema plugin '{}'. Error is '{}'.", + new Object[]{path, schema, e.getMessage()}); } }); } } /** - * Inserts a metadata into the database, optionally indexing it, and optionally - * applying automatic changes to it (update-fixed-info). - * - * @param context - * the context describing the user and service - * @param schema - * XSD this metadata conforms to - * @param metadataXml - * the metadata to store - * @param uuid - * unique id for this metadata - * @param owner - * user who owns this metadata - * @param groupOwner - * group this metadata belongs to - * @param source - * id of the origin of this metadata (harvesting source, etc.) - * @param metadataType - * whether this metadata is a template - * @param docType - * ?! - * @param category - * category of this metadata - * @param createDate - * date of creation - * @param changeDate - * date of modification - * @param ufo - * whether to apply automatic changes - * @param index - * whether to index this metadata - * @return id, as a string - * @throws Exception - * hmm - */ - @Override - public String insertMetadata(ServiceContext context, String schema, Element metadataXml, String uuid, int owner, - String groupOwner, String source, String metadataType, String docType, String category, String createDate, - String changeDate, boolean ufo, boolean index) throws Exception { - - boolean notifyChange = true; - - if (source == null) { - source = settingManager.getSiteId(); - } - - if (StringUtils.isBlank(metadataType)) { - metadataType = MetadataType.METADATA.codeString; - } - final Metadata newMetadata = new Metadata(); - newMetadata.setUuid(uuid); - final ISODate isoChangeDate = changeDate != null ? new ISODate(changeDate) : new ISODate(); - final ISODate isoCreateDate = createDate != null ? new ISODate(createDate) : new ISODate(); - newMetadata.getDataInfo().setChangeDate(isoChangeDate).setCreateDate(isoCreateDate).setSchemaId(schema) - .setDoctype(docType).setRoot(metadataXml.getQualifiedName()).setType(MetadataType.lookup(metadataType)); - newMetadata.getSourceInfo().setOwner(owner).setSourceId(source); - if (StringUtils.isNotEmpty(groupOwner)) { - newMetadata.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)); - } - if (StringUtils.isNotEmpty(category)) { - MetadataCategory metadataCategory = metadataCategoryRepository.findOneByName(category); - if (metadataCategory == null) { - throw new IllegalArgumentException("No category found with name: " + category); - } - newMetadata.getMetadataCategories().add(metadataCategory); - } else if (StringUtils.isNotEmpty(groupOwner)) { - // If the group has a default category, use it - Group group = groupRepository.findOne(Integer.valueOf(groupOwner)); - if (group.getDefaultCategory() != null) { - newMetadata.getMetadataCategories().add(group.getDefaultCategory()); - } - } - - boolean fullRightsForGroup = false; - - int finalId = insertMetadata(context, newMetadata, metadataXml, notifyChange, index, ufo, UpdateDatestamp.NO, - fullRightsForGroup, false).getId(); - - return String.valueOf(finalId); - } - - @Override - public AbstractMetadata insertMetadata(ServiceContext context, AbstractMetadata newMetadata, Element metadataXml, - boolean notifyChange, boolean index, boolean updateFixedInfo, UpdateDatestamp updateDatestamp, - boolean fullRightsForGroup, boolean forceRefreshReaders) throws Exception { - final String schema = newMetadata.getDataInfo().getSchemaId(); - - // Check if the schema is allowed by settings - String mdImportSetting = settingManager.getValue(Settings.METADATA_IMPORT_RESTRICT); - if (mdImportSetting != null && !mdImportSetting.equals("")) { - if (!newMetadata.getHarvestInfo().isHarvested() - && newMetadata.getDataInfo().getType() == MetadataType.METADATA - && !Arrays.asList(mdImportSetting.split(",")).contains(schema)) { - throw new IllegalArgumentException( - schema + " is not permitted in the database as a non-harvested metadata. " - + "Apply a import stylesheet to convert file to allowed schemas"); - } - } - - // --- force namespace prefix for iso19139 metadata - setNamespacePrefixUsingSchemas(schema, metadataXml); - - if (updateFixedInfo && newMetadata.getDataInfo().getType() == MetadataType.METADATA) { - String parentUuid = null; - metadataXml = updateFixedInfo(schema, Optional.absent(), newMetadata.getUuid(), metadataXml, - parentUuid, updateDatestamp, context); - } - - // --- store metadata - final AbstractMetadata savedMetadata = getXmlSerializer().insert(newMetadata, metadataXml, context); - - final String stringId = String.valueOf(savedMetadata.getId()); - String groupId = null; - final Integer groupIdI = newMetadata.getSourceInfo().getGroupOwner(); - if (groupIdI != null) { - groupId = String.valueOf(groupIdI); - } - metadataOperations.copyDefaultPrivForGroup(context, stringId, groupId, fullRightsForGroup); - - if (index) { - metadataIndexer.indexMetadata(stringId, forceRefreshReaders, null); - } - - if (notifyChange) { - // Notifies the metadata change to metatada notifier service - metadataUtils.notifyMetadataChange(metadataXml, stringId); - } - return savedMetadata; - } - - /** - * Retrieves a metadata (in xml) given its id; adds editing information if - * requested and validation errors if requested. - * - * @param forEditing - * Add extra element to build metadocument - * {@link EditLib#expandElements(String, Element)} - * @param keepXlinkAttributes - * When XLinks are resolved in non edit mode, do not remove XLink - * attributes. - */ - @Override - public Element getMetadata(ServiceContext srvContext, String id, boolean forEditing, - boolean withEditorValidationErrors, boolean keepXlinkAttributes) throws Exception { - boolean doXLinks = getXmlSerializer().resolveXLinks(); - Element metadataXml = getXmlSerializer().selectNoXLinkResolver(id, false, forEditing); - if (metadataXml == null) - return null; - - String version = null; - - if (forEditing) { // copy in xlink'd fragments but leave xlink atts to editor - if (doXLinks) - Processor.processXLink(metadataXml, srvContext); - String schema = metadataSchemaUtils.getMetadataSchema(id); - - // Inflate metadata - Path inflateStyleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.INFLATE_METADATA); - if (Files.exists(inflateStyleSheet)) { - // --- setup environment - Element env = new Element("env"); - env.addContent(new Element("lang").setText(srvContext.getLanguage())); - - // add original metadata to result - Element result = new Element("root"); - result.addContent(metadataXml); - result.addContent(env); - - metadataXml = Xml.transform(result, inflateStyleSheet); - } - - if (withEditorValidationErrors) { + * Inserts a metadata into the database, optionally indexing it, and optionally + * applying automatic changes to it (update-fixed-info). + * + * @param context the context describing the user and service + * @param schema XSD this metadata conforms to + * @param metadataXml the metadata to store + * @param uuid unique id for this metadata + * @param owner user who owns this metadata + * @param groupOwner group this metadata belongs to + * @param source id of the origin of this metadata (harvesting source, etc.) + * @param metadataType whether this metadata is a template + * @param docType ?! + * @param category category of this metadata + * @param createDate date of creation + * @param changeDate date of modification + * @param ufo whether to apply automatic changes + * @param index whether to index this metadata + * @return id, as a string + * @throws Exception hmm + */ + @Override + public String insertMetadata(ServiceContext context, String schema, Element metadataXml, String uuid, int owner, + String groupOwner, String source, String metadataType, String docType, String category, String createDate, + String changeDate, boolean ufo, boolean index) throws Exception { + + boolean notifyChange = true; + + if (source == null) { + source = settingManager.getSiteId(); + } + + if (StringUtils.isBlank(metadataType)) { + metadataType = MetadataType.METADATA.codeString; + } + final Metadata newMetadata = new Metadata(); + newMetadata.setUuid(uuid); + final ISODate isoChangeDate = changeDate != null ? new ISODate(changeDate) : new ISODate(); + final ISODate isoCreateDate = createDate != null ? new ISODate(createDate) : new ISODate(); + newMetadata.getDataInfo().setChangeDate(isoChangeDate).setCreateDate(isoCreateDate).setSchemaId(schema) + .setDoctype(docType).setRoot(metadataXml.getQualifiedName()).setType(MetadataType.lookup(metadataType)); + newMetadata.getSourceInfo().setOwner(owner).setSourceId(source); + if (StringUtils.isNotEmpty(groupOwner)) { + newMetadata.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)); + } + if (StringUtils.isNotEmpty(category)) { + MetadataCategory metadataCategory = metadataCategoryRepository.findOneByName(category); + if (metadataCategory == null) { + throw new IllegalArgumentException("No category found with name: " + category); + } + newMetadata.getMetadataCategories().add(metadataCategory); + } else if (StringUtils.isNotEmpty(groupOwner)) { + // If the group has a default category, use it + Group group = groupRepository.findOne(Integer.valueOf(groupOwner)); + if (group.getDefaultCategory() != null) { + newMetadata.getMetadataCategories().add(group.getDefaultCategory()); + } + } + + boolean fullRightsForGroup = false; + + int finalId = insertMetadata(context, newMetadata, metadataXml, notifyChange, index, ufo, UpdateDatestamp.NO, + fullRightsForGroup, false).getId(); + + return String.valueOf(finalId); + } + + @Override + public AbstractMetadata insertMetadata(ServiceContext context, AbstractMetadata newMetadata, Element metadataXml, + boolean notifyChange, boolean index, boolean updateFixedInfo, UpdateDatestamp updateDatestamp, + boolean fullRightsForGroup, boolean forceRefreshReaders) throws Exception { + final String schema = newMetadata.getDataInfo().getSchemaId(); + + // Check if the schema is allowed by settings + String mdImportSetting = settingManager.getValue(Settings.METADATA_IMPORT_RESTRICT); + if (mdImportSetting != null && !mdImportSetting.equals("")) { + if (!newMetadata.getHarvestInfo().isHarvested() + && newMetadata.getDataInfo().getType() == MetadataType.METADATA + && !Arrays.asList(mdImportSetting.split(",")).contains(schema)) { + throw new IllegalArgumentException( + schema + " is not permitted in the database as a non-harvested metadata. " + + "Apply a import stylesheet to convert file to allowed schemas"); + } + } + + // --- force namespace prefix for iso19139 metadata + setNamespacePrefixUsingSchemas(schema, metadataXml); + + if (updateFixedInfo && newMetadata.getDataInfo().getType() == MetadataType.METADATA) { + String parentUuid = null; + metadataXml = updateFixedInfo(schema, Optional.absent(), newMetadata.getUuid(), metadataXml, + parentUuid, updateDatestamp, context); + } + + // --- store metadata + final AbstractMetadata savedMetadata = getXmlSerializer().insert(newMetadata, metadataXml, context); + + final String stringId = String.valueOf(savedMetadata.getId()); + String groupId = null; + final Integer groupIdI = newMetadata.getSourceInfo().getGroupOwner(); + if (groupIdI != null) { + groupId = String.valueOf(groupIdI); + } + metadataOperations.copyDefaultPrivForGroup(context, stringId, groupId, fullRightsForGroup); + + if (index) { + metadataIndexer.indexMetadata(stringId, forceRefreshReaders, null); + } + + if (notifyChange) { + // Notifies the metadata change to metatada notifier service + metadataUtils.notifyMetadataChange(metadataXml, stringId); + } + return savedMetadata; + } + + /** + * Retrieves a metadata (in xml) given its id; adds editing information if + * requested and validation errors if requested. + * + * @param forEditing Add extra element to build metadocument + * {@link EditLib#expandElements(String, Element)} + * @param keepXlinkAttributes When XLinks are resolved in non edit mode, do not remove XLink + * attributes. + */ + @Override + public Element getMetadata(ServiceContext srvContext, String id, boolean forEditing, + boolean withEditorValidationErrors, boolean keepXlinkAttributes) throws Exception { + boolean doXLinks = getXmlSerializer().resolveXLinks(); + Element metadataXml = getXmlSerializer().selectNoXLinkResolver(id, false, forEditing); + if (metadataXml == null) + return null; + + String version = null; + + if (forEditing) { // copy in xlink'd fragments but leave xlink atts to editor + if (doXLinks) + Processor.processXLink(metadataXml, srvContext); + String schema = metadataSchemaUtils.getMetadataSchema(id); + + // Inflate metadata + Path inflateStyleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.INFLATE_METADATA); + if (Files.exists(inflateStyleSheet)) { + // --- setup environment + Element env = new Element("env"); + env.addContent(new Element("lang").setText(srvContext.getLanguage())); + + // add original metadata to result + Element result = new Element("root"); + result.addContent(metadataXml); + result.addContent(env); + + metadataXml = Xml.transform(result, inflateStyleSheet); + } + + if (withEditorValidationErrors) { final Pair versionAndReport = metadataValidator.doValidate(srvContext.getUserSession(), schema, id, metadataXml, srvContext.getLanguage(), forEditing); version = versionAndReport.two(); @@ -705,127 +704,131 @@ public Element getMetadata(ServiceContext srvContext, String id, boolean forEdit // to display errors related to elements. metadataXml.addContent(versionAndReport.one()); } else { - editLib.expandElements(schema, metadataXml); - version = editLib.getVersionForEditing(schema, id, metadataXml); - } - } else { - if (doXLinks) { - if (keepXlinkAttributes) { - Processor.processXLink(metadataXml, srvContext); - } else { - Processor.detachXLink(metadataXml, srvContext); - } - } - } - - metadataXml.addNamespaceDeclaration(Edit.NAMESPACE); - Element info = buildInfoElem(srvContext, id, version); - metadataXml.addContent(info); - - metadataXml.detach(); - return metadataXml; - } - - /** - * Retrieves a metadata (in xml) given its id. Use this method when you must - * retrieve a metadata in the same transaction. - */ - @Override - public Element getMetadata(String id) throws Exception { - Element md = getXmlSerializer().selectNoXLinkResolver(id, false, false); - if (md == null) - return null; - md.detach(); - return md; - } - - /** - * For update of owner info. - */ - @Override - public synchronized void updateMetadataOwner(final int id, final String owner, final String groupOwner) - throws Exception { - metadataRepository.update(id, new Updater() { - @Override - public void apply(@Nonnull Metadata entity) { - entity.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)); - entity.getSourceInfo().setOwner(Integer.valueOf(owner)); - } - }); - } - - /** - * Updates a metadata record. Deletes validation report currently in session (if - * any). If user asks for validation the validation report will be (re-)created - * then. - * - * @return metadata if the that was updated - */ - @Override - public synchronized AbstractMetadata updateMetadata(final ServiceContext context, final String metadataId, - final Element md, final boolean validate, final boolean ufo, final boolean index, final String lang, - final String changeDate, final boolean updateDateStamp) throws Exception { - Element metadataXml = md; - - // when invoked from harvesters, session is null? - UserSession session = context.getUserSession(); - if (session != null) { - session.removeProperty(Geonet.Session.VALIDATION_REPORT + metadataId); - } - String schema = metadataSchemaUtils.getMetadataSchema(metadataId); - if (ufo) { - String parentUuid = null; - Integer intId = Integer.valueOf(metadataId); - - final AbstractMetadata metadata = metadataUtils.findOne(metadataId); + editLib.expandElements(schema, metadataXml); + version = editLib.getVersionForEditing(schema, id, metadataXml); + } + } else { + if (doXLinks) { + if (keepXlinkAttributes) { + Processor.processXLink(metadataXml, srvContext); + } else { + Processor.detachXLink(metadataXml, srvContext); + } + } + } + + metadataXml.addNamespaceDeclaration(Edit.NAMESPACE); + Element info = buildInfoElem(srvContext, id, version); + metadataXml.addContent(info); + + metadataXml.detach(); + return metadataXml; + } + + /** + * Retrieves a metadata (in xml) given its id. Use this method when you must + * retrieve a metadata in the same transaction. + */ + @Override + public Element getMetadata(String id) throws Exception { + Element md = getXmlSerializer().selectNoXLinkResolver(id, false, false); + if (md == null) + return null; + md.detach(); + return md; + } + + /** + * For update of owner info. + */ + @Override + public synchronized void updateMetadataOwner(final int id, final String owner, final String groupOwner) + throws Exception { + metadataRepository.update(id, new Updater() { + @Override + public void apply(@Nonnull Metadata entity) { + entity.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)); + entity.getSourceInfo().setOwner(Integer.valueOf(owner)); + } + }); + } + + /** + * Updates a metadata record. Deletes validation report currently in session (if + * any). If user asks for validation the validation report will be (re-)created + * then. + * + * @return metadata if the that was updated + */ + @Override + public synchronized AbstractMetadata updateMetadata(final ServiceContext context, final String metadataId, + final Element md, final boolean validate, final boolean ufo, final boolean index, final String lang, + final String changeDate, final boolean updateDateStamp) throws Exception { + Log.trace(Geonet.DATA_MANAGER, "Update record with id " + metadataId); + + Element metadataXml = md; + + // when invoked from harvesters, session is null? + UserSession session = context.getUserSession(); + if (session != null) { + session.removeProperty(Geonet.Session.VALIDATION_REPORT + metadataId); + } + String schema = metadataSchemaUtils.getMetadataSchema(metadataId); + if (ufo) { + String parentUuid = null; + Integer intId = Integer.valueOf(metadataId); + + final AbstractMetadata metadata = metadataUtils.findOne(metadataId); String uuid = findUuid(metadataXml, schema, metadata); metadataXml = updateFixedInfo(schema, Optional.of(intId), uuid, metadataXml, parentUuid, - (updateDateStamp ? UpdateDatestamp.YES : UpdateDatestamp.NO), context); - } + (updateDateStamp ? UpdateDatestamp.YES : UpdateDatestamp.NO), context); + } - // --- force namespace prefix for iso19139 metadata - setNamespacePrefixUsingSchemas(schema, metadataXml); + // --- force namespace prefix for iso19139 metadata + setNamespacePrefixUsingSchemas(schema, metadataXml); - // Notifies the metadata change to metatada notifier service - final AbstractMetadata metadata = metadataUtils.findOne(metadataId); + // Notifies the metadata change to metatada notifier service + final AbstractMetadata metadata = metadataUtils.findOne(metadataId); String uuid = findUuid(metadataXml, schema, metadata); metadataUtils.checkMetadataWithSameUuidExist(uuid, metadata.getId()); - // --- write metadata to dbms - getXmlSerializer().update(metadataId, metadataXml, changeDate, updateDateStamp, uuid, context); - // Notifies the metadata change to metatada notifier service - metadataUtils.notifyMetadataChange(metadataXml, metadataId); - - try { - // --- do the validation last - it throws exceptions - if (session != null && validate) { - metadataValidator.doValidate(session, schema, metadataId, metadataXml, lang, false); - } - } finally { - if (index) { - // --- update search criteria - metadataIndexer.indexMetadata(metadataId, true, null); - } - } - - if (metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE) { - if (!index) { - metadataIndexer.indexMetadata(metadataId, true, null); - } - MetaSearcher searcher = searcherForReferencingMetadata(context, metadata); - Map result = ((LuceneSearcher) searcher).getAllMdInfo(context, 500); - for (Integer id : result.keySet()) { - IndexingList list = context.getBean(IndexingList.class); - list.add(id); - } - } - // Return an up to date metadata record - return metadataUtils.findOne(metadataId); - } + // --- write metadata to dbms + getXmlSerializer().update(metadataId, metadataXml, changeDate, updateDateStamp, uuid, context); + // Notifies the metadata change to metatada notifier service + metadataUtils.notifyMetadataChange(metadataXml, metadataId); + + try { + // --- do the validation last - it throws exceptions + if (session != null && validate) { + metadataValidator.doValidate(session, schema, metadataId, metadataXml, lang, false); + } + } finally { + if (index) { + // --- update search criteria + metadataIndexer.indexMetadata(metadataId, true, null); + } + } + + if (metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE) { + if (!index) { + metadataIndexer.indexMetadata(metadataId, true, null); + } + MetaSearcher searcher = searcherForReferencingMetadata(context, metadata); + Map result = ((LuceneSearcher) searcher).getAllMdInfo(context, 500); + for (Integer id : result.keySet()) { + IndexingList list = context.getBean(IndexingList.class); + list.add(id); + } + } + + Log.trace(Geonet.DATA_MANAGER, "Finishing update of record with id " + metadataId); + // Return an up to date metadata record + return metadataUtils.findOne(metadataId); + } private String findUuid(Element metadataXml, String schema, AbstractMetadata metadata) throws Exception { String uuid = null; @@ -839,469 +842,481 @@ private String findUuid(Element metadataXml, String schema, AbstractMetadata met } /** - * buildInfoElem contains similar portion of code with indexMetadata - */ - private Element buildInfoElem(ServiceContext context, String id, String version) throws Exception { - AbstractMetadata metadata = metadataUtils.findOne(id); - final MetadataDataInfo dataInfo = metadata.getDataInfo(); - String schema = dataInfo.getSchemaId(); - String createDate = dataInfo.getCreateDate().getDateAndTime(); - String changeDate = dataInfo.getChangeDate().getDateAndTime(); - String source = metadata.getSourceInfo().getSourceId(); - String isTemplate = dataInfo.getType().codeString; - @SuppressWarnings("deprecation") - String title = dataInfo.getTitle(); - String uuid = metadata.getUuid(); - String isHarvested = "" + Constants.toYN_EnabledChar(metadata.getHarvestInfo().isHarvested()); - String harvestUuid = metadata.getHarvestInfo().getUuid(); - String popularity = "" + dataInfo.getPopularity(); - String rating = "" + dataInfo.getRating(); - String owner = "" + metadata.getSourceInfo().getOwner(); - String displayOrder = "" + dataInfo.getDisplayOrder(); - - Element info = new Element(Edit.RootChild.INFO, Edit.NAMESPACE); - - addElement(info, Edit.Info.Elem.ID, id); - addElement(info, Edit.Info.Elem.SCHEMA, schema); - addElement(info, Edit.Info.Elem.CREATE_DATE, createDate); - addElement(info, Edit.Info.Elem.CHANGE_DATE, changeDate); - addElement(info, Edit.Info.Elem.IS_TEMPLATE, isTemplate); - addElement(info, Edit.Info.Elem.TITLE, title); - addElement(info, Edit.Info.Elem.SOURCE, source); - addElement(info, Edit.Info.Elem.UUID, uuid); - addElement(info, Edit.Info.Elem.IS_HARVESTED, isHarvested); - addElement(info, Edit.Info.Elem.POPULARITY, popularity); - addElement(info, Edit.Info.Elem.RATING, rating); - addElement(info, Edit.Info.Elem.DISPLAY_ORDER, displayOrder); - - if (metadata.getHarvestInfo().isHarvested()) { - if (harvestInfoProvider != null) { - info.addContent(harvestInfoProvider.getHarvestInfo(harvestUuid, id, uuid)); - } - } - if (version != null) { - addElement(info, Edit.Info.Elem.VERSION, version); - } - - Map map = Maps.newHashMap(); - map.put(id, info); - buildPrivilegesMetadataInfo(context, map); - - // add owner name - User user = userRepository.findOne(owner); - if (user != null) { - String ownerName = user.getName(); - addElement(info, Edit.Info.Elem.OWNERNAME, ownerName); - } - - for (MetadataCategory category : metadata.getMetadataCategories()) { - addElement(info, Edit.Info.Elem.CATEGORY, category.getName()); - } - - // add subtemplates - /* - * -- don't add as we need to investigate indexing for the fields -- in the - * metadata table used here List subList = getSubtemplates(dbms, schema); if - * (subList != null) { Element subs = new Element(Edit.Info.Elem.SUBTEMPLATES); - * subs.addContent(subList); info.addContent(subs); } - */ - - // Add validity information - List validationInfo = metadataValidationRepository - .findAllById_MetadataId(Integer.parseInt(id)); - if (validationInfo == null || validationInfo.size() == 0) { - addElement(info, Edit.Info.Elem.VALID, "-1"); - } else { - String isValid = "1"; - for (Object elem : validationInfo) { - MetadataValidation vi = (MetadataValidation) elem; - String type = vi.getId().getValidationType(); - if (!vi.isValid()) { - isValid = "0"; - } - - String ratio = "xsd".equals(type) ? "" : vi.getNumFailures() + "/" + vi.getNumTests(); - - info.addContent(new Element(Edit.Info.Elem.VALID + "_details") - .addContent(new Element("type").setText(type)).addContent(new Element("status") - .setText(vi.isValid() ? "1" : "0").addContent(new Element("ratio").setText(ratio)))); - } - addElement(info, Edit.Info.Elem.VALID, isValid); - } - - // add baseUrl of this site (from settings) - String protocol = settingManager.getValue(Settings.SYSTEM_SERVER_PROTOCOL); - String host = settingManager.getValue(Settings.SYSTEM_SERVER_HOST); - String port = settingManager.getValue(Settings.SYSTEM_SERVER_PORT); - if (port.equals("80")) { - port = ""; - } else { - port = ":" + port; - } - addElement(info, Edit.Info.Elem.BASEURL, protocol + "://" + host + port + baseURL); - addElement(info, Edit.Info.Elem.LOCSERV, "/srv/en"); - return info; - } - - /** - * Update metadata record (not template) using update-fixed-info.xsl - * - * @param uuid - * If the metadata is a new record (not yet saved), provide the uuid - * for that record - * @param updateDatestamp - * updateDatestamp is not used when running XSL transformation - */ - @Override - public Element updateFixedInfo(String schema, Optional metadataId, String uuid, Element md, - String parentUuid, UpdateDatestamp updateDatestamp, ServiceContext context) throws Exception { - boolean autoFixing = settingManager.getValueAsBool(Settings.SYSTEM_AUTOFIXING_ENABLE, true); - if (autoFixing) { - LOGGER_DATA_MANAGER.debug("Autofixing is enabled, trying update-fixed-info (updateDatestamp: {})", updateDatestamp.name()); - - AbstractMetadata metadata = null; - if (metadataId.isPresent()) { - metadata = metadataUtils.findOne(metadataId.get()); - boolean isTemplate = metadata != null && metadata.getDataInfo().getType() == MetadataType.TEMPLATE; - - // don't process templates - if (isTemplate) { + * buildInfoElem contains similar portion of code with indexMetadata + */ + private Element buildInfoElem(ServiceContext context, String id, String version) throws Exception { + AbstractMetadata metadata = metadataUtils.findOne(id); + final MetadataDataInfo dataInfo = metadata.getDataInfo(); + String schema = dataInfo.getSchemaId(); + String createDate = dataInfo.getCreateDate().getDateAndTime(); + String changeDate = dataInfo.getChangeDate().getDateAndTime(); + String source = metadata.getSourceInfo().getSourceId(); + String isTemplate = dataInfo.getType().codeString; + @SuppressWarnings("deprecation") + String title = dataInfo.getTitle(); + String uuid = metadata.getUuid(); + String isHarvested = "" + Constants.toYN_EnabledChar(metadata.getHarvestInfo().isHarvested()); + String harvestUuid = metadata.getHarvestInfo().getUuid(); + String popularity = "" + dataInfo.getPopularity(); + String rating = "" + dataInfo.getRating(); + String owner = "" + metadata.getSourceInfo().getOwner(); + String displayOrder = "" + dataInfo.getDisplayOrder(); + + Element info = new Element(Edit.RootChild.INFO, Edit.NAMESPACE); + + addElement(info, Edit.Info.Elem.ID, id); + addElement(info, Edit.Info.Elem.SCHEMA, schema); + addElement(info, Edit.Info.Elem.CREATE_DATE, createDate); + addElement(info, Edit.Info.Elem.CHANGE_DATE, changeDate); + addElement(info, Edit.Info.Elem.IS_TEMPLATE, isTemplate); + addElement(info, Edit.Info.Elem.TITLE, title); + addElement(info, Edit.Info.Elem.SOURCE, source); + addElement(info, Edit.Info.Elem.UUID, uuid); + addElement(info, Edit.Info.Elem.IS_HARVESTED, isHarvested); + addElement(info, Edit.Info.Elem.POPULARITY, popularity); + addElement(info, Edit.Info.Elem.RATING, rating); + addElement(info, Edit.Info.Elem.DISPLAY_ORDER, displayOrder); + + if (metadata.getHarvestInfo().isHarvested()) { + if (harvestInfoProvider != null) { + info.addContent(harvestInfoProvider.getHarvestInfo(harvestUuid, id, uuid)); + } + } + if (version != null) { + addElement(info, Edit.Info.Elem.VERSION, version); + } + + Map map = Maps.newHashMap(); + map.put(id, info); + buildPrivilegesMetadataInfo(context, map); + + // add owner name + User user = userRepository.findOne(owner); + if (user != null) { + String ownerName = user.getName(); + addElement(info, Edit.Info.Elem.OWNERNAME, ownerName); + } + + for (MetadataCategory category : metadata.getCategories()) { + addElement(info, Edit.Info.Elem.CATEGORY, category.getName()); + } + + // add subtemplates + /* + * -- don't add as we need to investigate indexing for the fields -- in the + * metadata table used here List subList = getSubtemplates(dbms, schema); if + * (subList != null) { Element subs = new Element(Edit.Info.Elem.SUBTEMPLATES); + * subs.addContent(subList); info.addContent(subs); } + */ + + // Add validity information + List validationInfo = metadataValidationRepository + .findAllById_MetadataId(Integer.parseInt(id)); + if (validationInfo == null || validationInfo.size() == 0) { + addElement(info, Edit.Info.Elem.VALID, "-1"); + } else { + String isValid = "1"; + for (Object elem : validationInfo) { + MetadataValidation vi = (MetadataValidation) elem; + String type = vi.getId().getValidationType(); + if (!vi.isValid()) { + isValid = "0"; + } + + String ratio = "xsd".equals(type) ? "" : vi.getNumFailures() + "/" + vi.getNumTests(); + + info.addContent(new Element(Edit.Info.Elem.VALID + "_details") + .addContent(new Element("type").setText(type)).addContent(new Element("status") + .setText(vi.isValid() ? "1" : "0").addContent(new Element("ratio").setText(ratio)))); + } + addElement(info, Edit.Info.Elem.VALID, isValid); + } + + // add baseUrl of this site (from settings) + String protocol = settingManager.getValue(Settings.SYSTEM_SERVER_PROTOCOL); + String host = settingManager.getValue(Settings.SYSTEM_SERVER_HOST); + String port = settingManager.getValue(Settings.SYSTEM_SERVER_PORT); + if (port.equals("80")) { + port = ""; + } else { + port = ":" + port; + } + addElement(info, Edit.Info.Elem.BASEURL, protocol + "://" + host + port + baseURL); + addElement(info, Edit.Info.Elem.LOCSERV, "/srv/en"); + return info; + } + + /** + * Update metadata record (not template) using update-fixed-info.xsl + * + * @param uuid If the metadata is a new record (not yet saved), provide the uuid + * for that record + * @param updateDatestamp updateDatestamp is not used when running XSL transformation + */ + @Override + public Element updateFixedInfo(String schema, Optional metadataId, String uuid, Element md, + String parentUuid, UpdateDatestamp updateDatestamp, ServiceContext context) throws Exception { + boolean autoFixing = settingManager.getValueAsBool(Settings.SYSTEM_AUTOFIXING_ENABLE, true); + if (autoFixing) { + LOGGER_DATA_MANAGER.debug("Autofixing is enabled, trying update-fixed-info (updateDatestamp: {})", + updateDatestamp.name()); + + AbstractMetadata metadata = null; + if (metadataId.isPresent()) { + metadata = metadataUtils.findOne(metadataId.get()); + boolean isTemplate = metadata != null && metadata.getDataInfo().getType() == MetadataType.TEMPLATE; + + // don't process templates + if (isTemplate) { LOGGER_DATA_MANAGER.debug("Not applying update-fixed-info for a template"); - return md; - } - } - - String currentUuid = metadata != null ? metadata.getUuid() : null; - String id = metadata != null ? metadata.getId() + "" : null; - uuid = uuid == null ? currentUuid : uuid; - - // --- setup environment - Element env = new Element("env"); - env.addContent(new Element("id").setText(id)); - env.addContent(new Element("uuid").setText(uuid)); - - env.addContent(thesaurusManager.buildResultfromThTable(context)); - - Element schemaLoc = new Element("schemaLocation"); - schemaLoc.setAttribute(schemaManager.getSchemaLocation(schema, context)); - env.addContent(schemaLoc); - - if (updateDatestamp == UpdateDatestamp.YES) { - env.addContent(new Element("changeDate").setText(new ISODate().toString())); - } - if (parentUuid != null) { - env.addContent(new Element("parentUuid").setText(parentUuid)); - } - if (metadataId.isPresent()) { - String metadataIdString = String.valueOf(metadataId.get()); - final Path resourceDir = Lib.resource.getDir(context, Params.Access.PRIVATE, metadataIdString); - env.addContent(new Element("datadir").setText(resourceDir.toString())); - } - - // add user information to env if user is authenticated (should be) - Element elUser = new Element("user"); - UserSession usrSess = context.getUserSession(); - if (usrSess.isAuthenticated()) { - String myUserId = usrSess.getUserId(); - User user = getApplicationContext().getBean(UserRepository.class).findOne(myUserId); - if (user != null) { - Element elUserDetails = new Element("details"); - elUserDetails.addContent(new Element("surname").setText(user.getSurname())); - elUserDetails.addContent(new Element("firstname").setText(user.getName())); - elUserDetails.addContent(new Element("organisation").setText(user.getOrganisation())); - elUserDetails.addContent(new Element("username").setText(user.getUsername())); - elUser.addContent(elUserDetails); - env.addContent(elUser); - } - } - - // add original metadata to result - Element result = new Element("root"); - result.addContent(md); - // add 'environment' to result - env.addContent(new Element("siteURL").setText(settingManager.getSiteURL(context))); - env.addContent(new Element("nodeURL").setText(settingManager.getNodeURL())); - env.addContent(new Element("node").setText(context.getNodeId())); - - // Settings were defined as an XML starting with root named config - // Only second level elements are defined (under system). - List config = settingManager.getAllAsXML(true).cloneContent(); - for (Object c : config) { - Element settings = (Element) c; - env.addContent(settings); - } - - result.addContent(env); - // apply update-fixed-info.xsl - Path styleSheet = metadataSchemaUtils.getSchemaDir(schema) - .resolve(metadata != null && metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE - ? Geonet.File.UPDATE_FIXED_INFO_SUBTEMPLATE - : Geonet.File.UPDATE_FIXED_INFO); - result = Xml.transform(result, styleSheet); - return result; - } else { + return md; + } + } + + String currentUuid = metadata != null ? metadata.getUuid() : null; + String id = metadata != null ? metadata.getId() + "" : null; + uuid = uuid == null ? currentUuid : uuid; + + // --- setup environment + Element env = new Element("env"); + env.addContent(new Element("id").setText(id)); + env.addContent(new Element("uuid").setText(uuid)); + + env.addContent(thesaurusManager.buildResultfromThTable(context)); + + Element schemaLoc = new Element("schemaLocation"); + schemaLoc.setAttribute(schemaManager.getSchemaLocation(schema, context)); + env.addContent(schemaLoc); + + if (updateDatestamp == UpdateDatestamp.YES) { + env.addContent(new Element("changeDate").setText(new ISODate().toString())); + } + if (parentUuid != null) { + env.addContent(new Element("parentUuid").setText(parentUuid)); + } + if (metadataId.isPresent()) { + String metadataIdString = String.valueOf(metadataId.get()); + final Path resourceDir = Lib.resource.getDir(context, Params.Access.PRIVATE, metadataIdString); + env.addContent(new Element("datadir").setText(resourceDir.toString())); + } + + // add user information to env if user is authenticated (should be) + Element elUser = new Element("user"); + UserSession usrSess = context.getUserSession(); + if (usrSess.isAuthenticated()) { + String myUserId = usrSess.getUserId(); + User user = getApplicationContext().getBean(UserRepository.class).findOne(myUserId); + if (user != null) { + Element elUserDetails = new Element("details"); + elUserDetails.addContent(new Element("surname").setText(user.getSurname())); + elUserDetails.addContent(new Element("firstname").setText(user.getName())); + elUserDetails.addContent(new Element("organisation").setText(user.getOrganisation())); + elUserDetails.addContent(new Element("username").setText(user.getUsername())); + elUser.addContent(elUserDetails); + env.addContent(elUser); + } + } + + // add original metadata to result + Element result = new Element("root"); + result.addContent(md); + // add 'environment' to result + env.addContent(new Element("siteURL").setText(settingManager.getSiteURL(context))); + env.addContent(new Element("nodeURL").setText(settingManager.getNodeURL())); + env.addContent(new Element("node").setText(context.getNodeId())); + + // Settings were defined as an XML starting with root named config + // Only second level elements are defined (under system). + List config = settingManager.getAllAsXML(true).cloneContent(); + for (Object c : config) { + Element settings = (Element) c; + env.addContent(settings); + } + + result.addContent(env); + // apply update-fixed-info.xsl + Path styleSheet = metadataSchemaUtils.getSchemaDir(schema) + .resolve(metadata != null && metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE + ? Geonet.File.UPDATE_FIXED_INFO_SUBTEMPLATE + : Geonet.File.UPDATE_FIXED_INFO); + result = Xml.transform(result, styleSheet); + return result; + } else { LOGGER_DATA_MANAGER.debug("Autofixing is disabled, not applying update-fixed-info"); - return md; - } - } - - /** - * Updates all children of the selected parent. Some elements are protected in - * the children according to the stylesheet used in - * xml/schemas/[SCHEMA]/update-child-from-parent-info.xsl. - * - * Children MUST be editable and also in the same schema of the parent. If not, - * child is not updated. - * - * @param srvContext - * service context - * @param parentUuid - * parent uuid - * @param children - * children - * @param params - * parameters - */ - @Override - public Set updateChildren(ServiceContext srvContext, String parentUuid, String[] children, - Map params) throws Exception { - String parentId = (String) params.get(Params.ID); - String parentSchema = (String) params.get(Params.SCHEMA); - - // --- get parent metadata in read/only mode - boolean forEditing = false, withValidationErrors = false, keepXlinkAttributes = false; - Element parent = getMetadata(srvContext, parentId, forEditing, withValidationErrors, keepXlinkAttributes); - - Element env = new Element("update"); - env.addContent(new Element("parentUuid").setText(parentUuid)); - env.addContent(new Element("siteURL").setText(settingManager.getSiteURL(srvContext))); - env.addContent(new Element("parent").addContent(parent)); - - // Set of untreated children (out of privileges, different schemas) - Set untreatedChildSet = new HashSet(); - - // only get iso19139 records - for (String childId : children) { - - // Check privileges - if (!accessManager.canEdit(srvContext, childId)) { - untreatedChildSet.add(childId); + return md; + } + } + + /** + * Updates all children of the selected parent. Some elements are protected in + * the children according to the stylesheet used in + * xml/schemas/[SCHEMA]/update-child-from-parent-info.xsl. + *

      + * Children MUST be editable and also in the same schema of the parent. If not, + * child is not updated. + * + * @param srvContext service context + * @param parentUuid parent uuid + * @param children children + * @param params parameters + */ + @Override + public Set updateChildren(ServiceContext srvContext, String parentUuid, String[] children, + Map params) throws Exception { + String parentId = (String) params.get(Params.ID); + String parentSchema = (String) params.get(Params.SCHEMA); + + // --- get parent metadata in read/only mode + boolean forEditing = false, withValidationErrors = false, keepXlinkAttributes = false; + Element parent = getMetadata(srvContext, parentId, forEditing, withValidationErrors, keepXlinkAttributes); + + Element env = new Element("update"); + env.addContent(new Element("parentUuid").setText(parentUuid)); + env.addContent(new Element("siteURL").setText(settingManager.getSiteURL(srvContext))); + env.addContent(new Element("parent").addContent(parent)); + + // Set of untreated children (out of privileges, different schemas) + Set untreatedChildSet = new HashSet(); + + // only get iso19139 records + for (String childId : children) { + + // Check privileges + if (!accessManager.canEdit(srvContext, childId)) { + untreatedChildSet.add(childId); LOGGER_DATA_MANAGER.debug("Could not update child ({}) because of privileges.", childId); - continue; - } - - Element child = getMetadata(srvContext, childId, forEditing, withValidationErrors, keepXlinkAttributes); - - String childSchema = child.getChild(Edit.RootChild.INFO, Edit.NAMESPACE) - .getChildText(Edit.Info.Elem.SCHEMA); - - // Check schema matching. CHECKME : this suppose that parent and - // child are in the same schema (even not profil different) - if (!childSchema.equals(parentSchema)) { - untreatedChildSet.add(childId); - LOGGER_DATA_MANAGER.debug("Could not update child ({}) because schema ({}) is different from the parent one ({}).", - new Object[] {childId, childSchema, parentSchema}); - continue; - } + continue; + } + + Element child = getMetadata(srvContext, childId, forEditing, withValidationErrors, keepXlinkAttributes); + + String childSchema = child.getChild(Edit.RootChild.INFO, Edit.NAMESPACE) + .getChildText(Edit.Info.Elem.SCHEMA); + + // Check schema matching. CHECKME : this suppose that parent and + // child are in the same schema (even not profil different) + if (!childSchema.equals(parentSchema)) { + untreatedChildSet.add(childId); + LOGGER_DATA_MANAGER.debug( + "Could not update child ({}) because schema ({}) is different from the parent one ({}).", + new Object[]{childId, childSchema, parentSchema}); + continue; + } LOGGER_DATA_MANAGER.debug("Updating child ({}) ...", childId); - // --- setup xml element to be processed by XSLT - - Element rootEl = new Element("root"); - Element childEl = new Element("child").addContent(child.detach()); - rootEl.addContent(childEl); - rootEl.addContent(env.detach()); - - // --- do an XSL transformation - - Path styleSheet = metadataSchemaUtils.getSchemaDir(parentSchema) - .resolve(Geonet.File.UPDATE_CHILD_FROM_PARENT_INFO); - Element childForUpdate = Xml.transform(rootEl, styleSheet, params); - - getXmlSerializer().update(childId, childForUpdate, new ISODate().toString(), true, null, srvContext); - - // Notifies the metadata change to metatada notifier service - metadataUtils.notifyMetadataChange(childForUpdate, childId); - - rootEl = null; - } - - return untreatedChildSet; - } - - // --------------------------------------------------------------------------- - // --- - // --- Static methods are for external modules like GAST to be able to use - // --- them. - // --- - // --------------------------------------------------------------------------- - - /** - * Add privileges information about metadata record which depends on context and - * usually could not be stored in db or Lucene index because depending on the - * current user or current client IP address. - * - * @param mdIdToInfoMap - * a map from the metadata Id -> the info element to which the - * privilege information should be added. - */ - @VisibleForTesting - @Override - public void buildPrivilegesMetadataInfo(ServiceContext context, Map mdIdToInfoMap) - throws Exception { - Collection metadataIds = Collections2.transform(mdIdToInfoMap.keySet(), - new Function() { - @Nullable - @Override - public Integer apply(String input) { - return Integer.valueOf(input); - } - }); - Specification operationAllowedSpec = OperationAllowedSpecs.hasMetadataIdIn(metadataIds); - - final Collection allUserGroups = accessManager.getUserGroups(context.getUserSession(), - context.getIpAddress(), false); - final SetMultimap operationsPerMetadata = loadOperationsAllowed(context, - where(operationAllowedSpec).and(OperationAllowedSpecs.hasGroupIdIn(allUserGroups))); - final Set visibleToAll = loadOperationsAllowed(context, - where(operationAllowedSpec).and(OperationAllowedSpecs.isPublic(ReservedOperation.view))).keySet(); - final Set downloadableByGuest = loadOperationsAllowed(context, - where(operationAllowedSpec).and(OperationAllowedSpecs.hasGroupId(ReservedGroup.guest.getId())) - .and(OperationAllowedSpecs.hasOperation(ReservedOperation.download))).keySet(); - final Map allSourceInfo = metadataRepository - .findAllSourceInfo(MetadataSpecs.hasMetadataIdIn(metadataIds)); - - for (Map.Entry entry : mdIdToInfoMap.entrySet()) { - Element infoEl = entry.getValue(); - final Integer mdId = Integer.valueOf(entry.getKey()); - MetadataSourceInfo sourceInfo = allSourceInfo.get(mdId); - Set operations = operationsPerMetadata.get(mdId); - if (operations == null) { - operations = Collections.emptySet(); - } - - boolean isOwner = accessManager.isOwner(context, sourceInfo); - - if (isOwner) { - operations = Sets.newHashSet(Arrays.asList(ReservedOperation.values())); - } - - if (isOwner || operations.contains(ReservedOperation.editing)) { - addElement(infoEl, Edit.Info.Elem.EDIT, "true"); - } - - if (isOwner) { - addElement(infoEl, Edit.Info.Elem.OWNER, "true"); - } - - addElement(infoEl, Edit.Info.Elem.IS_PUBLISHED_TO_ALL, visibleToAll.contains(mdId)); - addElement(infoEl, ReservedOperation.view.name(), operations.contains(ReservedOperation.view)); - addElement(infoEl, ReservedOperation.notify.name(), operations.contains(ReservedOperation.notify)); - addElement(infoEl, ReservedOperation.download.name(), operations.contains(ReservedOperation.download)); - addElement(infoEl, ReservedOperation.dynamic.name(), operations.contains(ReservedOperation.dynamic)); - addElement(infoEl, ReservedOperation.featured.name(), operations.contains(ReservedOperation.featured)); - - if (!operations.contains(ReservedOperation.download)) { - addElement(infoEl, Edit.Info.Elem.GUEST_DOWNLOAD, downloadableByGuest.contains(mdId)); - } - } - } - - private SetMultimap loadOperationsAllowed(ServiceContext context, - Specification operationAllowedSpec) { - final OperationAllowedRepository operationAllowedRepo = context.getBean(OperationAllowedRepository.class); - List operationsAllowed = operationAllowedRepo.findAll(operationAllowedSpec); - SetMultimap operationsPerMetadata = HashMultimap.create(); - for (OperationAllowed allowed : operationsAllowed) { - final OperationAllowedId id = allowed.getId(); - operationsPerMetadata.put(id.getMetadataId(), ReservedOperation.lookup(id.getOperationId())); - } - return operationsPerMetadata; - } - - /** - * - * @param md - * @throws Exception - */ - private void setNamespacePrefixUsingSchemas(String schema, Element md) throws Exception { - // --- if the metadata has no namespace or already has a namespace prefix - // --- then we must skip this phase - Namespace ns = md.getNamespace(); - if (ns == Namespace.NO_NAMESPACE) - return; - - MetadataSchema mds = schemaManager.getSchema(schema); - - // --- get the namespaces and add prefixes to any that are - // --- default (ie. prefix is '') if namespace match one of the schema - ArrayList nsList = new ArrayList(); - nsList.add(ns); - @SuppressWarnings("unchecked") - List additionalNamespaces = md.getAdditionalNamespaces(); - nsList.addAll(additionalNamespaces); - for (Object aNsList : nsList) { - Namespace aNs = (Namespace) aNsList; - if (aNs.getPrefix().equals("")) { // found default namespace - String prefix = mds.getPrefix(aNs.getURI()); - if (prefix == null) { - LOGGER_DATA_MANAGER.warn("Metadata record contains a default namespace {} (with no prefix) which does not match any {} schema's namespaces.", + // --- setup xml element to be processed by XSLT + + Element rootEl = new Element("root"); + Element childEl = new Element("child").addContent(child.detach()); + rootEl.addContent(childEl); + rootEl.addContent(env.detach()); + + // --- do an XSL transformation + + Path styleSheet = metadataSchemaUtils.getSchemaDir(parentSchema) + .resolve(Geonet.File.UPDATE_CHILD_FROM_PARENT_INFO); + Element childForUpdate = Xml.transform(rootEl, styleSheet, params); + + getXmlSerializer().update(childId, childForUpdate, new ISODate().toString(), true, null, srvContext); + + // Notifies the metadata change to metatada notifier service + metadataUtils.notifyMetadataChange(childForUpdate, childId); + + rootEl = null; + } + + return untreatedChildSet; + } + + // --------------------------------------------------------------------------- + // --- + // --- Static methods are for external modules like GAST to be able to use + // --- them. + // --- + // --------------------------------------------------------------------------- + + /** + * Add privileges information about metadata record which depends on context and + * usually could not be stored in db or Lucene index because depending on the + * current user or current client IP address. + * + * @param mdIdToInfoMap a map from the metadata Id -> the info element to which the + * privilege information should be added. + */ + @Override + public void buildPrivilegesMetadataInfo(ServiceContext context, Map mdIdToInfoMap) + throws Exception { + Collection metadataIds = Collections2.transform(mdIdToInfoMap.keySet(), + new Function() { + @Nullable + @Override + public Integer apply(String input) { + return Integer.valueOf(input); + } + }); + Specification operationAllowedSpec = OperationAllowedSpecs.hasMetadataIdIn(metadataIds); + + final Collection allUserGroups = accessManager.getUserGroups(context.getUserSession(), + context.getIpAddress(), false); + final SetMultimap operationsPerMetadata = loadOperationsAllowed(context, + where(operationAllowedSpec).and(OperationAllowedSpecs.hasGroupIdIn(allUserGroups))); + final Set visibleToAll = loadOperationsAllowed(context, + where(operationAllowedSpec).and(OperationAllowedSpecs.isPublic(ReservedOperation.view))).keySet(); + final Set downloadableByGuest = loadOperationsAllowed(context, + where(operationAllowedSpec).and(OperationAllowedSpecs.hasGroupId(ReservedGroup.guest.getId())) + .and(OperationAllowedSpecs.hasOperation(ReservedOperation.download))).keySet(); + final Map allSourceInfo = findAllSourceInfo((Specification) MetadataSpecs.hasMetadataIdIn(metadataIds)); + + for (Map.Entry entry : mdIdToInfoMap.entrySet()) { + Element infoEl = entry.getValue(); + final Integer mdId = Integer.valueOf(entry.getKey()); + MetadataSourceInfo sourceInfo = allSourceInfo.get(mdId); + Set operations = operationsPerMetadata.get(mdId); + if (operations == null) { + operations = Collections.emptySet(); + } + + boolean isOwner = accessManager.isOwner(context, sourceInfo); + + if (isOwner) { + operations = Sets.newHashSet(Arrays.asList(ReservedOperation.values())); + } + + if (isOwner || operations.contains(ReservedOperation.editing)) { + addElement(infoEl, Edit.Info.Elem.EDIT, "true"); + } + + if (isOwner) { + addElement(infoEl, Edit.Info.Elem.OWNER, "true"); + } + + addElement(infoEl, Edit.Info.Elem.IS_PUBLISHED_TO_ALL, visibleToAll.contains(mdId)); + addElement(infoEl, ReservedOperation.view.name(), operations.contains(ReservedOperation.view)); + addElement(infoEl, ReservedOperation.notify.name(), operations.contains(ReservedOperation.notify)); + addElement(infoEl, ReservedOperation.download.name(), operations.contains(ReservedOperation.download)); + addElement(infoEl, ReservedOperation.dynamic.name(), operations.contains(ReservedOperation.dynamic)); + addElement(infoEl, ReservedOperation.featured.name(), operations.contains(ReservedOperation.featured)); + + if (!operations.contains(ReservedOperation.download)) { + addElement(infoEl, Edit.Info.Elem.GUEST_DOWNLOAD, downloadableByGuest.contains(mdId)); + } + } + } + + protected SetMultimap loadOperationsAllowed(ServiceContext context, + Specification operationAllowedSpec) { + final OperationAllowedRepository operationAllowedRepo = context.getBean(OperationAllowedRepository.class); + List operationsAllowed = operationAllowedRepo.findAll(operationAllowedSpec); + SetMultimap operationsPerMetadata = HashMultimap.create(); + for (OperationAllowed allowed : operationsAllowed) { + final OperationAllowedId id = allowed.getId(); + operationsPerMetadata.put(id.getMetadataId(), ReservedOperation.lookup(id.getOperationId())); + } + return operationsPerMetadata; + } + + /** + * @param md + * @throws Exception + */ + private void setNamespacePrefixUsingSchemas(String schema, Element md) throws Exception { + // --- if the metadata has no namespace or already has a namespace prefix + // --- then we must skip this phase + Namespace ns = md.getNamespace(); + if (ns == Namespace.NO_NAMESPACE) + return; + + MetadataSchema mds = schemaManager.getSchema(schema); + + // --- get the namespaces and add prefixes to any that are + // --- default (ie. prefix is '') if namespace match one of the schema + ArrayList nsList = new ArrayList(); + nsList.add(ns); + @SuppressWarnings("unchecked") + List additionalNamespaces = md.getAdditionalNamespaces(); + nsList.addAll(additionalNamespaces); + for (Object aNsList : nsList) { + Namespace aNs = (Namespace) aNsList; + if (aNs.getPrefix().equals("")) { // found default namespace + String prefix = mds.getPrefix(aNs.getURI()); + if (prefix == null) { + LOGGER_DATA_MANAGER.warn( + "Metadata record contains a default namespace {} (with no prefix) which does not match any {} schema's namespaces.", aNs.getURI(), schema); - } - ns = Namespace.getNamespace(prefix, aNs.getURI()); - metadataValidator.setNamespacePrefix(md, ns); - if (!md.getNamespace().equals(ns)) { - md.removeNamespaceDeclaration(aNs); - md.addNamespaceDeclaration(ns); - } - } - } - } - - /** - * - * @param root - * @param name - * @param value - */ - private static void addElement(Element root, String name, Object value) { - root.addContent(new Element(name).setText(value == null ? "" : value.toString())); - } - - @Override - public AbstractMetadata save(AbstractMetadata info) { - if (info instanceof Metadata) { - return metadataRepository.save((Metadata) info); - } else { - throw new NotImplementedException("Unknown IMetadata subtype: " + info.getClass().getName()); - } - } - - @SuppressWarnings("unchecked") - @Override - public AbstractMetadata update(int id, @Nonnull Updater updater) { - return metadataRepository.update(id, (Updater) updater); - } - - @SuppressWarnings("unchecked") - @Override - public void deleteAll(Specification specs) { - try { - metadataRepository.deleteAll((Specification) specs); - } catch (Throwable t) { - t.printStackTrace(); - // Maybe it is not a Specification - } - } - - @Override - public void delete(Integer id) { - metadataRepository.delete(id); - } - - @Override - public void createBatchUpdateQuery(PathSpec servicesPath, String newUuid, - Specification harvested) { - metadataRepository.createBatchUpdateQuery(servicesPath, newUuid, harvested); - } + } + ns = Namespace.getNamespace(prefix, aNs.getURI()); + metadataValidator.setNamespacePrefix(md, ns); + if (!md.getNamespace().equals(ns)) { + md.removeNamespaceDeclaration(aNs); + md.addNamespaceDeclaration(ns); + } + } + } + } + + /** + * @param root + * @param name + * @param value + */ + protected static void addElement(Element root, String name, Object value) { + root.addContent(new Element(name).setText(value == null ? "" : value.toString())); + } + + @Override + public AbstractMetadata save(AbstractMetadata info) { + if (info instanceof Metadata) { + return metadataRepository.save((Metadata) info); + } else { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + info.getClass().getName()); + } + } + + @Override + public AbstractMetadata update(int id, @Nonnull Updater updater) { + try { + return metadataRepository.update(id, (Updater) updater); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + updater.getClass().getName()); + } + } + + @Override + public void deleteAll(Specification specs) { + try { + metadataRepository.deleteAll((Specification) specs); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + specs.getClass().getName()); + } + } + + @Override + @Transactional + public void delete(Integer id) { + Log.trace(Geonet.DATA_MANAGER, "Deleting record with id " + id); + if (metadataRepository.exists(id)) { + metadataRepository.delete(id); + } + } + + @Override + public void createBatchUpdateQuery(PathSpec servicesPath, String newUuid, + Specification harvested) { + try { + metadataRepository.createBatchUpdateQuery((PathSpec) servicesPath, newUuid, + (Specification) harvested); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + servicesPath.getClass().getName()); + } + } + + + @Override + public Map findAllSourceInfo(Specification specs) { + try { + return metadataRepository.findAllSourceInfo((Specification) specs); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + specs.getClass().getName()); + } + } } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java index 0ee92f546dd..a35d2063f87 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java @@ -1,7 +1,31 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import static org.springframework.data.jpa.domain.Specifications.where; +import java.util.Collection; import java.util.List; import org.apache.commons.lang.StringUtils; @@ -14,6 +38,8 @@ import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.domain.UserGroup; import org.fao.geonet.domain.UserGroupId; +import org.fao.geonet.events.md.MetadataPublished; +import org.fao.geonet.events.md.MetadataUnpublished; import org.fao.geonet.exceptions.ServiceNotAllowedEx; import org.fao.geonet.kernel.SvnManager; import org.fao.geonet.kernel.datamanager.IMetadataOperations; @@ -28,6 +54,8 @@ import org.fao.geonet.repository.specification.UserSpecs; import org.fao.geonet.utils.Log; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.annotation.Lazy; import org.springframework.data.jpa.domain.Specification; @@ -35,7 +63,7 @@ import jeeves.server.context.ServiceContext; -public class BaseMetadataOperations implements IMetadataOperations { +public class BaseMetadataOperations implements IMetadataOperations, ApplicationEventPublisherAware { @Autowired private IMetadataUtils metadataUtils; @@ -48,9 +76,21 @@ public class BaseMetadataOperations implements IMetadataOperations { @Autowired @Lazy private SettingManager settingManager; - @Autowired(required=false) + @Autowired(required = false) private SvnManager svnManager; + private ApplicationEventPublisher eventPublisher; + + /** + * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher) + */ + @Override + public void setApplicationEventPublisher( + ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } + + public void init(ServiceContext context, Boolean force) throws Exception { userRepository = context.getBean(UserRepository.class); metadataUtils = context.getBean(IMetadataUtils.class); @@ -65,13 +105,19 @@ public void init(ServiceContext context, Boolean force) throws Exception { */ @Override public void deleteMetadataOper(ServiceContext context, String metadataId, boolean skipAllReservedGroup) throws Exception { - OperationAllowedRepository operationAllowedRepository = context.getBean(OperationAllowedRepository.class); + deleteMetadataOper(metadataId, skipAllReservedGroup); + } + /** + * Removes all operations stored for a metadata. + */ + @Override + public void deleteMetadataOper(String metadataId, boolean skipAllReservedGroup) throws Exception { if (skipAllReservedGroup) { - int[] exclude = new int[] { ReservedGroup.all.getId(), ReservedGroup.intranet.getId(), ReservedGroup.guest.getId() }; - operationAllowedRepository.deleteAllByMetadataIdExceptGroupId(Integer.parseInt(metadataId), exclude); + int[] exclude = new int[]{ReservedGroup.all.getId(), ReservedGroup.intranet.getId(), ReservedGroup.guest.getId()}; + opAllowedRepo.deleteAllByMetadataIdExceptGroupId(Integer.parseInt(metadataId), exclude); } else { - operationAllowedRepository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(metadataId)); + opAllowedRepo.deleteAllByMetadataId(Integer.parseInt(metadataId)); } } @@ -93,15 +139,15 @@ public void setOperation(ServiceContext context, String mdId, String grpId, Stri /** * Set metadata privileges. - * + *

      * Administrator can set operation for any groups. - * + *

      * For reserved group (ie. Internet, Intranet & Guest), user MUST be reviewer of one group. For other group, if "Only set privileges to * user's groups" is set in catalog configuration user MUST be a member of the group. * - * @param mdId The metadata identifier + * @param mdId The metadata identifier * @param grpId The group identifier - * @param opId The operation identifier + * @param opId The operation identifier * @return true if the operation was set. */ @Override @@ -110,8 +156,37 @@ public boolean setOperation(ServiceContext context, int mdId, int grpId, int opI // Set operation if (opAllowed.isPresent()) { + return forceSetOperation(context, mdId, grpId, opId); + } + + return false; + } + + /** + * Set metadata privileges. + * + * @param mdId The metadata identifier + * @param grpId The group identifier + * @param opId The operation identifier + * @return true if the operation was set. + */ + @Override + public boolean forceSetOperation(ServiceContext context, int mdId, int grpId, int opId) throws Exception { + Optional opAllowed = _getOperationAllowedToAdd(context, mdId, grpId, opId, false); + + if (opAllowed.isPresent()) { + Log.trace(Geonet.DATA_MANAGER, "Operation is allowed"); opAllowedRepo.save(opAllowed.get()); svnManager.setHistory(mdId + "", context); + + //If it is published/unpublished, throw event + if (opId == ReservedOperation.view.getId() + && grpId == ReservedGroup.all.getId()) { + Log.trace(Geonet.DATA_MANAGER, "This is a publish event"); + this.eventPublisher.publishEvent(new MetadataPublished( + metadataUtils.findOne(Integer.valueOf(mdId)))); + } + return true; } @@ -128,16 +203,26 @@ public boolean setOperation(ServiceContext context, int mdId, int grpId, int opI */ @Override public Optional getOperationAllowedToAdd(final ServiceContext context, final int mdId, final int grpId, - final int opId) { + final int opId) { + return _getOperationAllowedToAdd(context, mdId, grpId, opId, true); + } + + private Optional _getOperationAllowedToAdd(final ServiceContext context, final int mdId, final int grpId, + final int opId, boolean shouldCheckPermission) { + Log.trace(Geonet.DATA_MANAGER, "_getOperationAllowedToAdd(" + mdId + ", " + + grpId + ", " + opId + ", " + shouldCheckPermission + ")"); final OperationAllowed operationAllowed = opAllowedRepo.findOneById_GroupIdAndId_MetadataIdAndId_OperationId(grpId, mdId, opId); - if (operationAllowed == null) { + if (operationAllowed == null && shouldCheckPermission) { + Log.trace(Geonet.DATA_MANAGER, "Checking if the operation is allowed, the operation is not yet present"); checkOperationPermission(context, grpId, userGroupRepo); } if (operationAllowed == null) { + Log.trace(Geonet.DATA_MANAGER, "Returning operation to add"); return Optional.of(new OperationAllowed(new OperationAllowedId().setGroupId(grpId).setMetadataId(mdId).setOperationId(opId))); } else { + Log.trace(Geonet.DATA_MANAGER, "Operation is already available"); return Optional.absent(); } } @@ -154,12 +239,12 @@ public void checkOperationPermission(ServiceContext context, int grpId, UserGrou if (ReservedGroup.isReserved(grpId)) { Specification hasUserIdAndProfile = where(UserGroupSpecs.hasProfile(Profile.Reviewer)) - .and(UserGroupSpecs.hasUserId(userId)); + .and(UserGroupSpecs.hasUserId(userId)); List groupIds = userGroupRepo.findGroupIds(hasUserIdAndProfile); if (groupIds.isEmpty()) { throw new ServiceNotAllowedEx( - "User can't set operation for group " + grpId + " because the user in not a " + "Reviewer of any group."); + "User can't set operation for group " + grpId + " because the user in not a " + "Reviewer of any group."); } } else { String userGroupsOnly = settingManager.getValue(Settings.SYSTEM_METADATAPRIVS_USERGROUPONLY); @@ -168,7 +253,7 @@ public void checkOperationPermission(ServiceContext context, int grpId, UserGrou if (userGroupRepo.exists(new UserGroupId().setGroupId(grpId).setUserId(userId))) { throw new ServiceNotAllowedEx( - "User can't set operation for group " + grpId + " because the user in not" + " member of this group."); + "User can't set operation for group " + grpId + " because the user in not" + " member of this group."); } } } @@ -177,7 +262,6 @@ public void checkOperationPermission(ServiceContext context, int grpId, UserGrou } /** - * * @param context * @param mdId * @param grpId @@ -190,7 +274,6 @@ public void unsetOperation(ServiceContext context, String mdId, String grpId, Re } /** - * * @param context * @param mdId * @param grpId @@ -209,9 +292,9 @@ public void unsetOperation(ServiceContext context, String mdId, String grpId, St // -------------------------------------------------------------------------- /** - * @param mdId metadata id + * @param mdId metadata id * @param groupId group id - * @param operId operation id + * @param operId operation id */ @Override public void unsetOperation(ServiceContext context, int mdId, int groupId, int operId) throws Exception { @@ -226,22 +309,30 @@ public void unsetOperation(ServiceContext context, int mdId, int groupId, int op @Override public void forceUnsetOperation(ServiceContext context, int mdId, int groupId, int operId) throws Exception { OperationAllowedId id = new OperationAllowedId().setGroupId(groupId).setMetadataId(mdId).setOperationId(operId); - final OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - if (repository.exists(id)) { - repository.delete(id); + if (opAllowedRepo.exists(id)) { + opAllowedRepo.delete(id); if (svnManager != null) { svnManager.setHistory(mdId + "", context); } + + //If it is published/unpublished, throw event + if (operId == ReservedOperation.view.getId() + && groupId == ReservedGroup.all.getId()) { + + this.eventPublisher.publishEvent(new MetadataUnpublished( + metadataUtils.findOne(Integer.valueOf(mdId)))); + } + } } /** * Sets VIEW and NOTIFY privileges for a metadata to a group. * - * @param context service context - * @param id metadata id - * @param groupId group id - * @param fullRightsForGroup + * @param context service context + * @param id metadata id + * @param groupId group id + * @param fullRightsForGroup * @throws Exception hmmm */ @Override @@ -275,4 +366,9 @@ public boolean isUserMetadataOwner(int userId) throws Exception { public boolean existsUser(ServiceContext context, int id) throws Exception { return userRepository.count(where(UserSpecs.hasUserId(id))) > 0; } + + @Override + public Collection getAllOperations(int id) { + return opAllowedRepo.findAllById_MetadataId(id); + } } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataSchemaUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataSchemaUtils.java index 49e51406625..5243891b5df 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataSchemaUtils.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataSchemaUtils.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import java.nio.file.Path; @@ -33,7 +56,6 @@ public void init(ServiceContext context, Boolean force) throws Exception { } /** - * * @param name * @return */ @@ -43,7 +65,6 @@ public MetadataSchema getSchema(String name) { } /** - * * @return */ @Override @@ -52,7 +73,6 @@ public Set getSchemas() { } /** - * * @param name * @return */ @@ -62,7 +82,6 @@ public boolean existsSchema(String name) { } /** - * * @param name * @return */ @@ -93,7 +112,8 @@ public String getMetadataSchema(String id) throws Exception { * @param md Record to checked against schemas */ @Override - public @CheckForNull String autodetectSchema(Element md) throws SchemaMatchConflictException, NoSchemaMatchesException { + public @CheckForNull + String autodetectSchema(Element md) throws SchemaMatchConflictException, NoSchemaMatchesException { return autodetectSchema(md, schemaManager.getDefaultSchema()); } @@ -101,17 +121,18 @@ public String getMetadataSchema(String id) throws Exception { * Checks autodetect elements in installed schemas to determine whether the metadata record belongs to that schema. Use this method when * you want to set the default schema to be returned when no other match can be found. * - * @param md Record to checked against schemas + * @param md Record to checked against schemas * @param defaultSchema Schema to be assigned when no other schema matches */ @Override - public @CheckForNull String autodetectSchema(Element md, String defaultSchema) - throws SchemaMatchConflictException, NoSchemaMatchesException { + public @CheckForNull + String autodetectSchema(Element md, String defaultSchema) + throws SchemaMatchConflictException, NoSchemaMatchesException { if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) Log.debug(Geonet.DATA_MANAGER, - "Autodetect schema for metadata with :\n * root element:'" + md.getQualifiedName() + "'\n * with namespace:'" - + md.getNamespace() + "\n * with additional namespaces:" + md.getAdditionalNamespaces().toString()); + "Autodetect schema for metadata with :\n * root element:'" + md.getQualifiedName() + "'\n * with namespace:'" + + md.getNamespace() + "\n * with additional namespaces:" + md.getAdditionalNamespaces().toString()); String schema = schemaManager.autodetectSchema(md, defaultSchema); if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) Log.debug(Geonet.DATA_MANAGER, "Schema detected was " + schema); diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java index 5c6b4346670..9dbf8edd4a8 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import java.util.List; @@ -115,6 +138,7 @@ public MetadataStatus setStatus(ServiceContext context, int id, int status, ISOD metadataIndexer.indexMetadata(Integer.toString(id), true, null); return statusObject; } + @Override public MetadataStatus setStatusExt(MetadataStatus metatatStatus) throws Exception { metadataStatusRepository.save(metatatStatus); @@ -129,14 +153,14 @@ public MetadataStatus setStatusExt(MetadataStatus metatatStatus) throws Exceptio */ @Override public MetadataStatus setStatusExt(ServiceContext context, int id, int status, ISODate changeDate, String changeMessage) - throws Exception { + throws Exception { MetadataStatus metatatStatus = new MetadataStatus(); metatatStatus.setChangeMessage(changeMessage); metatatStatus.setStatusValue(statusValueRepository.findOne(status)); int userId = context.getUserSession().getUserIdAsInt(); MetadataStatusId mdStatusId = new MetadataStatusId().setStatusId(status).setMetadataId(id).setChangeDate(changeDate) - .setUserId(userId); + .setUserId(userId); mdStatusId.setChangeDate(changeDate); metatatStatus.setId(mdStatusId); @@ -165,8 +189,8 @@ public void activateWorkflowIfConfigured(ServiceContext context, String newId, S final Matcher matcher = pattern.matcher(groupName); if (matcher.find()) { setStatus(context, Integer.valueOf(newId), Integer.valueOf(StatusValue.Status.DRAFT), new ISODate(), - String.format("Workflow automatically enabled for record in group %s. Record status is set to %s.", groupName, - StatusValue.Status.DRAFT)); + String.format("Workflow automatically enabled for record in group %s. Record status is set to %s.", groupName, + StatusValue.Status.DRAFT)); } } } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java index cbcde07371c..355bf37f76e 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java @@ -1,30 +1,53 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import static org.fao.geonet.repository.specification.MetadataSpecs.hasMetadataUuid; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.PostConstruct; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import org.apache.commons.lang.NotImplementedException; import org.fao.geonet.NodeInfo; import org.fao.geonet.constants.Edit; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.ISODate; import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataDataInfo; import org.fao.geonet.domain.MetadataHarvestInfo; import org.fao.geonet.domain.MetadataRatingByIp; import org.fao.geonet.domain.MetadataRatingByIpId; +import org.fao.geonet.domain.MetadataSourceInfo; import org.fao.geonet.domain.MetadataType; import org.fao.geonet.domain.Pair; import org.fao.geonet.domain.ReservedOperation; @@ -52,6 +75,8 @@ import org.jdom.Namespace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; @@ -62,207 +87,206 @@ import jeeves.server.context.ServiceContext; public class BaseMetadataUtils implements IMetadataUtils { - @Autowired - private MetadataRepository metadataRepository; - - @Autowired - private MetadataNotifierManager metadataNotifierManager; - - // FIXME Remove when get rid of Jeeves - private ServiceContext servContext; - @Autowired - private IMetadataSchemaUtils metadataSchemaUtils; - @Autowired - private IMetadataIndexer metadataIndexer; - @Autowired - private SchemaManager schemaManager; - @Autowired - protected MetadataRatingByIpRepository ratingByIpRepository; - @Autowired - @Lazy - private SettingManager settingManager; - - @Autowired - private IndexingList indexingList; - - @Autowired(required = false) - private XmlSerializer xmlSerializer; - - private Path stylePath; - - private IMetadataManager metadataManager; - - @Override - public void setMetadataManager(IMetadataManager metadataManager) { - this.metadataManager = metadataManager; - } - - @SuppressWarnings("unchecked") - public void init(ServiceContext context, Boolean force) throws Exception { - metadataRepository = context.getBean(MetadataRepository.class); - metadataNotifierManager = context.getBean(MetadataNotifierManager.class); - servContext = context; - schemaManager = context.getBean(SchemaManager.class); - metadataSchemaUtils = context.getBean(IMetadataSchemaUtils.class); - metadataIndexer = context.getBean(IMetadataIndexer.class); - ratingByIpRepository = context.getBean(MetadataRatingByIpRepository.class); - settingManager = context.getBean(SettingManager.class); - xmlSerializer = context.getBean(XmlSerializer.class); - indexingList = context.getBean(IndexingList.class); - - final GeonetworkDataDirectory dataDirectory = context.getBean(GeonetworkDataDirectory.class); - stylePath = dataDirectory.resolveWebResource(Geonet.Path.STYLESHEETS); - } - - /** - * Needed to avoid circular dependency injection - */ - @PostConstruct - public void init() { - this.metadataIndexer.setMetadataUtils(this); - } - - /** - * - * @param md - * @param metadataId - * @throws Exception - */ - @Override - public void notifyMetadataChange(Element md, String metadataId) throws Exception { - final AbstractMetadata metadata = findOne(metadataId); - if (metadata != null && metadata.getDataInfo().getType() == MetadataType.METADATA) { - MetadataSchema mds = schemaManager.getSchema(metadata.getDataInfo().getSchemaId()); - Pair editXpathFilter = mds.getOperationFilter(ReservedOperation.editing); - XmlSerializer.removeFilteredElement(md, editXpathFilter, mds.getNamespaces()); - - String uuid = getMetadataUuid(metadataId); - metadataNotifierManager.updateMetadata(md, metadataId, uuid, getServiceContext()); - } - } - - /** - * - * @param id - * @return - * @throws Exception - */ - public @Nullable @Override String getMetadataUuid(@Nonnull String id) throws Exception { - AbstractMetadata metadata = findOne(id); - - if (metadata == null) - return null; - - return metadata.getUuid(); - } - - protected ServiceContext getServiceContext() { - ServiceContext context = ServiceContext.get(); - return context == null ? servContext : context; - } - - /** - * Start an editing session. This will record the original metadata record in - * the session under the - * {@link org.fao.geonet.constants.Geonet.Session#METADATA_BEFORE_ANY_CHANGES} + - * id session property. - * - * The record contains geonet:info element. - * - * Note: Only the metadata record is stored in session. If the editing session - * upload new documents or thumbnails, those documents will not be cancelled. - * This needs improvements. - */ - @Override - public void startEditingSession(ServiceContext context, String id) throws Exception { - if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { - Log.debug(Geonet.EDITOR_SESSION, "Editing session starts for record " + id); - } - - boolean keepXlinkAttributes = true; - boolean forEditing = false; - boolean withValidationErrors = false; - Element metadataBeforeAnyChanges = context.getBean(IMetadataManager.class).getMetadata(context, id, forEditing, - withValidationErrors, keepXlinkAttributes); - context.getUserSession().setProperty(Geonet.Session.METADATA_BEFORE_ANY_CHANGES + id, metadataBeforeAnyChanges); - } - - /** - * Rollback to the record in the state it was when the editing session started - * (See {@link #startEditingSession(ServiceContext, String)}). - */ - @Override - public void cancelEditingSession(ServiceContext context, String id) throws Exception { - UserSession session = context.getUserSession(); - Element metadataBeforeAnyChanges = (Element) session - .getProperty(Geonet.Session.METADATA_BEFORE_ANY_CHANGES + id); - - if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { - Log.debug(Geonet.EDITOR_SESSION, "Editing session end. Cancel changes. Restore record " + id - + ". Replace by original record which was: "); - } - - if (metadataBeforeAnyChanges != null) { - if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { - Log.debug(Geonet.EDITOR_SESSION, " > restoring record: "); - Log.debug(Geonet.EDITOR_SESSION, Xml.getString(metadataBeforeAnyChanges)); - } - Element info = metadataBeforeAnyChanges.getChild(Edit.RootChild.INFO, Edit.NAMESPACE); - boolean validate = false; - boolean ufo = false; - boolean index = true; - metadataBeforeAnyChanges.removeChild(Edit.RootChild.INFO, Edit.NAMESPACE); - context.getBean(IMetadataManager.class).updateMetadata(context, id, metadataBeforeAnyChanges, validate, ufo, - index, context.getLanguage(), info.getChildText(Edit.Info.Elem.CHANGE_DATE), false); - endEditingSession(id, session); - } else { - if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { - Log.debug(Geonet.EDITOR_SESSION, " > nothing to cancel for record " + id - + ". Original record was null. Use starteditingsession to."); - } - } - } - - /** - * Remove the original record stored in session. - */ - @Override - public void endEditingSession(String id, UserSession session) { - if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { - Log.debug(Geonet.EDITOR_SESSION, "Editing session end."); - } - session.removeProperty(Geonet.Session.METADATA_BEFORE_ANY_CHANGES + id); - } - - /** - * - * @param md - * @return - * @throws Exception - */ - @Override - public Element enumerateTree(Element md) throws Exception { - metadataManager.getEditLib().enumerateTree(md); - return md; - } - - /** - * Extract UUID from the metadata record using the schema XSL for UUID - * extraction) - */ - @Override - public String extractUUID(String schema, Element md) throws Exception { - Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.EXTRACT_UUID); - String uuid = Xml.transform(md, styleSheet).getText().trim(); - - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) - Log.debug(Geonet.DATA_MANAGER, "Extracted UUID '" + uuid + "' for schema '" + schema + "'"); - - // --- needed to detach md from the document - md.detach(); - - return uuid; - } + @Autowired + private MetadataRepository metadataRepository; + + @Autowired + private MetadataNotifierManager metadataNotifierManager; + + // FIXME Remove when get rid of Jeeves + private ServiceContext servContext; + @Autowired + protected IMetadataSchemaUtils metadataSchemaUtils; + @Autowired + protected IMetadataIndexer metadataIndexer; + @Autowired + protected SchemaManager schemaManager; + @Autowired + protected MetadataRatingByIpRepository ratingByIpRepository; + @Autowired + @Lazy + protected SettingManager settingManager; + + @Autowired + private IndexingList indexingList; + + @Autowired(required = false) + protected XmlSerializer xmlSerializer; + + private Path stylePath; + + protected IMetadataManager metadataManager; + + @Override + public void setMetadataManager(IMetadataManager metadataManager) { + this.metadataManager = metadataManager; + } + + public void init(ServiceContext context, Boolean force) throws Exception { + metadataRepository = context.getBean(MetadataRepository.class); + metadataNotifierManager = context.getBean(MetadataNotifierManager.class); + servContext = context; + schemaManager = context.getBean(SchemaManager.class); + metadataSchemaUtils = context.getBean(IMetadataSchemaUtils.class); + metadataIndexer = context.getBean(IMetadataIndexer.class); + ratingByIpRepository = context.getBean(MetadataRatingByIpRepository.class); + settingManager = context.getBean(SettingManager.class); + xmlSerializer = context.getBean(XmlSerializer.class); + indexingList = context.getBean(IndexingList.class); + + final GeonetworkDataDirectory dataDirectory = context.getBean(GeonetworkDataDirectory.class); + stylePath = dataDirectory.resolveWebResource(Geonet.Path.STYLESHEETS); + } + + /** + * Needed to avoid circular dependency injection + */ + @PostConstruct + public void init() { + this.metadataIndexer.setMetadataUtils(this); + } + + /** + * @param md + * @param metadataId + * @throws Exception + */ + @Override + public void notifyMetadataChange(Element md, String metadataId) throws Exception { + final AbstractMetadata metadata = findOne(metadataId); + if (metadata != null && metadata.getDataInfo().getType() == MetadataType.METADATA) { + MetadataSchema mds = schemaManager.getSchema(metadata.getDataInfo().getSchemaId()); + Pair editXpathFilter = mds.getOperationFilter(ReservedOperation.editing); + XmlSerializer.removeFilteredElement(md, editXpathFilter, mds.getNamespaces()); + + String uuid = getMetadataUuid(metadataId); + metadataNotifierManager.updateMetadata(md, metadataId, uuid, getServiceContext()); + } + } + + /** + * @param id + * @return + * @throws Exception + */ + public @Nullable + @Override + String getMetadataUuid(@Nonnull String id) throws Exception { + AbstractMetadata metadata = findOne(id); + + if (metadata == null) + return null; + + return metadata.getUuid(); + } + + protected ServiceContext getServiceContext() { + ServiceContext context = ServiceContext.get(); + return context == null ? servContext : context; + } + + /** + * Start an editing session. This will record the original metadata record in + * the session under the + * {@link org.fao.geonet.constants.Geonet.Session#METADATA_BEFORE_ANY_CHANGES} + + * id session property. + *

      + * The record contains geonet:info element. + *

      + * Note: Only the metadata record is stored in session. If the editing session + * upload new documents or thumbnails, those documents will not be cancelled. + * This needs improvements. + */ + @Override + public Integer startEditingSession(ServiceContext context, String id) throws Exception { + if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { + Log.debug(Geonet.EDITOR_SESSION, "Editing session starts for record " + id); + } + + boolean keepXlinkAttributes = true; + boolean forEditing = false; + boolean withValidationErrors = false; + Element metadataBeforeAnyChanges = context.getBean(IMetadataManager.class).getMetadata(context, id, forEditing, + withValidationErrors, keepXlinkAttributes); + context.getUserSession().setProperty(Geonet.Session.METADATA_BEFORE_ANY_CHANGES + id, metadataBeforeAnyChanges); + return Integer.valueOf(id); + } + + /** + * Rollback to the record in the state it was when the editing session started + * (See {@link #startEditingSession(ServiceContext, String)}). + */ + @Override + public void cancelEditingSession(ServiceContext context, String id) throws Exception { + UserSession session = context.getUserSession(); + Element metadataBeforeAnyChanges = (Element) session + .getProperty(Geonet.Session.METADATA_BEFORE_ANY_CHANGES + id); + + if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { + Log.debug(Geonet.EDITOR_SESSION, "Editing session end. Cancel changes. Restore record " + id + + ". Replace by original record which was: "); + } + + if (metadataBeforeAnyChanges != null) { + if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { + Log.debug(Geonet.EDITOR_SESSION, " > restoring record: "); + Log.debug(Geonet.EDITOR_SESSION, Xml.getString(metadataBeforeAnyChanges)); + } + Element info = metadataBeforeAnyChanges.getChild(Edit.RootChild.INFO, Edit.NAMESPACE); + boolean validate = false; + boolean ufo = false; + boolean index = true; + metadataBeforeAnyChanges.removeChild(Edit.RootChild.INFO, Edit.NAMESPACE); + context.getBean(IMetadataManager.class).updateMetadata(context, id, metadataBeforeAnyChanges, validate, ufo, + index, context.getLanguage(), info.getChildText(Edit.Info.Elem.CHANGE_DATE), false); + endEditingSession(id, session); + } else { + if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { + Log.debug(Geonet.EDITOR_SESSION, " > nothing to cancel for record " + id + + ". Original record was null. Use starteditingsession to."); + } + } + } + + /** + * Remove the original record stored in session. + */ + @Override + public void endEditingSession(String id, UserSession session) { + if (Log.isDebugEnabled(Geonet.EDITOR_SESSION)) { + Log.debug(Geonet.EDITOR_SESSION, "Editing session end."); + } + session.removeProperty(Geonet.Session.METADATA_BEFORE_ANY_CHANGES + id); + } + + /** + * @param md + * @return + * @throws Exception + */ + @Override + public Element enumerateTree(Element md) throws Exception { + metadataManager.getEditLib().enumerateTree(md); + return md; + } + + /** + * Extract UUID from the metadata record using the schema XSL for UUID + * extraction) + */ + @Override + public String extractUUID(String schema, Element md) throws Exception { + Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.EXTRACT_UUID); + String uuid = Xml.transform(md, styleSheet).getText().trim(); + + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) + Log.debug(Geonet.DATA_MANAGER, "Extracted UUID '" + uuid + "' for schema '" + schema + "'"); + + // --- needed to detach md from the document + md.detach(); + + return uuid; + } /** * Extract metadata default language from the metadata record using the schema XSL for default language extraction) @@ -283,680 +307,693 @@ public String extractDefaultLanguage(String schema, Element md) throws Exception /** - * - * @param schema - * @param md - * @return - * @throws Exception - */ - @Override - public String extractDateModified(String schema, Element md) throws Exception { - Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.EXTRACT_DATE_MODIFIED); - String dateMod = Xml.transform(md, styleSheet).getText().trim(); - - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) - Log.debug(Geonet.DATA_MANAGER, "Extracted Date Modified '" + dateMod + "' for schema '" + schema + "'"); - - // --- needed to detach md from the document - md.detach(); - - return dateMod; - } - - /** - * - * @param schema - * @param uuid - * @param md - * @return - * @throws Exception - */ - @Override - public Element setUUID(String schema, String uuid, Element md) throws Exception { - // --- setup environment - - Element env = new Element("env"); - env.addContent(new Element("uuid").setText(uuid)); - - // --- setup root element - - Element root = new Element("root"); - root.addContent(md.detach()); - root.addContent(env.detach()); - - // --- do an XSL transformation - - Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.SET_UUID); - - return Xml.transform(root, styleSheet); - } - - /** - * - * @param md - * @return - * @throws Exception - */ - @Override - public Element extractSummary(Element md) throws Exception { - Path styleSheet = stylePath.resolve(Geonet.File.METADATA_BRIEF); - Element summary = Xml.transform(md, styleSheet); - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) - Log.debug(Geonet.DATA_MANAGER, "Extracted summary '\n" + Xml.getString(summary)); - - // --- needed to detach md from the document - md.detach(); - - return summary; - } - - /** - * - * @param uuid - * @return - * @throws Exception - */ - @Override - public @Nullable String getMetadataId(@Nonnull String uuid) throws Exception { - final List idList = findAllIdsBy(hasMetadataUuid(uuid)); - if (idList.isEmpty()) { - return null; - } - return String.valueOf(idList.get(0)); - } - - @SuppressWarnings("unchecked") - @Override - public List findAllIdsBy(Specification specs) { - try { - return metadataRepository.findAllIdsBy((Specification) specs); - } catch (Throwable t) { - // Maybe it is not a Specification - } - throw new NotImplementedException("Unknown IMetadata subtype: " + specs.getClass().getName()); - } - - /** - * - * @param id - * @return - */ - @Override - public String getVersion(String id) { - return metadataManager.getEditLib().getVersion(id); - } - - /** - * - * @param id - * @return - */ - @Override - public String getNewVersion(String id) { - return metadataManager.getEditLib().getNewVersion(id); - } - - @Override - public void setTemplate(final int id, final MetadataType type, final String title) throws Exception { - setTemplateExt(id, type); - metadataIndexer.indexMetadata(Integer.toString(id), true, null); - } - - @Override - public void setTemplateExt(final int id, final MetadataType metadataType) throws Exception { - metadataRepository.update(id, new Updater() { - @Override - public void apply(@Nonnull Metadata metadata) { - final MetadataDataInfo dataInfo = metadata.getDataInfo(); - dataInfo.setType(metadataType); - } - }); - } - - @Override - public void setHarvested(int id, String harvestUuid) throws Exception { - setHarvestedExt(id, harvestUuid); - metadataIndexer.indexMetadata(Integer.toString(id), true, null); - } - - /** - * Set metadata type to subtemplate and set the title. Only subtemplates need to - * persist the title as it is used to give a meaningful title for use when - * offering the subtemplate to users in the editor. - * - * @param id - * Metadata id to set to type subtemplate - * @param title - * Title of metadata of subtemplate/fragment - */ - @Override - public void setSubtemplateTypeAndTitleExt(final int id, String title) throws Exception { - metadataRepository.update(id, new Updater() { - @Override - public void apply(@Nonnull Metadata metadata) { - final MetadataDataInfo dataInfo = metadata.getDataInfo(); - dataInfo.setType(MetadataType.SUB_TEMPLATE); - if (title != null) { - dataInfo.setTitle(title); - } - } - }); - } - - @Override - public void setHarvestedExt(int id, String harvestUuid) throws Exception { - setHarvestedExt(id, harvestUuid, Optional.absent()); - } - - @Override - public void setHarvestedExt(final int id, final String harvestUuid, final Optional harvestUri) - throws Exception { - metadataRepository.update(id, new Updater() { - @Override - public void apply(Metadata metadata) { - MetadataHarvestInfo harvestInfo = metadata.getHarvestInfo(); - harvestInfo.setUuid(harvestUuid); - harvestInfo.setHarvested(harvestUuid != null); - harvestInfo.setUri(harvestUri.orNull()); - } - }); - } - - /** - * - * @param id - * @param displayOrder - * @throws Exception - */ - @Override - public void updateDisplayOrder(final String id, final String displayOrder) throws Exception { - metadataRepository.update(Integer.valueOf(id), new Updater() { - @Override - public void apply(Metadata entity) { - entity.getDataInfo().setDisplayOrder(Integer.parseInt(displayOrder)); - } - }); - } - - /** - * @throws Exception - * hmm - */ - @Override - public void increasePopularity(ServiceContext srvContext, String id) throws Exception { - // READONLYMODE - if (!srvContext.getBean(NodeInfo.class).isReadOnly()) { - // Update the popularity in database - int iId = Integer.parseInt(id); - metadataRepository.incrementPopularity(iId); - - // And register the metadata to be indexed in the near future - final IndexingList list = srvContext.getBean(IndexingList.class); - list.add(iId); - } else { - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) { - Log.debug(Geonet.DATA_MANAGER, - "GeoNetwork is operating in read-only mode. IncreasePopularity is skipped."); - } - } - } - - /** - * Rates a metadata. - * - * @param ipAddress - * ipAddress IP address of the submitting client - * @param rating - * range should be 1..5 - * @throws Exception - * hmm - */ - @Override - public int rateMetadata(final int metadataId, final String ipAddress, final int rating) throws Exception { - // Save rating for this IP - MetadataRatingByIp ratingEntity = new MetadataRatingByIp(); - ratingEntity.setRating(rating); - ratingEntity.setId(new MetadataRatingByIpId(metadataId, ipAddress)); - - ratingByIpRepository.save(ratingEntity); - - // calculate new rating - final int newRating = ratingByIpRepository.averageRating(metadataId); - - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) - Log.debug(Geonet.DATA_MANAGER, "Setting rating for id:" + metadataId + " --> rating is:" + newRating); - - metadataRepository.update(metadataId, new Updater() { - @Override - public void apply(Metadata entity) { - entity.getDataInfo().setRating(newRating); - } - }); - // And register the metadata to be indexed in the near future - indexingList.add(metadataId); - - return rating; - } - - /** - * Retrieves a metadata (in xml) given its id with no geonet:info. - */ - @SuppressWarnings("unchecked") - @Override - public Element getMetadataNoInfo(ServiceContext srvContext, String id) throws Exception { - Element md = srvContext.getBean(IMetadataManager.class).getMetadata(srvContext, id, false, false, false); - md.removeChild(Edit.RootChild.INFO, Edit.NAMESPACE); - - // Drop Geonet namespace declaration. It may be contained - // multiple times, so loop on all. - final List additionalNamespaces = new ArrayList(md.getAdditionalNamespaces()); - for (Namespace n : additionalNamespaces) { - if (Edit.NAMESPACE.getURI().equals(n.getURI())) { - md.removeNamespaceDeclaration(Edit.NAMESPACE); - } - } - return md; - } - - /** - * Retrieves a metadata element given it's ref. - */ - @Override - public Element getElementByRef(Element md, String ref) { - return metadataManager.getEditLib().findElement(md, ref); - } - - /** - * Returns true if the metadata exists in the database. - */ - @Override - public boolean existsMetadata(int id) throws Exception { - return exists(id); - } - - /** - * Returns true if the metadata uuid exists in the database. - */ - @Override - public boolean existsMetadataUuid(String uuid) throws Exception { - return !findAllIdsBy(hasMetadataUuid(uuid)).isEmpty(); - } - - /** - * Returns all the keywords in the system. - */ - @Override - public Element getKeywords() throws Exception { - // TODO ES - // Collection keywords = getSearchManager().getTerms("keyword"); - Element el = new Element("keywords"); - - // for (Object keyword : keywords) { - // el.addContent(new Element("keyword").setText((String) keyword)); - // } - return el; - } - - /** - * - * @param metadataId - * @return - * @throws Exception - */ - @Override - public Element getThumbnails(ServiceContext context, String metadataId) throws Exception { - Element md = getXmlSerializer().select(context, metadataId); - - if (md == null) - return null; - - md.detach(); - - String schema = metadataSchemaUtils.getMetadataSchema(metadataId); - - // --- do an XSL transformation - Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.EXTRACT_THUMBNAILS); - - Element result = Xml.transform(md, styleSheet); - result.addContent(new Element("id").setText(metadataId)); - - return result; - } - - /** - * - * @param context - * @param id - * @param small - * @param file - * @throws Exception - */ - @Override - public void setThumbnail(ServiceContext context, String id, boolean small, String file, boolean indexAfterChange) - throws Exception { - int pos = file.lastIndexOf('.'); - String ext = (pos == -1) ? "???" : file.substring(pos + 1); - - Element env = new Element("env"); - env.addContent(new Element("file").setText(file)); - env.addContent(new Element("ext").setText(ext)); - - String host = getSettingManager().getValue(Settings.SYSTEM_SERVER_HOST); - String port = getSettingManager().getValue(Settings.SYSTEM_SERVER_PORT); - String baseUrl = context.getBaseUrl(); - - env.addContent(new Element("host").setText(host)); - env.addContent(new Element("port").setText(port)); - env.addContent(new Element("baseUrl").setText(baseUrl)); - // TODO: Remove host, port, baseUrl and simplify the - // URL created in the XSLT. Keeping it for the time - // as many profiles depend on it. - env.addContent(new Element("url").setText(getSettingManager().getSiteURL(context))); - - manageThumbnail(context, id, small, env, Geonet.File.SET_THUMBNAIL, indexAfterChange); - } - - /** - * - * @param context - * @param id - * @param small - * @throws Exception - */ - @Override - public void unsetThumbnail(ServiceContext context, String id, boolean small, boolean indexAfterChange) - throws Exception { - Element env = new Element("env"); - - manageThumbnail(context, id, small, env, Geonet.File.UNSET_THUMBNAIL, indexAfterChange); - } - - /** - * - * @param context - * @param id - * @param small - * @param env - * @param styleSheet - * @param indexAfterChange - * @throws Exception - */ - private void manageThumbnail(ServiceContext context, String id, boolean small, Element env, String styleSheet, - boolean indexAfterChange) throws Exception { - boolean forEditing = false, withValidationErrors = false, keepXlinkAttributes = true; - Element md = context.getBean(IMetadataManager.class).getMetadata(context, id, forEditing, withValidationErrors, - keepXlinkAttributes); - - if (md == null) - return; - - md.detach(); - - String schema = metadataSchemaUtils.getMetadataSchema(id); - - // --- setup environment - String type = small ? "thumbnail" : "large_thumbnail"; - env.addContent(new Element("type").setText(type)); - transformMd(context, id, md, env, schema, styleSheet, indexAfterChange); - } - - /** - * - * @param context - * @param metadataId - * @param md - * @param env - * @param schema - * @param styleSheet - * @param indexAfterChange - * @throws Exception - */ - private void transformMd(ServiceContext context, String metadataId, Element md, Element env, String schema, - String styleSheet, boolean indexAfterChange) throws Exception { - - if (env.getChild("host") == null) { - String host = getSettingManager().getValue(Settings.SYSTEM_SERVER_HOST); - String port = getSettingManager().getValue(Settings.SYSTEM_SERVER_PORT); - env.addContent(new Element("host").setText(host)); - env.addContent(new Element("port").setText(port)); - } - - // --- setup root element - Element root = new Element("root"); - root.addContent(md); - root.addContent(env); - - // --- do an XSL transformation - Path styleSheetPath = metadataSchemaUtils.getSchemaDir(schema).resolve(styleSheet); - - md = Xml.transform(root, styleSheetPath); - String changeDate = null; - String uuid = null; - if (schemaManager.getSchema(schema).isReadwriteUUID()) { - uuid = extractUUID(schema, md); - } - - getXmlSerializer().update(metadataId, md, changeDate, true, uuid, context); - - if (indexAfterChange) { - // Notifies the metadata change to metatada notifier service - notifyMetadataChange(md, metadataId); - - // --- update search criteria - metadataIndexer.indexMetadata(metadataId, true, null); - } - } - - /** - * - * @param context - * @param id - * @param licenseurl - * @param imageurl - * @param jurisdiction - * @param licensename - * @param type - * @throws Exception - */ - @Override - public void setDataCommons(ServiceContext context, String id, String licenseurl, String imageurl, - String jurisdiction, String licensename, String type) throws Exception { - Element env = prepareCommonsEnv(licenseurl, imageurl, jurisdiction, licensename, type); - manageCommons(context, id, env, Geonet.File.SET_DATACOMMONS); - } - - private Element prepareCommonsEnv(String licenseurl, String imageurl, String jurisdiction, String licensename, - String type) { - Element env = new Element("env"); - env.addContent(new Element("imageurl").setText(imageurl)); - env.addContent(new Element("licenseurl").setText(licenseurl)); - env.addContent(new Element("jurisdiction").setText(jurisdiction)); - env.addContent(new Element("licensename").setText(licensename)); - env.addContent(new Element("type").setText(type)); - return env; - } - - /** - * - * @param context - * @param id - * @param licenseurl - * @param imageurl - * @param jurisdiction - * @param licensename - * @param type - * @throws Exception - */ - @Override - public void setCreativeCommons(ServiceContext context, String id, String licenseurl, String imageurl, - String jurisdiction, String licensename, String type) throws Exception { - Element env = prepareCommonsEnv(licenseurl, imageurl, jurisdiction, licensename, type); - manageCommons(context, id, env, Geonet.File.SET_CREATIVECOMMONS); - } - - /** - * Extract the title field from the Metadata Repository. This is only valid for - * subtemplates as the title can be stored with the subtemplate (since - * subtemplates don't have a title) - metadata records don't store the title - * here as this is part of the metadata. - * - * @param id - * metadata id to retrieve - */ - @Override - public String getMetadataTitle(String id) throws Exception { - AbstractMetadata md = findOne(id); - - if (md == null) { - throw new IllegalArgumentException("Metadata not found for id : " + id); - } else { - // get metadata title - return md.getDataInfo().getTitle(); - } - } - - /** - * - * @param context - * @param id - * @param env - * @param styleSheet - * @throws Exception - */ - private void manageCommons(ServiceContext context, String id, Element env, String styleSheet) throws Exception { - Lib.resource.checkEditPrivilege(context, id); - Element md = getXmlSerializer().select(context, id); - - if (md == null) - return; - - md.detach(); - - String schema = metadataSchemaUtils.getMetadataSchema(id); - transformMd(context, id, md, env, schema, styleSheet, true); - } - - private XmlSerializer getXmlSerializer() { - return xmlSerializer; - } - - private SettingManager getSettingManager() { - return this.settingManager; - } - - @SuppressWarnings("unchecked") - @Override - public long count(Specification specs) { - try { - return metadataRepository.count((Specification) specs); - } catch (Throwable t) { - // Maybe it is not a Specification - } - throw new NotImplementedException("Unknown IMetadata subtype: " + specs.getClass().getName()); - } - - @Override - public long count() { - return metadataRepository.count(); - } - - @Override - public AbstractMetadata findOne(int id) { - return metadataRepository.findOne(id); - } - - @Override - public AbstractMetadata findOneByUuid(String uuid) { - return metadataRepository.findOneByUuid(uuid); - } - - @Override - public AbstractMetadata findOne(Specification spec) { - return metadataRepository.findOne(spec); - } - - @Override - public AbstractMetadata findOne(String id) { - return metadataRepository.findOne(id); - } - - @Override - public List findAllByHarvestInfo_Uuid(String uuid) { - return metadataRepository.findAllByHarvestInfo_Uuid(uuid); - } - - @Override - public Iterable findAll(Set keySet) { - return metadataRepository.findAll(keySet); - } - - @SuppressWarnings("unchecked") - @Override - public List findAll(Specification specs) { - try { - return metadataRepository.findAll((Specification) specs); - } catch (Throwable t) { - // Maybe it is not a Specification - } - throw new NotImplementedException("Unknown IMetadata subtype: " + specs.getClass().getName()); - } - - @Override - public List findAllSimple(String harvestUuid) { - return metadataRepository.findAllSimple(harvestUuid); - } - - @Override - public boolean exists(Integer iId) { - return metadataRepository.exists(iId); - } - - @SuppressWarnings("unchecked") - @Override - public Element findAllAsXml(Specification specs, Sort sortByChangeDateDesc) { - try { - return metadataRepository.findAllAsXml((Specification) specs, sortByChangeDateDesc); - } catch (Throwable t) { - // Maybe it is not a Specification - } - throw new NotImplementedException("Unknown IMetadata subtype: " + specs.getClass().getName()); - } - - @Override - public MetadataReportsQueries getMetadataReports() { - return metadataRepository.getMetadataReports(); - } - - @SuppressWarnings("unchecked") - @Override - public Element findAllAsXml(@Nullable Specification specs, - @Nullable Pageable pageable) { - try { - return metadataRepository.findAllAsXml((Specification) specs, pageable); - } catch (Throwable t) { - // Maybe it is not a Specification - } - throw new NotImplementedException("Unknown IMetadata subtype: " + specs.getClass().getName()); - } - - protected MetadataRepository getMetadataRepository() { - return metadataRepository; - } - - protected SchemaManager getSchemaManager() { - return schemaManager; - } - - @Override - public boolean checkMetadataWithSameUuidExist(String uuid, int id) { - // Check if another record exist with that UUID - Metadata recordWithThatUuid = getMetadataRepository().findOneByUuid(uuid); - if (recordWithThatUuid != null && recordWithThatUuid.getId() != id) { - // If yes, this would have triggered a DataIntegrityViolationException - throw new IllegalArgumentException(String.format( - "Another record exist with UUID '%s'. This record as internal id '%d'. The record you're trying to update with id '%d' can not be saved.", - uuid, recordWithThatUuid.getId(), id)); - } - return false; - } + * @param schema + * @param md + * @return + * @throws Exception + */ + @Override + public String extractDateModified(String schema, Element md) throws Exception { + Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.EXTRACT_DATE_MODIFIED); + String dateMod = Xml.transform(md, styleSheet).getText().trim(); + + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) + Log.debug(Geonet.DATA_MANAGER, "Extracted Date Modified '" + dateMod + "' for schema '" + schema + "'"); + + // --- needed to detach md from the document + md.detach(); + + return dateMod; + } + + /** + * @param schema + * @param uuid + * @param md + * @return + * @throws Exception + */ + @Override + public Element setUUID(String schema, String uuid, Element md) throws Exception { + // --- setup environment + + Element env = new Element("env"); + env.addContent(new Element("uuid").setText(uuid)); + + // --- setup root element + + Element root = new Element("root"); + root.addContent(md.detach()); + root.addContent(env.detach()); + + // --- do an XSL transformation + + Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.SET_UUID); + + return Xml.transform(root, styleSheet); + } + + /** + * @param md + * @return + * @throws Exception + */ + @Override + public Element extractSummary(Element md) throws Exception { + Path styleSheet = stylePath.resolve(Geonet.File.METADATA_BRIEF); + Element summary = Xml.transform(md, styleSheet); + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) + Log.debug(Geonet.DATA_MANAGER, "Extracted summary '\n" + Xml.getString(summary)); + + // --- needed to detach md from the document + md.detach(); + + return summary; + } + + /** + * @param uuid + * @return + * @throws Exception + */ + @Override + public @Nullable + String getMetadataId(@Nonnull String uuid) throws Exception { + final List idList = findAllIdsBy(hasMetadataUuid(uuid)); + if (idList.isEmpty()) { + return null; + } + return String.valueOf(idList.get(0)); + } + + @Override + public List findAllIdsBy(Specification specs) { + try { + return metadataRepository.findAllIdsBy((Specification) specs); + } catch (ClassCastException t) { + // Maybe it is not a Specification + } + + return Collections.emptyList(); + } + + /** + * @param id + * @return + */ + @Override + public String getVersion(String id) { + return metadataManager.getEditLib().getVersion(id); + } + + /** + * @param id + * @return + */ + @Override + public String getNewVersion(String id) { + return metadataManager.getEditLib().getNewVersion(id); + } + + @Override + public void setTemplate(final int id, final MetadataType type, final String title) throws Exception { + setTemplateExt(id, type); + metadataIndexer.indexMetadata(Integer.toString(id), true, null); + } + + @Override + public void setTemplateExt(final int id, final MetadataType metadataType) throws Exception { + metadataRepository.update(id, new Updater() { + @Override + public void apply(@Nonnull Metadata metadata) { + final MetadataDataInfo dataInfo = metadata.getDataInfo(); + dataInfo.setType(metadataType); + } + }); + } + + @Override + public void setHarvested(int id, String harvestUuid) throws Exception { + setHarvestedExt(id, harvestUuid); + metadataIndexer.indexMetadata(Integer.toString(id), true, null); + } + + /** + * Set metadata type to subtemplate and set the title. Only subtemplates need to + * persist the title as it is used to give a meaningful title for use when + * offering the subtemplate to users in the editor. + * + * @param id Metadata id to set to type subtemplate + * @param title Title of metadata of subtemplate/fragment + */ + @Override + public void setSubtemplateTypeAndTitleExt(final int id, String title) throws Exception { + metadataRepository.update(id, new Updater() { + @Override + public void apply(@Nonnull Metadata metadata) { + final MetadataDataInfo dataInfo = metadata.getDataInfo(); + dataInfo.setType(MetadataType.SUB_TEMPLATE); + if (title != null) { + dataInfo.setTitle(title); + } + } + }); + } + + @Override + public void setHarvestedExt(int id, String harvestUuid) throws Exception { + setHarvestedExt(id, harvestUuid, Optional.absent()); + } + + @Override + public void setHarvestedExt(final int id, final String harvestUuid, final Optional harvestUri) + throws Exception { + metadataRepository.update(id, new Updater() { + @Override + public void apply(Metadata metadata) { + MetadataHarvestInfo harvestInfo = metadata.getHarvestInfo(); + harvestInfo.setUuid(harvestUuid); + harvestInfo.setHarvested(harvestUuid != null); + harvestInfo.setUri(harvestUri.orNull()); + } + }); + } + + /** + * @param id + * @param displayOrder + * @throws Exception + */ + @Override + public void updateDisplayOrder(final String id, final String displayOrder) throws Exception { + metadataRepository.update(Integer.valueOf(id), new Updater() { + @Override + public void apply(Metadata entity) { + entity.getDataInfo().setDisplayOrder(Integer.parseInt(displayOrder)); + } + }); + } + + /** + * @throws Exception hmm + */ + @Override + public void increasePopularity(ServiceContext srvContext, String id) throws Exception { + // READONLYMODE + if (!srvContext.getBean(NodeInfo.class).isReadOnly()) { + // Update the popularity in database + int iId = Integer.parseInt(id); + metadataRepository.incrementPopularity(iId); + + // And register the metadata to be indexed in the near future + final IndexingList list = srvContext.getBean(IndexingList.class); + list.add(iId); + } else { + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) { + Log.debug(Geonet.DATA_MANAGER, + "GeoNetwork is operating in read-only mode. IncreasePopularity is skipped."); + } + } + } + + /** + * Rates a metadata. + * + * @param ipAddress ipAddress IP address of the submitting client + * @param rating range should be 1..5 + * @throws Exception hmm + */ + @Override + public int rateMetadata(final int metadataId, final String ipAddress, final int rating) throws Exception { + // Save rating for this IP + MetadataRatingByIp ratingEntity = new MetadataRatingByIp(); + ratingEntity.setRating(rating); + ratingEntity.setId(new MetadataRatingByIpId(metadataId, ipAddress)); + + ratingByIpRepository.save(ratingEntity); + + // calculate new rating + final int newRating = ratingByIpRepository.averageRating(metadataId); + + if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) + Log.debug(Geonet.DATA_MANAGER, "Setting rating for id:" + metadataId + " --> rating is:" + newRating); + + metadataRepository.update(metadataId, new Updater() { + @Override + public void apply(Metadata entity) { + entity.getDataInfo().setRating(newRating); + } + }); + // And register the metadata to be indexed in the near future + indexingList.add(metadataId); + + return rating; + } + + /** + * Retrieves a metadata (in xml) given its id with no geonet:info. + */ + @SuppressWarnings("unchecked") + @Override + public Element getMetadataNoInfo(ServiceContext srvContext, String id) throws Exception { + Element md = srvContext.getBean(IMetadataManager.class).getMetadata(srvContext, id, false, false, false); + md.removeChild(Edit.RootChild.INFO, Edit.NAMESPACE); + + // Drop Geonet namespace declaration. It may be contained + // multiple times, so loop on all. + final List additionalNamespaces = new ArrayList(md.getAdditionalNamespaces()); + for (Namespace n : additionalNamespaces) { + if (Edit.NAMESPACE.getURI().equals(n.getURI())) { + md.removeNamespaceDeclaration(Edit.NAMESPACE); + } + } + return md; + } + + /** + * Retrieves a metadata element given it's ref. + */ + @Override + public Element getElementByRef(Element md, String ref) { + return metadataManager.getEditLib().findElement(md, ref); + } + + /** + * Returns true if the metadata exists in the database. + */ + @Override + public boolean existsMetadata(int id) throws Exception { + return exists(id); + } + + /** + * Returns true if the metadata uuid exists in the database. + */ + @Override + public boolean existsMetadataUuid(String uuid) throws Exception { + return !findAllIdsBy(hasMetadataUuid(uuid)).isEmpty(); + } + + /** + * Returns all the keywords in the system. + */ + @Override + public Element getKeywords() throws Exception { + // TODO ES + // Collection keywords = getSearchManager().getTerms("keyword"); + Element el = new Element("keywords"); + + // for (Object keyword : keywords) { + // el.addContent(new Element("keyword").setText((String) keyword)); + // } + return el; + } + + /** + * @param metadataId + * @return + * @throws Exception + */ + @Override + public Element getThumbnails(ServiceContext context, String metadataId) throws Exception { + Element md = getXmlSerializer().select(context, metadataId); + + if (md == null) + return null; + + md.detach(); + + String schema = metadataSchemaUtils.getMetadataSchema(metadataId); + + // --- do an XSL transformation + Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.EXTRACT_THUMBNAILS); + + Element result = Xml.transform(md, styleSheet); + result.addContent(new Element("id").setText(metadataId)); + + return result; + } + + /** + * @param context + * @param id + * @param small + * @param file + * @throws Exception + */ + @Override + public void setThumbnail(ServiceContext context, String id, boolean small, String file, boolean indexAfterChange) + throws Exception { + int pos = file.lastIndexOf('.'); + String ext = (pos == -1) ? "???" : file.substring(pos + 1); + + Element env = new Element("env"); + env.addContent(new Element("file").setText(file)); + env.addContent(new Element("ext").setText(ext)); + + String host = getSettingManager().getValue(Settings.SYSTEM_SERVER_HOST); + String port = getSettingManager().getValue(Settings.SYSTEM_SERVER_PORT); + String baseUrl = context.getBaseUrl(); + + env.addContent(new Element("host").setText(host)); + env.addContent(new Element("port").setText(port)); + env.addContent(new Element("baseUrl").setText(baseUrl)); + // TODO: Remove host, port, baseUrl and simplify the + // URL created in the XSLT. Keeping it for the time + // as many profiles depend on it. + env.addContent(new Element("url").setText(getSettingManager().getSiteURL(context))); + + manageThumbnail(context, id, small, env, Geonet.File.SET_THUMBNAIL, indexAfterChange); + } + + /** + * @param context + * @param id + * @param small + * @throws Exception + */ + @Override + public void unsetThumbnail(ServiceContext context, String id, boolean small, boolean indexAfterChange) + throws Exception { + Element env = new Element("env"); + + manageThumbnail(context, id, small, env, Geonet.File.UNSET_THUMBNAIL, indexAfterChange); + } + + /** + * @param context + * @param id + * @param small + * @param env + * @param styleSheet + * @param indexAfterChange + * @throws Exception + */ + private void manageThumbnail(ServiceContext context, String id, boolean small, Element env, String styleSheet, + boolean indexAfterChange) throws Exception { + boolean forEditing = false, withValidationErrors = false, keepXlinkAttributes = true; + Element md = context.getBean(IMetadataManager.class).getMetadata(context, id, forEditing, withValidationErrors, + keepXlinkAttributes); + + if (md == null) + return; + + md.detach(); + + String schema = metadataSchemaUtils.getMetadataSchema(id); + + // --- setup environment + String type = small ? "thumbnail" : "large_thumbnail"; + env.addContent(new Element("type").setText(type)); + transformMd(context, id, md, env, schema, styleSheet, indexAfterChange); + } + + /** + * @param context + * @param metadataId + * @param md + * @param env + * @param schema + * @param styleSheet + * @param indexAfterChange + * @throws Exception + */ + private void transformMd(ServiceContext context, String metadataId, Element md, Element env, String schema, + String styleSheet, boolean indexAfterChange) throws Exception { + + if (env.getChild("host") == null) { + String host = getSettingManager().getValue(Settings.SYSTEM_SERVER_HOST); + String port = getSettingManager().getValue(Settings.SYSTEM_SERVER_PORT); + env.addContent(new Element("host").setText(host)); + env.addContent(new Element("port").setText(port)); + } + + // --- setup root element + Element root = new Element("root"); + root.addContent(md); + root.addContent(env); + + // --- do an XSL transformation + Path styleSheetPath = metadataSchemaUtils.getSchemaDir(schema).resolve(styleSheet); + + md = Xml.transform(root, styleSheetPath); + String changeDate = null; + String uuid = null; + if (schemaManager.getSchema(schema).isReadwriteUUID()) { + uuid = extractUUID(schema, md); + } + + getXmlSerializer().update(metadataId, md, changeDate, true, uuid, context); + + if (indexAfterChange) { + // Notifies the metadata change to metatada notifier service + notifyMetadataChange(md, metadataId); + + // --- update search criteria + metadataIndexer.indexMetadata(metadataId, true, null); + } + } + + /** + * @param context + * @param id + * @param licenseurl + * @param imageurl + * @param jurisdiction + * @param licensename + * @param type + * @throws Exception + */ + @Override + public void setDataCommons(ServiceContext context, String id, String licenseurl, String imageurl, + String jurisdiction, String licensename, String type) throws Exception { + Element env = prepareCommonsEnv(licenseurl, imageurl, jurisdiction, licensename, type); + manageCommons(context, id, env, Geonet.File.SET_DATACOMMONS); + } + + private Element prepareCommonsEnv(String licenseurl, String imageurl, String jurisdiction, String licensename, + String type) { + Element env = new Element("env"); + env.addContent(new Element("imageurl").setText(imageurl)); + env.addContent(new Element("licenseurl").setText(licenseurl)); + env.addContent(new Element("jurisdiction").setText(jurisdiction)); + env.addContent(new Element("licensename").setText(licensename)); + env.addContent(new Element("type").setText(type)); + return env; + } + + /** + * @param context + * @param id + * @param licenseurl + * @param imageurl + * @param jurisdiction + * @param licensename + * @param type + * @throws Exception + */ + @Override + public void setCreativeCommons(ServiceContext context, String id, String licenseurl, String imageurl, + String jurisdiction, String licensename, String type) throws Exception { + Element env = prepareCommonsEnv(licenseurl, imageurl, jurisdiction, licensename, type); + manageCommons(context, id, env, Geonet.File.SET_CREATIVECOMMONS); + } + + /** + * Extract the title field from the Metadata Repository. This is only valid for + * subtemplates as the title can be stored with the subtemplate (since + * subtemplates don't have a title) - metadata records don't store the title + * here as this is part of the metadata. + * + * @param id metadata id to retrieve + */ + @Override + public String getMetadataTitle(String id) throws Exception { + AbstractMetadata md = findOne(id); + + if (md == null) { + throw new IllegalArgumentException("Metadata not found for id : " + id); + } else { + // get metadata title + return md.getDataInfo().getTitle(); + } + } + + /** + * @param context + * @param id + * @param env + * @param styleSheet + * @throws Exception + */ + private void manageCommons(ServiceContext context, String id, Element env, String styleSheet) throws Exception { + Lib.resource.checkEditPrivilege(context, id); + Element md = getXmlSerializer().select(context, id); + + if (md == null) + return; + + md.detach(); + + String schema = metadataSchemaUtils.getMetadataSchema(id); + transformMd(context, id, md, env, schema, styleSheet, true); + } + + private XmlSerializer getXmlSerializer() { + return xmlSerializer; + } + + private SettingManager getSettingManager() { + return this.settingManager; + } + + @Override + public long count(Specification specs) { + try { + return metadataRepository.count((Specification) specs); + } catch (Throwable t) { + // Maybe it is not a Specification + } + throw new NotImplementedException("Unknown IMetadata subtype: " + specs.getClass().getName()); + } + + @Override + public long count() { + return metadataRepository.count(); + } + + @Override + public AbstractMetadata findOne(int id) { + return metadataRepository.findOne(id); + } + + @Override + public AbstractMetadata findOneByUuid(String uuid) { + AbstractMetadata metadata = null; + try { + metadata = metadataRepository.findOneByUuid(uuid); + } catch (IncorrectResultSizeDataAccessException e){ + Log.warning(Geonet.GEONETWORK, String.format( + "More than one record found with UUID '%s'. Error is '%s'.", + uuid, e.getMessage())); + } + return metadata; + } + + @Override + public List findAllByUuid(String uuid) { + return metadataRepository.findAllByUuid(uuid); + } + + @Override + public AbstractMetadata findOne(Specification spec) { + try { + return metadataRepository.findOne((Specification) spec); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + spec.getClass().getName()); + } + } + + @Override + public AbstractMetadata findOne(String id) { + return metadataRepository.findOne(id); + } + + @Override + public List findAllByHarvestInfo_Uuid(String uuid) { + return metadataRepository.findAllByHarvestInfo_Uuid(uuid); + } + + @Override + public Iterable findAll(Set keySet) { + return metadataRepository.findAll(keySet); + } + + @Override + public List findAll(Specification spec, Sort order) { + return metadataRepository.findAll((Specification) spec, order); + } + + @Override + public List findAll(Specification specs) { + try { + return metadataRepository.findAll((Specification) specs); + } catch (Throwable t) { + // Maybe it is not a Specification + } + throw new NotImplementedException("Unknown AbstractMetadata subtype: " + specs.getClass().getName()); + } + + @Override + public List findAllSimple(String harvestUuid) { + return metadataRepository.findAllSimple(harvestUuid); + } + + @Override + public boolean exists(Integer iId) { + return metadataRepository.exists(iId); + } + + @Override + public Element findAllAsXml(Specification specs, Sort sortByChangeDateDesc) { + try { + return metadataRepository.findAllAsXml((Specification) specs, sortByChangeDateDesc); + + } catch (Throwable t) { + // Maybe it is not a Specification + } + throw new NotImplementedException("Unknown AbstractMetadata subtype: " + specs.getClass().getName()); + } + + @Override + public MetadataReportsQueries getMetadataReports() { + return metadataRepository.getMetadataReports(); + } + + @Override + public Element findAllAsXml(@Nullable Specification specs, + @Nullable Pageable pageable) { + try { + return metadataRepository.findAllAsXml((Specification) specs, pageable); + } catch (Throwable t) { + // Maybe it is not a Specification + } + throw new NotImplementedException("Unknown AbstractMetadata subtype: " + specs.getClass().getName()); + } + + protected MetadataRepository getMetadataRepository() { + return metadataRepository; + } + + protected SchemaManager getSchemaManager() { + return schemaManager; + } + + @Override + public boolean checkMetadataWithSameUuidExist(String uuid, int id) { + // Check if another record exist with that UUID + Metadata recordWithThatUuid = getMetadataRepository().findOneByUuid(uuid); + if (recordWithThatUuid != null && recordWithThatUuid.getId() != id) { + // If yes, this would have triggered a DataIntegrityViolationException + throw new IllegalArgumentException(String.format( + "Another record exist with UUID '%s'. This record as internal id '%d'. The record you're trying to update with id '%d' can not be saved.", + uuid, recordWithThatUuid.getId(), id)); + } + return false; + } + + @Override + public Page> findAllIdsAndChangeDates(Pageable pageable) { + return metadataRepository.findAllIdsAndChangeDates(pageable); + } + + @Override + public Map findAllSourceInfo(Specification spec) { + try { + return metadataRepository.findAllSourceInfo((Specification) spec); + } catch (Throwable t) { + // Maybe it is not a Specification + } + throw new NotImplementedException("Unknown AbstractMetadata subtype: " + spec.getClass().getName()); + } } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java index 1cee711f235..6fa07316b00 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java @@ -1,3 +1,26 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.base; import jeeves.server.UserSession; @@ -22,6 +45,7 @@ import org.fao.geonet.kernel.schema.MetadataSchema; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.repository.MetadataValidationRepository; +import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.fao.geonet.utils.XmlErrorHandler; import org.jdom.Attribute; @@ -109,17 +133,17 @@ public void validateMetadata(String schema, Element xml, ServiceContext context, theNSs.add(Namespace.getNamespace("svrl", "http://purl.oclc.org/dsdl/svrl")); List informationalReports = Xml.selectNodes(schemaTronReport, - "geonet:report[@geonet:required != '" + SchematronRequirement.REQUIRED + "']", theNSs); + "geonet:report[@geonet:required != '" + SchematronRequirement.REQUIRED + "']", theNSs); for (Object informationalReport : informationalReports) { ((Element) informationalReport).detach(); } List failedAssert = Xml.selectNodes(schemaTronReport, - "geonet:report[@geonet:required = '" + SchematronRequirement.REQUIRED + "']/svrl:schematron-output/svrl:failed-assert", - theNSs); + "geonet:report[@geonet:required = '" + SchematronRequirement.REQUIRED + "']/svrl:schematron-output/svrl:failed-assert", + theNSs); List failedSchematronVerification = Xml.selectNodes(schemaTronReport, - "geonet:report[@geonet:required = '" + SchematronRequirement.REQUIRED + "']/geonet:schematronVerificationError", - theNSs); + "geonet:report[@geonet:required = '" + SchematronRequirement.REQUIRED + "']/geonet:schematronVerificationError", + theNSs); if ((!failedAssert.isEmpty()) || (!failedSchematronVerification.isEmpty())) { StringBuilder errorReport = new StringBuilder(); @@ -164,7 +188,7 @@ public void validateMetadata(String schema, Element xml, ServiceContext context, } throw new SchematronValidationErrorEx( - "Schematron errors detected for file " + fileName + " - " + errorReport + " for more details", schemaTronReport); + "Schematron errors detected for file " + fileName + " - " + errorReport + " for more details", schemaTronReport); } } @@ -209,6 +233,12 @@ public void setNamespacePrefix(final Element md, final Namespace ns) { */ @Override public void validate(String schema, Element md) throws Exception { + + if (Log.isTraceEnabled(Geonet.DATA_MANAGER)) { + Log.trace(Geonet.DATA_MANAGER, "Validating record "); + Log.trace(Geonet.DATA_MANAGER, (new org.jdom.output.XMLOutputter()).outputString(md)); + } + XmlErrorHandler eh = new XmlErrorHandler(); Element xsdErrors = validateInfo(schema, md, eh); if (xsdErrors != null) { @@ -289,7 +319,7 @@ public Element buildErrorReport(String type, String errorCode, String message, S * Creates XML schematron report for each set of rules defined in schema directory. */ private Element getSchemaTronXmlReport(String schema, Element md, String lang, Map valTypeAndStatus) - throws Exception { + throws Exception { // NOTE: this method assumes that you've run enumerateTree on the metadata MetadataSchema metadataSchema = metadataSchemaUtils.getSchema(schema); @@ -328,7 +358,7 @@ private Element getSchemaTronXmlReport(String schema, Element md, String lang, M faileAssertElements.next(); invalidRules++; } - Integer[] results = { invalidRules != 0 ? 0 : 1, firedRules, invalidRules }; + Integer[] results = {invalidRules != 0 ? 0 : 1, firedRules, invalidRules}; if (valTypeAndStatus != null) { valTypeAndStatus.put(ruleId, results); } @@ -354,7 +384,7 @@ private Element getSchemaTronXmlReport(String schema, Element md, String lang, M * Used by harvesters that need to validate metadata. * * @param metadata metadata - * @param lang Language from context + * @param lang Language from context */ @Override public boolean doValidate(AbstractMetadata metadata, String lang) { @@ -380,13 +410,13 @@ public boolean doValidate(AbstractMetadata metadata, String lang) { } if (xsdErrorCount > 0) { validations.add(new MetadataValidation().setId(new MetadataValidationId(metadataId, "xsd")) - .setStatus(MetadataValidationStatus.INVALID).setRequired(true).setNumTests(xsdErrorCount) - .setNumFailures(xsdErrorCount)); + .setStatus(MetadataValidationStatus.INVALID).setRequired(true).setNumTests(xsdErrorCount) + .setNumFailures(xsdErrorCount)); LOGGER.debug("Invalid."); valid = false; } else { validations.add(new MetadataValidation().setId(new MetadataValidationId(metadataId, "xsd")) - .setStatus(MetadataValidationStatus.VALID).setRequired(true).setNumTests(1).setNumFailures(0)); + .setStatus(MetadataValidationStatus.VALID).setRequired(true).setNumTests(1).setNumFailures(0)); LOGGER.debug("Valid."); } try { @@ -410,11 +440,10 @@ public boolean doValidate(AbstractMetadata metadata, String lang) { /** * Used by the validate embedded service. The validation report is stored in the session. - * */ @Override public Pair doValidate(UserSession session, String schema, String metadataId, Element md, String lang, - boolean forEditing) throws Exception { + boolean forEditing) throws Exception { int intMetadataId = Integer.parseInt(metadataId); String version = null; LOGGER.debug("Creating validation report for record #{} [schema: {}].", metadataId, schema); @@ -440,14 +469,14 @@ public Pair doValidate(UserSession session, String schema, Stri if (xsdErrorCount > 0) { errorReport.addContent(xsdErrors); validations.add(new MetadataValidation().setId(new MetadataValidationId(intMetadataId, "xsd")) - .setStatus(MetadataValidationStatus.INVALID).setRequired(true).setNumTests(xsdErrorCount) - .setNumFailures(xsdErrorCount)); + .setStatus(MetadataValidationStatus.INVALID).setRequired(true).setNumTests(xsdErrorCount) + .setNumFailures(xsdErrorCount)); if (LOGGER.isDebugEnabled()) { LOGGER.debug(" - XSD error: {}", Xml.getString(xsdErrors)); } } else { validations.add(new MetadataValidation().setId(new MetadataValidationId(intMetadataId, "xsd")) - .setStatus(MetadataValidationStatus.VALID).setRequired(true).setNumTests(1).setNumFailures(0)); + .setStatus(MetadataValidationStatus.VALID).setRequired(true).setNumTests(1).setNumFailures(0)); LOGGER.trace("Valid."); } @@ -488,19 +517,19 @@ public Pair doValidate(UserSession session, String schema, Stri /** * Creates XML schematron report for each set of rules defined in schema directory. This method assumes that you've run enumerateTree on * the metadata - * + *

      * Returns null if no error on validation. */ @Override public Element applyCustomSchematronRules(String schema, int metadataId, Element md, String lang, - List validations) { + List validations) { return schematronValidator.applyCustomSchematronRules(schema, metadataId, md, lang, validations); } /** * Saves validation status information into the database for the current record. * - * @param id the metadata record internal identifier + * @param id the metadata record internal identifier * @param validations the validation reports for each type of validation and schematron validation */ private void saveValidationStatus(int id, List validations) { diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataIndexer.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataIndexer.java new file mode 100644 index 00000000000..471ff454938 --- /dev/null +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataIndexer.java @@ -0,0 +1,77 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.draft; + +import java.util.Vector; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.kernel.datamanager.IMetadataIndexer; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataIndexer; +import org.fao.geonet.kernel.search.SearchManager; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.utils.Log; +import org.jdom.Element; +import org.springframework.beans.factory.annotation.Autowired; + +import jeeves.server.context.ServiceContext; + +public class DraftMetadataIndexer extends BaseMetadataIndexer implements IMetadataIndexer { + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + + @Override + public void init(ServiceContext context, Boolean force) throws Exception { + super.init(context, force); + metadataDraftRepository = context.getBean(MetadataDraftRepository.class); + } + + @Override + /** + * Adds the specific draft related fields. + * + * @param fullMd + * @param moreFields + */ + protected void addExtraFields(AbstractMetadata fullMd, Vector moreFields) { + super.addExtraFields(fullMd, moreFields); + + if (fullMd instanceof MetadataDraft) { + Log.trace(Geonet.DATA_MANAGER, "We are indexing a draft with uuid " + fullMd.getUuid()); + moreFields.addElement(SearchManager.makeField(Geonet.IndexFieldNames.DRAFT, "y", true, true)); + } else { + if (metadataDraftRepository.findOneByUuid(fullMd.getUuid()) != null) { + Log.trace(Geonet.DATA_MANAGER, + "We are indexing a record with a draft associated with uuid " + fullMd.getUuid()); + moreFields.addElement(SearchManager.makeField(Geonet.IndexFieldNames.DRAFT, "e", true, true)); + } else { + Log.trace(Geonet.DATA_MANAGER, + "We are indexing a record with no draft associated with uuid " + fullMd.getUuid()); + moreFields.addElement(SearchManager.makeField(Geonet.IndexFieldNames.DRAFT, "n", true, true)); + } + } + } +} diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataManager.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataManager.java new file mode 100644 index 00000000000..41d83ba7456 --- /dev/null +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataManager.java @@ -0,0 +1,210 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.draft; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.PostConstruct; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.*; +import org.fao.geonet.kernel.datamanager.IMetadataManager; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataManager; +import org.fao.geonet.notifier.MetadataNotifierManager; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.repository.PathSpec; +import org.fao.geonet.repository.Updater; +import org.fao.geonet.repository.specification.MetadataSpecs; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; + +import jeeves.server.context.ServiceContext; + +public class DraftMetadataManager extends BaseMetadataManager implements IMetadataManager { + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + + /** + * To avoid cyclic references on autowired + */ + @PostConstruct + @Override + public void init() { + super.init(); + } + + public void init(ServiceContext context, Boolean force) throws Exception { + metadataDraftRepository = context.getBean(MetadataDraftRepository.class); + super.init(context, force); + } + + + /** + * Removes a metadata. + */ + @Override + public void deleteMetadata(ServiceContext context, String metadataId) throws Exception { + AbstractMetadata findOne = metadataUtils.findOne(metadataId); + + if (findOne != null) { + boolean isMetadata = findOne.getDataInfo().getType() == MetadataType.METADATA; + + + if (findOne instanceof Metadata) { + // Check if exists draft version and don't allow to remove until draft is removed + long countDraft = metadataDraftRepository.count((Specification) MetadataSpecs.hasMetadataUuid(findOne.getUuid())); + + if (countDraft > 0) { + throw new Exception("The metadata " + metadataId + " has a draft version. Delete it first to remove the published version."); + } + + } + + deleteMetadataFromDB(context, metadataId); + + // Notifies the metadata change to metatada notifier service + if (isMetadata) { + context.getBean(MetadataNotifierManager.class).deleteMetadata(metadataId, findOne.getUuid(), context); + } + } + + // --- update search criteria + getSearchManager().delete(metadataId + ""); + // _entityManager.flush(); + // _entityManager.clear(); + } + /** + * For update of owner info. + */ + @Override + public synchronized void updateMetadataOwner(final int id, final String owner, final String groupOwner) + throws Exception { + + if (metadataDraftRepository.exists(id)) { + metadataDraftRepository.update(id, new Updater() { + @Override + public void apply(@Nonnull MetadataDraft entity) { + entity.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)); + entity.getSourceInfo().setOwner(Integer.valueOf(owner)); + } + }); + } else { + super.updateMetadataOwner(id, owner, groupOwner); + } + } + + @Override + public AbstractMetadata save(AbstractMetadata info) { + if (info instanceof Metadata) { + return super.save((Metadata) info); + } else if (info instanceof MetadataDraft) { + return metadataDraftRepository.save((MetadataDraft) info); + } else { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + info.getClass().getName()); + } + } + + @Override + public AbstractMetadata update(int id, @Nonnull Updater updater) { + AbstractMetadata md = null; + Log.trace(Geonet.DATA_MANAGER, "AbstractMetadata.update(" + id + ")"); + try { + Log.trace(Geonet.DATA_MANAGER, "Updating metadata table."); + md = super.update(id, updater); + } catch (ClassCastException t) { + // That's fine, maybe we are on the draft side + } catch (Throwable e) { + Log.error(Geonet.DATA_MANAGER, e.getMessage(), e); + } + if (md == null) { + try { + Log.trace(Geonet.DATA_MANAGER, "Updating draft table."); + md = metadataDraftRepository.update(id, (Updater) updater); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + updater.getClass().getName()); + } + } + return md; + } + + @Override + public void deleteAll(Specification specs) { + try { + super.deleteAll(specs); + } catch (ClassCastException t) { + // That's fine, maybe we are on the draft side + } + try { + metadataDraftRepository.deleteAll((Specification) specs); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + specs.getClass().getName()); + } + } + + @Override + public void delete(Integer id) { + super.delete(id); + if (metadataDraftRepository.exists(id)) { + metadataDraftRepository.delete(id); + } + } + + @Override + public void createBatchUpdateQuery(PathSpec servicesPath, String newUuid, + Specification harvested) { + try { + super.createBatchUpdateQuery(servicesPath, newUuid, harvested); + } catch (ClassCastException t) { + // That's fine, maybe we are on the draft side + } + try { + metadataDraftRepository.createBatchUpdateQuery((PathSpec) servicesPath, newUuid, + (Specification) harvested); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + servicesPath.getClass().getName()); + } + } + + @Override + public Map findAllSourceInfo(Specification specs) { + Map res = new HashMap(); + try { + res.putAll(super.findAllSourceInfo(specs)); + } catch (ClassCastException t) { + // That's fine, maybe we are on the draft side + } + try { + res.putAll(metadataDraftRepository.findAllSourceInfo((Specification) specs)); + } catch (ClassCastException t) { + // That's fine, maybe we are on the metadata side + } + + return res; + } + +} diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataUtils.java new file mode 100644 index 00000000000..61aa77ce60f --- /dev/null +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataUtils.java @@ -0,0 +1,640 @@ +//============================================================================= +//=== Copyright (C) 2001-2011 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.kernel.datamanager.draft; + +import static org.fao.geonet.repository.specification.MetadataSpecs.hasMetadataUuid; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import javax.annotation.Nonnull; +import javax.persistence.EntityNotFoundException; + +import org.apache.commons.io.FileUtils; +import org.eclipse.jetty.io.RuntimeIOException; +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataCategory; +import org.fao.geonet.domain.MetadataDataInfo; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.domain.MetadataFileUpload; +import org.fao.geonet.domain.MetadataHarvestInfo; +import org.fao.geonet.domain.MetadataSourceInfo; +import org.fao.geonet.domain.MetadataStatus; +import org.fao.geonet.domain.MetadataStatusId; +import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.domain.OperationAllowed; +import org.fao.geonet.domain.Pair; +import org.fao.geonet.domain.ReservedOperation; +import org.fao.geonet.domain.StatusValue; +import org.fao.geonet.kernel.AccessManager; +import org.fao.geonet.kernel.UpdateDatestamp; +import org.fao.geonet.kernel.datamanager.IMetadataManager; +import org.fao.geonet.kernel.datamanager.IMetadataOperations; +import org.fao.geonet.kernel.datamanager.IMetadataStatus; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataUtils; +import org.fao.geonet.kernel.metadata.StatusActions; +import org.fao.geonet.kernel.metadata.StatusActionsFactory; +import org.fao.geonet.lib.Lib; +import org.fao.geonet.repository.GroupRepository; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.repository.MetadataFileUploadRepository; +import org.fao.geonet.repository.SimpleMetadata; +import org.fao.geonet.repository.StatusValueRepository; +import org.fao.geonet.repository.Updater; +import org.fao.geonet.repository.specification.MetadataFileUploadSpecs; +import org.fao.geonet.utils.Log; +import org.fao.geonet.utils.Xml; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; + +import com.google.common.base.Optional; + +import jeeves.server.context.ServiceContext; + +public class DraftMetadataUtils extends BaseMetadataUtils { + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + @Autowired + private IMetadataOperations metadataOperations; + @Autowired + private IMetadataStatus metadataStatus; + @Autowired + private GroupRepository groupRepository; + @Autowired + private StatusValueRepository statusValueRepository; + @Autowired + private AccessManager am; + + private ServiceContext context; + + public void init(ServiceContext context, Boolean force) throws Exception { + this.metadataDraftRepository = context.getBean(MetadataDraftRepository.class); + this.metadataOperations = context.getBean(IMetadataOperations.class); + this.metadataStatus = context.getBean(IMetadataStatus.class); + this.groupRepository = context.getBean(GroupRepository.class); + this.statusValueRepository = context.getBean(StatusValueRepository.class); + this.am = context.getBean(AccessManager.class); + this.context = context; + super.init(context, force); + } + + @Override + public void setTemplateExt(final int id, final MetadataType metadataType) throws Exception { + if (metadataDraftRepository.exists(id)) { + metadataDraftRepository.update(id, new Updater() { + @Override + public void apply(@Nonnull MetadataDraft metadata) { + final MetadataDataInfo dataInfo = metadata.getDataInfo(); + dataInfo.setType(metadataType); + } + }); + } else { + super.setTemplateExt(id, metadataType); + } + } + + /** + * Set metadata type to subtemplate and set the title. Only subtemplates need to + * persist the title as it is used to give a meaningful title for use when + * offering the subtemplate to users in the editor. + * + * @param id + * Metadata id to set to type subtemplate + * @param title + * Title of metadata of subtemplate/fragment + */ + @Override + public void setSubtemplateTypeAndTitleExt(final int id, String title) throws Exception { + if (metadataDraftRepository.exists(id)) { + metadataDraftRepository.update(id, new Updater() { + @Override + public void apply(@Nonnull MetadataDraft metadata) { + final MetadataDataInfo dataInfo = metadata.getDataInfo(); + dataInfo.setType(MetadataType.SUB_TEMPLATE); + if (title != null) { + dataInfo.setTitle(title); + } + } + }); + + } else { + super.setSubtemplateTypeAndTitleExt(id, title); + } + } + + @Override + public void setHarvestedExt(final int id, final String harvestUuid, final Optional harvestUri) + throws Exception { + if (metadataDraftRepository.exists(id)) { + metadataDraftRepository.update(id, new Updater() { + @Override + public void apply(MetadataDraft metadata) { + MetadataHarvestInfo harvestInfo = metadata.getHarvestInfo(); + harvestInfo.setUuid(harvestUuid); + harvestInfo.setHarvested(harvestUuid != null); + harvestInfo.setUri(harvestUri.orNull()); + } + }); + } else { + super.setHarvestedExt(id, harvestUuid, harvestUri); + } + } + + /** + * @param id + * @param displayOrder + * @throws Exception + */ + @Override + public void updateDisplayOrder(final String idString, final String displayOrder) throws Exception { + Integer id = Integer.valueOf(idString); + if (metadataDraftRepository.exists(id)) { + metadataDraftRepository.update(id, new Updater() { + @Override + public void apply(MetadataDraft entity) { + entity.getDataInfo().setDisplayOrder(Integer.parseInt(displayOrder)); + } + }); + } else { + super.updateDisplayOrder(idString, displayOrder); + } + } + + /** + * Rates a metadata. + * + * @param ipAddress + * ipAddress IP address of the submitting client + * @param rating + * range should be 1..5 + * @throws Exception + * hmm + */ + @Override + public int rateMetadata(final int metadataId, final String ipAddress, final int rating) throws Exception { + final int newRating = ratingByIpRepository.averageRating(metadataId); + + if (metadataDraftRepository.exists(metadataId)) { + metadataDraftRepository.update(metadataId, new Updater() { + @Override + public void apply(MetadataDraft entity) { + entity.getDataInfo().setRating(newRating); + } + }); + } else { + return super.rateMetadata(metadataId, ipAddress, rating); + } + return rating; + } + + @SuppressWarnings("unchecked") + @Override + public long count(Specification specs) { + long tmp = 0; + try { + tmp += super.count((Specification) specs); + } catch (ClassCastException t) { + // Maybe it is not a Specification + } + try { + tmp += metadataDraftRepository.count((Specification) specs); + } catch (ClassCastException t) { + // Maybe it is not a Specification + } + return tmp; + } + + @Override + public long count() { + return super.count() + metadataDraftRepository.count(); + } + + @Override + public AbstractMetadata findOne(int id) { + if (super.exists(id)) { + return super.findOne(id); + } + return metadataDraftRepository.findOne(id); + } + + @Override + public boolean existsMetadataUuid(String uuid) throws Exception { + return super.existsMetadataUuid(uuid) || !findAllIdsBy(hasMetadataUuid(uuid)).isEmpty(); + } + + /** + * If the user has permission to see the draft, draft goes first + */ + @Override + public AbstractMetadata findOneByUuid(String uuid) { + AbstractMetadata md = super.findOneByUuid(uuid); + try { + if (md != null && am.canEdit(context, Integer.toString(md.getId()))) { + AbstractMetadata tmp = metadataDraftRepository.findOneByUuid(uuid); + if (tmp != null) { + md = tmp; + } + } else if (md == null) { + // A draft without an approved md + md = metadataDraftRepository.findOneByUuid(uuid); + } + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, e, e); + } + return md; + } + + /** + * Return all records, including drafts. + */ + @Override + public List findAllByUuid(String uuid) { + List res = new LinkedList(); + res.addAll(super.findAllByUuid(uuid)); + res.addAll(metadataDraftRepository.findAllByUuid(uuid)); + return res; + } + + @Override + public AbstractMetadata findOne(Specification spec) { + AbstractMetadata md = null; + + try { + md = super.findOne(spec); + } catch (ClassCastException t) { + // That's fine, it can be a draft specification + } + + if (md == null) { + try { + md = metadataDraftRepository.findOne((Specification) spec); + } catch (ClassCastException t) { + throw new ClassCastException("Unknown AbstractMetadata subtype: " + spec.getClass().getName()); + } + } + + return md; + } + + @Override + public AbstractMetadata findOne(String id) { + AbstractMetadata md = super.findOne(id); + if (md == null) { + md = metadataDraftRepository.findOne(id); + } + return md; + } + + @Override + public List findAllByHarvestInfo_Uuid(String uuid) { + List list = new LinkedList(); + list.addAll(metadataDraftRepository.findAllByHarvestInfo_Uuid(uuid)); + list.addAll(super.findAllByHarvestInfo_Uuid(uuid)); + return list; + } + + @Override + public Iterable findAll(Set keySet) { + List list = new LinkedList(); + for (AbstractMetadata md : super.findAll(keySet)) { + list.add(md); + } + list.addAll(metadataDraftRepository.findAll(keySet)); + return list; + } + + @Override + public List findAll(Specification specs) { + List list = new LinkedList(); + try { + list.addAll(super.findAll(specs)); + } catch (ClassCastException t) { + // That's fine, maybe it is a draft specification + } + try { + list.addAll(metadataDraftRepository.findAll((Specification) specs)); + } catch (ClassCastException t) { + // That's fine, maybe it is a metadata specification + } + return list; + } + + @Override + public List findAllSimple(String harvestUuid) { + List list = super.findAllSimple(harvestUuid); + list.addAll(metadataDraftRepository.findAllSimple(harvestUuid)); + return list; + } + + @Override + public boolean exists(Integer iId) { + return super.exists(iId) || metadataDraftRepository.exists(iId); + } + + @Override + public Page> findAllIdsAndChangeDates(Pageable pageable) { + List> list = new LinkedList>(); + + list.addAll(super.findAllIdsAndChangeDates(pageable).getContent()); + list.addAll(metadataDraftRepository.findAllIdsAndChangeDates(pageable).getContent()); + + Page> res = new PageImpl>(list, pageable, list.size()); + return res; + } + + @Override + public Map findAllSourceInfo(Specification spec) { + Map map = new LinkedHashMap(); + try { + map.putAll(super.findAllSourceInfo(spec)); + } catch (ClassCastException t) { + // Maybe it is not a Specification + } + + try { + map.putAll(metadataDraftRepository.findAllSourceInfo((Specification) spec)); + } catch (ClassCastException t) { + // Maybe it is not a Specification + } + return map; + } + + @Override + public List findAllIdsBy(Specification specs) { + List res = new LinkedList(); + + try { + res.addAll(super.findAllIdsBy(specs)); + } catch (ClassCastException t) { + // Maybe it is not a Specification + } + + try { + res.addAll(metadataDraftRepository.findAllIdsBy((Specification) specs)); + } catch (ClassCastException t) { + // Maybe it is not a Specification + } + + return res; + } + + /** + * Start an editing session. This will record the original metadata record in + * the session under the + * {@link org.fao.geonet.constants.Geonet.Session#METADATA_BEFORE_ANY_CHANGES} + + * id session property. + *

      + * The record contains geonet:info element. + *

      + * Note: Only the metadata record is stored in session. If the editing session + * upload new documents or thumbnails, those documents will not be cancelled. + * This needs improvements. + */ + @Override + public Integer startEditingSession(ServiceContext context, String id) throws Exception { + // Check id + AbstractMetadata md = findOne(Integer.valueOf(id)); + + if (md == null) { + throw new EntityNotFoundException("We couldn't find the metadata to edit"); + } + + // Do we have a metadata draft already? + if (metadataDraftRepository.findOneByUuid(md.getUuid()) != null) { + id = Integer.toString(metadataDraftRepository.findOneByUuid(md.getUuid()).getId()); + + Log.trace(Geonet.DATA_MANAGER, "Editing draft with id " + id); + } else if ((context.getBean(IMetadataManager.class) instanceof DraftMetadataManager) + && metadataStatus.getCurrentStatus(Integer.valueOf(id)).equals(StatusValue.Status.APPROVED)) { + id = createDraft(context, id, md); + + Log.trace(Geonet.DATA_MANAGER, "Creating draft with id " + id + " to edit."); + } + + if (Log.isTraceEnabled(Geonet.DATA_MANAGER)) { + Log.trace(Geonet.DATA_MANAGER, "Editing record with id = " + id); + Log.trace(Geonet.DATA_MANAGER, "Status of record: " + metadataStatus.getCurrentStatus(Integer.valueOf(id))); + } + return super.startEditingSession(context, id); + } + + private String createDraft(ServiceContext context, String id, AbstractMetadata md) + throws Exception, IOException, JDOMException { + // We have to create the draft using the metadata information + String parentUuid = null; + + String groupOwner = null; + String source = null; + Integer owner = 1; + + if (md.getSourceInfo() != null) { + if (md.getSourceInfo().getSourceId() != null) { + source = md.getSourceInfo().getSourceId().toString(); + } + if (md.getSourceInfo().getGroupOwner() != null) { + groupOwner = md.getSourceInfo().getGroupOwner().toString(); + } + owner = md.getSourceInfo().getOwner(); + } + + id = createDraft(context, id, groupOwner, source, owner, parentUuid, md.getDataInfo().getType().codeString, + md.getUuid()); + return id; + } + + protected String createDraft(ServiceContext context, String templateId, String groupOwner, String source, int owner, + String parentUuid, String isTemplate, String uuid) throws Exception { + + Log.trace(Geonet.DATA_MANAGER, "createDraft(" + templateId + "," + groupOwner + "," + source + "," + owner + "," + + parentUuid + "," + isTemplate + "," + uuid + ")"); + + Metadata templateMetadata = getMetadataRepository().findOne(templateId); + if (templateMetadata == null) { + throw new IllegalArgumentException("Template id not found : " + templateId); + } + + String schema = templateMetadata.getDataInfo().getSchemaId(); + String data = templateMetadata.getData(); + Element xml = Xml.loadString(data, false); + if (templateMetadata.getDataInfo().getType() == MetadataType.METADATA) { + xml = metadataManager.updateFixedInfo(schema, Optional.absent(), uuid, xml, parentUuid, + UpdateDatestamp.NO, context); + } + MetadataDraft newMetadata = new MetadataDraft(); + newMetadata.setUuid(uuid); + newMetadata.setApprovedVersion(templateMetadata); + newMetadata.getDataInfo().setChangeDate(new ISODate()).setCreateDate(new ISODate()).setSchemaId(schema) + .setType(MetadataType.lookup(isTemplate)); + if (groupOwner != null) { + newMetadata.getSourceInfo().setGroupOwner(Integer.valueOf(groupOwner)); + } + newMetadata.getSourceInfo().setOwner(owner); + + if (source != null) { + newMetadata.getSourceInfo().setSourceId(source); + } + // If there is a default category for the group, use it: + if (groupOwner != null) { + Group group = groupRepository.findOne(Integer.valueOf(groupOwner)); + if (group.getDefaultCategory() != null) { + newMetadata.getCategories().add(group.getDefaultCategory()); + } + } + + for (MetadataCategory mc : templateMetadata.getCategories()) { + newMetadata.getCategories().add(mc); + } + + try { + newMetadata = (MetadataDraft) metadataManager.insertMetadata(context, newMetadata, xml, false, true, true, + UpdateDatestamp.YES, false, true); + + Integer finalId = newMetadata.getId(); + + cloneFiles(templateMetadata, newMetadata); + + // Remove all default privileges: + metadataOperations.deleteMetadataOper(String.valueOf(finalId), false); + + // Copy privileges from original metadata + for (OperationAllowed op : metadataOperations.getAllOperations(templateMetadata.getId())) { + + // Only interested in editing and reviewing privileges + // No one else should be able to see it + if (op.getId().getOperationId() == ReservedOperation.editing.getId()) { + Log.trace(Geonet.DATA_MANAGER, "Assign operation: " + op); + metadataOperations.forceSetOperation(context, finalId, op.getId().getGroupId(), + op.getId().getOperationId()); + } else { + Log.trace(Geonet.DATA_MANAGER, "Skipping operation: " + op); + } + } + + // Enable workflow on draft and make sure original record has also the workflow + // enabled + Set metadataIds = new HashSet(); + metadataIds.add(finalId); + + if (context.getBean(IMetadataStatus.class) + .getCurrentStatus(Integer.valueOf(templateId)) == StatusValue.Status.UNKNOWN) { + metadataIds.add(Integer.valueOf(templateId)); + } + + // --- use StatusActionsFactory and StatusActions class to + // --- change status and carry out behaviours for status changes + StatusActionsFactory saf = context.getBean(StatusActionsFactory.class); + StatusActions sa = saf.createStatusActions(context); + + int author = context.getUserSession().getUserIdAsInt(); + Integer status = Integer.valueOf(StatusValue.Status.DRAFT); + StatusValue statusValue = statusValueRepository.findOne(status); + + for (Integer mdId : metadataIds) { + MetadataStatus metadataStatus = new MetadataStatus(); + + MetadataStatusId mdStatusId = new MetadataStatusId().setStatusId(status).setMetadataId(mdId) + .setChangeDate(new ISODate()).setUserId(author); + + metadataStatus.setId(mdStatusId); + metadataStatus.setStatusValue(statusValue); + metadataStatus.setChangeMessage("Editing instance created"); + + List listOfStatusChange = new ArrayList<>(1); + listOfStatusChange.add(metadataStatus); + sa.onStatusChange(listOfStatusChange); + } + return String.valueOf(finalId); + } catch (Throwable t) { + Log.error(Geonet.DATA_MANAGER, "Editing instance creation failed", t); + } + return templateId; + } + + public void cloneFiles(AbstractMetadata original, AbstractMetadata dest) { + try { + Consumer duplicate = new Consumer() { + @Override + public void accept(Path t) { + try { + Path des = Lib.resource.getDir(context, "public", dest.getId()); + FileUtils.copyFile(t.toFile(), des.resolve(t.getFileName()).toFile()); + } catch (IOException e) { + Log.error(Geonet.RESOURCES, "Failed copy of resources: " + e.getMessage(), e); + } + } + }; + + Path original_dir = Lib.resource.getDir(context, "public", original.getId()); + if (Files.exists(original_dir)) { + Files.newDirectoryStream(original_dir).forEach(duplicate); + } + cloneStoreFileUploadRequests(original, dest); + + } catch (Exception ex) { + Log.error(Geonet.RESOURCES, "Failed copy of resources: " + ex.getMessage(), ex); + throw new RuntimeIOException(ex); + } + } + + /** + * Stores a file upload request in the MetadataFileUploads table. + */ + private void cloneStoreFileUploadRequests(AbstractMetadata original, AbstractMetadata copy) { + MetadataFileUploadRepository repo = context.getBean(MetadataFileUploadRepository.class); + + repo.deleteAll(MetadataFileUploadSpecs.hasMetadataId(copy.getId())); + + for (MetadataFileUpload mfu : repo.findAll(MetadataFileUploadSpecs.hasMetadataId(original.getId()))) { + MetadataFileUpload metadataFileUpload = new MetadataFileUpload(); + + metadataFileUpload.setMetadataId(copy.getId()); + metadataFileUpload.setFileName(mfu.getFileName()); + metadataFileUpload.setFileSize(mfu.getFileSize()); + metadataFileUpload.setUploadDate(mfu.getUploadDate()); + metadataFileUpload.setUserName(mfu.getUserName()); + + repo.save(metadataFileUpload); + } + } + +} diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java b/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java index 55e943ef802..1d733df49ca 100644 --- a/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java +++ b/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java @@ -48,8 +48,8 @@ import org.fao.geonet.Util; import org.fao.geonet.constants.Geonet; import org.fao.geonet.constants.Params; -import org.fao.geonet.domain.Group; import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; import org.fao.geonet.domain.ISODate; import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataCategory; @@ -65,12 +65,12 @@ import org.fao.geonet.exceptions.UnAuthorizedException; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; +import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.lib.Lib; import org.fao.geonet.repository.GroupRepository; import org.fao.geonet.repository.MetadataCategoryRepository; import org.fao.geonet.repository.MetadataRelationRepository; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.repository.Updater; @@ -449,7 +449,7 @@ public void handleInfo(Element info, int index) throws Exception { final String finalRating = rating; final Element finalCategs = categs; final String finalGroupId = groupId; - context.getBean(MetadataRepository.class).update(iMetadataId, new Updater() { + context.getBean(IMetadataManager.class).update(iMetadataId, new Updater() { @Override public void apply(@Nonnull final Metadata metadata) { final MetadataDataInfo dataInfo = metadata.getDataInfo(); @@ -536,7 +536,7 @@ public static void addCategoriesToMetadata(AbstractMetadata metadata, Element fi if (Log.isDebugEnabled(Geonet.MEF)) { Log.debug(Geonet.MEF, " - Setting category : " + catName); } - metadata.getMetadataCategories().add(oneByName); + metadata.getCategories().add(oneByName); } } } diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java b/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java index dc9d8d96da5..702c6d7d81d 100644 --- a/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java +++ b/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java @@ -26,7 +26,6 @@ import static com.google.common.xml.XmlEscapers.xmlContentEscaper; import static org.fao.geonet.Constants.CHARSET; import static org.fao.geonet.constants.Geonet.IndexFieldNames.LOCALE; -import static org.fao.geonet.constants.Geonet.IndexFieldNames.UUID; import static org.fao.geonet.kernel.mef.MEFConstants.FILE_INFO; import static org.fao.geonet.kernel.mef.MEFConstants.FILE_METADATA; import static org.fao.geonet.kernel.mef.MEFConstants.MD_DIR; @@ -52,13 +51,12 @@ import org.fao.geonet.ZipUtil; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.AbstractMetadata; -import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataRelation; import org.fao.geonet.domain.MetadataType; import org.fao.geonet.domain.Pair; import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.kernel.SchemaManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.mef.MEFLib.Format; import org.fao.geonet.kernel.mef.MEFLib.Version; import org.fao.geonet.kernel.search.IndexAndTaxonomy; @@ -67,10 +65,10 @@ import org.fao.geonet.kernel.search.SearchManager; import org.fao.geonet.lib.Lib; import org.fao.geonet.repository.MetadataRelationRepository; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.utils.IO; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; -import org.jdom.Attribute; import org.jdom.Element; import jeeves.server.context.ServiceContext; @@ -86,6 +84,20 @@ class MEF2Exporter { public static Path doExport(ServiceContext context, Set uuids, Format format, boolean skipUUID, Path stylePath, boolean resolveXlink, boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation) throws Exception { + return doExport(context, uuids, format, skipUUID, stylePath, resolveXlink, removeXlinkAttribute, skipError, addSchemaLocation, false); + } + + /** + * Create a MEF2 file in ZIP format. + * + * @param uuids List of records to export. + * @param format {@link Format} to export. + * @return MEF2 File + */ + public static Path doExport(ServiceContext context, Set uuids, + Format format, boolean skipUUID, Path stylePath, boolean resolveXlink, + boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation, + boolean approved) throws Exception { Path file = Files.createTempFile("mef-", ".mef"); SearchManager searchManager = context.getBean(SearchManager.class); @@ -121,13 +133,25 @@ public static Path doExport(ServiceContext context, Set uuids, html.addContent(body); for (Object uuid1 : uuids) { String uuid = (String) uuid1; + final String cleanUUID = cleanForCsv(uuid); try { IndexSearcher searcher = new IndexSearcher(indexReaderAndTaxonomy.indexReader); BooleanQuery query = new BooleanQuery(); - query.add(new BooleanClause(new TermQuery(new Term(UUID, uuid)), BooleanClause.Occur.MUST)); + + AbstractMetadata md = context.getBean(IMetadataUtils.class).findOneByUuid(uuid); + + //Here we just care if we need the approved version explicitly. + //IMetadataUtils already filtered draft for non editors. + + if(approved) { + md = context.getBean(MetadataRepository.class).findOneByUuid(uuid); + } + String id = String.valueOf(md.getId()); + + query.add(new BooleanClause(new TermQuery(new Term(LuceneIndexField.ID, id)), BooleanClause.Occur.MUST)); query.add(new BooleanClause(new TermQuery(new Term(LOCALE, contextLang)), BooleanClause.Occur.SHOULD)); TopDocs topDocs = searcher.search(query, NoFilterFilter.instance(), 5); - String mdSchema = null, mdTitle = null, mdAbstract = null, id = null, isHarvested = null; + String mdSchema = null, mdTitle = null, mdAbstract = null, isHarvested = null; MetadataType mdType = null; for (ScoreDoc scoreDoc : topDocs.scoreDocs) { @@ -142,9 +166,6 @@ public static Path doExport(ServiceContext context, Set uuids, if (mdAbstract == null || contextLang.equals(locale)) { mdAbstract = doc.get(LuceneIndexField.ABSTRACT); } - if (id == null) { - id = doc.get(LuceneIndexField.ID); - } if (isHarvested == null) { isHarvested = doc.get(Geonet.IndexFieldNames.IS_HARVESTED); } @@ -158,8 +179,8 @@ public static Path doExport(ServiceContext context, Set uuids, if (mdType == null) { mdType = MetadataType.METADATA; } - csvBuilder.append('"').append(cleanForCsv(mdSchema)).append("\";\""). - append(cleanForCsv(uuid)).append("\";\""). + csvBuilder.append('"').append(cleanForCsv(mdSchema)).append("\";\""). + append(cleanUUID).append("\";\""). append(cleanForCsv(id)).append("\";\""). append(mdType.toString()).append("\";\""). append(cleanForCsv(isHarvested)).append("\";\""). @@ -189,7 +210,7 @@ public static Path doExport(ServiceContext context, Set uuids, )) ))); csvBuilder.append('"').append(cleanForCsv(mdSchema)).append("\";\""). - append(cleanForCsv(uuid)).append("\";\""). + append(cleanUUID).append("\";\""). append(cleanForCsv(id)).append("\";\""). append(mdType.toString()).append("\";\""). append(cleanForCsv(isHarvested)).append("\";\""). @@ -218,7 +239,7 @@ public static Path doExport(ServiceContext context, Set uuids, ))) )) ))); - createMetadataFolder(context, uuid, zipFs, skipUUID, stylePath, + createMetadataFolder(context, md, zipFs, skipUUID, stylePath, format, resolveXlink, removeXlinkAttribute, addSchemaLocation); } catch (Throwable t) { if (skipError) { Log.error(Geonet.MEF, "Error exporting metadata to MEF file: " + uuid1, t); @@ -260,16 +281,16 @@ private static String cleanForCsv(String csvColumnText) { * @param zipFs Zip file to add new record */ private static void createMetadataFolder(ServiceContext context, - String uuid, FileSystem zipFs, boolean skipUUID, + AbstractMetadata metadata, FileSystem zipFs, boolean skipUUID, Path stylePath, Format format, boolean resolveXlink, boolean removeXlinkAttribute, boolean addSchemaLocation) throws Exception { - final Path metadataRootDir = zipFs.getPath(uuid); + final Path metadataRootDir = zipFs.getPath(metadata.getUuid()); Files.createDirectories(metadataRootDir); Pair recordAndMetadataForExport = - MEFLib.retrieveMetadata(context, uuid, resolveXlink, removeXlinkAttribute, addSchemaLocation); + MEFLib.retrieveMetadata(context, metadata, resolveXlink, removeXlinkAttribute, addSchemaLocation); AbstractMetadata record = recordAndMetadataForExport.one(); String xmlDocumentAsString = recordAndMetadataForExport.two(); @@ -296,7 +317,7 @@ private static void createMetadataFolder(ServiceContext context, // --- save Feature Catalog String ftUUID = getFeatureCatalogID(context, record.getId()); if (!ftUUID.equals("")) { - Pair ftrecordAndMetadata = MEFLib.retrieveMetadata(context, ftUUID, resolveXlink, removeXlinkAttribute, addSchemaLocation); + Pair ftrecordAndMetadata = MEFLib.retrieveMetadata(context, record, resolveXlink, removeXlinkAttribute, addSchemaLocation); Path featureMdDir = metadataRootDir.resolve(SCHEMA); Files.createDirectories(featureMdDir); Files.write(featureMdDir.resolve(FILE_METADATA), ftrecordAndMetadata.two().getBytes(CHARSET)); diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java b/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java index a9abdd1dd99..cc56907cd9a 100644 --- a/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java +++ b/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java @@ -23,13 +23,7 @@ package org.fao.geonet.kernel.mef; -import static org.fao.geonet.kernel.mef.MEFConstants.FILE_INFO; -import static org.fao.geonet.kernel.mef.MEFConstants.FILE_METADATA; - -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; - +import jeeves.server.context.ServiceContext; import org.fao.geonet.Constants; import org.fao.geonet.ZipUtil; import org.fao.geonet.constants.Geonet; @@ -37,37 +31,81 @@ import org.fao.geonet.domain.MetadataType; import org.fao.geonet.domain.Pair; import org.fao.geonet.domain.ReservedOperation; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.mef.MEFLib.Format; import org.fao.geonet.kernel.mef.MEFLib.Version; import org.fao.geonet.lib.Lib; import org.fao.geonet.utils.IO; import org.fao.geonet.utils.Log; -import jeeves.server.context.ServiceContext; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.fao.geonet.kernel.mef.MEFConstants.FILE_INFO; +import static org.fao.geonet.kernel.mef.MEFConstants.FILE_METADATA; /** * Export MEF file */ class MEFExporter { /** - * Create a metadata folder according to MEF {@link Version} 1 specification and return file - * path.

      Template or subtemplate could not be exported in MEF format. Use XML export + * Create a metadata folder according to MEF {@link Version} 1 specification and + * return file path. + *

      + * Template or subtemplate could not be exported in MEF format. Use XML export * instead. * * @param uuid UUID of the metadata record to export. * @param format {@link org.fao.geonet.kernel.mef.MEFLib.Format} * @return the path of the generated MEF file. */ - public static Path doExport(ServiceContext context, String uuid, - Format format, boolean skipUUID, boolean resolveXlink, - boolean removeXlinkAttribute, boolean addSchemaLocation) throws Exception { - Pair recordAndMetadata = - MEFLib.retrieveMetadata(context, uuid, resolveXlink, removeXlinkAttribute, addSchemaLocation); + public static Path doExport(ServiceContext context, String uuid, Format format, boolean skipUUID, + boolean resolveXlink, boolean removeXlinkAttribute, boolean addSchemaLocation, + boolean approved) throws Exception { + + //Search by ID, not by UUID + Integer id = + context.getBean(IMetadataUtils.class).findOneByUuid(uuid).getId(); + + //Here we just care if we need the approved version explicitly. + //IMetadataUtils already filtered draft for non editors. + if (approved) { + id = Integer.valueOf(context.getBean(IMetadataUtils.class).getMetadataId(uuid)); + } + + return doExport(context, id, format, skipUUID, resolveXlink, removeXlinkAttribute, addSchemaLocation); + } + + /** + * Create a metadata folder according to MEF {@link Version} 1 specification and + * return file path. + *

      + * Template or subtemplate could not be exported in MEF format. Use XML export + * instead. + * + * @param id unique ID of the metadata record to export. + * @param format {@link org.fao.geonet.kernel.mef.MEFLib.Format} + * @return the path of the generated MEF file. + */ + public static Path doExport(ServiceContext context, Integer id, Format format, boolean skipUUID, + boolean resolveXlink, boolean removeXlinkAttribute, boolean addSchemaLocation) throws Exception { + Pair recordAndMetadata = MEFLib.retrieveMetadata(context, id, resolveXlink, + removeXlinkAttribute, addSchemaLocation); + return export(context, id, format, skipUUID, recordAndMetadata); + } + + private static Path export(ServiceContext context, Integer id, Format format, boolean skipUUID, + Pair recordAndMetadata) + throws Exception, IOException, UnsupportedEncodingException, URISyntaxException { AbstractMetadata record = recordAndMetadata.one(); String xmlDocumentAsString = recordAndMetadata.two(); - if (record.getDataInfo().getType() == MetadataType.SUB_TEMPLATE || - record.getDataInfo().getType() == MetadataType.TEMPLATE_OF_SUB_TEMPLATE) { + if (record.getDataInfo().getType() == MetadataType.SUB_TEMPLATE + || record.getDataInfo().getType() == MetadataType.TEMPLATE_OF_SUB_TEMPLATE) { throw new Exception("Cannot export sub template"); } @@ -81,11 +119,10 @@ public static Path doExport(ServiceContext context, String uuid, Files.write(zipFs.getPath(FILE_METADATA), binData); // --- save info file - binData = MEFLib.buildInfoFile(context, record, format, pubDir, priDir, - skipUUID).getBytes(Constants.ENCODING); + binData = MEFLib.buildInfoFile(context, record, format, pubDir, priDir, skipUUID) + .getBytes(Constants.ENCODING); Files.write(zipFs.getPath(FILE_INFO), binData); - if (format == Format.PARTIAL || format == Format.FULL) { if (Files.exists(pubDir) && !IO.isEmptyDir(pubDir)) { IO.copyDirectoryOrFile(pubDir, zipFs.getPath(pubDir.getFileName().toString()), false); @@ -101,7 +138,8 @@ public static Path doExport(ServiceContext context, String uuid, } catch (Exception e) { // Current user could not download private data - Log.warning(Geonet.MEF, "Error encounteres while trying to import private resources of MEF file. MEF UUID: " + uuid, e); + Log.warning(Geonet.MEF, + "Error encounteres while trying to import private resources of MEF file. MEF ID: " + id, e); } } @@ -111,4 +149,3 @@ public static Path doExport(ServiceContext context, String uuid, } // ============================================================================= - diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java b/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java index 0776b432758..995ccf82b32 100644 --- a/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java +++ b/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java @@ -122,8 +122,18 @@ public static List doImport(Element params, ServiceContext context, Path public static Path doExport(ServiceContext context, String uuid, String format, boolean skipUUID, boolean resolveXlink, - boolean removeXlinkAttribute, boolean addSchemaLocation) throws Exception { + boolean removeXlinkAttribute, boolean addSchemaLocation, + boolean approved) throws Exception { return MEFExporter.doExport(context, uuid, Format.parse(format), + skipUUID, resolveXlink, removeXlinkAttribute, addSchemaLocation, approved); + } + + // -------------------------------------------------------------------------- + + public static Path doExport(ServiceContext context, Integer id, + String format, boolean skipUUID, boolean resolveXlink, + boolean removeXlinkAttribute, boolean addSchemaLocation) throws Exception { + return MEFExporter.doExport(context, id, Format.parse(format), skipUUID, resolveXlink, removeXlinkAttribute, addSchemaLocation); } @@ -131,10 +141,12 @@ public static Path doExport(ServiceContext context, String uuid, public static Path doMEF2Export(ServiceContext context, Set uuids, String format, boolean skipUUID, Path stylePath, boolean resolveXlink, - boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation) + boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation, + boolean approved) throws Exception { return MEF2Exporter.doExport(context, uuids, Format.parse(format), - skipUUID, stylePath, resolveXlink, removeXlinkAttribute, skipError, addSchemaLocation); + skipUUID, stylePath, resolveXlink, removeXlinkAttribute, + skipError, addSchemaLocation, approved); } // -------------------------------------------------------------------------- @@ -173,20 +185,45 @@ public static Version getMEFVersion(Path mefFile) { * @return A pair composed of the domain object metadata AND the record to be exported (includes * Xlink resolution and filters depending on user session). */ - static Pair retrieveMetadata(ServiceContext context, String uuid, + static Pair retrieveMetadata(ServiceContext context, AbstractMetadata metadata, boolean resolveXlink, boolean removeXlinkAttribute, boolean addSchemaLocation) throws Exception { - final AbstractMetadata metadata = context.getBean(IMetadataUtils.class).findOneByUuid(uuid); + if (metadata == null) { + throw new MetadataNotFoundEx(""); + } + + + return retrieveMetadata(context, removeXlinkAttribute, addSchemaLocation, metadata); + } + + /** + * Get metadata record. + * + * @return A pair composed of the domain object metadata AND the record to be exported (includes + * Xlink resolution and filters depending on user session). + */ + static Pair retrieveMetadata(ServiceContext context, Integer id, + boolean resolveXlink, + boolean removeXlinkAttribute, + boolean addSchemaLocation) + throws Exception { + + final AbstractMetadata metadata = context.getBean(IMetadataUtils.class).findOne(id); if (metadata == null) { - throw new MetadataNotFoundEx("uuid=" + uuid); + throw new MetadataNotFoundEx("id=" + id); } - // Retrieve the metadata document + return retrieveMetadata(context, removeXlinkAttribute, addSchemaLocation, metadata); + } + + private static Pair retrieveMetadata(ServiceContext context, boolean removeXlinkAttribute, + boolean addSchemaLocation, final AbstractMetadata metadata) throws Exception { + // Retrieve the metadata document // using data manager in order to // apply all filters (like XLinks, // withheld) @@ -226,7 +263,7 @@ static Pair retrieveMetadata(ServiceContext context, S } return Pair.read(metadata, metadataForExportAsString); - } + } /** * Add file to ZIP file @@ -350,7 +387,7 @@ static Element buildInfoCategories(AbstractMetadata md) Element categ = new Element("categories"); - for (MetadataCategory category : md.getMetadataCategories()) { + for (MetadataCategory category : md.getCategories()) { String name = category.getName(); Element cat = new Element("category"); diff --git a/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.java b/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.java index 5a5a30ad19a..eb22fdd160b 100644 --- a/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.java +++ b/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.java @@ -27,6 +27,8 @@ import jeeves.server.context.ServiceContext; import org.apache.commons.lang.StringUtils; import org.fao.geonet.ApplicationContextHolder; +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.ISODate; import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataStatus; @@ -38,8 +40,13 @@ import org.fao.geonet.domain.StatusValueType; import org.fao.geonet.domain.User; import org.fao.geonet.domain.User_; +import org.fao.geonet.events.md.MetadataStatusChanged; +import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.datamanager.IMetadataStatus; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.kernel.datamanager.draft.DraftMetadataManager; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.SortUtils; @@ -47,6 +54,8 @@ import org.fao.geonet.repository.UserRepository; import org.fao.geonet.util.MailUtil; import org.fao.geonet.util.XslUtil; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import java.text.MessageFormat; @@ -68,6 +77,9 @@ public class DefaultStatusActions implements StatusActions { protected ServiceContext context; protected String language; protected DataManager dm; + + @Autowired + protected IMetadataUtils metadataUtils; protected String siteUrl; protected String siteName; protected UserSession session; @@ -90,6 +102,7 @@ public void init(ServiceContext context) throws Exception { this.context = context; ApplicationContext applicationContext = ApplicationContextHolder.get(); this._statusValueRepository = applicationContext.getBean(StatusValueRepository.class); + this.metadataUtils = applicationContext.getBean(IMetadataUtils.class); this.language = context.getLanguage(); SettingManager sm = applicationContext.getBean(SettingManager.class); @@ -123,10 +136,16 @@ public void init(ServiceContext context) throws Exception { * @param minorEdit If true then the edit was a minor edit. */ public void onEdit(int id, boolean minorEdit) throws Exception { - if (!minorEdit && dm.getCurrentStatus(id).equals(StatusValue.Status.APPROVED)) { + if (Log.isTraceEnabled(Geonet.DATA_MANAGER)) { + Log.trace(Geonet.DATA_MANAGER, "DefaultStatusActions.onEdit(" + id + + ", " + minorEdit + ") with status " + dm.getCurrentStatus(id)); + } + if (!minorEdit && dm.getCurrentStatus(id).equals(StatusValue.Status.APPROVED) && + (context.getBean(IMetadataManager.class) instanceof DraftMetadataManager)) { ResourceBundle messages = ResourceBundle.getBundle("org.fao.geonet.api.Messages", new Locale(this.language)); String changeMessage = String.format(messages.getString("status_email_text"), replyToDescr, replyTo, id); unsetAllOperations(id); + Log.trace(Geonet.DATA_MANAGER, "Set DRAFT to current record with id " + id); dm.setStatus(context, id, Integer.valueOf(StatusValue.Status.DRAFT), new ISODate(), changeMessage); } } @@ -153,7 +172,7 @@ public Set onStatusChange(List listOfStatus) throws Exc // --- For the workflow, if the status is already set to value // of status then do nothing. This does not apply to task and event. if (status.getStatusValue().getType().equals(StatusValueType.workflow) && - (statusId).equals(currentStatus)) { + (statusId).equals(currentStatus)) { if (context.isDebugEnabled()) context.debug(String.format("Metadata %s already has status %s ", status.getId().getMetadataId(), status.getId().getStatusId())); @@ -176,6 +195,19 @@ public Set onStatusChange(List listOfStatus) throws Exc "Failed to send notification on status change for metadata %s with status %s. Error is: %s", status.getId().getMetadataId(), status.getId().getStatusId(), e.getMessage())); } + + //Throw events + Log.trace(Geonet.DATA_MANAGER, "Throw workflow events."); + for (Integer mid : listOfId) { + if (!unchanged.contains(mid)) { + Log.debug(Geonet.DATA_MANAGER, " > Status changed for record (" + mid + ") to status " + status); + context.getApplicationContext().publishEvent(new MetadataStatusChanged( + metadataUtils.findOne(Integer.valueOf(mid)), + status.getStatusValue(), status.getChangeMessage(), + status.getId().getUserId())); + } + } + } return unchanged; @@ -186,16 +218,23 @@ public Set onStatusChange(List listOfStatus) throws Exc * This apply specific rules depending on status change. * The default rules are: *

        - *
      • DISABLED When approved, the record is automatically published.
      • - *
      • When draft or rejected, unpublish the record.
      • + *
      • DISABLED When approved, the record is automatically published.
      • + *
      • When draft or rejected, unpublish the record.
      • *
      + * * @param status * @throws Exception */ private void applyRulesForStatusChange(MetadataStatus status) throws Exception { String statusId = status.getId().getStatusId() + ""; if (statusId.equals(StatusValue.Status.APPROVED)) { - // setAllOperations(mid); + // setAllOperations(mid); - this is a short cut that could be enabled + AccessManager accessManager = context.getBean(AccessManager.class); + if (!accessManager.canReview(context, String.valueOf(status.getId().getMetadataId()))) { + throw new SecurityException(String.format( + "You can't edit record with ID %s", + String.valueOf(status.getId().getMetadataId()))); + } } else if (statusId.equals(StatusValue.Status.DRAFT) || statusId.equals(StatusValue.Status.REJECTED)) { unsetAllOperations(status.getId().getMetadataId()); @@ -205,6 +244,7 @@ private void applyRulesForStatusChange(MetadataStatus status) throws Exception { /** * Send email to a list of users. The list of users is defined based on * the notification level of the status. See {@link StatusValueNotificationLevel}. + * * @param userToNotify * @param status * @throws Exception @@ -251,8 +291,8 @@ private void notify(List userToNotify, MetadataStatus status) throws Excep owner == null ? "" : owner.getName() + " " + owner.getSurname(), siteUrl); - MetadataRepository metadataRepository = ApplicationContextHolder.get().getBean(MetadataRepository.class); - Metadata metadata = metadataRepository.findOne(status.getId().getMetadataId()); + IMetadataUtils metadataRepository = ApplicationContextHolder.get().getBean(IMetadataUtils.class); + AbstractMetadata metadata = metadataRepository.findOne(status.getId().getMetadataId()); subject = compileMessageWithIndexFields(subject, metadata.getUuid(), this.language); message = compileMessageWithIndexFields(message, metadata.getUuid(), this.language); @@ -291,12 +331,14 @@ protected List getUserToNotify(MetadataStatus status) { SortUtils.createSort(User_.name)); for (Pair p : results) { users.add(p.two()); - }; + } + ; } else if (notificationLevel == StatusValueNotificationLevel.recordUserAuthor) { Iterable records = this.context.getBean(MetadataRepository.class).findAll(listOfId); for (Metadata r : records) { users.add(userRepository.findOne(r.getSourceInfo().getOwner())); - }; + } + ; } else if (notificationLevel.name().startsWith("catalogueProfile")) { String profileId = notificationLevel.name().replace( "catalogueProfile", ""); @@ -323,6 +365,8 @@ protected List getUserToNotify(MetadataStatus status) { * @param mdId The metadata id to unset privileges on */ private void unsetAllOperations(int mdId) throws Exception { + Log.trace(Geonet.DATA_MANAGER, "DefaultStatusActions.unsetAllOperations(" + mdId + ")"); + int allGroup = 1; for (ReservedOperation op : ReservedOperation.values()) { dm.forceUnsetOperation(context, mdId, allGroup, op.getId()); @@ -333,10 +377,10 @@ private void unsetAllOperations(int mdId) throws Exception { * Substitute lucene index field values in message. * Lucene field are identified using {{index:fieldName}} tag. * - * @param message The message to work on - * @param uuid The record UUID - * @param language The language (define the index to look into) - * @return The message with field substituted by values + * @param message The message to work on + * @param uuid The record UUID + * @param language The language (define the index to look into) + * @return The message with field substituted by values */ public static String compileMessageWithIndexFields(String message, String uuid, String language) { // Search lucene field to replace @@ -369,9 +413,9 @@ private String getTranslatedStatusName(int statusValueId) { /** * Send the email message about change of status on a group of metadata records. * - * @param sendTo The recipient email address - * @param subject Subject to be used for email notices - * @param message Text of the mail + * @param sendTo The recipient email address + * @param subject Subject to be used for email notices + * @param message Text of the mail */ protected void sendEmail(String sendTo, String subject, String message) throws Exception { diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java index 1c9fbf6b188..4d3a0c41b13 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/GetRecord.java @@ -107,7 +107,7 @@ public static Record buildRecordStat(ServiceContext context, Specification)hasMetadataUuid(uuid), prefix); } } diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListIdentifiers.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListIdentifiers.java index 074028d898a..82aa2bd1da1 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListIdentifiers.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListIdentifiers.java @@ -123,7 +123,7 @@ private Header buildHeader(ServiceContext context, int id, String prefix) throws //--- find and add categories (here called sets) - for (MetadataCategory category : metadata.getMetadataCategories()) { + for (MetadataCategory category : metadata.getCategories()) { h.addSet(category.getName()); } diff --git a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java index 60047a4d6aa..81da0d91bb0 100644 --- a/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java +++ b/core/src/main/java/org/fao/geonet/kernel/oaipmh/services/ListRecords.java @@ -24,13 +24,12 @@ package org.fao.geonet.kernel.oaipmh.services; -import jeeves.server.context.ServiceContext; +import static org.fao.geonet.repository.specification.MetadataSpecs.hasMetadataId; -import org.fao.geonet.kernel.oaipmh.Lib; -import org.fao.geonet.kernel.oaipmh.ResumptionTokenCache; +import org.fao.geonet.domain.Metadata; import org.fao.geonet.kernel.SchemaManager; +import org.fao.geonet.kernel.oaipmh.ResumptionTokenCache; import org.fao.geonet.kernel.setting.SettingManager; -import org.fao.geonet.repository.specification.MetadataSpecs; import org.fao.oaipmh.exceptions.CannotDisseminateFormatException; import org.fao.oaipmh.exceptions.IdDoesNotExistException; import org.fao.oaipmh.requests.ListRecordsRequest; @@ -38,8 +37,9 @@ import org.fao.oaipmh.responses.ListRecordsResponse; import org.fao.oaipmh.responses.Record; import org.fao.oaipmh.util.SearchResult; +import org.springframework.data.jpa.domain.Specification; -import static org.fao.geonet.repository.specification.MetadataSpecs.*; +import jeeves.server.context.ServiceContext; //============================================================================= @@ -97,7 +97,7 @@ private Record buildRecord(ServiceContext context, int id, String prefix) throws // be called several times for a list of MD records // and we do not want to stop because of one error try { - return GetRecord.buildRecordStat(context, hasMetadataId(id), prefix); + return GetRecord.buildRecordStat(context, (Specification)hasMetadataId(id), prefix); } catch (IdDoesNotExistException e) { return null; } catch (CannotDisseminateFormatException e2) { diff --git a/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java b/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java index eb69b9f8c7e..3b9f5192e8b 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java @@ -54,6 +54,7 @@ import org.jdom.JDOMException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; import java.io.IOException; @@ -400,12 +401,12 @@ public boolean rebuildIndex(ServiceContext context, boolean xlinks, for (Iterator iter = sm.getSelection(bucket).iterator(); iter.hasNext(); ) { String uuid = (String) iter.next(); -// String id = dataMan.getMetadataId(uuid); - AbstractMetadata metadata = metadataRepository.findOneByUuid(uuid); - if (metadata != null) { + for (AbstractMetadata metadata : metadataRepository.findAllByUuid(uuid)) { listOfIdsToIndex.add(metadata.getId() + ""); - } else { - System.out.println(String.format( + } + + if(!metadataRepository.existsMetadataUuid(uuid)) { + Log.warning(Geonet.INDEX_ENGINE, String.format( "Selection contains uuid '%s' not found in database", uuid)); } } @@ -416,8 +417,8 @@ public boolean rebuildIndex(ServiceContext context, boolean xlinks, sendDocumentsToIndex(); } else { final Specifications metadataSpec = - Specifications.where(MetadataSpecs.isType(MetadataType.METADATA)) - .or(MetadataSpecs.isType(MetadataType.TEMPLATE)); + Specifications.where((Specification)MetadataSpecs.isType(MetadataType.METADATA)) + .or((Specification)MetadataSpecs.isType(MetadataType.TEMPLATE)); final List metadataIds = metadataRepository.findAllIdsBy( Specifications.where(metadataSpec) ); diff --git a/core/src/main/java/org/fao/geonet/kernel/search/IndexFields.java b/core/src/main/java/org/fao/geonet/kernel/search/IndexFields.java index 76f2901efa4..8534bfcc1a6 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/IndexFields.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/IndexFields.java @@ -67,6 +67,7 @@ public class IndexFields { public static final String DENOMINATOR = "denominator"; public static final String DIGITAL = "digital"; public static final String DOWNLOAD = "download"; + public static final String DRAFT = "_draft"; public static final String DUMMY = "_dummy"; public static final String EAST = "eastBL"; public static final String GROUP_OWNER = "_groupOwner"; diff --git a/core/src/main/java/org/fao/geonet/kernel/search/LuceneIndexField.java b/core/src/main/java/org/fao/geonet/kernel/search/LuceneIndexField.java index 6b43a47dcf2..a53e078f1db 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/LuceneIndexField.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/LuceneIndexField.java @@ -67,6 +67,7 @@ public class LuceneIndexField { public static final String DENOMINATOR = "denominator"; public static final String DIGITAL = "digital"; public static final String DOWNLOAD = "download"; + public static final String DRAFT = "_draft"; public static final String DUMMY = "_dummy"; public static final String EAST = "eastBL"; public static final String GROUP_OWNER = "_groupOwner"; diff --git a/core/src/main/java/org/fao/geonet/kernel/search/LuceneQueryBuilder.java b/core/src/main/java/org/fao/geonet/kernel/search/LuceneQueryBuilder.java index 6d0da4fe919..b7a798f6883 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/LuceneQueryBuilder.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/LuceneQueryBuilder.java @@ -103,6 +103,10 @@ public class LuceneQueryBuilder { * Template = "n" is added if not set in search criteria. */ private boolean templateCriteriaAdded; + /** + * No draft is shown if not set explicitly in search criteria. + */ + private boolean draftCriteriaAdded; /** @@ -276,6 +280,7 @@ private Query buildBaseQuery(LuceneQueryInput luceneQueryInput) { spatialCriteriaAdded = false; temporalCriteriaAdded = false; templateCriteriaAdded = false; + draftCriteriaAdded = false; for (Iterator>> i = searchCriteria.entrySet().iterator(); i.hasNext(); ) { Entry> entry = i.next(); @@ -338,6 +343,9 @@ private Query buildBaseQuery(LuceneQueryInput luceneQueryInput) { templateCriteriaAdded = true; } + // Search only for non-draft not set by search criteria before + draftCriteria(null, query); + if (StringUtils.isNotEmpty(_language)) { if (Log.isDebugEnabled(Geonet.LUCENE)) Log.debug(Geonet.LUCENE, "adding locale query for language " + _language); @@ -516,6 +524,10 @@ private void addANDCriteria(String fieldName, Set fieldValues, String si else if (LuceneIndexField.IS_TEMPLATE.equals(fieldName) || SearchParameter.TEMPLATE.equals(fieldName)) { templateCriteria(fieldValue, query); } + // draft + else if (LuceneIndexField.DRAFT.equals(fieldName)) { + draftCriteria(fieldValue, query); + } // all -- mapped to same Lucene field as 'any' else if ("all".equals(fieldName)) { addRequiredTextField(fieldValue, LuceneIndexField.ANY, similarity, (criteriaIsASet ? bq : query)); @@ -636,6 +648,32 @@ private void phraseCriteria(String fieldValue, BooleanQuery query, BooleanClause } } + /** + * Adds draft to query. + */ + private void draftCriteria(String fieldValue, BooleanQuery query) { + + if (!draftCriteriaAdded) { + BooleanClause.Occur templateOccur = LuceneUtils.convertRequiredAndProhibitedToOccur(true, false); + + Query templateQ; + if (fieldValue != null) { + if (fieldValue.contains(OR_SEPARATOR)) { + templateQ = new BooleanQuery(); + addSeparatedTextField(fieldValue, OR_SEPARATOR, LuceneIndexField.DRAFT, (BooleanQuery) templateQ); + } else { + templateQ = new TermQuery(new Term(LuceneIndexField.DRAFT, fieldValue)); + } + } else { + templateQ = new BooleanQuery(); + addSeparatedTextField("n or e", OR_SEPARATOR, LuceneIndexField.DRAFT, (BooleanQuery) templateQ); + } + query.add(templateQ, templateOccur); + + draftCriteriaAdded = true; + } + } + /** * Adds template searchterm to query. */ diff --git a/core/src/main/java/org/fao/geonet/kernel/search/UnusedSearcher.java b/core/src/main/java/org/fao/geonet/kernel/search/UnusedSearcher.java index 228b7a32156..4ee6cd09d69 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/UnusedSearcher.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/UnusedSearcher.java @@ -22,17 +22,22 @@ package org.fao.geonet.kernel.search; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; - -import org.fao.geonet.Util; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import org.fao.geonet.GeonetContext; +import org.fao.geonet.Util; import org.fao.geonet.constants.Geonet; -import org.fao.geonet.domain.*; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.domain.OperationAllowed; +import org.fao.geonet.domain.ReservedGroup; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.setting.SettingManager; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.specification.MetadataSpecs; import org.fao.geonet.repository.specification.OperationAllowedSpecs; @@ -41,9 +46,8 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; //============================================================================== @@ -82,12 +86,14 @@ public void search(ServiceContext context, Element request, context.info("UnusedSearcher : using maxDiff=" + maxDiff); //--- proper search - final Specifications spec = Specifications.where(MetadataSpecs.isType(MetadataType.TEMPLATE)).and(MetadataSpecs.isHarvested(false)) - .and(MetadataSpecs.hasSource(siteId)); + final Specifications spec = + Specifications.where((Specification)MetadataSpecs.isType(MetadataType.TEMPLATE)) + .and((Specification)MetadataSpecs.isHarvested(false)) + .and((Specification)MetadataSpecs.hasSource(siteId)); - final List list = context.getBean(MetadataRepository.class).findAll(spec); + final List list = context.getBean(IMetadataUtils.class).findAll(spec); - for (Metadata rec : list) { + for (AbstractMetadata rec : list) { int id = rec.getId(); ISODate createDate = rec.getDataInfo().getCreateDate(); diff --git a/core/src/main/java/org/fao/geonet/services/Utils.java b/core/src/main/java/org/fao/geonet/services/Utils.java index c5a4df8e134..3b8dd6daab7 100644 --- a/core/src/main/java/org/fao/geonet/services/Utils.java +++ b/core/src/main/java/org/fao/geonet/services/Utils.java @@ -23,7 +23,9 @@ package org.fao.geonet.services; -import jeeves.server.context.ServiceContext; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; import org.apache.lucene.document.Document; import org.apache.lucene.index.Term; @@ -34,16 +36,17 @@ import org.fao.geonet.Util; import org.fao.geonet.constants.Geonet; import org.fao.geonet.constants.Params; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.exceptions.MissingParameterEx; -import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.AccessManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.search.IndexAndTaxonomy; import org.fao.geonet.kernel.search.SearchManager; import org.fao.geonet.kernel.search.index.GeonetworkMultiReader; +import org.fao.geonet.repository.MetadataDraftRepository; import org.jdom.Element; -import java.io.IOException; -import java.util.Collections; -import java.util.Set; +import jeeves.server.context.ServiceContext; public class Utils { @@ -64,7 +67,7 @@ public static String getIdentifierFromParameters(Element params, String id; GeonetContext gc = (GeonetContext) context .getHandlerContext(Geonet.CONTEXT_NAME); - DataManager dm = gc.getBean(DataManager.class); + IMetadataUtils dm = gc.getBean(IMetadataUtils.class); id = lookupByFileId(params, gc); if (id == null) { @@ -73,6 +76,21 @@ public static String getIdentifierFromParameters(Element params, String uuid = Util.getParam(params, uuidParamName); // lookup ID by UUID id = dm.getMetadataId(uuid); + + //Do we want the draft version? + Boolean approved = Util.getParam(params, "approved", true); + if(!approved) { + //Is the user editor for this metadata? + AccessManager am = context.getBean(AccessManager.class); + if(am.canEdit(context, id)) { + AbstractMetadata draft = gc.getBean(MetadataDraftRepository.class).findOneByUuid(uuid); + if(draft != null) { + id = String.valueOf(draft.getId()); + } + } + } + + } catch (MissingParameterEx x) { // request does not contain UUID; use ID from request try { diff --git a/core/src/main/resources/config-spring-geonetwork.xml b/core/src/main/resources/config-spring-geonetwork.xml index a2ade7a5591..1b3372fa168 100644 --- a/core/src/main/resources/config-spring-geonetwork.xml +++ b/core/src/main/resources/config-spring-geonetwork.xml @@ -173,5 +173,5 @@ - + diff --git a/core/src/test/java/org/fao/geonet/DataManagerWorksWithoutTransactionIntegrationTest.java b/core/src/test/java/org/fao/geonet/DataManagerWorksWithoutTransactionIntegrationTest.java index bae64caa259..2e74b705c2b 100644 --- a/core/src/test/java/org/fao/geonet/DataManagerWorksWithoutTransactionIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/DataManagerWorksWithoutTransactionIntegrationTest.java @@ -72,7 +72,7 @@ public void run() throws Exception { AbstractMetadata updateMd = _dataManager.updateMetadata(serviceContext, mdId, newMd, false, false, false, "eng", new ISODate().getDateAndTime(), false); assertNotNull(updateMd); - final boolean hasNext = updateMd.getMetadataCategories().iterator().hasNext(); + final boolean hasNext = updateMd.getCategories().iterator().hasNext(); assertTrue(hasNext); } }); diff --git a/core/src/test/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigrationTest.java b/core/src/test/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigrationTest.java index 82a5e7cc6c2..e0297f2730d 100644 --- a/core/src/test/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigrationTest.java +++ b/core/src/test/java/org/fao/geonet/MergeUsersByUsernameDatabaseMigrationTest.java @@ -34,6 +34,7 @@ import org.jdom.Element; import org.junit.Before; import org.junit.Test; +import org.springframework.data.jpa.domain.Specification; import java.io.ByteArrayInputStream; import java.util.Comparator; @@ -204,10 +205,10 @@ public void testTransferMetadata() throws Exception { assertEquals((Integer) greatestProfileUser.getId(), metadata.getSourceInfo().getOwner()); assertEquals(groupId, metadata.getSourceInfo().getGroupOwner()); } - assertEquals(0, metadataRepository.findAll(MetadataSpecs.isOwnedByUser(user1.getId())).size()); - assertEquals(0, metadataRepository.findAll(MetadataSpecs.isOwnedByUser(user2.getId())).size()); - assertEquals(0, metadataRepository.findAll(MetadataSpecs.isOwnedByUser(user3.getId())).size()); - assertEquals(6, metadataRepository.findAll(MetadataSpecs.isOwnedByUser(user4.getId())).size()); + assertEquals(0, metadataRepository.findAll((Specification)MetadataSpecs.isOwnedByUser(user1.getId())).size()); + assertEquals(0, metadataRepository.findAll((Specification)MetadataSpecs.isOwnedByUser(user2.getId())).size()); + assertEquals(0, metadataRepository.findAll((Specification)MetadataSpecs.isOwnedByUser(user3.getId())).size()); + assertEquals(6, metadataRepository.findAll((Specification)MetadataSpecs.isOwnedByUser(user4.getId())).size()); } private Group newGroup(String groupName) { diff --git a/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java b/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java index 4efed71f714..d093bb0de6e 100644 --- a/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java @@ -202,7 +202,7 @@ public void testCreateMetadataWithTemplateMetadata() throws Exception { final AbstractMetadata metadata = new Metadata(); metadata.setDataAndFixCR(sampleMetadataXml) .setUuid(UUID.randomUUID().toString()); - metadata.getMetadataCategories().add(category); + metadata.getCategories().add(category); metadata.getDataInfo().setSchemaId("iso19139"); metadata.getSourceInfo().setSourceId(source.getUuid()).setOwner(1); @@ -211,8 +211,8 @@ public void testCreateMetadataWithTemplateMetadata() throws Exception { principal.getId(), templateMd.getUuid(), MetadataType.METADATA.codeString, true); AbstractMetadata newMetadata = _metadataRepository.findOne(newMetadataId); - assertEquals(1, newMetadata.getMetadataCategories().size()); - assertEquals(category, newMetadata.getMetadataCategories().iterator().next()); + assertEquals(1, newMetadata.getCategories().size()); + assertEquals(category, newMetadata.getCategories().iterator().next()); assertEqualsText(metadata.getUuid(), newMetadata.getXmlData(false), "gmd:parentIdentifier/gco:CharacterString"); } @@ -270,7 +270,7 @@ public void testDeleteBatchMetadata() throws Exception { assertEquals(startIndexDocs + (2 * numDocsPerMd), numDocs(searchManager, lang)); assertEquals(startMdCount + 2, _metadataRepository.count()); - Specification spec = where(MetadataSpecs.hasMetadataId(md1)).or(MetadataSpecs.hasMetadataId(md2)); + Specification spec = where((Specification)MetadataSpecs.hasMetadataId(md1)).or((Specification)MetadataSpecs.hasMetadataId(md2)); _dataManager.batchDeleteMetadataAndUpdateIndex(spec); assertEquals(startMdCount, _metadataRepository.count()); diff --git a/core/src/test/java/org/fao/geonet/kernel/DataManagerWorksWithoutTransactionIntegrationTest.java b/core/src/test/java/org/fao/geonet/kernel/DataManagerWorksWithoutTransactionIntegrationTest.java index a6ab3efd362..b9daced0e8c 100644 --- a/core/src/test/java/org/fao/geonet/kernel/DataManagerWorksWithoutTransactionIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/DataManagerWorksWithoutTransactionIntegrationTest.java @@ -82,7 +82,7 @@ MetadataType.METADATA.codeString, null, metadataCategory, new ISODate().getDateA AbstractMetadata updateMd = dm.updateMetadata(serviceContext, mdId, newMd, false, false, false, "eng", new ISODate().getDateAndTime(), false); assertNotNull(updateMd); - final boolean hasNext = updateMd.getMetadataCategories().iterator().hasNext(); + final boolean hasNext = updateMd.getCategories().iterator().hasNext(); assertTrue(hasNext); } }); diff --git a/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataCategoryTest.java b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataCategoryTest.java new file mode 100644 index 00000000000..49caed79fcb --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataCategoryTest.java @@ -0,0 +1,121 @@ +/* + * 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.kernel.datamanager; + +import jeeves.server.context.ServiceContext; +import org.fao.geonet.AbstractCoreIntegrationTest; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataCategory; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataCategory; +import org.fao.geonet.repository.MetadataCategoryRepository; +import org.fao.geonet.repository.MetadataRepository; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.Assert.*; + +/** + * Tests for {@link BaseMetadataCategory}. + * + * @author delawen María Arias de Reyna + */ +public class BaseMetadataCategoryTest extends AbstractCoreIntegrationTest { + + @Autowired + private IMetadataCategory metadataCategory; + + @Autowired + private MetadataCategoryRepository metadataCategoryRepository; + + @Autowired + private MetadataRepository metadataRepository; + + private Metadata md; + private MetadataCategory mdc; + + @Before + public void init() { + md = new Metadata(); + populate(md); + metadataRepository.save(md); + } + + @Test + public void test() throws Exception { + + assertTrue(metadataCategory.getCategories(String.valueOf(md.getId())).isEmpty()); + + assertFalse(metadataCategory.isCategorySet(String.valueOf(md.getId()), mdc.getId())); + + ServiceContext context = createServiceContext(); + + assertTrue(metadataCategory.setCategory(context, String.valueOf(md.getId()), String.valueOf(mdc.getId()))); + + assertTrue(metadataCategory.isCategorySet(String.valueOf(md.getId()), mdc.getId())); + assertFalse(metadataCategory.getCategories(String.valueOf(md.getId())).isEmpty()); + assertEquals(md.getCategories().size(), 1); + + metadataCategory.unsetCategory(context, String.valueOf(md.getId()), mdc.getId()); + + assertFalse(metadataCategory.isCategorySet(String.valueOf(md.getId()), mdc.getId())); + } + + @Test + public void testCornerCases() throws Exception { + + ServiceContext context = createServiceContext(); + assertTrue(metadataCategory.setCategory(context, String.valueOf(md.getId()), String.valueOf(mdc.getId()))); + assertFalse(metadataCategory.setCategory(context, String.valueOf(md.getId()), String.valueOf(mdc.getId()))); + + assertTrue(metadataCategory.unsetCategory(context, String.valueOf(md.getId()), mdc.getId())); + assertFalse(metadataCategory.unsetCategory(context, String.valueOf(md.getId()), mdc.getId())); + + assertFalse(metadataCategory.setCategory(context, String.valueOf(Integer.MAX_VALUE), String.valueOf(mdc.getId()))); + assertFalse(metadataCategory.unsetCategory(context, String.valueOf(Integer.MAX_VALUE), mdc.getId())); + } + + @Test(expected = IllegalArgumentException.class) + public void whenNoMetadata() throws Exception { + metadataCategory.getCategories("-20"); + } + + @After + public void cleanup() { + metadataRepository.delete(md); + } + + private void populate(AbstractMetadata md) { + md.setUuid("test-metadata"); + md.setData(""); + md.getSourceInfo().setGroupOwner(1); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("isoFake"); + + mdc = metadataCategoryRepository.findAll().get(0); + } + +} diff --git a/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataManagerTest.java b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataManagerTest.java new file mode 100644 index 00000000000..2d655bf167f --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataManagerTest.java @@ -0,0 +1,167 @@ +/* + * 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.kernel.datamanager; + +import jeeves.server.context.ServiceContext; +import org.apache.commons.io.IOUtils; +import org.fao.geonet.AbstractCoreIntegrationTest; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.domain.User; +import org.fao.geonet.kernel.XmlSerializerIntegrationTest; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataManager; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataUtils; +import org.fao.geonet.repository.GroupRepository; +import org.fao.geonet.repository.UserRepository; +import org.fao.geonet.repository.specification.MetadataSpecs; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * Tests for {@link BaseMetadataManager} and {@link BaseMetadataUtils}. + * + * @author delawen María Arias de Reyna + */ +public class BaseMetadataManagerTest extends AbstractCoreIntegrationTest { + + @Autowired + private BaseMetadataManager metadataManager; + + @Autowired + private BaseMetadataUtils metadataUtils; + + @Autowired + private UserRepository userRepository; + + @Autowired + private GroupRepository groupRepository; + + private User user; + private Group group; + private AbstractMetadata md; + + @Before + public void init() { + + user = userRepository.findAll().get(0); + + for (Group g : groupRepository.findAll()) { + if (!g.isReserved()) { + group = g; + break; + } + } + + } + + @Test + public void testSave() throws Exception { + assertTrue(metadataUtils.findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + md = metadataManager.save(createMetadata()); + assertNotNull(md); + assertNotNull(metadataUtils.findOne(md.getId())); + metadataManager.delete(md.getId()); + } + + @Test + public void testCreate() throws Exception { + ServiceContext context = createServiceContext(); + loginAsAdmin(context); + + md = metadataManager.save(createMetadata()); + + List templates = metadataUtils + .findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)); + + assertFalse(templates.isEmpty()); + + String id = metadataManager.createMetadata(context, String.valueOf(templates.get(0).getId()), + String.valueOf(group.getId()), "test", user.getId(), null, MetadataType.METADATA.codeString, true); + + assertNotNull(id); + assertTrue(metadataUtils.exists(md.getId())); + assertTrue(metadataUtils.existsMetadata(md.getId())); + assertTrue(metadataUtils.existsMetadataUuid(md.getUuid())); + + assertNotNull(metadataUtils.findOne(id)); + + metadataManager.delete(Integer.valueOf(id)); + + assertNull(metadataUtils.findOne(id)); + + metadataManager.delete(md.getId()); + + assertNull(metadataUtils.findOne(md.getId())); + assertFalse(metadataUtils.exists(md.getId())); + assertFalse(metadataUtils.existsMetadata(md.getId())); + assertFalse(metadataUtils.existsMetadataUuid(md.getUuid())); + } + + @Test + public void testSpecifications() throws Exception { + + assertTrue(metadataUtils + .findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + + md = metadataManager.save(createMetadata()); + + assertFalse(metadataUtils + .findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + + assertFalse(metadataUtils + .findAllIdsBy(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + + assertFalse(metadataUtils + .findAll(MetadataSpecs.hasMetadataId(md.getId())).isEmpty()); + } + + private AbstractMetadata createMetadata() throws IOException { + AbstractMetadata md = new Metadata(); + md.setUuid("test-metadata"); + try (InputStream is = XmlSerializerIntegrationTest.class.getResourceAsStream("valid-metadata.iso19139.xml")) { + md.setData(IOUtils.toString(is)); + } + md.getSourceInfo().setGroupOwner(group.getId()); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("iso19139"); + md.getDataInfo().setType(MetadataType.TEMPLATE); + return md; + } + + @After + public void cleanup() { + + } + +} diff --git a/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataOperationTest.java b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataOperationTest.java new file mode 100644 index 00000000000..cc10a19d204 --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataOperationTest.java @@ -0,0 +1,155 @@ +/* + * 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.kernel.datamanager; + +import jeeves.server.context.ServiceContext; +import org.fao.geonet.AbstractCoreIntegrationTest; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.Profile; +import org.fao.geonet.domain.ReservedOperation; +import org.fao.geonet.domain.User; +import org.fao.geonet.domain.UserGroup; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataOperations; +import org.fao.geonet.repository.GroupRepository; +import org.fao.geonet.repository.MetadataRepository; +import org.fao.geonet.repository.UserGroupRepository; +import org.fao.geonet.repository.UserRepository; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link BaseMetadataOperations}. + * + * @author delawen María Arias de Reyna + */ +public class BaseMetadataOperationTest extends AbstractCoreIntegrationTest { + + @Autowired + private BaseMetadataOperations metadataOperation; + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserGroupRepository userGroupRepository; + + @Autowired + private GroupRepository groupRepository; + + @Autowired + private MetadataRepository metadataRepository; + + private User user; + private Group group; + private Group groupOwner; + private Metadata md; + + @Before + public void init() { + user = new User(); + user.setUsername("testuser"); + user.setProfile(Profile.Reviewer); + user.setName("test"); + user.setEnabled(true); + user = userRepository.save(user); + + group = new Group(); + group.setName("test-group"); + group = groupRepository.save(group); + + groupOwner = new Group(); + groupOwner.setName("test-group-owner"); + groupOwner = groupRepository.save(groupOwner); + + UserGroup userGroup = new UserGroup(); + userGroup.setGroup(group); + userGroup.setUser(user); + userGroup.setProfile(Profile.Reviewer); + userGroupRepository.save(userGroup); + + md = new Metadata(); + md.setUuid("test-metadata"); + md.setData(""); + md.getSourceInfo().setGroupOwner(groupOwner.getId()); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("isoFake"); + metadataRepository.save(md); + } + + @Test + public void test() throws Exception { + ServiceContext context = createServiceContext(); + + assertTrue(metadataOperation.getAllOperations(md.getId()).isEmpty()); + assertTrue(metadataOperation.existsUser(context, user.getId())); + + assertTrue(metadataOperation + .getOperationAllowedToAdd(context, md.getId(), group.getId(), ReservedOperation.view.getId()) + .isPresent()); + assertTrue(metadataOperation + .getOperationAllowedToAdd(context, md.getId(), group.getId(), ReservedOperation.editing.getId()) + .isPresent()); + + assertTrue(metadataOperation.setOperation(context, md.getId(), group.getId(), + ReservedOperation.view.getId())); + + assertTrue(metadataOperation.setOperation(context, md.getId(), group.getId(), + ReservedOperation.editing.getId())); + + assertFalse(metadataOperation + .getOperationAllowedToAdd(context, md.getId(), group.getId(), ReservedOperation.view.getId()) + .isPresent()); + assertFalse(metadataOperation + .getOperationAllowedToAdd(context, md.getId(), group.getId(), ReservedOperation.editing.getId()) + .isPresent()); + + assertTrue(metadataOperation.existsUser(context, user.getId())); + + metadataOperation.unsetOperation(context, md.getId(), group.getId(), ReservedOperation.view.getId()); + metadataOperation.unsetOperation(context, md.getId(), group.getId(), ReservedOperation.editing.getId()); + + assertTrue(metadataOperation + .getOperationAllowedToAdd(context, md.getId(), group.getId(), ReservedOperation.view.getId()) + .isPresent()); + assertTrue(metadataOperation + .getOperationAllowedToAdd(context, md.getId(), group.getId(), ReservedOperation.editing.getId()) + .isPresent()); + + } + + + @After + public void cleanup() { + metadataRepository.delete(md); + groupRepository.delete(group); + groupRepository.delete(groupOwner); + userRepository.delete(user); + } +} diff --git a/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataValidatorTest.java b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataValidatorTest.java new file mode 100644 index 00000000000..1d586ee74b4 --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/datamanager/BaseMetadataValidatorTest.java @@ -0,0 +1,108 @@ +/* + * 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.kernel.datamanager; + +import org.apache.commons.io.IOUtils; +import org.fao.geonet.AbstractCoreIntegrationTest; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.kernel.XmlSerializerIntegrationTest; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataValidator; +import org.fao.geonet.repository.GroupRepository; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Tests for {@link BaseMetadataValidator}. + * + * @author delawen María Arias de Reyna + */ +public class BaseMetadataValidatorTest extends AbstractCoreIntegrationTest { + + @Autowired + private IMetadataManager metadataManager; + + @Autowired + private IMetadataValidator metadataValidator; + @Autowired + private GroupRepository groupRepository; + + private Group group; + private AbstractMetadata md; + + @Before + public void init() { + + for (Group g : groupRepository.findAll()) { + if (!g.isReserved()) { + group = g; + break; + } + } + + try { + md = metadataManager.save(createMetadata()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Should behave like the same function on {@link BaseMetadataManagerTest} + * + * @throws Exception + */ + @Test + public void testCreate() throws Exception { + metadataValidator.doValidate(md, "eng"); + } + + private AbstractMetadata createMetadata() throws IOException { + AbstractMetadata md = new Metadata(); + md.setUuid("test-metadata"); + try (InputStream is = XmlSerializerIntegrationTest.class.getResourceAsStream("valid-metadata.iso19139.xml")) { + md.setData(IOUtils.toString(is)); + } + md.getSourceInfo().setGroupOwner(group.getId()); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("iso19139"); + md.getDataInfo().setType(MetadataType.TEMPLATE); + return md; + } + + @After + public void cleanup() { + + metadataManager.delete(md.getId()); + + } + +} diff --git a/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataCategoryTest.java b/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataCategoryTest.java new file mode 100644 index 00000000000..4b20992e69e --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataCategoryTest.java @@ -0,0 +1,122 @@ +/* + * 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.kernel.datamanager; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.fao.geonet.AbstractCoreIntegrationTest; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataCategory; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.kernel.datamanager.base.BaseMetadataCategory; +import org.fao.geonet.repository.MetadataCategoryRepository; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.repository.MetadataRepository; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import jeeves.server.context.ServiceContext; + +/** + * Tests for {@link DraftMetadataCategory}. + * + * @author delawen María Arias de Reyna + */ +@ContextConfiguration(inheritLocations = true, locations = {"classpath:draft-test-context.xml"}) +public class DraftMetadataCategoryTest extends AbstractCoreIntegrationTest { + + @Autowired + private BaseMetadataCategory metadataCategory; + + @Autowired + private MetadataCategoryRepository metadataCategoryRepository; + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + + @Autowired + private MetadataRepository metadataRepository; + + private MetadataDraft md; + private Metadata record; + private MetadataCategory mdc; + + @Before + public void init() { + record = new Metadata(); + populate(record); + metadataRepository.save(record); + + md = new MetadataDraft(); + populate(md); + md.setApprovedVersion(record); + metadataDraftRepository.save(md); + } + + /** + * On draft metadata, categories don't work. + * + * @throws Exception + */ + @Test + public void test() throws Exception { + + assertTrue(metadataDraftRepository.exists(md.getId())); + + assertTrue(metadataCategory.getCategories(String.valueOf(md.getId())).isEmpty()); + + assertFalse(metadataCategory.isCategorySet(String.valueOf(md.getId()), mdc.getId())); + + ServiceContext context = createServiceContext(); + + assertFalse(metadataCategory.setCategory(context, String.valueOf(md.getId()), String.valueOf(mdc.getId()))); + + assertFalse(metadataCategory.isCategorySet(String.valueOf(md.getId()), mdc.getId())); + assertTrue(metadataCategory.getCategories(String.valueOf(md.getId())).isEmpty()); + + metadataCategory.unsetCategory(context, String.valueOf(md.getId()), mdc.getId()); + } + + @After + public void cleanup() { + metadataDraftRepository.delete(md); + metadataRepository.delete(record); + } + + private void populate(AbstractMetadata md) { + md.setUuid("test-metadata"); + md.setData(""); + md.getSourceInfo().setGroupOwner(1); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("isoFake"); + + mdc = metadataCategoryRepository.findAll().get(0); + } + +} diff --git a/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataManagerTest.java b/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataManagerTest.java new file mode 100644 index 00000000000..c6bbfc131ed --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataManagerTest.java @@ -0,0 +1,213 @@ +/* + * 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.kernel.datamanager; + +import jeeves.server.context.ServiceContext; +import org.apache.commons.io.IOUtils; +import org.fao.geonet.AbstractCoreIntegrationTest; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.domain.User; +import org.fao.geonet.kernel.XmlSerializerIntegrationTest; +import org.fao.geonet.kernel.datamanager.draft.DraftMetadataManager; +import org.fao.geonet.kernel.datamanager.draft.DraftMetadataUtils; +import org.fao.geonet.repository.GroupRepository; +import org.fao.geonet.repository.UserRepository; +import org.fao.geonet.repository.specification.MetadataSpecs; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * Tests for {@link DraftMetadataManager} and {@link DraftMetadataUtils}. + * + * @author delawen María Arias de Reyna + */ +@ContextConfiguration(inheritLocations = true, locations = {"classpath:draft-test-context.xml"}) +public class DraftMetadataManagerTest extends AbstractCoreIntegrationTest { + + @Autowired + private IMetadataManager metadataManager; + + @Autowired + private IMetadataUtils metadataUtils; + + @Autowired + private UserRepository userRepository; + + @Autowired + private GroupRepository groupRepository; + + private User user; + private Group group; + private AbstractMetadata md; + + @Before + public void init() { + + user = userRepository.findAll().get(0); + + for (Group g : groupRepository.findAll()) { + if (!g.isReserved()) { + group = g; + break; + } + } + } + + @Test + public void usingDraftUtilities() { + assertTrue(metadataManager instanceof DraftMetadataManager); + assertTrue(metadataUtils instanceof DraftMetadataUtils); + } + + @Test + public void testSave() throws Exception { + assertTrue(metadataUtils.findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + + Metadata record = createMetadata(); + record = (Metadata) metadataManager.save(record); + + assertNotNull(record); + assertNotNull(record.getId()); + + md = metadataManager.save(createMetadataDraft(record)); + + assertNotNull(md); + assertNotNull(metadataUtils.findOne(md.getId())); + assertTrue(metadataUtils.exists(md.getId())); + assertTrue(metadataUtils.existsMetadata(md.getId())); + assertTrue(metadataUtils.existsMetadataUuid(md.getUuid())); + + metadataManager.delete(md.getId()); + + assertNull(metadataUtils.findOne(md.getId())); + assertFalse(metadataUtils.exists(md.getId())); + assertFalse(metadataUtils.existsMetadata(md.getId())); + + assertTrue(metadataUtils.existsMetadataUuid(md.getUuid())); + metadataManager.delete(record.getId()); + assertFalse(metadataUtils.existsMetadataUuid(md.getUuid())); + + } + + /** + * Should behave like the same function on {@link BaseMetadataManagerTest} + * + * @throws Exception + */ + @Test + public void testCreate() throws Exception { + ServiceContext context = createServiceContext(); + loginAsAdmin(context); + + md = metadataManager.save(createMetadata()); + + List templates = metadataUtils + .findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)); + + assertFalse(templates.isEmpty()); + + String id = metadataManager.createMetadata(context, String.valueOf(templates.get(0).getId()), + String.valueOf(group.getId()), "test", user.getId(), null, MetadataType.METADATA.codeString, true); + + assertNotNull(id); + assertTrue(metadataUtils.exists(md.getId())); + assertTrue(metadataUtils.existsMetadata(md.getId())); + assertTrue(metadataUtils.existsMetadataUuid(md.getUuid())); + + assertNotNull(metadataUtils.findOne(id)); + + metadataManager.delete(Integer.valueOf(id)); + + assertNull(metadataUtils.findOne(id)); + + metadataManager.delete(md.getId()); + + assertNull(metadataUtils.findOne(md.getId())); + assertFalse(metadataUtils.exists(md.getId())); + assertFalse(metadataUtils.existsMetadata(md.getId())); + assertFalse(metadataUtils.existsMetadataUuid(md.getUuid())); + } + + @Test + public void testSpecifications() throws Exception { + + assertTrue(metadataUtils.findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + + md = metadataManager.save(createMetadata()); + + assertFalse(metadataUtils.findAll(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + + assertFalse(metadataUtils.findAllIdsBy(MetadataSpecs.hasType(MetadataType.TEMPLATE)).isEmpty()); + + assertFalse(metadataUtils.findAll(MetadataSpecs.hasMetadataId(md.getId())).isEmpty()); + } + + private Metadata createMetadata() throws IOException { + Metadata md = new Metadata(); + md.setUuid("test-metadata"); + try (InputStream is = XmlSerializerIntegrationTest.class.getResourceAsStream("valid-metadata.iso19139.xml")) { + md.setData(IOUtils.toString(is)); + } + md.getSourceInfo().setGroupOwner(group.getId()); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("iso19139"); + md.getDataInfo().setType(MetadataType.TEMPLATE); + return md; + } + + + private AbstractMetadata createMetadataDraft(Metadata record) throws IOException { + MetadataDraft md = new MetadataDraft(); + md.setUuid("test-metadata"); + md.setApprovedVersion(record); + try (InputStream is = XmlSerializerIntegrationTest.class.getResourceAsStream("valid-metadata.iso19139.xml")) { + md.setData(IOUtils.toString(is)); + } + md.getSourceInfo().setGroupOwner(group.getId()); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("iso19139"); + md.getDataInfo().setType(MetadataType.TEMPLATE); + return md; + } + + @After + public void cleanup() { + + } + +} diff --git a/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataUtilsTest.java b/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataUtilsTest.java new file mode 100644 index 00000000000..28cfcb24114 --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/datamanager/DraftMetadataUtilsTest.java @@ -0,0 +1,210 @@ +/* + * 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.kernel.datamanager; + +import jeeves.server.context.ServiceContext; +import org.apache.commons.io.IOUtils; +import org.fao.geonet.AbstractCoreIntegrationTest; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.domain.MetadataSourceInfo; +import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.domain.Profile; +import org.fao.geonet.domain.StatusValue; +import org.fao.geonet.domain.User; +import org.fao.geonet.domain.UserGroup; +import org.fao.geonet.kernel.XmlSerializerIntegrationTest; +import org.fao.geonet.kernel.datamanager.draft.DraftMetadataManager; +import org.fao.geonet.kernel.datamanager.draft.DraftMetadataUtils; +import org.fao.geonet.repository.GroupRepository; +import org.fao.geonet.repository.UserGroupRepository; +import org.fao.geonet.repository.UserRepository; +import org.fao.geonet.repository.specification.MetadataSpecs; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * Tests for {@link DraftMetadataUtils}. + * + * @author delawen María Arias de Reyna + */ +@ContextConfiguration(inheritLocations = true, locations = {"classpath:draft-test-context.xml"}) +public class DraftMetadataUtilsTest extends AbstractCoreIntegrationTest { + + private static final String UUID = "test-metadata" + Math.random(); + + @Autowired + private IMetadataManager metadataManager; + + @Autowired + private IMetadataUtils metadataUtils; + + @Autowired + private IMetadataStatus metadataStatus; + + @Autowired + private UserRepository userRepository; + + @Autowired + private GroupRepository groupRepository; + + @Autowired + private UserGroupRepository userGroupRepository; + + private User user; + private Group group; + private AbstractMetadata md; + + @Before + public void init() throws IOException { + user = new User(); + user.setUsername(UUID); + user.setProfile(Profile.Reviewer); + user.setName(UUID); + user.setEnabled(true); + user = userRepository.save(user); + + group = new Group(); + group.setName(UUID); + group = groupRepository.save(group); + + UserGroup userGroup = new UserGroup(); + userGroup.setGroup(group); + userGroup.setUser(user); + userGroup.setProfile(Profile.Reviewer); + userGroupRepository.save(userGroup); + + md = metadataManager.save(createMetadata()); + } + + @Test + public void usingDraftUtilities() { + assertTrue(metadataManager instanceof DraftMetadataManager); + assertTrue(metadataUtils instanceof DraftMetadataUtils); + } + + @Test + public void testEditing() throws Exception { + + ServiceContext context = createServiceContext(); + loginAs(user); + + assertTrue(metadataUtils.findOne(md.getId()) instanceof Metadata); + + HashSet set = new HashSet(); + set.add(md.getId()); + Iterable mds = metadataUtils.findAll(set); + Iterator it = mds.iterator(); + + assertTrue(it.hasNext()); + it.next(); + assertFalse(it.hasNext()); + + Integer id = metadataUtils.startEditingSession(context, String.valueOf(md.getId())); + + assertNotNull(id); + + metadataStatus.setStatus(context, id, Integer.valueOf(StatusValue.Status.APPROVED), new ISODate(), + "Approve record"); + id = metadataUtils.startEditingSession(context, String.valueOf(md.getId())); + + assertTrue(id != md.getId()); + assertTrue(metadataUtils.findOne(id) instanceof MetadataDraft); + + metadataUtils.cancelEditingSession(context, id.toString()); + + Integer id2 = metadataUtils.startEditingSession(context, String.valueOf(md.getId())); + + assertNotNull(id2); + assertEquals(id, id2); + + metadataUtils.endEditingSession(id.toString(), context.getUserSession()); + + id2 = metadataUtils.startEditingSession(context, String.valueOf(md.getId())); + + assertNotNull(id2); + assertEquals(id, id2); + + metadataUtils.endEditingSession(id.toString(), context.getUserSession()); + + assertTrue(metadataUtils.findOneByUuid(UUID) instanceof MetadataDraft); + + } + + private Metadata createMetadata() throws IOException { + Metadata md = new Metadata(); + md.setUuid(UUID); + try (InputStream is = XmlSerializerIntegrationTest.class.getResourceAsStream("valid-metadata.iso19139.xml")) { + md.setData(IOUtils.toString(is)); + } + md.getSourceInfo().setGroupOwner(group.getId()); + md.getSourceInfo().setOwner(user.getId()); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("iso19139"); + md.getDataInfo().setType(MetadataType.TEMPLATE); + return md; + } + + @Test + public void auxiliaryFunctions() throws Exception { + assertNull(metadataUtils.getMetadataId("-1")); + assertTrue(Integer.valueOf(metadataUtils.getMetadataId(md.getUuid())) == md.getId()); + + Map res = metadataUtils.findAllSourceInfo(MetadataSpecs.hasMetadataId(md.getId())); + assertNotNull(res); + assertFalse(res.isEmpty()); + } + + @Test + public void getMetadataIdNotExist() throws Exception { + assertNull(metadataUtils.getMetadataId("-1")); + } + + @After + public void cleanup() throws Exception { + + while(metadataUtils.existsMetadataUuid(UUID)) { + AbstractMetadata md = metadataUtils.findOneByUuid(UUID); + metadataManager.delete(md.getId()); + } + + userRepository.delete(user.getId()); + groupRepository.delete(group.getId()); + } + +} diff --git a/core/src/test/java/org/fao/geonet/kernel/mef/MEFExporterIntegrationTest.java b/core/src/test/java/org/fao/geonet/kernel/mef/MEFExporterIntegrationTest.java index fa15b04c35c..5dfad7d8fb6 100644 --- a/core/src/test/java/org/fao/geonet/kernel/mef/MEFExporterIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/mef/MEFExporterIntegrationTest.java @@ -49,7 +49,7 @@ public void testDoExport() throws Exception { importMetadata.getMefFilesToLoad().add("mef2-example-2md.zip"); importMetadata.invoke(); - Path path = MEFExporter.doExport(context, "da165110-88fd-11da-a88f-000d939bc5d8", MEFLib.Format.FULL, false, false, false, false); + Path path = MEFExporter.doExport(context, "da165110-88fd-11da-a88f-000d939bc5d8", MEFLib.Format.FULL, false, false, false, false, true); try (FileSystem zipFs = ZipUtil.openZipFs(path)) { assertTrue(Files.exists(zipFs.getPath("metadata.xml"))); @@ -58,7 +58,7 @@ public void testDoExport() throws Exception { assertTrue(Files.exists(zipFs.getPath("public/thumbnail.gif"))); assertTrue(Files.exists(zipFs.getPath("public/thumbnail_s.gif"))); } - path = MEFExporter.doExport(context, "0e1943d6-64e8-4430-827c-b465c3e9e55c", MEFLib.Format.FULL, false, false, false, false); + path = MEFExporter.doExport(context, "0e1943d6-64e8-4430-827c-b465c3e9e55c", MEFLib.Format.FULL, false, false, false, false, true); try (FileSystem zipFs = ZipUtil.openZipFs(path)) { assertTrue(Files.exists(zipFs.getPath("metadata.xml"))); diff --git a/core/src/test/java/org/fao/geonet/kernel/search/LuceneQueryTest.java b/core/src/test/java/org/fao/geonet/kernel/search/LuceneQueryTest.java index 61ea07f0e32..64f0c4264f8 100644 --- a/core/src/test/java/org/fao/geonet/kernel/search/LuceneQueryTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/search/LuceneQueryTest.java @@ -35,12 +35,10 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.core.KeywordAnalyzer; import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper; -import org.apache.lucene.facet.FacetsConfig; import org.apache.lucene.search.Query; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.NodeInfo; import org.fao.geonet.kernel.GeonetworkDataDirectory; -import org.fao.geonet.kernel.search.LuceneConfig.LuceneConfigNumericField; import org.fao.geonet.kernel.search.facet.Dimension; import org.fao.geonet.kernel.search.facet.Facets; import org.fao.geonet.kernel.search.facet.SummaryType; @@ -65,10 +63,11 @@ @ContextConfiguration(locations = "classpath:web-test-context.xml") public class LuceneQueryTest { - private Set _tokenizedFieldSet; - private Map _numericFieldSet; + private static final String _BASE_TEMPLATE_CLAUSE = " +_isTemplate:n"; + private static final String _BASE_DRAFT_CLAUSE = " +(_draft:n _draft:e)"; + private static final String _BASE_CLAUSE = _BASE_TEMPLATE_CLAUSE + _BASE_DRAFT_CLAUSE; + private Set _tokenizedFieldSet; private PerFieldAnalyzerWrapper _analyzer; - private FacetsConfig _taxonomyConfiguration; private LuceneConfig luceneConfig; @Before @@ -98,8 +97,6 @@ public void before() throws Exception { luceneConfig.configure(configFile); _tokenizedFieldSet = luceneConfig.getTokenizedField(); - _numericFieldSet = luceneConfig.getNumericFields(); - _taxonomyConfiguration = luceneConfig.getTaxonomyConfiguration(); } /** @@ -121,7 +118,7 @@ public void testSingleORSingleValue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(any:xxx title:xxx) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(any:xxx title:xxx)" + _BASE_CLAUSE, query.toString()); } /** @@ -147,7 +144,7 @@ public void testMoreThanOneORParamSingleValue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(inspiretheme:xxx title:xxx any:yyy category:yyy) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(inspiretheme:xxx title:xxx any:yyy category:yyy)" + _BASE_CLAUSE, query.toString()); } /** @@ -173,7 +170,7 @@ public void testSingleORSingleValueAndANDparam() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(inspiretheme:xxx title:xxx) +any:yyy +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(inspiretheme:xxx title:xxx) +any:yyy" + _BASE_CLAUSE, query.toString()); } /** @@ -200,7 +197,7 @@ public void testSingleORSingleValueAndANDIsTemplateparam() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(inspiretheme:xxx title:xxx) +(_isTemplate:y _isTemplate:n)", query.toString()); + assertEquals("unexpected Lucene query", "+(inspiretheme:xxx title:xxx) +(_isTemplate:y _isTemplate:n)" + _BASE_DRAFT_CLAUSE, query.toString()); } /** * Tests parameters for disjunctions. They are of the form paramA_OR_paramB. @@ -221,7 +218,7 @@ public void testSingleORMultipleValue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+((+any:xxx +any:zzz) title:xxx title:zzz) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+((+any:xxx +any:zzz) title:xxx title:zzz)" + _BASE_CLAUSE, query.toString()); } @@ -244,7 +241,7 @@ public void testMultiORSingleValue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(any:xxx title:xxx inspiretheme:xxx) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(any:xxx title:xxx inspiretheme:xxx)" + _BASE_CLAUSE, query.toString()); } @@ -267,7 +264,7 @@ public void testMultiORMultipleValue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+((+any:xxx +any:zzz) title:xxx title:zzz inspiretheme:xxx zzz) +_isTemplate:n", + assertEquals("unexpected Lucene query", "+((+any:xxx +any:zzz) title:xxx title:zzz inspiretheme:xxx zzz)" + _BASE_CLAUSE, query.toString()); } @@ -290,7 +287,7 @@ public void testMultiORWithSecurityField() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(any:xxx) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(any:xxx)" + _BASE_CLAUSE, query.toString()); } /** @@ -313,7 +310,7 @@ public void testMultiORMultipleNonTokenizedValuesWithOr() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+(any:xxx any:zzz title:xxx title:zzz inspiretheme:xxx " - + "inspiretheme:zzz) +_isTemplate:n", query.toString()); + + "inspiretheme:zzz)" + _BASE_CLAUSE, query.toString()); } /** @@ -335,7 +332,7 @@ public void testSingleORSingleTokenWithUUID() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_uuid:xxx title:xxx) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(_uuid:xxx title:xxx)" + _BASE_CLAUSE, query.toString()); } /** @@ -357,7 +354,7 @@ public void testSingleORMultipleTokenWithUUID() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_uuid:xxx _uuid:yyy title:xxx title:yyy) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(_uuid:xxx _uuid:yyy title:xxx title:yyy)" + _BASE_CLAUSE, query.toString()); } @Test @@ -375,7 +372,7 @@ public void testSingleORWithBB() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query // (no BBox supported for FIELD OR: query is stripped) - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -397,7 +394,7 @@ public void testSingleORSingleValueWithALL() throws InterruptedException { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(any:xxx title:xxx) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(any:xxx title:xxx)" + _BASE_CLAUSE, query.toString()); } @@ -421,7 +418,7 @@ public void testSingleORMultiValueWithOR() { LuceneQueryBuilder luceneQueryBuilder = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null); Query query = luceneQueryBuilder.build(lQI); // verify query - assertEquals("unexpected Lucene query", "+((+any:xxx +any:yyy) title:xxx title:yyy) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+((+any:xxx +any:yyy) title:xxx title:yyy)" + _BASE_CLAUSE, query.toString()); } @@ -445,7 +442,7 @@ public void testSingleORMultiValueWithWithout() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query // (no 'without' supported for FIELD OR: query is stripped) - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -467,7 +464,7 @@ public void testSingleORWithMultipleTokenPhrase() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query // (no phrase queries supported for FIELD OR: query is stripped) - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -489,7 +486,7 @@ public void testSingleORWithTemporal() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query // (no temporal queries supported for FIELD OR: query is stripped) - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } @@ -509,7 +506,7 @@ public void testSingleTokenAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:hoeperdepoep +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:hoeperdepoep" + _BASE_CLAUSE, query.toString()); } /** @@ -531,7 +528,7 @@ public void testMultiAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+any:hoeperdepoep +any:demo) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+any:hoeperdepoep +any:demo)" + _BASE_CLAUSE, query.toString()); } @Test @@ -547,7 +544,7 @@ public void testSingleTokenQMarkWildcardAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:hoeper?poep +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:hoeper?poep" + _BASE_CLAUSE, query.toString()); } /** @@ -566,7 +563,7 @@ public void testSingleTokenWildcardAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:hoeper*poep +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:hoeper*poep" + _BASE_CLAUSE, query.toString()); } /** @@ -585,7 +582,7 @@ public void testSingleTokenWildcardWhitespace() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+any:hoeper +any:*) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+any:hoeper +any:*)" + _BASE_CLAUSE, query.toString()); } /** @@ -604,7 +601,7 @@ public void testNoTokenAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -623,7 +620,7 @@ public void testSingleTokenAnyCaseInsensitive() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:hoeperdepoep +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:hoeperdepoep" + _BASE_CLAUSE, query.toString()); } /** @@ -645,7 +642,7 @@ public void testSingleTokenAnyFuzzy() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:asjemenou~2 +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:asjemenou~2" + _BASE_CLAUSE, query.toString()); } /** @@ -664,7 +661,7 @@ public void testSingleTokenUUID() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_uuid:ad2aa2c7-f099-47cb-8a38-4effe2a2d250 +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_uuid:ad2aa2c7-f099-47cb-8a38-4effe2a2d250" + _BASE_CLAUSE, query.toString()); } /** @@ -684,7 +681,7 @@ public void testDoubleTokenUUID() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+(_uuid:63C2378A-17A7-B863-BFF4-CC3EF507D10D " + - "_uuid:ad2aa2c7-f099-47cb-8a38-4effe2a2d250) +_isTemplate:n", query.toString()); + "_uuid:ad2aa2c7-f099-47cb-8a38-4effe2a2d250)" + _BASE_CLAUSE, query.toString()); } /** @@ -703,7 +700,7 @@ public void testDoubleTokenUUIDWithNonStandardUUID() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_uuid:63C2378A-17A7-B863-BFF4-CC3EF507D10D _uuid:BAR_DEN) +_isTemplate:n", + assertEquals("unexpected Lucene query", "+(_uuid:63C2378A-17A7-B863-BFF4-CC3EF507D10D _uuid:BAR_DEN)" + _BASE_CLAUSE, query.toString()); } @@ -727,7 +724,7 @@ public void testSingleTokenAnyFuzzy1() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:asjemenou +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:asjemenou" + _BASE_CLAUSE, query.toString()); } /** @@ -746,7 +743,7 @@ public void testMultipleTokensAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+any:deze +any:die) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+any:deze +any:die)" + _BASE_CLAUSE, query.toString()); } /** @@ -768,7 +765,7 @@ public void testMultipleTokensAnyFuzzy() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+any:bloody~2 +any:hell~2) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+any:bloody~2 +any:hell~2)" + _BASE_CLAUSE, query.toString()); } /** @@ -787,7 +784,7 @@ public void testSingleTokenOr() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(any:hoeperdepoep) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(any:hoeperdepoep)" + _BASE_CLAUSE, query.toString()); } /** @@ -806,7 +803,7 @@ public void testMultipleTokenOr() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(any:hoep any:poep) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(any:hoep any:poep)" + _BASE_CLAUSE, query.toString()); } /** @@ -828,7 +825,7 @@ public void testMultipleTokenOrFuzzy() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(any:hoep~2 any:poep~2) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(any:hoep~2 any:poep~2)" + _BASE_CLAUSE, query.toString()); } /** @@ -847,7 +844,7 @@ public void testSingleTokenAll() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:hoeperdepoep +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:hoeperdepoep" + _BASE_CLAUSE, query.toString()); } /** @@ -869,7 +866,7 @@ public void testMultipleTokensAllFuzzy() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+any:bloody~2 +any:hell~2) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+any:bloody~2 +any:hell~2)" + _BASE_CLAUSE, query.toString()); } /** @@ -888,8 +885,8 @@ public void testSingleTokenWithout() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - //assertEquals("+(+MatchAllDocsQuery -any:hoeperdepoep) +_isTemplate:n", query.toString()); - assertEquals("unexpected Lucene query", "+(+*:* -any:hoeperdepoep) +_isTemplate:n", query.toString()); + //assertEquals("+(+MatchAllDocsQuery -any:hoeperdepoep)" + _BASE_TEMPLATE_CLASE, query.toString()); + assertEquals("unexpected Lucene query", "+(+*:* -any:hoeperdepoep)" + _BASE_CLAUSE, query.toString()); } /** @@ -908,7 +905,7 @@ public void testMultipleTokenWithout() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+*:* -any:hip -any:hop) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+*:* -any:hip -any:hop)" + _BASE_CLAUSE, query.toString()); } /** @@ -930,7 +927,7 @@ public void testMultipleTokenWithoutfuzzy() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+*:* -any:hip -any:hop) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+*:* -any:hip -any:hop)" + _BASE_CLAUSE, query.toString()); } /** @@ -949,7 +946,7 @@ public void testSingleTokenPhrase() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:\"humph\" +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:\"humph\"" + _BASE_CLAUSE, query.toString()); } /** @@ -968,7 +965,7 @@ public void testMultipleTokenPhrase() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:\"that one\" +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:\"that one\"" + _BASE_CLAUSE, query.toString()); } /** @@ -990,7 +987,7 @@ public void testMultipleTokenPhraseFuzzy() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:\"that one\" +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:\"that one\"" + _BASE_CLAUSE, query.toString()); } /** @@ -1009,7 +1006,7 @@ public void testSingleTopicCategory() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+topicCat:biota* +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+topicCat:biota*" + _BASE_CLAUSE, query.toString()); } /** @@ -1034,7 +1031,7 @@ public void testMultipleAndTopicCategories() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+topicCat:biota* +topicCat:boundaries +topicCat:environment*) +_isTemplate:n", + assertEquals("unexpected Lucene query", "+(+topicCat:biota* +topicCat:boundaries +topicCat:environment*)" + _BASE_CLAUSE, query.toString()); } @@ -1054,7 +1051,7 @@ public void testSingleTokenStarWildcardAtTheEndAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:hoeper* +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:hoeper*" + _BASE_CLAUSE, query.toString()); } /** @@ -1073,7 +1070,7 @@ public void testSingleTokenQMarkWildcardAtTheEndAny() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+any:hoeper? +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+any:hoeper?" + _BASE_CLAUSE, query.toString()); } @Test @@ -1090,7 +1087,7 @@ public void testMultipleOrTopicCategories() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(topicCat:biota* topicCat:boundaries topicCat:environment) +_isTemplate:n", + assertEquals("unexpected Lucene query", "+(topicCat:biota* topicCat:boundaries topicCat:environment)" + _BASE_CLAUSE, query.toString()); } @@ -1110,7 +1107,7 @@ public void testIsTemplateY() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_isTemplate:y", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:y" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -1129,7 +1126,7 @@ public void testIsTemplateS() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_isTemplate:s", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:s" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -1148,7 +1145,7 @@ public void testIsTemplateWTF() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -1164,7 +1161,7 @@ public void testIsTemplateNoValue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -1183,7 +1180,7 @@ public void testDateFrom() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+changeDate:[12-05-1989 TO *] +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+changeDate:[12-05-1989 TO *]" + _BASE_CLAUSE, query.toString()); } /** @@ -1202,7 +1199,7 @@ public void testDateTo() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+changeDate:[* TO 12-05-1989T23:59:59] +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+changeDate:[* TO 12-05-1989T23:59:59]" + _BASE_CLAUSE, query.toString()); } @@ -1225,7 +1222,7 @@ public void testDateToFrom() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+changeDate:[11-05-1989 TO 12-05-1989T23:59:59] +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+changeDate:[11-05-1989 TO 12-05-1989T23:59:59]" + _BASE_CLAUSE, query.toString()); } /** @@ -1244,7 +1241,7 @@ public void testSource() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_source:f74e4ccf-755a-48ef-bedf-990f9872298b +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_source:f74e4ccf-755a-48ef-bedf-990f9872298b" + _BASE_CLAUSE, query.toString()); } /** @@ -1263,7 +1260,7 @@ public void testTitle() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+title:humph +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+title:humph" + _BASE_CLAUSE, query.toString()); } /** @@ -1282,7 +1279,7 @@ public void testAltTitle() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+altTitle:humph +_isTemplate:n", + assertEquals("unexpected Lucene query", "+altTitle:humph" + _BASE_CLAUSE, query.toString()); } @@ -1302,7 +1299,7 @@ public void testProtocol() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+protocol:download +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+protocol:download" + _BASE_CLAUSE, query.toString()); } /** @@ -1321,7 +1318,7 @@ public void testType() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+type:dataset +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+type:dataset" + _BASE_CLAUSE, query.toString()); } /** @@ -1340,7 +1337,7 @@ public void testSingleInspire() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+inspirecat:true +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+inspirecat:true" + _BASE_CLAUSE, query.toString()); } /** @@ -1360,7 +1357,7 @@ public void testSingleInspireTheme() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+inspiretheme:Addresses +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+inspiretheme:Addresses" + _BASE_CLAUSE, query.toString()); } /** @@ -1380,7 +1377,7 @@ public void testSingleMultiTokenInspireTheme() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+inspiretheme:\"Administrative units\" +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+inspiretheme:\"Administrative units\"" + _BASE_CLAUSE, query.toString()); } /** @@ -1402,7 +1399,7 @@ public void testMultipleInspireTheme() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+inspiretheme:\"Cadastral parcels\" +inspiretheme:Hydrography*) +_isTemplate:n", + assertEquals("unexpected Lucene query", "+(+inspiretheme:\"Cadastral parcels\" +inspiretheme:Hydrography*)" + _BASE_CLAUSE, query.toString()); } @@ -1422,7 +1419,7 @@ public void testSingleTokenInspireAnnex() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+inspireannex:joostmaghetweten +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+inspireannex:joostmaghetweten" + _BASE_CLAUSE, query.toString()); } /** @@ -1441,7 +1438,7 @@ public void testSingleThemeKey() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+keyword:hoeperdepoep +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+keyword:hoeperdepoep" + _BASE_CLAUSE, query.toString()); } /** @@ -1463,7 +1460,7 @@ public void testMultipleThemeKey() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+keyword:hoeperdepoep +keyword:\"zat op de stoep\") +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+keyword:hoeperdepoep +keyword:\"zat op de stoep\")" + _BASE_CLAUSE, query.toString()); } /** @@ -1486,7 +1483,7 @@ public void testMultipleThemeKeyOrSeparated() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(keyword:\"hoeperdepoep\" keyword:\"zat op de stoep\") +_isTemplate:n", + assertEquals("unexpected Lucene query", "+(keyword:\"hoeperdepoep\" keyword:\"zat op de stoep\")" + _BASE_CLAUSE, query.toString()); } @@ -1507,7 +1504,7 @@ public void testSingleCategory() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_cat:hoeperdepoep +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_cat:hoeperdepoep" + _BASE_CLAUSE, query.toString()); } /** @@ -1526,7 +1523,7 @@ public void testParentUUID() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+parentUuid:as432f-s45hj3-vcx35s-fsd8sf +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+parentUuid:as432f-s45hj3-vcx35s-fsd8sf" + _BASE_CLAUSE, query.toString()); } /** @@ -1545,7 +1542,7 @@ public void testOperatesOn() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+operatesOn:value +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+operatesOn:value" + _BASE_CLAUSE, query.toString()); } /** @@ -1564,7 +1561,7 @@ public void testSchema() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_schema:value +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_schema:value" + _BASE_CLAUSE, query.toString()); } /** @@ -1585,7 +1582,7 @@ public void testTemporalExtent() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); - String expected = "+(tempExtentBegin:[2010-04-01T17:35:00 TO *] tempExtentEnd:[2010-04-01T17:35:00 TO *]) +_isTemplate:n"; + String expected = "+(tempExtentBegin:[2010-04-01T17:35:00 TO *] tempExtentEnd:[2010-04-01T17:35:00 TO *])" + _BASE_CLAUSE; assertEquals("unexpected Lucene query", expected, query.toString()); // test extTo @@ -1598,7 +1595,7 @@ public void testTemporalExtent() { // build lucene query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI2); - expected = "+(tempExtentBegin:[* TO 2010-04-27T17:43:00] tempExtentEnd:[* TO 2010-04-27T17:43:00]) +_isTemplate:n"; + expected = "+(tempExtentBegin:[* TO 2010-04-27T17:43:00] tempExtentEnd:[* TO 2010-04-27T17:43:00])" + _BASE_CLAUSE; assertEquals("unexpected Lucene query", expected, query.toString()); // test extfrom and extTo @@ -1618,7 +1615,7 @@ public void testTemporalExtent() { expected = "+(tempExtentBegin:[2010-04-08T17:46:00 TO 2010-04-27T17:43:00] tempExtentEnd:[2010-04-08T17:46:00 TO " + "2010-04-27T17:43:00] (+tempExtentEnd:[2010-04-27T17:43:00 TO *] +tempExtentBegin:[* TO 2010-04-08T17:46:00])) " + - "+_isTemplate:n"; + "+_isTemplate:n" + _BASE_DRAFT_CLAUSE; assertEquals("unexpected Lucene query", expected, query.toString()); // create request object @@ -1658,7 +1655,7 @@ public void testMultipleCategory() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+_cat:hoeperdepoep +_cat:\"zat op de stoep\") +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+_cat:hoeperdepoep +_cat:\"zat op de stoep\")" + _BASE_CLAUSE, query.toString()); } @Test @@ -1674,7 +1671,7 @@ public void testMultipleOrCategory() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_cat:hoeperdepoep _cat:\"zat op de stoep\") +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(_cat:hoeperdepoep _cat:\"zat op de stoep\")" + _BASE_CLAUSE, query.toString()); } @@ -1694,7 +1691,7 @@ public void testEditableTrue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } /** @@ -1713,7 +1710,7 @@ public void testFeaturedTrue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(+_op6:1 +_op0:1) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(+_op6:1 +_op0:1)" + _BASE_CLAUSE, query.toString()); } /** @@ -1732,7 +1729,7 @@ public void testFeaturedNotTrue() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } @@ -1752,7 +1749,7 @@ public void testSingleGroup() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee)" + _BASE_CLAUSE, query.toString()); } /** @@ -1774,7 +1771,7 @@ public void testMultiGroup() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe) +_isTemplate:n", + assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe)" + _BASE_CLAUSE, query.toString()); } @@ -1800,7 +1797,7 @@ public void testMultiGroupReviewer() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe) +_isTemplate:n", + assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe)" + _BASE_CLAUSE, query.toString()); } @@ -1826,7 +1823,7 @@ public void testMultiGroupOwner() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe _owner:yeah!) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe _owner:yeah!)" + _BASE_CLAUSE, query.toString()); } /** @@ -1851,7 +1848,7 @@ public void testMultiGroupAdmin() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe _dummy:0) +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+(_op0:hatsjekidee _op0:nou moe _dummy:0)" + _BASE_CLAUSE, query.toString()); } /** @@ -1883,7 +1880,7 @@ public void testBBEquals() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+eastBL:[55.0 TO 55.0] +westBL:[43.0 TO 43.0] +southBL:[9.0 TO 9.0] +northBL:[12.0 TO " + - "12.0] +_isTemplate:n", query.toString()); + "12.0]" + _BASE_CLAUSE, query.toString()); } /** @@ -1915,7 +1912,7 @@ public void testBBOverlaps() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+westBL:[-180.0 TO 55.0] +eastBL:[43.0 TO 180.0] +northBL:[9.0 TO 90.0] +southBL:[-90" + - ".0 TO 12.0] +_isTemplate:n", query.toString()); + ".0 TO 12.0]" + _BASE_CLAUSE, query.toString()); } /** @@ -1947,7 +1944,7 @@ public void testBBEncloses() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+eastBL:[55.0 TO 180.0] +westBL:[-180.0 TO 43.0] +southBL:[-90.0 TO 9.0] +northBL:[12" + - ".0 TO 90.0] +_isTemplate:n", query.toString()); + ".0 TO 90.0]" + _BASE_CLAUSE, query.toString()); } /** @@ -1979,7 +1976,7 @@ public void testBBFullyEnclosedWithin() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+eastBL:[43.0 TO 55.0] +westBL:[43.0 TO 55.0] +southBL:[9.0 TO 12.0] +northBL:[9.0 TO " + - "12.0] +_isTemplate:n", query.toString()); + "12.0]" + _BASE_CLAUSE, query.toString()); } /** @@ -2011,7 +2008,7 @@ public void testBBFullyOutsideOf() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "westBL:[55.0 TO 180.0] eastBL:[-180.0 TO 43.0] northBL:[-90.0 TO 0.0] southBL:[30.0 TO" + - " 90.0] +_isTemplate:n", query.toString()); + " 90.0]" + _BASE_CLAUSE, query.toString()); } /** @@ -2043,7 +2040,7 @@ public void testBBOverlapsStandard() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+westBL:[-180.0 TO 180.0] +eastBL:[-180.0 TO 180.0] +northBL:[-90.0 TO 90.0] " + - "+southBL:[-90.0 TO 90.0] +_isTemplate:n", query.toString()); + "+southBL:[-90.0 TO 90.0]" + _BASE_CLAUSE, query.toString()); } /** @@ -2113,7 +2110,7 @@ public void testRandomTest1() { assertEquals("unexpected Lucene query", "+(_op0:0 _op0:1) +title:hoi " + "+westBL:[-180.0 TO 180.0] +eastBL:[-180.0 TO 180.0] " + "+northBL:[-90.0 TO 90.0] +southBL:[-90.0 TO 90.0] " - + "+_isTemplate:n", + + "+_isTemplate:n" + _BASE_DRAFT_CLAUSE, query.toString()); } @@ -2224,7 +2221,7 @@ public void testPopularGet() { Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query assertEquals("unexpected Lucene query", "+westBL:[-180.0 TO 180.0] +eastBL:[-180.0 TO 180.0] +northBL:[-90.0 TO 90.0] " - + "+southBL:[-90.0 TO 90.0] +_isTemplate:n", query.toString()); + + "+southBL:[-90.0 TO 90.0]" + _BASE_CLAUSE, query.toString()); } @@ -2244,7 +2241,7 @@ public void testDynamic() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+dynamic:true +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+dynamic:true" + _BASE_CLAUSE, query.toString()); } /** @@ -2263,7 +2260,7 @@ public void testDownload() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+download:true +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+download:true" + _BASE_CLAUSE, query.toString()); } @@ -2284,7 +2281,7 @@ public void testDigitalAndPaper() { // build lucene query Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); // verify query - assertEquals("unexpected Lucene query", "+digital:true +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+digital:true" + _BASE_CLAUSE, query.toString()); // create request object with with digital=off, paper=on @@ -2297,7 +2294,7 @@ public void testDigitalAndPaper() { // build lucene query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQIa); // verify query - assertEquals("unexpected Lucene query", "+paper:true +_isTemplate:n", query.toString()); + assertEquals("unexpected Lucene query", "+paper:true" + _BASE_CLAUSE, query.toString()); } /** @@ -2366,7 +2363,7 @@ public void testWithoutOperationParameter() { Element request = factory.element("request"); LuceneQueryInput lQI = new LuceneQueryInput(request); Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); - assertTrue(query.toString().equals("+_isTemplate:n")); + assertTrue(query.toString().equals("+_isTemplate:n" + _BASE_DRAFT_CLAUSE)); } /** @@ -2377,7 +2374,7 @@ public void testDrilldownQuery() { Element request = buildSingleDrilldownQuery("keyword/ocean/salinity"); LuceneQueryInput lQI = new LuceneQueryInput(request); Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); - assertEquals("unexpected Lucene query", "+(+_isTemplate:n) +ConstantScore($facets:keywordoceansalinity)^0.0", query.toString()); + assertEquals("unexpected Lucene query", "+(+_isTemplate:n +(_draft:n _draft:e)) +ConstantScore($facets:keywordoceansalinity)^0.0", query.toString()); } /** @@ -2388,7 +2385,7 @@ public void testEncodedDrilldownQuery() { Element request = buildSingleDrilldownQuery("keyword/oceans%2Frivers/salinity"); LuceneQueryInput lQI = new LuceneQueryInput(request); Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); - assertEquals("unexpected Lucene query", "+(+_isTemplate:n) +ConstantScore($facets:keywordoceans/riverssalinity)^0.0", query.toString()); + assertEquals("unexpected Lucene query", "+(+_isTemplate:n +(_draft:n _draft:e)) +ConstantScore($facets:keywordoceans/riverssalinity)^0.0", query.toString()); } /** @@ -2404,7 +2401,7 @@ public void testMultipleDrilldownQueryUsingAnd() { ); LuceneQueryInput lQI = new LuceneQueryInput(request); Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); - assertEquals("unexpected Lucene query", "+(+_isTemplate:n) +ConstantScore($facets:keywordoceansalinity $facets:keywordoceanchemistry $facets:keywordoceantemperature)^0.0", query.toString()); + assertEquals("unexpected Lucene query", "+(+_isTemplate:n +(_draft:n _draft:e)) +ConstantScore($facets:keywordoceansalinity $facets:keywordoceanchemistry $facets:keywordoceantemperature)^0.0", query.toString()); } /** @@ -2420,7 +2417,7 @@ public void testMultipleDrilldownUsingFacetParameters() { ); LuceneQueryInput lQI = new LuceneQueryInput(request); Query query = new LuceneQueryBuilder(luceneConfig, _tokenizedFieldSet, _analyzer, null).build(lQI); - assertEquals("unexpected Lucene query", "+(+_isTemplate:n) +ConstantScore($facets:keywordoceansalinity $facets:keywordoceanchemistry $facets:keywordoceantemperature)^0.0", query.toString()); + assertEquals("unexpected Lucene query", "+(+_isTemplate:n +(_draft:n _draft:e)) +ConstantScore($facets:keywordoceansalinity $facets:keywordoceanchemistry $facets:keywordoceantemperature)^0.0", query.toString()); } private Element buildSingleDrilldownQuery(String drilldownPath) { diff --git a/core/src/test/resources/WEB-INF/config-lucene.xml b/core/src/test/resources/WEB-INF/config-lucene.xml index b81f4ece64c..90d10ae166e 100644 --- a/core/src/test/resources/WEB-INF/config-lucene.xml +++ b/core/src/test/resources/WEB-INF/config-lucene.xml @@ -151,6 +151,7 @@ + diff --git a/core/src/test/resources/draft-test-context.xml b/core/src/test/resources/draft-test-context.xml new file mode 100644 index 00000000000..ef9e88e08ac --- /dev/null +++ b/core/src/test/resources/draft-test-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/docs/manuals b/docs/manuals index f565d329e71..6f072c42083 160000 --- a/docs/manuals +++ b/docs/manuals @@ -1 +1 @@ -Subproject commit f565d329e71eca222a0c2a1cfef24b761a0d49ec +Subproject commit 6f072c42083837d88d6950dae5f629838b6f720c diff --git a/domain/src/main/java/org/fao/geonet/domain/AbstractMetadata.java b/domain/src/main/java/org/fao/geonet/domain/AbstractMetadata.java index 15500a8d42d..daa3ca233ee 100644 --- a/domain/src/main/java/org/fao/geonet/domain/AbstractMetadata.java +++ b/domain/src/main/java/org/fao/geonet/domain/AbstractMetadata.java @@ -23,6 +23,9 @@ package org.fao.geonet.domain; import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -64,7 +67,6 @@ @MappedSuperclass public abstract class AbstractMetadata extends GeonetEntity { static final String ID_SEQ_NAME = "metadata_id_seq"; - public static final String METADATA_CATEG_JOIN_TABLE_NAME = "MetadataCateg"; public static final String METADATA_CATEG_JOIN_TABLE_CATEGORY_ID = "categoryId"; private int _id; private String _uuid; @@ -80,7 +82,7 @@ public abstract class AbstractMetadata extends GeonetEntity { * @return the id of the metadata */ @Id - @SequenceGenerator(name = Metadata.ID_SEQ_NAME, initialValue = 100, allocationSize = 1) + @SequenceGenerator(name=AbstractMetadata.ID_SEQ_NAME, initialValue = 100, allocationSize = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = ID_SEQ_NAME) @Column(nullable = false) public int getId() { @@ -309,8 +311,59 @@ protected static void transform(Document in, AbstractMetadata out) { } @Transient - public abstract Set getMetadataCategories(); - - public abstract void setMetadataCategories(@Nonnull Set categories); - + public Set getCategories() { + return (Set) metadataCategories; + } + @Transient + protected Set metadataCategories = new HashSet(); + + @Override + public String toString() { + final int maxLen = 3; + return this.getClass().getSimpleName() + " [_id=" + _id + ", " + (_uuid != null ? "_uuid=" + _uuid + ", " : "") + // + (_data != null ? "_data=" + _data + ", " : "") + + (_dataInfo != null ? "_dataInfo=" + _dataInfo + ", " : "") + + (_sourceInfo != null ? "_sourceInfo=" + _sourceInfo + ", " : "") + + (_harvestInfo != null ? "_harvestInfo=" + _harvestInfo + ", " : "") + + (metadataCategories != null ? "metadataCategories=" + toString(metadataCategories, maxLen) : "") + + "]"; + } + + private String toString(Collection collection, int maxLen) { + StringBuilder builder = new StringBuilder(); + builder.append("["); + int i = 0; + for (Iterator iterator = collection.iterator(); iterator.hasNext() && i < maxLen; i++) { + if (i > 0) + builder.append(", "); + builder.append(iterator.next()); + } + builder.append("]"); + return builder.toString(); + } + + @Override + @Transient + public AbstractMetadata clone() { + AbstractMetadata clone = new TransientMetadata(); + + //clone.setData(this.getData()); + clone.setId(this.getId()); + clone.setUuid(this.getUuid()); + if(this.getDataInfo() != null) { + clone.setDataInfo(this.getDataInfo().clone()); + } + if (this.getHarvestInfo() != null) { + clone.setHarvestInfo(this.getHarvestInfo().clone()); + } + if(this.getSourceInfo() != null) { + clone.setSourceInfo(this.getSourceInfo().clone()); + } + + return clone; + } + + private class TransientMetadata extends AbstractMetadata { + + } } diff --git a/domain/src/main/java/org/fao/geonet/domain/Metadata.java b/domain/src/main/java/org/fao/geonet/domain/Metadata.java index 62d39db7a12..22147dc09e1 100644 --- a/domain/src/main/java/org/fao/geonet/domain/Metadata.java +++ b/domain/src/main/java/org/fao/geonet/domain/Metadata.java @@ -23,14 +23,15 @@ package org.fao.geonet.domain; -import org.apache.lucene.document.Document; -import org.fao.geonet.domain.userfeedback.UserFeedback; -import org.fao.geonet.entitylistener.MetadataEntityListenerManager; +import java.io.Serializable; +import java.util.List; +import java.util.Set; import javax.annotation.Nonnull; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.CascadeType; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.FetchType; @@ -39,10 +40,10 @@ import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; -import java.io.Serializable; -import java.util.HashSet; -import java.util.List; -import java.util.Set; + +import org.apache.lucene.document.Document; +import org.fao.geonet.domain.userfeedback.UserFeedback; +import org.fao.geonet.entitylistener.MetadataEntityListenerManager; /** * @See {@link AbstractMetadata} @@ -56,11 +57,10 @@ public class Metadata extends AbstractMetadata implements Serializable { private static final long serialVersionUID = -5557599895424227101L; public static final String TABLENAME = "Metadata"; - private Set metadataCategories = new HashSet(); + public static final String METADATA_CATEG_JOIN_TABLE_NAME = "MetadataCateg"; private List userFeedbacks; public Metadata() { - super(); } public static Metadata createFromLuceneIndexDocument(Document doc) { @@ -75,8 +75,12 @@ public static Metadata createFromLuceneIndexDocument(Document doc) { * * @return the metadata categories */ - @ManyToMany(cascade = { CascadeType.DETACH, CascadeType.REFRESH }, fetch = FetchType.EAGER) - @JoinTable(name = METADATA_CATEG_JOIN_TABLE_NAME, joinColumns = @JoinColumn(name = "metadataId"), inverseJoinColumns = @JoinColumn(name = METADATA_CATEG_JOIN_TABLE_CATEGORY_ID)) + @ManyToMany(cascade = {CascadeType.DETACH, CascadeType.REFRESH}, + fetch = FetchType.EAGER) + @JoinTable(name = METADATA_CATEG_JOIN_TABLE_NAME, + joinColumns = @JoinColumn(name = "metadataId"), + inverseJoinColumns = @JoinColumn(name = + METADATA_CATEG_JOIN_TABLE_CATEGORY_ID)) @Nonnull public Set getMetadataCategories() { return metadataCategories; @@ -87,7 +91,7 @@ public Set getMetadataCategories() { * * @param categories */ - public void setMetadataCategories(@Nonnull Set categories) { + protected void setMetadataCategories(@Nonnull Set categories) { this.metadataCategories = categories; } diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataDataInfo.java b/domain/src/main/java/org/fao/geonet/domain/MetadataDataInfo.java index e349fc7c285..157c7868a00 100644 --- a/domain/src/main/java/org/fao/geonet/domain/MetadataDataInfo.java +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataDataInfo.java @@ -385,4 +385,37 @@ public boolean equals(Object obj) { return false; return true; } + + @Override + public String toString() { + return "MetadataDataInfo [" + (_title != null ? "_title=" + _title + ", " : "") + + (_changeDate != null ? "_changeDate=" + _changeDate + ", " : "") + + (_createDate != null ? "_createDate=" + _createDate + ", " : "") + + (_schemaId != null ? "_schemaId=" + _schemaId + ", " : "") + "_template=" + _template + ", " + + (_root != null ? "_root=" + _root + ", " : "") + + (_doctype != null ? "_doctype=" + _doctype + ", " : "") + + (_extra != null ? "_extra=" + _extra + ", " : "") + + (_displayOrder != null ? "_displayOrder=" + _displayOrder + ", " : "") + "_rating=" + _rating + + ", _popularity=" + _popularity + "]"; + } + + public MetadataDataInfo clone() { + MetadataDataInfo clon = new MetadataDataInfo(); + if (this.getChangeDate() != null) { + clon.setChangeDate(this.getChangeDate().clone()); + } + if (this.getCreateDate() != null) { + clon.setCreateDate(this.getCreateDate().clone()); + } + clon.setDisplayOrder(this.getDisplayOrder()); + clon.setDoctype(this.getDoctype()); + clon.setExtra(this.getExtra()); + clon.setPopularity(this.getPopularity()); + clon.setRating(this.getRating()); + clon.setRoot(this.getRoot()); + clon.setSchemaId(this.getSchemaId()); + clon.setType(this.getType()); + + return clon; + } } diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataDraft.java b/domain/src/main/java/org/fao/geonet/domain/MetadataDraft.java new file mode 100644 index 00000000000..257f2959418 --- /dev/null +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataDraft.java @@ -0,0 +1,79 @@ +/* + * 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.domain; + +import java.io.Serializable; + +import javax.annotation.Nonnull; +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.apache.lucene.document.Document; +import org.fao.geonet.entitylistener.MetadataDraftEntityListenerManager; + +/** + * This is a normal {@link Metadata} but on its draft version. + *

      + * Privileges, categories and non-XML properties will not be stored. + * + * @author María Arias de Reyna + */ +@Entity +@Table(name = MetadataDraft.TABLENAME) +@Access(AccessType.PROPERTY) +@EntityListeners(MetadataDraftEntityListenerManager.class) +public class MetadataDraft extends AbstractMetadata implements Serializable { + + private static final long serialVersionUID = -1933627969445820867L; + public static final String TABLENAME = "MetadataDraft"; + + public MetadataDraft() { + } + + public static MetadataDraft createFromLuceneIndexDocument(Document doc) { + MetadataDraft metadata = new MetadataDraft(); + transform(doc, metadata); + return metadata; + } + + public Metadata approvedVersion; + + @Nonnull + @JoinColumn(nullable=false, updatable=false, unique=true, insertable=true) + @OneToOne(fetch=FetchType.EAGER, optional=false, orphanRemoval=false) + public Metadata getApprovedVersion() { + return approvedVersion; + } + + public void setApprovedVersion(Metadata approvedVersion) { + this.approvedVersion = approvedVersion; + } + +} diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataHarvestInfo.java b/domain/src/main/java/org/fao/geonet/domain/MetadataHarvestInfo.java index 3a7c181c74c..5b724664c69 100644 --- a/domain/src/main/java/org/fao/geonet/domain/MetadataHarvestInfo.java +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataHarvestInfo.java @@ -160,4 +160,21 @@ public boolean equals(Object obj) { return false; return true; } + + @Override + public String toString() { + return "MetadataHarvestInfo [_harvested=" + _harvested + ", " + (_uuid != null ? "_uuid=" + _uuid + ", " : "") + + (_uri != null ? "_uri=" + _uri : "") + "]"; + } + + @Override + protected MetadataHarvestInfo clone() { + MetadataHarvestInfo clon = new MetadataHarvestInfo(); + + clon.setHarvested(this.isHarvested()); + clon.setUri(this.getUri()); + clon.setUuid(this.getUuid()); + + return clon; + } } diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataRelation.java b/domain/src/main/java/org/fao/geonet/domain/MetadataRelation.java index b18ec420079..8c453e96770 100644 --- a/domain/src/main/java/org/fao/geonet/domain/MetadataRelation.java +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataRelation.java @@ -30,10 +30,10 @@ /** * Tables that links related metadata.

      Object is its own entity so that it is easier to add * relations without having to load the related metadata.

      Note: It is important that both - * Metadata are managed (have been saved or loaded from the database.) For example: + * Metadata are managed (have been saved or loaded from the MetadataRepository.) For example: *

      
      - *      Metadata metadata1 = _metadataUtils.findOne(id);
      - *      Metadata metadata2 = _metadataUtils.findOne(id2);
      + *      Metadata metadata1 = _metadataRepo.findOne(id);
      + *      Metadata metadata2 = _metadataRepo.findOne(id2);
        *      new MetadataRelation(metadata1, metadata2);
        *     
      *

      diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataSourceInfo.java b/domain/src/main/java/org/fao/geonet/domain/MetadataSourceInfo.java index 542441e9786..7ac24615c1b 100644 --- a/domain/src/main/java/org/fao/geonet/domain/MetadataSourceInfo.java +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataSourceInfo.java @@ -132,4 +132,22 @@ public int hashCode() { result = 31 * result + (_owner != null ? _owner.hashCode() : 0); return result; } + + @Override + public String toString() { + return "MetadataSourceInfo [" + (_sourceId != null ? "_sourceId=" + _sourceId + ", " : "") + + (_groupOwner != null ? "_groupOwner=" + _groupOwner + ", " : "") + + (_owner != null ? "_owner=" + _owner : "") + "]"; + } + + @Override + protected MetadataSourceInfo clone() { + MetadataSourceInfo clon = new MetadataSourceInfo(); + + clon.setGroupOwner(this.getGroupOwner()); + clon.setOwner(this.getOwner()); + clon.setSourceId(this.getSourceId()); + + return clon; + } } diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java b/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java index 0e46e85a8e0..66c5cd2df9d 100644 --- a/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java @@ -251,4 +251,15 @@ public Element getAsXml() { public static class EntityListener extends AbstractEntityListenerManager { } + + @Override + public String toString() { + return "MetadataStatus [" + (id != null ? "id=" + id + ", " : "") + + (changeMessage != null ? "changeMessage=" + changeMessage + ", " : "") + + (statusValue != null ? "statusValue=" + statusValue + ", " : "") + "_owner=" + _owner + ", " + + (_duedate != null ? "_duedate=" + _duedate + ", " : "") + + (_closedate != null ? "_closedate=" + _closedate + ", " : "") + + (previousState != null ? "previousState=" + previousState + ", " : "") + + (currentState != null ? "currentState=" + currentState : "") + "]"; + } } diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataStatusId.java b/domain/src/main/java/org/fao/geonet/domain/MetadataStatusId.java index b5dcf4986b5..070ae74aace 100644 --- a/domain/src/main/java/org/fao/geonet/domain/MetadataStatusId.java +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataStatusId.java @@ -154,4 +154,10 @@ public boolean equals(Object obj) { return false; return true; } + + @Override + public String toString() { + return "MetadataStatusId [" + (_changedate != null ? "_changedate=" + _changedate + ", " : "") + "_metadataId=" + + _metadataId + ", _statusId=" + _statusId + ", _userId=" + _userId + "]"; + } } diff --git a/domain/src/main/java/org/fao/geonet/domain/SchematronCriteriaType.java b/domain/src/main/java/org/fao/geonet/domain/SchematronCriteriaType.java index c49fd7d8afb..1bb3ef16d55 100644 --- a/domain/src/main/java/org/fao/geonet/domain/SchematronCriteriaType.java +++ b/domain/src/main/java/org/fao/geonet/domain/SchematronCriteriaType.java @@ -26,7 +26,10 @@ */ package org.fao.geonet.domain; -import org.fao.geonet.ApplicationContextHolder; +import java.util.Arrays; +import java.util.List; + +import org.fao.geonet.repository.MetadataDraftRepository; import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.UserRepository; import org.fao.geonet.repository.specification.MetadataSpecs; @@ -39,9 +42,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; -import java.util.Arrays; -import java.util.List; - /** * Used on {@link SchematronCriteria} * @@ -63,10 +63,14 @@ public boolean accepts(ApplicationContext applicationContext, String value, int for (int i = 0; i < values.length; i++) { ids[i] = Integer.valueOf(values[i]); } - final Specification correctOwner = MetadataSpecs.isOwnedByOneOfFollowingGroups(Arrays.asList(ids)); - final Specification correctId = MetadataSpecs.hasMetadataId(metadataId); + final Specification correctOwnerDraft = (Specification)MetadataSpecs.isOwnedByOneOfFollowingGroups(Arrays.asList(ids)); + final Specification correctIdDraft = (Specification)MetadataSpecs.hasMetadataId(metadataId); + final Specifications finalSpecDraft = Specifications.where(correctIdDraft).and(correctOwnerDraft); + final Specification correctOwner = (Specification)MetadataSpecs.isOwnedByOneOfFollowingGroups(Arrays.asList(ids)); + final Specification correctId = (Specification)MetadataSpecs.hasMetadataId(metadataId); final Specifications finalSpec = Specifications.where(correctId).and(correctOwner); - return applicationContext.getBean(MetadataRepository.class).count(finalSpec) > 0; + return applicationContext.getBean(MetadataRepository.class).count(finalSpec) + + applicationContext.getBean(MetadataDraftRepository.class).count(finalSpecDraft) > 0; } }), diff --git a/domain/src/main/java/org/fao/geonet/entitylistener/MetadataDraftEntityListenerManager.java b/domain/src/main/java/org/fao/geonet/entitylistener/MetadataDraftEntityListenerManager.java new file mode 100644 index 00000000000..71f86eea44f --- /dev/null +++ b/domain/src/main/java/org/fao/geonet/entitylistener/MetadataDraftEntityListenerManager.java @@ -0,0 +1,42 @@ +package org.fao.geonet.entitylistener; + +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PostRemove; +import javax.persistence.PostUpdate; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; + +import org.fao.geonet.domain.MetadataDraft; + +public class MetadataDraftEntityListenerManager extends AbstractEntityListenerManager { + @PrePersist + public void prePresist(final MetadataDraft entity) { + handleEvent(PersistentEventType.PrePersist, entity); + } + @PreRemove + public void preRemove(final MetadataDraft entity) { + handleEvent(PersistentEventType.PreRemove, entity); + } + @PostPersist + public void postPersist(final MetadataDraft entity) { + handleEvent(PersistentEventType.PostPersist, entity); + } + @PostRemove + public void postRemove(final MetadataDraft entity) { + handleEvent(PersistentEventType.PostRemove, entity); + } + @PreUpdate + public void preUpdate(final MetadataDraft entity) { + handleEvent(PersistentEventType.PreUpdate, entity); + } + @PostUpdate + public void postUpdate(final MetadataDraft entity) { + handleEvent(PersistentEventType.PostUpdate, entity); + } + @PostLoad + public void postLoad(final MetadataDraft entity) { + handleEvent(PersistentEventType.PostLoad, entity); + } +} diff --git a/domain/src/main/java/org/fao/geonet/repository/HarvestHistoryRepositoryCustom.java b/domain/src/main/java/org/fao/geonet/repository/HarvestHistoryRepositoryCustom.java index 83e94707c9a..62e4df76b32 100644 --- a/domain/src/main/java/org/fao/geonet/repository/HarvestHistoryRepositoryCustom.java +++ b/domain/src/main/java/org/fao/geonet/repository/HarvestHistoryRepositoryCustom.java @@ -23,10 +23,12 @@ package org.fao.geonet.repository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nonnull; - import java.util.Collection; /** @@ -42,6 +44,9 @@ public interface HarvestHistoryRepositoryCustom { * @param ids the ids of the history elements to delete * @return number or entities deleted */ + @Modifying(clearAutomatically = true) + @Transactional + @Query(value = "DELETE FROM HarvestHistory h where h.id in (?1)") int deleteAllById(Collection ids); /** @@ -50,5 +55,8 @@ public interface HarvestHistoryRepositoryCustom { * @param harvesterUuid the harvester uuid. * @return number or entities modified */ + @Transactional + @Query(value = "UPDATE HarvestHistory SET deleted_JpaWorkaround = 'y'" + "WHERE harvesterUuid in (:uuid)") + @Modifying(clearAutomatically = true) int markAllAsDeleted(@Param("uuid") @Nonnull String harvesterUuid); } diff --git a/domain/src/main/java/org/fao/geonet/repository/HarvestHistoryRepositoryImpl.java b/domain/src/main/java/org/fao/geonet/repository/HarvestHistoryRepositoryImpl.java deleted file mode 100644 index 7f9f26e903e..00000000000 --- a/domain/src/main/java/org/fao/geonet/repository/HarvestHistoryRepositoryImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.repository; - -import org.fao.geonet.domain.Constants; -import org.fao.geonet.domain.HarvestHistory; -import org.fao.geonet.domain.HarvestHistory_; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaDelete; -import javax.persistence.criteria.CriteriaUpdate; -import javax.persistence.criteria.Root; - -import java.util.Collection; - -/** - * Implementation for custom methods for the HarvestHistoryRepository class. - *

      - * User: Jesse Date: 9/20/13 Time: 4:03 PM - */ -public class HarvestHistoryRepositoryImpl implements HarvestHistoryRepositoryCustom { - - @PersistenceContext - EntityManager _entityManager; - - @Override - @Transactional - public int deleteAllById(Collection ids) { - final CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); - CriteriaDelete delete = cb.createCriteriaDelete(HarvestHistory.class); - final Root root = delete.from(HarvestHistory.class); - - delete.where(root.get(HarvestHistory_.id).in(ids)); - - final int deleted = _entityManager.createQuery(delete).executeUpdate(); - - _entityManager.flush(); - _entityManager.clear(); - - return deleted; - } - - @Override - @Transactional - public int markAllAsDeleted(@Nonnull String harvesterUuid) { - final CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); - final CriteriaUpdate update = cb.createCriteriaUpdate(HarvestHistory.class); - final Root root = update.from(HarvestHistory.class); - - update.set(root.get(HarvestHistory_.deleted_JpaWorkaround), Constants.YN_TRUE); - update.where(cb.equal(root.get(HarvestHistory_.harvesterUuid), harvesterUuid)); - - int updated = _entityManager.createQuery(update).executeUpdate(); - _entityManager.flush(); - _entityManager.clear(); - - return updated; - - } -} diff --git a/domain/src/main/java/org/fao/geonet/repository/HarvesterSettingRepositoryOverridesImpl.java b/domain/src/main/java/org/fao/geonet/repository/HarvesterSettingRepositoryOverridesImpl.java index 06c4c9f9057..08b9f54b80a 100644 --- a/domain/src/main/java/org/fao/geonet/repository/HarvesterSettingRepositoryOverridesImpl.java +++ b/domain/src/main/java/org/fao/geonet/repository/HarvesterSettingRepositoryOverridesImpl.java @@ -42,6 +42,7 @@ import org.apache.commons.collections.CollectionUtils; import org.fao.geonet.domain.HarvesterSetting; import org.fao.geonet.domain.HarvesterSetting_; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.transaction.annotation.Transactional; import com.google.common.collect.Lists; @@ -90,6 +91,7 @@ public void delete(final HarvesterSetting setting) { * @param settingId the id of the entity to delete */ @Transactional + @Modifying(clearAutomatically=true) public void delete(final int settingId) { final List toRemove = Lists.newArrayList(settingId); int i = 0; @@ -106,9 +108,6 @@ public void delete(final int settingId) { delete.where(root.get(HarvesterSetting_.id).in(toRemove)); _entityManager.createQuery(delete).executeUpdate(); - - _entityManager.flush(); - _entityManager.clear(); } diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataDraftRepository.java b/domain/src/main/java/org/fao/geonet/repository/MetadataDraftRepository.java new file mode 100644 index 00000000000..7cdbeddec75 --- /dev/null +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataDraftRepository.java @@ -0,0 +1,71 @@ +/* + * 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.repository; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.fao.geonet.domain.MetadataDraft; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** + * Data Access object for the {@link MetadataDraft} entities. + * + * The use of this class is discouraged, you should use IMetadataUtils or IMetadataManager instead. + * + * @author Jesse + */ +public interface MetadataDraftRepository + extends GeonetRepository, + MetadataRepositoryCustom, + JpaSpecificationExecutor { + /** + * Find one metadata by the metadata's uuid. + * + * @param uuid the uuid of the metadata to find + * @return one metadata or null. + */ + @Nullable + MetadataDraft findOneByUuid(@Nonnull String uuid); + + /** + * Find all metadata by the metadata's uuid. + * + * @param uuid the uuid of the metadata to find + * @return one metadata or null. + */ + @Nullable + List findAllByUuid(@Nonnull String uuid); + + /** + * Find all metadata harvested by the identified harvester. + * + * @param uuid the uuid of the harvester + * @return all metadata harvested by the identified harvester. + */ + @Nonnull + List findAllByHarvestInfo_Uuid(@Nonnull String uuid); +} diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataDraftRepositoryImpl.java b/domain/src/main/java/org/fao/geonet/repository/MetadataDraftRepositoryImpl.java new file mode 100644 index 00000000000..65ef1024e11 --- /dev/null +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataDraftRepositoryImpl.java @@ -0,0 +1,257 @@ +/* + * 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.repository; + +import com.google.common.collect.Maps; + +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.domain.MetadataDraft_; +import org.fao.geonet.domain.MetadataDataInfo_; +import org.fao.geonet.domain.MetadataSourceInfo; +import org.fao.geonet.domain.Pair; +import org.fao.geonet.repository.reports.MetadataReportsQueries; +import org.jdom.Element; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.repository.NoRepositoryBean; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.persistence.Tuple; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Root; + +/** + * Implementation for all {@link Metadata} queries that cannot be automatically generated by + * Spring-data. + * + * @author Jesse + */ +@NoRepositoryBean +public class MetadataDraftRepositoryImpl implements MetadataRepositoryCustom { + + @PersistenceContext + EntityManager _entityManager; + + @Override + public MetadataReportsQueries getMetadataReports() { + return new MetadataReportsQueries(_entityManager); + } + + @Override + public MetadataDraft findOne(String id) { + try { + return _entityManager.find(MetadataDraft.class, Integer.parseInt(id)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("id parameter of findByIdString must be parsable to an integer. It was '" + id + "'"); + } + } + + @Override + public + @Nonnull + Page> findAllIdsAndChangeDates(@Nullable Pageable pageable) { + CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); + CriteriaQuery cbQuery = cb.createQuery(Tuple.class); + Root root = cbQuery.from(MetadataDraft.class); + + cbQuery.multiselect(cb.count(root)); + Long total = (Long) _entityManager.createQuery(cbQuery).getSingleResult().get(0); + cbQuery.multiselect(root.get(MetadataDraft_.id), root.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.changeDate)); + + if (pageable != null && pageable.getSort() != null) { + final Sort sort = pageable.getSort(); + List orders = SortUtils.sortToJpaOrders(cb, sort, root); + + cbQuery.orderBy(orders); + } + + TypedQuery query = _entityManager.createQuery(cbQuery); + if (pageable != null) { + query.setFirstResult(pageable.getOffset()); + query.setMaxResults(pageable.getPageSize()); + } + + ArrayList> finalResults = new ArrayList>(); + for (Tuple tuple : query.getResultList()) { + final Integer mdId = (Integer) tuple.get(0); + final ISODate changeDate = (ISODate) tuple.get(1); + finalResults.add(Pair.read(mdId, changeDate)); + } + return new PageImpl>(finalResults, pageable, total); + } + + @Nonnull + @Override + public List findAllIdsBy(@Nonnull Specification spec) { + CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); + CriteriaQuery cbQuery = cb.createQuery(Integer.class); + Root root = cbQuery.from(MetadataDraft.class); + cbQuery.select(root.get(MetadataDraft_.id)); + + cbQuery.where(spec.toPredicate(root, cbQuery, cb)); + return _entityManager.createQuery(cbQuery).getResultList(); + } + + + @Override + public MetadataDraft findOneOldestByChangeDate() { + final CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); + final CriteriaQuery query = cb.createQuery(MetadataDraft.class); + final Root metadataRoot = query.from(MetadataDraft.class); + final Path changeDate = metadataRoot.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.changeDate); + query.orderBy(cb.asc(changeDate)); + return _entityManager.createQuery(query).setMaxResults(1).getSingleResult(); + } + + @Override + public Map findAllSourceInfo(Specification spec) { + CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); + CriteriaQuery cbQuery = cb.createQuery(Object[].class); + Root root = cbQuery.from(MetadataDraft.class); + cbQuery.select(cb.array(root.get(MetadataDraft_.id), root.get(MetadataDraft_.sourceInfo))); + + cbQuery.where(spec.toPredicate(root, cbQuery, cb)); + Map results = Maps.newHashMap(); + final List resultList = _entityManager.createQuery(cbQuery).getResultList(); + for (Object[] objects : resultList) { + final Integer metadataId = (Integer) objects[0]; + final MetadataSourceInfo sourceInfo = (MetadataSourceInfo) objects[1]; + results.put(metadataId, sourceInfo); + } + + return results; + } + + /** + * @see org.fao.geonet.repository.MetadataRepositoryCustom#findAllSimple(org.springframework.data.jpa.domain.Specification) + */ + @Override + public List findAllSimple(String id) { + Query query = _entityManager.createQuery( + "select new org.fao.geonet.repository.SimpleMetadata(" + + "id, uuid, dataInfo.changeDate, dataInfo.type_JPAWorkaround) " + + "from Metadata where harvestInfo.uuid = :id").setParameter("id", id); + + //TODO paginate + + return query.getResultList(); + } + + + @Override + public Element findAllUuidsAndChangeDatesAndSchemaId(List ids, @Nonnull Pageable pageable) { + CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); + CriteriaQuery cbQuery = cb.createQuery(Tuple.class); + Root root = cbQuery.from(MetadataDraft.class); + + cbQuery.multiselect(root.get(MetadataDraft_.uuid), root.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.changeDate), root.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.schemaId)); + + cbQuery.where(root.get(MetadataDraft_.id).in(ids), cb.equal(root.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.type_JPAWorkaround), 'n')); + + if (pageable != null && pageable.getSort() != null) { + final Sort sort = pageable.getSort(); + List orders = SortUtils.sortToJpaOrders(cb, sort, root); + + cbQuery.orderBy(orders); + } + + TypedQuery query = _entityManager.createQuery(cbQuery); + if (pageable != null) { + query.setFirstResult(pageable.getOffset()); + query.setMaxResults(pageable.getPageSize()); + } + + Element result = new Element("metadata"); + for (Tuple tuple : query.getResultList()) { + + Element record = new Element("record"); + Element uuid = new Element("uuid"); + Element schemaid = new Element("schemaid"); + Element changedate = new Element("changedate"); + + uuid.addContent((String) tuple.get(0)); + changedate.addContent(((ISODate) tuple.get(1)).toString()); + schemaid.addContent((String) tuple.get(2)); + + record.addContent(uuid); + record.addContent(changedate); + record.addContent(schemaid); + + result.addContent(record); + + } + return result; + } + + @Override + public Element findAllUuidsAndChangeDatesAndSchemaId(List ids) { + CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); + CriteriaQuery cbQuery = cb.createQuery(Tuple.class); + Root root = cbQuery.from(MetadataDraft.class); + + cbQuery.multiselect(root.get(MetadataDraft_.uuid), root.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.changeDate), root.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.schemaId)); + + cbQuery.where(root.get(MetadataDraft_.id).in(ids), cb.equal(root.get(MetadataDraft_.dataInfo).get(MetadataDataInfo_.type_JPAWorkaround), 'n')); + + TypedQuery query = _entityManager.createQuery(cbQuery); + + Element result = new Element("metadata"); + for (Tuple tuple : query.getResultList()) { + + Element record = new Element("record"); + Element uuid = new Element("uuid"); + Element schemaid = new Element("schemaid"); + Element changedate = new Element("changedate"); + + uuid.addContent((String) tuple.get(0)); + changedate.addContent(((ISODate) tuple.get(1)).toString()); + schemaid.addContent((String) tuple.get(2)); + + record.addContent(uuid); + record.addContent(changedate); + record.addContent(schemaid); + + result.addContent(record); + + } + return result; + } + +} diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataRepository.java b/domain/src/main/java/org/fao/geonet/repository/MetadataRepository.java index 68c7fb71483..73c5db4f51b 100644 --- a/domain/src/main/java/org/fao/geonet/repository/MetadataRepository.java +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataRepository.java @@ -51,6 +51,16 @@ public interface MetadataRepository extends GeonetRepository, */ @Nullable Metadata findOneByUuid(@Nonnull String uuid); + + + /** + * Find all metadata by the metadata's uuid. + * + * @param uuid the uuid of the metadata to find + * @return a list of metadata. + */ + @Nullable + List findAllByUuid(@Nonnull String uuid); /** * Find all metadata harvested by the identified harvester. diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataRepositoryCustom.java b/domain/src/main/java/org/fao/geonet/repository/MetadataRepositoryCustom.java index 735f8fe0fc2..39522036e4e 100644 --- a/domain/src/main/java/org/fao/geonet/repository/MetadataRepositoryCustom.java +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataRepositoryCustom.java @@ -66,7 +66,7 @@ public interface MetadataRepositoryCustom { * @param id the id in string form instead of integer. */ @Nullable - AbstractMetadata findOne(@Nonnull String id); + T findOne(@Nonnull String id); /** * Find the list of Metadata Ids and changes dates for the metadata.

      When constructing sort @@ -95,7 +95,7 @@ public interface MetadataRepositoryCustom { * @return the metadata with the oldest change date */ @Nullable - AbstractMetadata findOneOldestByChangeDate(); + T findOneOldestByChangeDate(); /** * Load the source info objects for all the metadata selected by the spec. diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepository.java b/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepository.java index b1960bcfab8..015623f3546 100644 --- a/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepository.java +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepository.java @@ -23,23 +23,26 @@ package org.fao.geonet.repository; +import java.util.List; + +import javax.annotation.Nonnull; + import org.fao.geonet.domain.MetadataStatus; import org.fao.geonet.domain.MetadataStatusId; -import org.fao.geonet.domain.StatusValueType; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; - -import javax.annotation.Nonnull; - -import java.util.List; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; /** * Data Access object for accessing {@link MetadataStatus} entities. * * @author Jesse */ -public interface MetadataStatusRepository extends GeonetRepository, MetadataStatusRepositoryCustom, - JpaSpecificationExecutor { +public interface MetadataStatusRepository extends GeonetRepository, + MetadataStatusRepositoryCustom, + JpaSpecificationExecutor { /** * Find all the MetadataStatus objects by the associated metadata id. * @@ -49,6 +52,30 @@ public interface MetadataStatusRepository extends GeonetRepository findAllById_MetadataId(int metadataId, Sort sort); + + /** + * Delete all the entities that are related to the indicated metadata. + * + * @param metadataId the id of the metadata. + * @return the number of rows deleted. + */ + + @Modifying(clearAutomatically=true) + @Transactional + @Query(value="DELETE FROM MetadataStatus s WHERE s.id.metadataId = ?1") + int deleteAllById_MetadataId(Integer metadataId); + + /** + * Delete all the entities that are related to the indicated user. + * + * @param userId the id of the user. + * @return the number of rows deleted. + */ + + @Modifying(clearAutomatically=true) + @Transactional + @Query(value="DELETE FROM MetadataStatus s WHERE s.id.userId = ?1") + int deleteAllById_UserId(Integer userId); } diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryCustom.java b/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryCustom.java index 7eef88b32fa..f489dd3772e 100644 --- a/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryCustom.java +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryCustom.java @@ -23,36 +23,21 @@ package org.fao.geonet.repository; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import org.fao.geonet.domain.MetadataStatus; import org.fao.geonet.domain.StatusValueType; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.List; - /** * Custom repository methods for the MetadataValidationRepository User: Jesse * Date: 9/5/13 Time: 10:17 PM */ public interface MetadataStatusRepositoryCustom { - /** - * Delete all the entities that are related to the indicated metadata. - * - * @param metadataId the id of the metadata. - * @return the number of rows deleted. - */ - int deleteAllById_MetadataId(int metadataId); - - /** - * Delete all the entities that are related to the indicated user. - * - * @param userId the id of the user. - * @return the number of rows deleted. - */ - int deleteAllById_UserId(int userId); - /** * Find all the MetadataStatus objects corresponding to a type by the associated * metadata id. diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryImpl.java b/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryImpl.java index 2e8f60cb3ed..f2b403e51e5 100644 --- a/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryImpl.java +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataStatusRepositoryImpl.java @@ -23,23 +23,12 @@ package org.fao.geonet.repository; -import org.fao.geonet.domain.ISODate; -import org.fao.geonet.domain.MetadataStatus; -import org.fao.geonet.domain.MetadataStatusId_; -import org.fao.geonet.domain.MetadataStatus_; -import org.fao.geonet.domain.StatusValue; -import org.fao.geonet.domain.StatusValueType; -import org.fao.geonet.domain.StatusValue_; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.transaction.annotation.Transactional; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; -import javax.persistence.Query; -import javax.persistence.Tuple; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; @@ -47,7 +36,16 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import java.util.List; + +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.MetadataStatus; +import org.fao.geonet.domain.MetadataStatusId_; +import org.fao.geonet.domain.MetadataStatus_; +import org.fao.geonet.domain.StatusValue; +import org.fao.geonet.domain.StatusValueType; +import org.fao.geonet.domain.StatusValue_; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; /** * Data Access object for accessing @@ -60,29 +58,6 @@ public class MetadataStatusRepositoryImpl implements MetadataStatusRepositoryCus @PersistenceContext EntityManager _entityManager; - @Override - @Transactional - public int deleteAllById_MetadataId(final int metadataId) { - String entityType = MetadataStatus.class.getSimpleName(); - String metadataIdPropName = MetadataStatusId_.metadataId.getName(); - Query query = _entityManager - .createQuery("DELETE FROM " + entityType + " WHERE " + metadataIdPropName + " = " + metadataId); - final int deleted = query.executeUpdate(); - _entityManager.flush(); - _entityManager.clear(); - return deleted; - } - - @Override - public int deleteAllById_UserId(final int userId) { - String entityType = MetadataStatus.class.getSimpleName(); - String userIdPropName = MetadataStatusId_.userId.getName(); - Query query = _entityManager - .createQuery("DELETE FROM " + entityType + " WHERE " + userIdPropName + " = " + userId); - final int deleted = query.executeUpdate(); - return deleted; - } - @Nonnull @Override public List findAllByIdAndByType(int metadataId, StatusValueType type, Sort sort) { diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataValidationRepositoryCustom.java b/domain/src/main/java/org/fao/geonet/repository/MetadataValidationRepositoryCustom.java index 07bc45ad016..73aa2e9e20c 100644 --- a/domain/src/main/java/org/fao/geonet/repository/MetadataValidationRepositoryCustom.java +++ b/domain/src/main/java/org/fao/geonet/repository/MetadataValidationRepositoryCustom.java @@ -23,6 +23,10 @@ package org.fao.geonet.repository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + /** * Custom repository methods for the MetadataValidationRepository User: Jesse Date: 9/5/13 Time: * 10:17 PM @@ -34,5 +38,8 @@ public interface MetadataValidationRepositoryCustom { * @param metadataId the id of the metadata. * @return the number of rows deleted */ - int deleteAllById_MetadataId(int metadataId); + @Modifying(clearAutomatically=true) + @Transactional + @Query(value="DELETE FROM MetadataValidation v where v.id.metadataId = ?1") + int deleteAllById_MetadataId(Integer metadataId); } diff --git a/domain/src/main/java/org/fao/geonet/repository/MetadataValidationRepositoryImpl.java b/domain/src/main/java/org/fao/geonet/repository/MetadataValidationRepositoryImpl.java deleted file mode 100644 index b0d156e587a..00000000000 --- a/domain/src/main/java/org/fao/geonet/repository/MetadataValidationRepositoryImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.repository; - -import org.fao.geonet.domain.MetadataValidation; -import org.fao.geonet.domain.MetadataValidationId_; -import org.springframework.transaction.annotation.Transactional; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.Query; - -/** - * Data Access object for accessing {@link org.fao.geonet.domain.MetadataValidation} entities. - * - * @author Jesse - */ -public class MetadataValidationRepositoryImpl implements MetadataValidationRepositoryCustom { - - @PersistenceContext - EntityManager _entityManager; - - @Override - @Transactional - public int deleteAllById_MetadataId(final int metadataId) { - String entityType = MetadataValidation.class.getSimpleName(); - String metadataIdPropName = MetadataValidationId_.metadataId.getName(); - Query query = _entityManager.createQuery("DELETE FROM " + entityType + " WHERE " + metadataIdPropName + " = " + metadataId); - final int deleted = query.executeUpdate(); - _entityManager.flush(); - _entityManager.clear(); - return deleted; - } -} diff --git a/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepository.java b/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepository.java index 9732678a412..6c494f8b024 100644 --- a/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepository.java +++ b/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepository.java @@ -23,14 +23,17 @@ package org.fao.geonet.repository; -import org.fao.geonet.domain.OperationAllowed; -import org.fao.geonet.domain.OperationAllowedId; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.List; +import org.fao.geonet.domain.OperationAllowed; +import org.fao.geonet.domain.OperationAllowedId; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; /** * Data Access object for finding and saving {@link OperationAllowed} entities. @@ -80,4 +83,42 @@ public interface OperationAllowedRepository extends GeonetRepository findAllIds(@Nonnull Specification spec, @Nonnull */ @Nonnegative int deleteAllByMetadataIdExceptGroupId(int metadataId, int[] groupId); - - /** - * Delete all the {@link OperationAllowed} with the given id in the id component selected by the - * idAttribute. - * - * @param idAttribute The attribute of {@link OperationAllowedId} to match against the provided - * id. - * @param id the id to use as the key for selecting which entities to delete. - * @return the number of entities deleted. - */ - @Nonnegative - int deleteAllByIdAttribute(@Nonnull SingularAttribute idAttribute, int id); } diff --git a/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepositoryImpl.java b/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepositoryImpl.java index 8b86494fce5..bcf92516d63 100644 --- a/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepositoryImpl.java +++ b/domain/src/main/java/org/fao/geonet/repository/OperationAllowedRepositoryImpl.java @@ -23,12 +23,7 @@ package org.fao.geonet.repository; -import com.google.common.base.Optional; - -import org.fao.geonet.domain.*; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; +import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @@ -40,7 +35,18 @@ import javax.persistence.criteria.Root; import javax.persistence.metamodel.SingularAttribute; -import java.util.List; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataSourceInfo_; +import org.fao.geonet.domain.Metadata_; +import org.fao.geonet.domain.OperationAllowed; +import org.fao.geonet.domain.OperationAllowedId; +import org.fao.geonet.domain.OperationAllowedId_; +import org.fao.geonet.domain.OperationAllowed_; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.transaction.annotation.Transactional; + +import com.google.common.base.Optional; /** * Implementation for all {@link OperationAllowed} queries that cannot be automatically generated by @@ -102,6 +108,7 @@ public List findAllIds(Specification spec, SingularAt @Transactional @Override + @Modifying(clearAutomatically=true) public int deleteAllByMetadataIdExceptGroupId(int metadataId, int[] groupId) { final String opAllowedEntityName = OperationAllowed.class.getSimpleName(); final String metadataIdPath = SortUtils.createPath(OperationAllowed_.id, OperationAllowedId_.metadataId); @@ -117,23 +124,7 @@ public int deleteAllByMetadataIdExceptGroupId(int metadataId, int[] groupId) { + metadataId + groupExcluded.toString()); final int affected = query.executeUpdate(); - _entityManager.flush(); - _entityManager.clear(); return affected; } - - @Transactional - @Override - public int deleteAllByIdAttribute(SingularAttribute idAttribute, int id) { - final String opAllowedEntityName = OperationAllowed.class.getSimpleName(); - final String idPath = SortUtils.createPath(OperationAllowed_.id, idAttribute); - final Query query = _entityManager.createQuery("DELETE FROM " + opAllowedEntityName + " where " + idPath + " = " + id); - - final int affected = query.executeUpdate(); - _entityManager.flush(); - _entityManager.clear(); - return affected; - } - } diff --git a/domain/src/main/java/org/fao/geonet/repository/SchematronCriteriaRepositoryImpl.java b/domain/src/main/java/org/fao/geonet/repository/SchematronCriteriaRepositoryImpl.java index e9a0e430904..271f5843b49 100644 --- a/domain/src/main/java/org/fao/geonet/repository/SchematronCriteriaRepositoryImpl.java +++ b/domain/src/main/java/org/fao/geonet/repository/SchematronCriteriaRepositoryImpl.java @@ -26,6 +26,7 @@ import org.fao.geonet.domain.SchematronCriteria; import org.fao.geonet.domain.SchematronCriteriaGroup; import org.fao.geonet.domain.SchematronCriteriaGroup_; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; @@ -54,6 +55,7 @@ public class SchematronCriteriaRepositoryImpl { * @param id id of criteria to delete */ @Transactional + @Modifying(clearAutomatically=true) public void delete(Integer id) { SchematronCriteria criteria = _entityManager.getReference(SchematronCriteria.class, id); final CriteriaBuilder builder = _entityManager.getCriteriaBuilder(); @@ -73,11 +75,6 @@ public void delete(Integer id) { } _entityManager.persist(group); } - - _entityManager.flush(); - - _entityManager.getEntityManagerFactory().getCache().evict(SchematronCriteria.class); - _entityManager.getEntityManagerFactory().getCache().evict(SchematronCriteriaGroup.class); } /** diff --git a/domain/src/main/java/org/fao/geonet/repository/reports/MetadataReportsQueries.java b/domain/src/main/java/org/fao/geonet/repository/reports/MetadataReportsQueries.java index 34a3315fbef..b84ce1fc64e 100644 --- a/domain/src/main/java/org/fao/geonet/repository/reports/MetadataReportsQueries.java +++ b/domain/src/main/java/org/fao/geonet/repository/reports/MetadataReportsQueries.java @@ -97,7 +97,7 @@ public List getUpdatedMetadata(ISODate dateFrom, ISO * Retrieves created metadata in the period specified, that is not available in ALL group. * Optionally filters metadata in groups. */ - public List getInternalMetadata(ISODate dateFrom, ISODate dateTo, Set groups, + public List getInternalMetadata(ISODate dateFrom, ISODate dateTo, Set groups, @Nonnull Specification operationAllowedSpecification) { final CriteriaBuilder cb = _entityManager.getCriteriaBuilder(); final CriteriaQuery cbQuery = cb.createQuery(Metadata.class); diff --git a/domain/src/main/java/org/fao/geonet/repository/specification/MetadataFileUploadSpecs.java b/domain/src/main/java/org/fao/geonet/repository/specification/MetadataFileUploadSpecs.java index 0e97e884ea0..ee3e8ed0fe6 100644 --- a/domain/src/main/java/org/fao/geonet/repository/specification/MetadataFileUploadSpecs.java +++ b/domain/src/main/java/org/fao/geonet/repository/specification/MetadataFileUploadSpecs.java @@ -51,6 +51,17 @@ public Predicate toPredicate(Root root, CriteriaQuery que }; } + public static Specification hasMetadataId(final int id) { + return new Specification() { + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path updloadIdAttributePath = root.get(MetadataFileUpload_.metadataId); + Predicate pdloadIdEqualPredicate = cb.equal(updloadIdAttributePath, cb.literal(id)); + return pdloadIdEqualPredicate; + } + }; + } + public static Specification uploadDateBetweenAndByGroups(final ISODate uploadFrom, final ISODate uploadTo, final Collection groups) { diff --git a/domain/src/main/java/org/fao/geonet/repository/specification/MetadataSpecs.java b/domain/src/main/java/org/fao/geonet/repository/specification/MetadataSpecs.java index bd552fa1636..61acab23d28 100644 --- a/domain/src/main/java/org/fao/geonet/repository/specification/MetadataSpecs.java +++ b/domain/src/main/java/org/fao/geonet/repository/specification/MetadataSpecs.java @@ -47,96 +47,96 @@ private MetadataSpecs() { // no instantiation } - public static Specification hasSchemaId(final String schemaId) { - return new Specification() { + public static Specification hasSchemaId(final String schemaId) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path schemaIdPath = root.get(Metadata_.dataInfo).get(MetadataDataInfo_.schemaId); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path schemaIdPath = root.get(AbstractMetadata_.dataInfo).get(MetadataDataInfo_.schemaId); return cb.equal(schemaIdPath, cb.literal(schemaId)); } }; } - public static Specification hasOwner(final int owner) { - return new Specification() { + public static Specification hasOwner(final int owner) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path ownerPath = root.get(Metadata_.sourceInfo).get(MetadataSourceInfo_.owner); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path ownerPath = root.get(AbstractMetadata_.sourceInfo).get(MetadataSourceInfo_.owner); return cb.equal(ownerPath, cb.literal(owner)); } }; } - public static Specification hasMetadataId(final int metadataId) { - return new Specification() { + public static Specification hasMetadataId(final int metadataId) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path userIdAttributePath = root.get(Metadata_.id); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path userIdAttributePath = root.get(AbstractMetadata_.id); return cb.equal(userIdAttributePath, cb.literal(metadataId)); } }; } - public static Specification hasMetadataUuid(final String uuid) { - return new Specification() { + public static Specification hasMetadataUuid(final String uuid) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path userNameAttributePath = root.get(Metadata_.uuid); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path userNameAttributePath = root.get(AbstractMetadata_.uuid); return cb.equal(userNameAttributePath, cb.literal(uuid)); } }; } - public static Specification hasHarvesterUuid(final String harvesterUuid) { - return new Specification() { + public static Specification hasHarvesterUuid(final String harvesterUuid) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path userNameAttributePath = root.get(Metadata_.harvestInfo).get(MetadataHarvestInfo_.uuid); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path userNameAttributePath = root.get(AbstractMetadata_.harvestInfo).get(MetadataHarvestInfo_.uuid); Predicate uuidEqualPredicate = cb.equal(userNameAttributePath, cb.literal(harvesterUuid)); return uuidEqualPredicate; } }; } - public static Specification isOwnedByUser(final int userId) { - return new Specification() { + public static Specification isOwnedByUser(final int userId) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path ownerPath = root.get(Metadata_.sourceInfo).get(MetadataSourceInfo_.owner); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path ownerPath = root.get(AbstractMetadata_.sourceInfo).get(MetadataSourceInfo_.owner); Predicate equalUserIdPredicate = cb.equal(ownerPath, cb.literal(userId)); return equalUserIdPredicate; } }; } - public static Specification hasSource(final String sourceUuid) { - return new Specification() { + public static Specification hasSource(final String sourceUuid) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path sourceAttributePath = root.get(Metadata_.sourceInfo).get(MetadataSourceInfo_.sourceId); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path sourceAttributePath = root.get(AbstractMetadata_.sourceInfo).get(MetadataSourceInfo_.sourceId); Predicate equalSourceIdPredicate = cb.equal(sourceAttributePath, cb.literal(sourceUuid)); return equalSourceIdPredicate; } }; } - public static Specification isType(final MetadataType type) { - return new Specification() { + public static Specification isType(final MetadataType type) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path templateAttributePath = root.get(Metadata_.dataInfo).get(MetadataDataInfo_.type_JPAWorkaround); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path templateAttributePath = root.get(AbstractMetadata_.dataInfo).get(MetadataDataInfo_.type_JPAWorkaround); Predicate equalTemplatePredicate = cb.equal(templateAttributePath, cb.literal(type.code)); return equalTemplatePredicate; } }; } - public static Specification isHarvested(final boolean isHarvested) { - return new Specification() { + public static Specification isHarvested(final boolean isHarvested) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path userNameAttributePath = root.get(Metadata_.harvestInfo).get(MetadataHarvestInfo_.harvested_JPAWorkaround); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path userNameAttributePath = root.get(AbstractMetadata_.harvestInfo).get(MetadataHarvestInfo_.harvested_JPAWorkaround); Predicate equalHarvestPredicate = cb.equal(userNameAttributePath, cb.literal(Constants.toYN_EnabledChar(isHarvested))); return equalHarvestPredicate; } @@ -144,39 +144,39 @@ public Predicate toPredicate(Root root, CriteriaQuery query, Criter } - public static Specification hasMetadataIdIn(final Collection mdIds) { - return new Specification() { + public static Specification hasMetadataIdIn(final Collection mdIds) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - return root.get(Metadata_.id).in(mdIds); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + return root.get(AbstractMetadata_.id).in(mdIds); } }; } - public static Specification hasMetadataUuidIn(final Collection uuids) { - return new Specification() { + public static Specification hasMetadataUuidIn(final Collection uuids) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - return root.get(Metadata_.uuid).in(uuids); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + return root.get(AbstractMetadata_.uuid).in(uuids); } }; } - public static Specification hasType(final MetadataType metadataType) { - return new Specification() { + public static Specification hasType(final MetadataType metadataType) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - final Path typeChar = root.get(Metadata_.dataInfo).get(MetadataDataInfo_.type_JPAWorkaround); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + final Path typeChar = root.get(AbstractMetadata_.dataInfo).get(MetadataDataInfo_.type_JPAWorkaround); return cb.equal(typeChar, metadataType.code); } }; } - public static Specification isOwnedByOneOfFollowingGroups(final List groups) { - return new Specification() { + public static Specification isOwnedByOneOfFollowingGroups(final List groups) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - return root.get(Metadata_.sourceInfo).get(MetadataSourceInfo_.groupOwner).in(groups); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + return root.get(AbstractMetadata_.sourceInfo).get(MetadataSourceInfo_.groupOwner).in(groups); } }; } @@ -189,7 +189,7 @@ public Predicate toPredicate(Root root, CriteriaQuery query, Criter * @return a specification for finding all metadata containing a {@link MetadataCategory} with * the provided category */ - public static Specification hasCategory(final MetadataCategory category) { + public static Specification hasCategory(final MetadataCategory category) { return new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { @@ -200,21 +200,21 @@ public Predicate toPredicate(Root root, CriteriaQuery query, Criter }; } - public static Specification hasExtra(final String extra) { - return new Specification() { + public static Specification hasExtra(final String extra) { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - return cb.equal(root.get(Metadata_.dataInfo).get(MetadataDataInfo_.extra), extra); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + return cb.equal(root.get(AbstractMetadata_.dataInfo).get(MetadataDataInfo_.extra), extra); } }; } - public static Specification isIso19139Schema() { - return new Specification() { + public static Specification isIso19139Schema() { + return new Specification() { @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - Path schemaIdAttributePath = root.get(Metadata_.dataInfo).get(MetadataDataInfo_.schemaId); + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + Path schemaIdAttributePath = root.get(AbstractMetadata_.dataInfo).get(MetadataDataInfo_.schemaId); Predicate likeSchemaIdPredicate = cb.like(schemaIdAttributePath, cb.literal("iso19139")); return likeSchemaIdPredicate; } diff --git a/domain/src/test/java/org/fao/geonet/repository/BasicAbstractMetadataTest.java b/domain/src/test/java/org/fao/geonet/repository/BasicAbstractMetadataTest.java new file mode 100644 index 00000000000..7be00d82806 --- /dev/null +++ b/domain/src/test/java/org/fao/geonet/repository/BasicAbstractMetadataTest.java @@ -0,0 +1,104 @@ +/* + * 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.repository; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataDraft; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Basic Metadata and Draft Tests + * + * @author delawen María Arias de Reyna + */ +public class BasicAbstractMetadataTest extends AbstractSpringDataTest { + + @Autowired + MetadataRepository _metadatarepo; + + @Autowired + MetadataDraftRepository _metadatadraftrepo; + + @Autowired + MetadataCategoryRepository _categoryRepo; + + @Test + public void testMetadataWithRepositoryDAO() throws Exception { + Metadata md = new Metadata(); + populate(md); + _metadatarepo.save(md); + + md = _metadatarepo.findOneByUuid(md.getUuid()); + + assertNotNull(md); + + _metadatarepo.delete(md); + + md = _metadatarepo.findOneByUuid(md.getUuid()); + + assertNull(md); + } + + @Test + public void testDraftWithRepositoryDAO() throws Exception { + Metadata record = new Metadata(); + populate(record); + _metadatarepo.save(record); + + MetadataDraft md = new MetadataDraft(); + populate(md); + md.setApprovedVersion(record); + _metadatadraftrepo.save(md); + + md = _metadatadraftrepo.findOneByUuid(md.getUuid()); + + assertNotNull(md); + + _metadatadraftrepo.delete(md); + + md = _metadatadraftrepo.findOneByUuid(md.getUuid()); + + assertNull(md); + + + _metadatarepo.delete(record); + } + + private void populate(AbstractMetadata md) { + md.setUuid("test-metadata"); + md.setData(""); + md.getSourceInfo().setGroupOwner(1); + md.getSourceInfo().setOwner(1); + md.getSourceInfo().setSourceId("test-faking"); + md.getDataInfo().setSchemaId("isoFake"); + } + + + +} diff --git a/domain/src/test/java/org/fao/geonet/repository/GeonetRepositoryTest.java b/domain/src/test/java/org/fao/geonet/repository/GeonetRepositoryTest.java index 2e3b93a5ac9..6d6b75588fa 100644 --- a/domain/src/test/java/org/fao/geonet/repository/GeonetRepositoryTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/GeonetRepositoryTest.java @@ -23,6 +23,17 @@ package org.fao.geonet.repository; +import static junit.framework.Assert.assertNull; +import static org.fao.geonet.repository.specification.MetadataSpecs.hasMetadataId; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.springframework.data.jpa.domain.Specifications.where; + +import javax.annotation.Nullable; +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Root; + import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataSourceInfo_; import org.fao.geonet.domain.Metadata_; @@ -32,20 +43,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; import org.springframework.orm.jpa.JpaObjectRetrievalFailureException; -import javax.annotation.Nullable; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Root; - -import static junit.framework.Assert.assertNull; -import static org.fao.geonet.repository.specification.MetadataSpecs.hasMetadataId; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.springframework.data.jpa.domain.Specifications.where; - /** * Test class for GeonetRepository. *

      @@ -91,7 +92,7 @@ public void testBatchUpdateAttributes() throws Exception { Metadata md3 = _repo.save(MetadataRepositoryTest.newMetadata(_inc)); - final Specifications spec = where(hasMetadataId(md2.getId())).or(hasMetadataId(md3.getId())); + final Specifications spec = where((Specification)hasMetadataId(md2.getId())).or((Specification)hasMetadataId(md3.getId())); PathSpec dataPathSpec = new PathSpec() { @Override public Path getPath(Root root) { @@ -136,7 +137,7 @@ public void testDeleteAllSpec() throws Exception { Metadata md3 = _repo.save(MetadataRepositoryTest.newMetadata(_inc)); - final Specifications spec = where(hasMetadataId(md2.getId())).or(hasMetadataId(md3.getId())); + final Specifications spec = where((Specification)hasMetadataId(md2.getId())).or((Specification)hasMetadataId(md3.getId())); final int deleted = _repo.deleteAll(spec); @@ -157,7 +158,7 @@ public void testFindAllAsXmlSpec() throws Exception { Metadata md3 = _repo.save(MetadataRepositoryTest.newMetadata(_inc)); - final Specifications spec = where(hasMetadataId(md2.getId())).or(hasMetadataId(md3.getId())); + final Specifications spec = where((Specification)hasMetadataId(md2.getId())).or((Specification)hasMetadataId(md3.getId())); Element xmlResponse = _repo.findAllAsXml(); assertEquals(3, Xml.selectNodes(xmlResponse, "record").size()); diff --git a/domain/src/test/java/org/fao/geonet/repository/MetadataCategoryRepositoryTest.java b/domain/src/test/java/org/fao/geonet/repository/MetadataCategoryRepositoryTest.java index 1e3e2e6c5e5..fbe897584b7 100644 --- a/domain/src/test/java/org/fao/geonet/repository/MetadataCategoryRepositoryTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/MetadataCategoryRepositoryTest.java @@ -105,12 +105,12 @@ public void testDeleteDeletesFromMetadata() throws Exception { MetadataCategory cat2 = _repo.save(newMetadataCategory(_inc)); Metadata metadata1 = MetadataRepositoryTest.newMetadata(_inc); - metadata1.getMetadataCategories().add(cat1); - metadata1.getMetadataCategories().add(cat2); + metadata1.getCategories().add(cat1); + metadata1.getCategories().add(cat2); metadata1 = _metadataRepo.save(metadata1); Metadata metadata2 = MetadataRepositoryTest.newMetadata(_inc); - metadata2.getMetadataCategories().add(cat1); + metadata2.getCategories().add(cat1); metadata2 = _metadataRepo.save(metadata2); _repo.deleteCategoryAndMetadataReferences(cat1.getId()); @@ -120,11 +120,11 @@ public void testDeleteDeletesFromMetadata() throws Exception { // org.fao.geonet.services.category.Remove assumes that this test passes. If this test can't pass // then there needs to be a way to fix Remove as well. - final Set foundCategories = _metadataRepo.findOne(metadata1.getId()).getMetadataCategories(); + final Set foundCategories = _metadataRepo.findOne(metadata1.getId()).getCategories(); assertEquals(1, foundCategories.size()); assertEquals(cat2.getId(), foundCategories.iterator().next().getId()); - assertEquals(0, _metadataRepo.findOne(metadata2.getId()).getMetadataCategories().size()); + assertEquals(0, _metadataRepo.findOne(metadata2.getId()).getCategories().size()); } private MetadataCategory newMetadataCategory() { diff --git a/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryIssue1876IntegrationTest.java b/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryIssue1876IntegrationTest.java index e63381d54ae..b448ff6fcb5 100644 --- a/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryIssue1876IntegrationTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryIssue1876IntegrationTest.java @@ -45,7 +45,7 @@ public void setUp() throws UnknownHostException, IOException { @Test(timeout = 500) public void testSelectMetadata() { Metadata m = mdRepo.findOne(16667); - Set d = m.getMetadataCategories(); + Set d = m.getCategories(); Assert.assertNotNull("Metadata is null", m); } diff --git a/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryTest.java b/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryTest.java index 45aec7e5d86..8aae1adf5f6 100644 --- a/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/MetadataRepositoryTest.java @@ -119,7 +119,7 @@ public void testFindAllIdsBy() throws Exception { assertEquals(3, _repo.count()); - List ids = _repo.findAllIdsBy(MetadataSpecs.hasMetadataUuid(metadata.getUuid())); + List ids = _repo.findAllIdsBy((Specification)MetadataSpecs.hasMetadataUuid(metadata.getUuid())); assertArrayEquals(new Integer[]{metadata.getId()}, ids.toArray(new Integer[1])); } @@ -243,7 +243,7 @@ public void testFindAllSourceInfo() throws Exception { Metadata metadata2 = _repo.save(newMetadata()); Metadata metadata3 = _repo.save(newMetadata()); - final Specification spec = MetadataSpecs.hasMetadataIdIn(Arrays.asList(metadata.getId(), metadata3.getId())); + final Specification spec = (Specification)MetadataSpecs.hasMetadataIdIn(Arrays.asList(metadata.getId(), metadata3.getId())); final Map allSourceInfo = _repo.findAllSourceInfo(Specifications.where(spec)); assertEquals(2, allSourceInfo.size()); diff --git a/domain/src/test/java/org/fao/geonet/repository/MetadataStatusRepositoryTest.java b/domain/src/test/java/org/fao/geonet/repository/MetadataStatusRepositoryTest.java index e2ffbe08520..96e457813dd 100644 --- a/domain/src/test/java/org/fao/geonet/repository/MetadataStatusRepositoryTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/MetadataStatusRepositoryTest.java @@ -24,17 +24,22 @@ package org.fao.geonet.repository; -import org.fao.geonet.domain.*; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Sort; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static org.junit.Assert.assertEquals; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static org.junit.Assert.assertEquals; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.MetadataStatus; +import org.fao.geonet.domain.MetadataStatusId; +import org.fao.geonet.domain.MetadataStatusId_; +import org.fao.geonet.domain.MetadataStatus_; +import org.fao.geonet.domain.StatusValue; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; public class MetadataStatusRepositoryTest extends AbstractSpringDataTest { diff --git a/domain/src/test/java/org/fao/geonet/repository/OperationAllowedRepositoryTest.java b/domain/src/test/java/org/fao/geonet/repository/OperationAllowedRepositoryTest.java index 869d6bc5769..50015e00a2f 100644 --- a/domain/src/test/java/org/fao/geonet/repository/OperationAllowedRepositoryTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/OperationAllowedRepositoryTest.java @@ -125,7 +125,7 @@ public void deleteAllByMetadataIdExceptGroupId() { @Test public void testDeleteByMetadataId() { - _opAllowRepo.deleteAllByIdAttribute(OperationAllowedId_.metadataId, _md1.getId()); + _opAllowRepo.deleteAllByMetadataId(_md1.getId()); List opAllowedFound = _opAllowRepo.findAll(); @@ -141,7 +141,7 @@ public void testDeleteByMetadataId() { @Test public void testDeleteByGroupId() { - _opAllowRepo.deleteAllByIdAttribute(OperationAllowedId_.groupId, _allGroup.getId()); + _opAllowRepo.deleteAllByGroupId(_allGroup.getId()); List opAllowedFound = _opAllowRepo.findAll(); diff --git a/domain/src/test/java/org/fao/geonet/repository/report/ReportsQueriesTest.java b/domain/src/test/java/org/fao/geonet/repository/report/ReportsQueriesTest.java index 45cfc397fda..5211398954f 100644 --- a/domain/src/test/java/org/fao/geonet/repository/report/ReportsQueriesTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/report/ReportsQueriesTest.java @@ -232,7 +232,7 @@ public void testGetInternalMetadata() throws Exception { ISODate dateFrom = new ISODate("2014-01-01T00:00:0"); ISODate dateTo = new ISODate("2014-04-01T00:00:0"); Set groupsSet = new HashSet(); - List updatedMetadata = _metadataRepository.getMetadataReports(). + List updatedMetadata = _metadataRepository.getMetadataReports(). getInternalMetadata(dateFrom, dateTo, groupsSet, OperationAllowedSpecs.isPublic(ReservedOperation.view)); assertEquals(1, updatedMetadata.size()); diff --git a/domain/src/test/java/org/fao/geonet/repository/specification/MetadataSpecsTest.java b/domain/src/test/java/org/fao/geonet/repository/specification/MetadataSpecsTest.java index 2aa1b866f7f..246cf0d6ed6 100644 --- a/domain/src/test/java/org/fao/geonet/repository/specification/MetadataSpecsTest.java +++ b/domain/src/test/java/org/fao/geonet/repository/specification/MetadataSpecsTest.java @@ -70,7 +70,7 @@ public class MetadataSpecsTest extends AbstractSpringDataTest { @Test public void testHasMetadataId() throws Exception { Metadata md1 = _repository.save(newMetadata(_inc)); - assertFindsCorrectMd(md1, hasMetadataId(md1.getId()), true); + assertFindsCorrectMd(md1, (Specification)hasMetadataId(md1.getId()), true); } @Test @@ -87,13 +87,13 @@ public void testHasMetadataType() throws Exception { metadata3.getDataInfo().setType(MetadataType.TEMPLATE); Metadata md3 = _repository.save(metadata3); - assertEquals(1, _repository.findAll(hasType(MetadataType.METADATA)).size()); - assertEquals(1, _repository.findAll(hasType(MetadataType.SUB_TEMPLATE)).size()); - assertEquals(1, _repository.findAll(hasType(MetadataType.TEMPLATE)).size()); + assertEquals(1, _repository.findAll((Specification)hasType(MetadataType.METADATA)).size()); + assertEquals(1, _repository.findAll((Specification)hasType(MetadataType.SUB_TEMPLATE)).size()); + assertEquals(1, _repository.findAll((Specification)hasType(MetadataType.TEMPLATE)).size()); - assertEquals(md1.getId(), _repository.findOne(hasType(MetadataType.METADATA)).getId()); - assertEquals(md2.getId(), _repository.findOne(hasType(MetadataType.SUB_TEMPLATE)).getId()); - assertEquals(md3.getId(), _repository.findOne(hasType(MetadataType.TEMPLATE)).getId()); + assertEquals(md1.getId(), _repository.findOne((Specification)hasType(MetadataType.METADATA)).getId()); + assertEquals(md2.getId(), _repository.findOne((Specification)hasType(MetadataType.SUB_TEMPLATE)).getId()); + assertEquals(md3.getId(), _repository.findOne((Specification)hasType(MetadataType.TEMPLATE)).getId()); } @Test @@ -108,12 +108,12 @@ public void testHasSchemaId() throws Exception { metadata2.getDataInfo().setSchemaId(schemaId2); Metadata md2 = _repository.save(metadata2); - assertEquals(1, _repository.findAll(hasSchemaId(schemaId1)).size()); - assertEquals(1, _repository.findAll(hasSchemaId(schemaId2)).size()); - assertEquals(0, _repository.findAll(hasSchemaId("other")).size()); + assertEquals(1, _repository.findAll((Specification)hasSchemaId(schemaId1)).size()); + assertEquals(1, _repository.findAll((Specification)hasSchemaId(schemaId2)).size()); + assertEquals(0, _repository.findAll((Specification)hasSchemaId("other")).size()); - assertEquals(md1.getId(), _repository.findOne(hasSchemaId(schemaId1)).getId()); - assertEquals(md2.getId(), _repository.findOne(hasSchemaId(schemaId2)).getId()); + assertEquals(md1.getId(), _repository.findOne((Specification)hasSchemaId(schemaId1)).getId()); + assertEquals(md2.getId(), _repository.findOne((Specification)hasSchemaId(schemaId2)).getId()); } @Test @@ -124,35 +124,35 @@ public void testHasCategory() throws Exception { final MetadataCategory cat4 = _categoryRepo.save(MetadataCategoryRepositoryTest.newMetadataCategory(_inc)); final Metadata metadata = newMetadata(_inc); - metadata.getMetadataCategories().add(cat1); - metadata.getMetadataCategories().add(cat2); + metadata.getCategories().add(cat1); + metadata.getCategories().add(cat2); Metadata md1 = _repository.save(metadata); final Metadata metadata2 = newMetadata(_inc); - metadata2.getMetadataCategories().add(cat1); - metadata2.getMetadataCategories().add(cat3); + metadata2.getCategories().add(cat1); + metadata2.getCategories().add(cat3); Metadata md2 = _repository.save(metadata2); final Metadata metadata3 = newMetadata(_inc); - metadata3.getMetadataCategories().add(cat2); + metadata3.getCategories().add(cat2); Metadata md3 = _repository.save(metadata3); - List found = _repository.findAll(hasCategory(cat1), SortUtils.createSort(Metadata_.id)); + List found = _repository.findAll((Specification)hasCategory(cat1), SortUtils.createSort(Metadata_.id)); assertEquals(2, found.size()); assertEquals(md1.getId(), found.get(0).getId()); assertEquals(md2.getId(), found.get(1).getId()); - found = _repository.findAll(hasCategory(cat2), SortUtils.createSort(Metadata_.id)); + found = _repository.findAll((Specification)hasCategory(cat2), SortUtils.createSort(Metadata_.id)); assertEquals(2, found.size()); assertEquals(md1.getId(), found.get(0).getId()); assertEquals(md3.getId(), found.get(1).getId()); - found = _repository.findAll(hasCategory(cat3), SortUtils.createSort(Metadata_.id)); + found = _repository.findAll((Specification)hasCategory(cat3), SortUtils.createSort(Metadata_.id)); assertEquals(1, found.size()); assertEquals(md2.getId(), found.get(0).getId()); - found = _repository.findAll(hasCategory(cat4), SortUtils.createSort(Metadata_.id)); + found = _repository.findAll((Specification)hasCategory(cat4), SortUtils.createSort(Metadata_.id)); assertEquals(0, found.size()); } @@ -174,11 +174,11 @@ public void testIsOwnedByOneOfFollowingGroups() throws Exception { metadata3.getSourceInfo().setGroupOwner(3); Metadata md3 = _repository.save(metadata3); - List found = _repository.findAll(isOwnedByOneOfFollowingGroups(Arrays.asList(1))); + List found = _repository.findAll((Specification)isOwnedByOneOfFollowingGroups(Arrays.asList(1))); assertEquals(1, found.size()); assertEquals(md1.getId(), found.get(0).getId()); - found = _repository.findAll(isOwnedByOneOfFollowingGroups(Arrays.asList(1, 3)), SortUtils.createSort(Metadata_.id)); + found = _repository.findAll((Specification)isOwnedByOneOfFollowingGroups(Arrays.asList(1, 3)), SortUtils.createSort(Metadata_.id)); assertEquals(2, found.size()); assertEquals(md1.getId(), found.get(0).getId()); assertEquals(md3.getId(), found.get(1).getId()); @@ -187,7 +187,7 @@ public void testIsOwnedByOneOfFollowingGroups() throws Exception { @Test public void testHasMetadataUuid() throws Exception { Metadata md1 = _repository.save(newMetadata(_inc)); - Specification spec = hasMetadataUuid(md1.getUuid()); + Specification spec = (Specification)hasMetadataUuid(md1.getUuid()); assertFindsCorrectMd(md1, spec, true); } @@ -196,7 +196,7 @@ public void testHasMetadataUuid() throws Exception { @Test public void testHasHarvesterUuid() throws Exception { Metadata md1 = _repository.save(newMetadata(_inc)); - Specification spec = hasHarvesterUuid(md1.getHarvestInfo().getUuid()); + Specification spec = (Specification)hasHarvesterUuid(md1.getHarvestInfo().getUuid()); assertFindsCorrectMd(md1, spec, true); } @@ -209,8 +209,8 @@ public void testIsHarvested() throws Exception { md2.getHarvestInfo().setHarvested(true); md2 = _repository.save(md2); - assertFindsCorrectMd(md1, isHarvested(false), false); - assertFindsCorrectMd(md2, isHarvested(true), false); + assertFindsCorrectMd(md1, (Specification)isHarvested(false), false); + assertFindsCorrectMd(md2, (Specification)isHarvested(true), false); } @Test @@ -220,14 +220,14 @@ public void testHasMetadataIdIn() throws Exception { Metadata md2 = newMetadata(_inc); md2 = _repository.save(md2); - List all = _repository.findAll(hasMetadataIdIn(Arrays.asList(md1.getId()))); + List all = _repository.findAll((Specification)hasMetadataIdIn(Arrays.asList(md1.getId()))); assertEquals(1, all.size()); assertEquals(md1.getId(), all.get(0).getId()); - all = _repository.findAll(hasMetadataIdIn(Arrays.asList(md1.getId(), md2.getId()))); + all = _repository.findAll((Specification)hasMetadataIdIn(Arrays.asList(md1.getId(), md2.getId()))); assertEquals(2, all.size()); - all = _repository.findAll(hasMetadataIdIn(Collections.emptyList())); + all = _repository.findAll((Specification)hasMetadataIdIn(Collections.emptyList())); assertTrue(all.isEmpty()); @@ -242,14 +242,14 @@ public void testIsTemplate() throws Exception { md2.getDataInfo().setType(MetadataType.SUB_TEMPLATE); md2 = _repository.save(md2); - assertFindsCorrectMd(md1, isType(MetadataType.METADATA), false); - assertFindsCorrectMd(md2, isType(MetadataType.SUB_TEMPLATE), false); + assertFindsCorrectMd(md1, (Specification)isType(MetadataType.METADATA), false); + assertFindsCorrectMd(md2, (Specification)isType(MetadataType.SUB_TEMPLATE), false); } @Test public void testHasSource() throws Exception { Metadata md1 = _repository.save(newMetadata(_inc)); - Specification spec = hasSource(md1.getSourceInfo().getSourceId()); + Specification spec = (Specification)hasSource(md1.getSourceInfo().getSourceId()); assertFindsCorrectMd(md1, spec, true); } @@ -260,9 +260,9 @@ public void testHasExtra() throws Exception { entity.getDataInfo().setExtra(extra); Metadata md1 = _repository.save(entity); - assertFindsCorrectMd(md1, hasExtra(extra), true); + assertFindsCorrectMd(md1, (Specification)hasExtra(extra), true); - assertEquals(0, _repository.count(hasExtra("wrong extra"))); + assertEquals(0, _repository.count((Specification)hasExtra("wrong extra"))); } private void assertFindsCorrectMd(Metadata md1, Specification spec, boolean addNewMetadata) { diff --git a/events/src/main/java/org/fao/geonet/events/hooks/group/GroupModified.java b/events/src/main/java/org/fao/geonet/events/hooks/group/GroupModified.java index 50029145aa0..0d0e9fbb9fa 100644 --- a/events/src/main/java/org/fao/geonet/events/hooks/group/GroupModified.java +++ b/events/src/main/java/org/fao/geonet/events/hooks/group/GroupModified.java @@ -61,11 +61,11 @@ public Class getEntityClass() { */ @Override public void handleEvent(PersistentEventType type, Group entity) { - if (type == PersistentEventType.PostPersist) { + if (type == PersistentEventType.PrePersist) { this.eventPublisher.publishEvent(new GroupCreated(entity)); - } else if (type == PersistentEventType.PostUpdate) { + } else if (type == PersistentEventType.PreUpdate) { this.eventPublisher.publishEvent(new GroupUpdated(entity)); - } else if (type == PersistentEventType.PostRemove) { + } else if (type == PersistentEventType.PreRemove) { this.eventPublisher.publishEvent(new GroupRemoved(entity)); } } diff --git a/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataDraftModified.java b/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataDraftModified.java new file mode 100644 index 00000000000..d344be029e8 --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataDraftModified.java @@ -0,0 +1,82 @@ +/* + * 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.events.hooks.md; + +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.entitylistener.GeonetworkEntityListener; +import org.fao.geonet.entitylistener.PersistentEventType; +import org.fao.geonet.events.md.MetadataDraftAdd; +import org.fao.geonet.events.md.MetadataDraftRemove; +import org.fao.geonet.events.md.MetadataDraftUpdate; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.stereotype.Component; + +/** + * Hook events to database events + * + * @author delawen + */ +@Component +public class MetadataDraftModified + implements GeonetworkEntityListener, + ApplicationEventPublisherAware { + + private ApplicationEventPublisher eventPublisher; + + /** + * @see org.fao.geonet.entitylistener.GeonetworkEntityListener#getEntityClass() + */ + @Override + public Class getEntityClass() { + return MetadataDraft.class; + } + + /** + * @see org.fao.geonet.entitylistener.GeonetworkEntityListener#handleEvent(org.fao.geonet.entitylistener.PersistentEventType, + * java.lang.Object) + */ + @Override + public void handleEvent(PersistentEventType type, MetadataDraft entity) { + if (type == PersistentEventType.PrePersist) { + this.eventPublisher.publishEvent(new MetadataDraftAdd(entity)); + } else if (type == PersistentEventType.PreUpdate) { + this.eventPublisher.publishEvent(new MetadataDraftUpdate(entity)); + } else if (type == PersistentEventType.PreRemove) { + this.eventPublisher.publishEvent(new MetadataDraftRemove(entity)); + } + } + + /** + * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher) + */ + @Override + public void setApplicationEventPublisher( + ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } +} diff --git a/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataModified.java b/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataModified.java index 4064a576659..b78b3b12710 100644 --- a/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataModified.java +++ b/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataModified.java @@ -61,14 +61,12 @@ public Class getEntityClass() { */ @Override public void handleEvent(PersistentEventType type, Metadata entity) { - if (entity.getId() == 0 - && (type == PersistentEventType.PrePersist || type == PersistentEventType.PreUpdate)) { + if (type == PersistentEventType.PrePersist) { this.eventPublisher.publishEvent(new MetadataAdd(entity)); - } else if ((type == PersistentEventType.PostPersist || type == PersistentEventType.PostUpdate) - && entity.getId() != 0) { + } else if (type == PersistentEventType.PreUpdate) { this.eventPublisher.publishEvent(new MetadataUpdate(entity)); - } else if (type == PersistentEventType.PostRemove) { - this.eventPublisher.publishEvent(new MetadataRemove(entity)); + } else if (type == PersistentEventType.PreRemove) { + this.eventPublisher.publishEvent(new MetadataRemove(entity.clone())); } } diff --git a/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataSharingUpdate.java b/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataSharingUpdate.java new file mode 100644 index 00000000000..706d34f9781 --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/hooks/md/MetadataSharingUpdate.java @@ -0,0 +1,79 @@ +/* + * 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.events.hooks.md; + +import org.fao.geonet.domain.OperationAllowed; +import org.fao.geonet.entitylistener.GeonetworkEntityListener; +import org.fao.geonet.entitylistener.PersistentEventType; +import org.fao.geonet.events.md.sharing.MetadataShare; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.stereotype.Component; + +/** + * Hook events to database events + * + * @author delawen + */ +@Component +public class MetadataSharingUpdate implements GeonetworkEntityListener, + ApplicationEventPublisherAware { + + private ApplicationEventPublisher eventPublisher; + + /** + * @see org.fao.geonet.entitylistener.GeonetworkEntityListener#getEntityClass() + */ + @Override + public Class getEntityClass() { + return OperationAllowed.class; + } + + /** + * @see org.fao.geonet.entitylistener.GeonetworkEntityListener#handleEvent(org.fao.geonet.entitylistener.PersistentEventType, + * java.lang.Object) + */ + @Override + public void handleEvent(PersistentEventType type, OperationAllowed entity) { + if (type == PersistentEventType.PrePersist) { + this.eventPublisher.publishEvent(new MetadataShare(entity, MetadataShare.Type.ADD)); + } else if (type == PersistentEventType.PreUpdate) { + this.eventPublisher.publishEvent(new MetadataShare(entity, MetadataShare.Type.UPDATE)); + } else if (type == PersistentEventType.PreRemove) { + this.eventPublisher.publishEvent(new MetadataShare(entity, MetadataShare.Type.REMOVE)); + } + } + + /** + * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher) + */ + @Override + public void setApplicationEventPublisher( + ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } +} diff --git a/events/src/main/java/org/fao/geonet/events/hooks/user/UserGroupModified.java b/events/src/main/java/org/fao/geonet/events/hooks/user/UserGroupModified.java index a9b59ccb85c..c7d8e4e7ec5 100644 --- a/events/src/main/java/org/fao/geonet/events/hooks/user/UserGroupModified.java +++ b/events/src/main/java/org/fao/geonet/events/hooks/user/UserGroupModified.java @@ -63,7 +63,7 @@ public void handleEvent(PersistentEventType type, UserGroup entity) { if (type == PersistentEventType.PrePersist || type == PersistentEventType.PreUpdate) { this.eventPublisher.publishEvent(new GroupJoined(entity)); - } else if (type == PersistentEventType.PostRemove) { + } else if (type == PersistentEventType.PreRemove) { this.eventPublisher.publishEvent(new GroupLeft(entity)); } } diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataDraftAdd.java b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftAdd.java new file mode 100644 index 00000000000..ca6c504670d --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftAdd.java @@ -0,0 +1,41 @@ +/* + * 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.events.md; + +import org.fao.geonet.domain.MetadataDraft; + +/** + * Event launched when a metadata is created on the database + * + * @author delawen + */ +public class MetadataDraftAdd extends MetadataEvent { + + private static final long serialVersionUID = 324534556246220509L; + + public MetadataDraftAdd(MetadataDraft md) { + super(md); + } + +} diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataDraftIndexCompleted.java b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftIndexCompleted.java new file mode 100644 index 00000000000..addb5006f20 --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftIndexCompleted.java @@ -0,0 +1,47 @@ +/* + * 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.events.md; + +import org.fao.geonet.domain.MetadataDraft; + +/** + * Event launched when the indexation of a metadata record is finished + * + * @author delawen + */ +public class MetadataDraftIndexCompleted extends MetadataEvent { + + private static final long serialVersionUID = 6646733956246220509L; + + /** + * @param metadata + */ + public MetadataDraftIndexCompleted(MetadataDraft metadata) { + super(metadata); + } + +} diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataDraftRemove.java b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftRemove.java new file mode 100644 index 00000000000..c5923b684aa --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftRemove.java @@ -0,0 +1,41 @@ +/* + * 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.events.md; + +import org.fao.geonet.domain.MetadataDraft; + +/** + * Event launched when a metadata is removed from the database + * + * @author delawen + */ +public class MetadataDraftRemove extends MetadataEvent { + + private static final long serialVersionUID = 324534556246220509L; + + public MetadataDraftRemove(MetadataDraft md) { + super(md); + } + +} diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataDraftUpdate.java b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftUpdate.java new file mode 100644 index 00000000000..19b73dec5c0 --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataDraftUpdate.java @@ -0,0 +1,41 @@ +/* + * 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.events.md; + +import org.fao.geonet.domain.MetadataDraft; + +/** + * Event launched when a metadata is updated on the database + * + * @author delawen + */ +public class MetadataDraftUpdate extends MetadataEvent { + + private static final long serialVersionUID = 324534556246220509L; + + public MetadataDraftUpdate(MetadataDraft md) { + super(md); + } + +} diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataPublished.java b/events/src/main/java/org/fao/geonet/events/md/MetadataPublished.java new file mode 100644 index 00000000000..a81945bcae1 --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataPublished.java @@ -0,0 +1,41 @@ +/* + * 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.events.md; + +import org.fao.geonet.domain.AbstractMetadata; + +/** + * Event launched when a metadata is published + * + * @author delawen + */ +public class MetadataPublished extends MetadataEvent { + + private static final long serialVersionUID = 324534556246220509L; + + public MetadataPublished(AbstractMetadata md) { + super(md); + } + +} diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataRemove.java b/events/src/main/java/org/fao/geonet/events/md/MetadataRemove.java index 46a01d7dd95..4d73e6ea0a3 100644 --- a/events/src/main/java/org/fao/geonet/events/md/MetadataRemove.java +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataRemove.java @@ -23,7 +23,7 @@ package org.fao.geonet.events.md; -import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.AbstractMetadata; /** * Event launched when a metadata is removed from the database @@ -34,7 +34,7 @@ public class MetadataRemove extends MetadataEvent { private static final long serialVersionUID = 324534556246220509L; - public MetadataRemove(Metadata md) { + public MetadataRemove(AbstractMetadata md) { super(md); } diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataStatusChanged.java b/events/src/main/java/org/fao/geonet/events/md/MetadataStatusChanged.java new file mode 100644 index 00000000000..1c0d4151688 --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataStatusChanged.java @@ -0,0 +1,63 @@ +/* + * 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.events.md; + +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.StatusValue; + +/** + * Event launched when a record changes its status + * + * @author delawen + */ +public class MetadataStatusChanged extends MetadataEvent { + + private static final long serialVersionUID = 324534556246220509L; + private StatusValue status; + private String message; + private Integer user; + + public MetadataStatusChanged(AbstractMetadata abstractMetadata, StatusValue status, String message, Integer user) { + super(abstractMetadata); + if (status == null) { + throw new IllegalArgumentException("Status can't be null"); + } + this.status = status; + this.message = message; + this.user = user; + } + + public StatusValue getStatus() { + return status; + } + + public String getMessage() { + return message; + } + + public Integer getUser() { + return user; + } + +} diff --git a/events/src/main/java/org/fao/geonet/events/md/MetadataUnpublished.java b/events/src/main/java/org/fao/geonet/events/md/MetadataUnpublished.java new file mode 100644 index 00000000000..6e44f9e2dc9 --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/MetadataUnpublished.java @@ -0,0 +1,41 @@ +/* + * 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.events.md; + +import org.fao.geonet.domain.AbstractMetadata; + +/** + * Event launched when a metadata is unpublished + * + * @author delawen + */ +public class MetadataUnpublished extends MetadataEvent { + + private static final long serialVersionUID = 324534556246220509L; + + public MetadataUnpublished(AbstractMetadata md) { + super(md); + } + +} diff --git a/events/src/main/java/org/fao/geonet/events/md/sharing/MetadataShare.java b/events/src/main/java/org/fao/geonet/events/md/sharing/MetadataShare.java new file mode 100644 index 00000000000..451073d3e4d --- /dev/null +++ b/events/src/main/java/org/fao/geonet/events/md/sharing/MetadataShare.java @@ -0,0 +1,78 @@ +/* + * 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.events.md.sharing; + +import org.fao.geonet.domain.OperationAllowed; +import org.springframework.context.ApplicationEvent; + +/** + * Event launched when a metadata sharing/privileges is modified + * + * @author delawen + */ +public class MetadataShare extends ApplicationEvent { + + private static final long serialVersionUID = -748471747316454884L; + + public enum Type { + ADD, UPDATE, REMOVE + } + + private OperationAllowed op; + private Type type; + private Integer record; + + + public MetadataShare(OperationAllowed op, Type type) { + super(op); + this.setOp(op); + this.setType(type); + this.setRecord(op.getId().getMetadataId()); + } + + public OperationAllowed getOp() { + return op; + } + + private void setOp(OperationAllowed op) { + this.op = op; + } + + public Type getType() { + return type; + } + + private void setType(Type type) { + this.type = type; + } + + private void setRecord(Integer record) { + this.record = record; + } + + public Integer getRecord() { + return record; + } + +} diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java index 23085f3f4aa..bd2f18d58c3 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java @@ -90,7 +90,7 @@ public void addCategories(AbstractMetadata metadata, Iterable categories } final MetadataCategory metadataCategory = nameToCategoryMap.get(catId); if (metadataCategory != null) { - metadata.getMetadataCategories().add(metadataCategory); + metadata.getCategories().add(metadataCategory); } else { log.warning("Unable to map category: " + catId + " (" + name + ") to a category in Geonetwork"); } @@ -105,7 +105,7 @@ public void addCategories(AbstractMetadata metadata, Iterable categories } else { final MetadataCategory metadataCategory = nameToCategoryMap.get(catId); if (metadataCategory != null) { - metadata.getMetadataCategories().add(metadataCategory); + metadata.getCategories().add(metadataCategory); } else { log.warning("Unable to map category: " + catId + " to a category in Geonetwork"); } diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/HarvestManagerImpl.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/HarvestManagerImpl.java index cbb2a832d98..969d803467b 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/HarvestManagerImpl.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/HarvestManagerImpl.java @@ -599,7 +599,7 @@ public synchronized OperResult clearBatch(String id) throws Exception { String harvesterUUID = ah.getParams().getUuid(); - final Specification specification = MetadataSpecs.hasHarvesterUuid(harvesterUUID); + final Specification specification = (Specification) MetadataSpecs.hasHarvesterUuid(harvesterUUID); int numberOfRecordsRemoved = dataMan.batchDeleteMetadataAndUpdateIndex(specification); ah.emptyResult(); elapsedTime = (System.currentTimeMillis() - elapsedTime) / 1000; diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/arcsde/ArcSDEHarvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/arcsde/ArcSDEHarvester.java index d70b88ca090..852e52ed4d6 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/arcsde/ArcSDEHarvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/arcsde/ArcSDEHarvester.java @@ -67,6 +67,7 @@ import org.fao.geonet.utils.Xml; import org.jdom.Element; import org.jdom.Namespace; +import org.springframework.data.jpa.domain.Specification; import com.google.common.collect.Sets; @@ -348,7 +349,7 @@ private void align(Map metadataList) throws Exception { // not in this harvesting result // Set idsResultHs = Sets.newHashSet(idsForHarvestingResult); - List existingMetadata = context.getBean(MetadataRepository.class).findAllIdsBy(MetadataSpecs.hasHarvesterUuid(params.getUuid())); + List existingMetadata = context.getBean(MetadataRepository.class).findAllIdsBy((Specification) MetadataSpecs.hasHarvesterUuid(params.getUuid())); for (Integer existingId : existingMetadata) { if (cancelMonitor.get()) { @@ -387,10 +388,10 @@ private void updateMetadata(Element xml, String id, GroupMapper localGroups, fin true); OperationAllowedRepository operationAllowedRepository = context.getBean(OperationAllowedRepository.class); - operationAllowedRepository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(id)); + operationAllowedRepository.deleteAllByMetadataId(Integer.parseInt(id)); aligner.addPrivileges(id, params.getPrivileges(), localGroups, dataMan, context, log); - metadata.getMetadataCategories().clear(); + metadata.getCategories().clear(); aligner.addCategories(metadata, params.getCategories(), localCateg, context, log, null, true); dataMan.flush(); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/csw/Aligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/csw/Aligner.java index 3b7565ea799..9ffae132f50 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/csw/Aligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/csw/Aligner.java @@ -385,11 +385,11 @@ private boolean updatingLocalMetadata(RecordInfo ri, String id, Boolean force) t } OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(id)); + repository.deleteAllByMetadataId(Integer.parseInt(id)); addPrivileges(id, params.getPrivileges(), localGroups, dataMan, context, log); - metadata.getMetadataCategories().clear(); + metadata.getCategories().clear(); addCategories(metadata, params.getCategories(), localCateg, context, log, null, true); return true; diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/fragment/FragmentHarvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/fragment/FragmentHarvester.java index 26c6aa88a69..57a5a52877c 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/fragment/FragmentHarvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/fragment/FragmentHarvester.java @@ -576,7 +576,7 @@ private void update(String id, Element template, String title, boolean isSubtemp final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); AbstractMetadata metadata = metadataRepository.findOne(iId); OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, iId); + repository.deleteAllByMetadataId(iId); if (isSubtemplate) { // Note: we use fragmentAllPrivs here because subtemplates need to be @@ -586,7 +586,7 @@ private void update(String id, Element template, String title, boolean isSubtemp addPrivileges(id, params.privileges, localGroups, dataMan, context, log); } - metadata.getMetadataCategories().clear(); + metadata.getCategories().clear(); addCategories(metadata, params.categories, localCateg, context, log, null, true); if (isSubtemplate) { @@ -632,7 +632,7 @@ private void createMetadata(String recUuid, Element template) throws Exception, if (metadataCategory == null) { throw new IllegalArgumentException("No category found with name: " + params.isoCategory); } - metadata.getMetadataCategories().add(metadataCategory); + metadata.getCategories().add(metadataCategory); } metadata = dataMan.insertMetadata(context, metadata, template, true, false, false, UpdateDatestamp.NO, false, false); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java index 11839030348..e1eef2141ea 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java @@ -230,10 +230,10 @@ private void updateMetadata(RecordInfo ri, String id) throws Exception { final AbstractMetadata metadata = dataMan.updateMetadata(context, id, md, validate, ufo, index, language, ri.changeDate, false); OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(id)); + repository.deleteAllByMetadataId(Integer.parseInt(id)); addPrivileges(id, params.getPrivileges(), localGroups, dataMan, context, log); - metadata.getMetadataCategories().clear(); + metadata.getCategories().clear(); addCategories(metadata, params.getCategories(), localCateg, context, log, null, true); dataMan.flush(); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java index 3b960c6e66b..b1c0959e025 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java @@ -840,7 +840,8 @@ private void updateMetadata(RecordInfo ri, String id, Element md, metadataManager.save(metadata); } } - metadata.getMetadataCategories().clear(); + + metadata.getCategories().clear(); addCategories(metadata, params.getCategories(), localCateg, context, log, null, true); metadata = metadataRepository.findOne(id); @@ -867,7 +868,7 @@ private void updateMetadata(RecordInfo ri, String id, Element md, } OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(id)); + repository.deleteAllByMetadataId(Integer.parseInt(id)); if (((ArrayList) params.getGroupCopyPolicy()).size() == 0) { addPrivileges(id, params.getPrivileges(), localGroups, dataMan, context, log); } else { diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java index 52fe6329c57..504b63b94b0 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java @@ -264,7 +264,7 @@ public boolean apply(@Nullable String input) { log.debug(" - Setting categories : " + categories); } - metadata.getMetadataCategories().addAll(categories); + metadata.getCategories().addAll(categories); } private void addPrivileges(String id) throws Exception { @@ -325,7 +325,6 @@ private void updateCategories(String id, Element info) throws Exception { //--- remove old categories - @SuppressWarnings("unchecked") Collection locCateg = dataMan.getCategories(id); for (MetadataCategory el : locCateg) { diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java index fb7d1688890..95acc4c43b5 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java @@ -56,6 +56,7 @@ import org.fao.geonet.resources.Resources; import org.fao.geonet.utils.IO; import org.jdom.Element; +import org.springframework.data.jpa.domain.Specification; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -141,7 +142,7 @@ private HarvestResult align(Path root) throws Exception { log.debug("Starting to delete locally existing metadata " + "from the same source if they " + " were not in this harvesting result..."); - List existingMetadata = context.getBean(MetadataRepository.class).findAllIdsBy(MetadataSpecs.hasHarvesterUuid(params.getUuid())); + List existingMetadata = context.getBean(MetadataRepository.class).findAllIdsBy((Specification) MetadataSpecs.hasHarvesterUuid(params.getUuid())); for (Integer existingId : existingMetadata) { if (cancelMonitor.get()) { @@ -186,10 +187,10 @@ void updateMetadata(Element xml, final String id, GroupMapper localGroups, true); OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(id)); + repository.deleteAllByMetadataId(Integer.parseInt(id)); aligner.addPrivileges(id, params.getPrivileges(), localGroups, dataMan, context, log); - metadata.getMetadataCategories().clear(); + metadata.getCategories().clear(); aligner.addCategories(metadata, params.getCategories(), localCateg, context, log, null, true); dataMan.flush(); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java index 6cd1fa25308..5b79a512ebd 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java @@ -494,10 +494,10 @@ private void updateMetadata(XmlRequest t, RecordInfo ri, String id, String proce //--- web interface so we have to re-set both OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(id)); + repository.deleteAllByMetadataId(Integer.parseInt(id)); addPrivileges(id, params.getPrivileges(), localGroups, dataMan, context, log); - metadata.getMetadataCategories().clear(); + metadata.getCategories().clear(); addCategories(metadata, params.getCategories(), localCateg, context,log, null, true); dataMan.flush(); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/ogcwxs/Harvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/ogcwxs/Harvester.java index cdee8ee7785..0a15b7c4802 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/ogcwxs/Harvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/ogcwxs/Harvester.java @@ -845,7 +845,7 @@ private WxSLayerRegistry addLayerMetadata(Element layer, Element capa) throws JD if (metadataCategory == null) { throw new IllegalArgumentException("No category found with name: " + params.datasetCategory); } - metadata.getMetadataCategories().add(metadataCategory); + metadata.getCategories().add(metadataCategory); } if (!dataMan.existsMetadataUuid(reg.uuid)) { result.addedMetadata++; diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java index 9b3ae6602e2..0c9983d51e3 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java @@ -484,10 +484,10 @@ private void updateMetadata(RemoteFile rf, RecordInfo record, Boolean force) thr //--- the administrator could change privileges and categories using the //--- web interface so we have to re-set both OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByIdAttribute(OperationAllowedId_.metadataId, Integer.parseInt(record.id)); + repository.deleteAllByMetadataId(Integer.parseInt(record.id)); addPrivileges(record.id, params.getPrivileges(), localGroups, dataMan, context, log); - metadata.getMetadataCategories().clear(); + metadata.getCategories().clear(); addCategories(metadata, params.getCategories(), localCateg, context, log, null, true); dataMan.flush(); diff --git a/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java b/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java index 6edf1366282..bef8d287d00 100644 --- a/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java +++ b/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java @@ -34,6 +34,7 @@ import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.InspireAtomFeed; +import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataType; import org.fao.geonet.inspireatom.util.InspireAtomUtil; import org.fao.geonet.kernel.DataManager; @@ -47,6 +48,7 @@ import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.jdom.Element; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; import jeeves.server.context.ServiceContext; @@ -149,7 +151,9 @@ public final void harvestServiceMetadata(final ServiceContext context, final Str SettingManager sm = context.getBean(SettingManager.class); final IMetadataUtils metadataUtils = gc.getBean(IMetadataUtils.class); - AbstractMetadata iso19139Metadata = metadataUtils.findOne(Specifications.where(MetadataSpecs.isType(MetadataType.METADATA)).and(MetadataSpecs.isIso19139Schema())); + AbstractMetadata iso19139Metadata = metadataUtils.findOne( + Specifications.where((Specification) MetadataSpecs.isType(MetadataType.METADATA)) + .and((Specification) MetadataSpecs.isIso19139Schema())); Element result = new Element("response"); diff --git a/integration-test/src/test/resources/features/001.login.feature b/integration-test/src/test/resources/features/001.login.feature index d9ac53b6db1..c429cb86472 100644 --- a/integration-test/src/test/resources/features/001.login.feature +++ b/integration-test/src/test/resources/features/001.login.feature @@ -8,7 +8,7 @@ Feature: GeoNetwork Login And I enter "{adminUser}" into input field having xpath "//*[@id='inputUsername']" And I enter "{adminPassword}" into input field having xpath "//*[@id='inputPassword']" And I click on element having css "form > button.btn-primary" - And I wait for 1 sec + And I wait 3 seconds for element having css ".gn-user-info" to display And I hover over element having css ".gn-user-info" When I click on element having css ".fa-sign-out" @@ -19,7 +19,8 @@ Feature: GeoNetwork Login And I enter "{adminUser}" into input field having xpath "//*[@id='inputUsername']" And I enter "AnInvalidPassword" into input field having xpath "//*[@id='inputPassword']" And I click on element having css "form > button.btn-primary" - And I wait for 1 sec - Then element having xpath "//strong[text() = 'Incorrect username or password.']" should be present + And I wait 3 seconds for element having xpath "//p[@data-ng-show='signinFailure']" to display + Then element having xpath "//p[@data-ng-show='signinFailure']" should be present # TODO: Create guest account when enabled or not + diff --git a/integration-test/src/test/resources/features/002.load_samples.feature b/integration-test/src/test/resources/features/002.load_samples.feature index 59cd5bec054..c76427842e7 100644 --- a/integration-test/src/test/resources/features/002.load_samples.feature +++ b/integration-test/src/test/resources/features/002.load_samples.feature @@ -10,9 +10,9 @@ Feature: GeoNetwork Load samples and template Then element having id "gn-btn-loadTemplates" should be enabled Then element having id "gn-btn-loadSamples" should be enabled And I click on element having id "gn-btn-loadTemplates" - And I wait for 5 sec + And I wait 5 seconds for element having css "div#gn-templates-reportStatus.panel-success" to display Then element having xpath "//*[@id='gn-templates-reportStatus']" should have attribute "class" with value "panel panel-default panel-success" And I click on element having id "gn-btn-loadSamples" - And I wait for 5 sec + And I wait 5 seconds for element having css "div#gn-samples-reportStatus.panel-success" to display Then element having xpath "//*[@id='gn-samples-reportStatus']" should have attribute "class" with value "panel panel-default panel-success" And I sign out diff --git a/integration-test/src/test/resources/features/020.group.feature b/integration-test/src/test/resources/features/020.group.feature index 3ec194ecbb4..6059725658a 100644 --- a/integration-test/src/test/resources/features/020.group.feature +++ b/integration-test/src/test/resources/features/020.group.feature @@ -3,6 +3,7 @@ Feature: GeoNetwork Manage group Scenario: I login and create group1 with only mandatory field ie. name Given I login as admin/admin and navigate to admin.console#/organization/groups + And I wait 5 seconds for element having id "gn-btn-group-add" to display And I click on element having id "gn-btn-group-add" Then element having id "gn-btn-group-save" should be disabled And I enter "itest-group1" into input field having id "groupname" @@ -33,7 +34,8 @@ Feature: GeoNetwork Manage group Then I click on element having id "gn-btn-group-save" Then I wait for 1 sec - # Check group is created + + Scenario: Check group is created Then I click on link having partial text "itest-group2" # TODO: Check all properties are here diff --git a/integration-test/src/test/resources/features/030.create_new_user.feature b/integration-test/src/test/resources/features/030.create_new_user.feature index a8e810cbec8..e9b20a6e1a1 100644 --- a/integration-test/src/test/resources/features/030.create_new_user.feature +++ b/integration-test/src/test/resources/features/030.create_new_user.feature @@ -11,6 +11,7 @@ Feature: GeoNetwork Create new user And I enter "Beta" into input field having name "surname" And I enter "etabeta@email.com" into input field having name "email" And I click on element having id "gn-btn-user-save" + And I wait 10 seconds for element having css ".alert-success" to display And I sign out Then I login as etabeta/PassTests and navigate to catalog.search diff --git a/integration-test/src/test/resources/features/031.delete_new_user.feature b/integration-test/src/test/resources/features/031.delete_new_user.feature index 51a30485985..d41f950df9e 100644 --- a/integration-test/src/test/resources/features/031.delete_new_user.feature +++ b/integration-test/src/test/resources/features/031.delete_new_user.feature @@ -3,6 +3,7 @@ Feature: GeoNetwork Create new user Scenario: I login and delete the etabeta user Given I login as admin/admin and navigate to admin.console#/organization + And I wait 10 seconds for element having css "a.list-group-item" to display And I click on link having partial text "Eta Beta" And I click on element having css "button.btn-danger" And I accept alert diff --git a/integration-test/src/test/resources/features/032.create_advanced_users.feature b/integration-test/src/test/resources/features/032.create_advanced_users.feature index cff1dff248b..3cd44fea450 100644 --- a/integration-test/src/test/resources/features/032.create_advanced_users.feature +++ b/integration-test/src/test/resources/features/032.create_advanced_users.feature @@ -1,8 +1,9 @@ Feature: Create (draft) editor Setup environment to test draft feature - Scenario: Setup environment to test draft feature + Scenario: Add editortest Given I login as admin/admin and navigate to admin.console#/organization + Then I maximize browser window # Add editor And I click on element having id "gn-btn-user-add" @@ -15,10 +16,12 @@ Feature: Create (draft) editor And I click on element having css "div[data-gn-multiselect*='Editor'] option" And I double click on element having css "div[data-gn-multiselect*='Editor'] option" And I wait 3 seconds for element having css "select[data-ng-model='currentSelectionRight'] > option" to display - When I click on element having id "gn-btn-user-save" + Then I scroll to top of page + And I click on element having id "gn-btn-user-save" And I wait 5 seconds for element having css "div.alert.gn-info" to display # Add editor 2 + Scenario: Add editortest2 Then I navigate to "{endPointToTest}/srv/eng/admin.console#/organization" And I click on element having id "gn-btn-user-add" And I enter "editortest2" into input field having id "username" @@ -30,10 +33,12 @@ Feature: Create (draft) editor And I click on element having css "div[data-gn-multiselect*='Editor'] option" And I double click on element having css "div[data-gn-multiselect*='Editor'] option" And I wait 3 seconds for element having css "select[data-ng-model='currentSelectionRight'] > option" to display + Then I scroll to top of page When I click on element having id "gn-btn-user-save" And I wait 5 seconds for element having css "div.alert.gn-info" to display # Add reviewer + Scenario: Add reviewertest And I click on element having id "gn-btn-user-add" And I enter "reviewertest" into input field having id "username" And I enter "editorpass" into input field having id "gn-user-password" @@ -44,14 +49,9 @@ Feature: Create (draft) editor And I click on element having css "div[data-gn-multiselect*='Reviewer'] option" And I double click on element having css "div[data-gn-multiselect*='Reviewer'] option" And I wait 3 seconds for element having css "select[data-ng-model='currentSelectionRight'] > option" to display + Then I scroll to top of page When I click on element having id "gn-btn-user-save" - Then I wait 5 seconds for element having css "div.alert.gn-info" to display - - # Import templates, just in case - Given I navigate to "{endPointToTest}/srv/eng/admin.console#/metadata" - Then I click on element having css "div.list-group > li:nth-child(1) > h4 > i" - Then I click on element having css "button > i.gn-recordtype-y" - Then I wait 10 seconds for element having css "div.panel-success" to display + And I wait 5 seconds for element having css "div.alert.gn-info" to display # Logout admin And I sign out diff --git a/integration-test/src/test/resources/features/040.create_and_publish_metadata.feature b/integration-test/src/test/resources/features/040.create_and_publish_metadata.feature index 34e37d1316f..c712359b74c 100644 --- a/integration-test/src/test/resources/features/040.create_and_publish_metadata.feature +++ b/integration-test/src/test/resources/features/040.create_and_publish_metadata.feature @@ -1,9 +1,7 @@ Feature: Create and publish record - An editor creates and publishes a record + Reviewers should be able to publish and change the workflow status - Scenario: Editor creates and publishes a record - - # Create new record + Scenario: Create new record Given I login as editortest/editorpass and navigate to catalog.edit#/create Then I click on link having partial text "preferred" And I wait 10 seconds for element having css "div.btn-group > button.btn-success" to display @@ -11,27 +9,42 @@ Feature: Create and publish record And I wait 10 seconds for element having css "div.gn-title" to display Then I clear input field having css "div.gn-title input" Then I enter "itest-metadata" into input field having css "div.gn-title input" - Then I click on element having css "button.btn-default > i.fa-sign-out" - And I wait for 3 sec - - # Add privileges to group for edit - When I navigate to "{endPointToTest}/srv/eng/catalog.edit#/board" + Then I click on element having id "gn-editor-btn-close" + + Scenario: Enable Workflow + Then I wait 10 seconds for element having css "table.gn-results-editor tr td" to display Then I click on link having text "itest-metadata" - Then I click on element having css "div.gn-md-actions-btn i.fa-cog" - And I wait 3 seconds for element having css "i.fa-key" to display - Then I click on element having css "i.fa-key" + Then I click on element having id "gn-button-manage-record" + And I wait 3 seconds for element having css ".gn-md-actions-btn i.fa-code-fork" to display + Then I click on element having css ".gn-md-actions-btn i.fa-code-fork" + Then I wait 3 seconds for element having css "div.alert-success" to display + + Scenario: Add privileges to group for edit + Then I click on element having id "gn-button-manage-record" + And I wait 3 seconds for element having css ".gn-md-actions-btn i.fa-key" to display + Then I click on element having css ".gn-md-actions-btn i.fa-key" And I wait for 1 sec Then I click on element having xpath "//*[@id="opsForm"]/table/tbody/tr[4]/td[5]/input" - And I wait for 1 sec + And I wait 5 seconds for element having css "#gn-share-btn-replace" to display Then I click on element having css "#gn-share-btn-replace" Then I wait 3 seconds for element having css "div.alert-success" to display - And I sign out + Then I sign out - # Publish record as reviewer + Scenario: Publish record as reviewer Given I login as reviewertest/editorpass and navigate to catalog.edit#/board Then I click on link having text "itest-metadata" - Then I click on element having css "div.gn-md-actions-btn i.fa-cog" - And I wait 10 seconds for element having css "i.fa-unlock" to display - Then I click on element having css "i.fa-unlock" + Then I click on element having id "gn-button-manage-record" + And I wait 10 seconds for element having css ".gn-md-actions-btn i.fa-unlock" to display + Then I click on element having css ".gn-md-actions-btn i.fa-unlock" + Then I accept alert And I wait 5 seconds for element having css "div.alert-success" to display And I sign out + + Scenario: Check metadata view as anonymous + When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search" + Then I enter "itest" into input field having css "div[data-ng-search-form] div.gn-form-any input" + Then I click on element having css "div[data-ng-search-form] button.btn-primary i.fa-search" + Then I wait for 1 sec + Then I click on link having text "itest-metadata" + And I wait 10 seconds for element having css "div.gn-md-view" to display + Then element having css ".see-draft" should not be present diff --git a/integration-test/src/test/resources/features/041.edit_created_metadata.feature b/integration-test/src/test/resources/features/041.edit_created_metadata.feature index ec141bc254f..ad6d8cc6412 100644 --- a/integration-test/src/test/resources/features/041.edit_created_metadata.feature +++ b/integration-test/src/test/resources/features/041.edit_created_metadata.feature @@ -1,25 +1,35 @@ Feature: Create Draft When trying to edit the published record, a draft is created - Scenario: When trying to edit the published record, a draft is created - - # Edit published record + Scenario: Edit published record Given I login as editortest/editorpass and navigate to catalog.edit#/board + Then I wait 10 seconds for element having css "table.gn-results-editor tr td" to display Then I click on link having text "itest-metadata" Then I click on element having css ".gn-md-edit-btn" And I wait 10 seconds for element having css "div.gn-title" to display Then I clear input field having css "div.gn-title input" Then I enter "itest-draft" into input field having css "div.gn-title input" Then I click on element having css "#gn-editor-btn-close" - And I wait for 3 sec + Then I wait for 3 sec - #Check metadata has been modified + Scenario: Check metadata is viewable by editor (new value) When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search" - Then I click on link having text "itest-draft" - And I wait 10 seconds for element having css "div.gn-md-view" to display + Then I enter "itest" into input field having css "div[data-ng-search-form] div.gn-form-any input" + Then I click on element having css "div[data-ng-search-form] button.btn-primary i.fa-search" + Then I wait for 1 sec + Then I click on link having text "itest-metadata" + And I wait 2 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display + Then I click on element having css ".gn-view-approved .see-draft-not-approved" + And I wait 2 seconds for element having css ".gn-view-not-approved .see-draft-approved" to display + Then I click on element having css ".gn-view-not-approved .see-draft-approved" + And I wait 2 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display And I sign out - # Check metadata view as anonymous + Scenario: Check metadata view as anonymous (old value) When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search" - Then I click on link having text "itest-draft" + Then I enter "itest" into input field having css "div[data-ng-search-form] div.gn-form-any input" + Then I click on element having css "div[data-ng-search-form] button.btn-primary i.fa-search" + Then I wait for 1 sec + Then I click on link having text "itest-metadata" And I wait 10 seconds for element having css "div.gn-md-view" to display + Then element having css ".see-draft" should not be present diff --git a/integration-test/src/test/resources/features/042.edit_with_another_editor.feature b/integration-test/src/test/resources/features/042.edit_with_another_editor.feature index 976efa7ae89..46fde8b3767 100644 --- a/integration-test/src/test/resources/features/042.edit_with_another_editor.feature +++ b/integration-test/src/test/resources/features/042.edit_with_another_editor.feature @@ -1,26 +1,37 @@ Feature: Edit with second editor When another editor tries to edit a published metadata, it uses the same draft - Scenario: When another editor tries to edit a published metadata, it uses the same draft - # Edit published record + Scenario: Edit published record Given I login as editortest2/editorpass and navigate to catalog.edit#/board - Then I click on link having text "itest-draft" - Then I click on element having css "a.gn-md-edit-btn" + Then I click on link having text "itest-metadata" + And I wait 2 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display + Then I click on element having css ".gn-view-approved .see-draft-not-approved" + And I wait 3 seconds for element having css ".gn-view-not-approved .gn-md-edit-btn" to display + Then I click on element having css ".gn-view-not-approved .gn-md-edit-btn" And I wait 10 seconds for element having css "div.gn-title" to display Then I clear input field having css "div.gn-title input" Then I enter "itest-second Version" into input field having css "div.gn-title input" - Then I click on element having css "#gn-editor-btn-close" + Then I click on element having id "gn-editor-btn-close" And I wait for 3 sec - #Check metadata has been modified - When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search" - Then I click on link having text "itest-second Version" - And I wait 10 seconds for element having css "div.gn-md-view" to display + Scenario: Check metadata has been modified (new value) + Given I navigate to "{endPointToTest}/srv/eng/catalog.search#/search?resultType=details&sortBy=relevance&from=1&to=20&fast=index&_content_type=json&any=itest" + And I wait 3 seconds for element having css "div.gn-md-title" to display + Then I click on element having css "div.gn-md-title a[title='itest-metadata']" + And I wait 2 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display + Then I click on element having css ".gn-view-approved .see-draft-not-approved" + And I wait 2 seconds for element having css ".gn-view-not-approved .see-draft-approved" to display + #Not supported + #Then element having text "itest-second Version" should be present + Then I click on element having css ".gn-view-not-approved .see-draft-approved" + And I wait 2 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display # Logout as second editor And I sign out - # Check metadata view as anonymous - When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search" - Then I click on link having text "itest-second Version" + Scenario: Check metadata view as anonymous (old value) + Given I navigate to "{endPointToTest}/srv/eng/catalog.search#/search?resultType=details&sortBy=relevance&from=1&to=20&fast=index&_content_type=json&any=itest" + And I wait 3 seconds for element having css "div.gn-md-title" to display + Then I click on element having css "div.gn-md-title a[title='itest-metadata']" And I wait 10 seconds for element having css "div.gn-md-view" to display + Then element having css ".see-draft" should not be present diff --git a/integration-test/src/test/resources/features/043.edit_and_publish_with_reviewer.feature b/integration-test/src/test/resources/features/043.edit_and_publish_with_reviewer.feature index 0483de16cf2..1b7641f1330 100644 --- a/integration-test/src/test/resources/features/043.edit_and_publish_with_reviewer.feature +++ b/integration-test/src/test/resources/features/043.edit_and_publish_with_reviewer.feature @@ -1,33 +1,61 @@ Feature: Edit with reviewer When a reviewer tries to edit a published metadata, it uses the same draft - Scenario: When a reviewer tries to edit a published metadata, it uses the same draft + + Scenario: Edit published record Given I login as reviewertest/editorpass and navigate to catalog.edit#/board - # Edit published record - Then I click on link having text "itest-second Version" + Then I click on link having text "itest-metadata" Then I click on element having css "a.gn-md-edit-btn" And I wait 10 seconds for element having css "div.gn-title" to display Then I clear input field having css "div.gn-title input" Then I enter "itest-third Version" into input field having css "div.gn-title input" - Then I click on element having css "button.btn-primary > i.fa-save" + Then I click on element having id "gn-editor-btn-save" And I wait for 3 sec - #Check metadata has been modified - When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search" + Scenario: Check metadata has been modified (new value) + When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search?resultType=details&sortBy=relevance&from=1&to=20&fast=index&_content_type=json&any=itest" Then I accept alert + Then I click on link having text "itest-metadata" + And I wait 4 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display + Then I click on element having css ".gn-view-approved .see-draft-not-approved" + And I wait 4 seconds for element having css ".gn-view-not-approved .see-draft-approved" to display + Then I click on element having css ".gn-view-not-approved .see-draft-approved" + And I wait 4 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display + And I sign out + + Scenario: Approve record as reviewer + Given I login as reviewertest/editorpass and navigate to catalog.edit#/board + Then I click on link having text "itest-metadata" + And I wait 5 seconds for element having css ".gn-view-approved .see-draft-not-approved" to display + Then I click on element having css ".gn-view-approved .see-draft-not-approved" + Then I click on element having css ".gn-view-not-approved #gn-button-manage-record" + And I wait 3 seconds for element having css ".gn-view-not-approved .gn-md-actions-btn i.fa-code-fork" to display + Then I click on element having css ".gn-view-not-approved .gn-md-actions-btn i.fa-code-fork" + Then I wait 3 seconds for element having id "gn-workflow-status-change" to display + Then I wait 3 seconds for element having css "#gn-workflow-status-change input[type='radio']" to be enabled + Then I select "2" option by value from radio button group having name "status" + Then I click on element having id "gn-record-status-accept" + And I wait 5 seconds for element having css "div.alert-success" to display + And I sign out + + Scenario: Check metadata view as anonymous (new value) + Given I navigate to "{endPointToTest}/srv/eng/catalog.search#/search?resultType=details&sortBy=relevance&from=1&to=20&fast=index&_content_type=json&any=itest" Then I click on link having text "itest-third Version" And I wait 10 seconds for element having css "div.gn-md-view" to display + Then element having css ".see-draft" should not be present - # Unpublish record - When I navigate to "{endPointToTest}/srv/eng/catalog.edit#/board" + Scenario: Unpublish record + Given I login as reviewertest/editorpass and navigate to catalog.edit#/board Then I click on link having text "itest-third Version" - Then I click on element having css "div.gn-md-actions-btn i.fa-cog" + Then I click on element having id "gn-button-manage-record" And I wait 10 seconds for element having css "i.fa-lock" to display Then I click on element having css "i.fa-lock" And I wait 5 seconds for element having css "div.alert-success" to display And I sign out - # Check metadata not viewed as anonymous + Scenario: Check metadata not viewed as anonymous When I navigate to "{endPointToTest}/srv/eng/catalog.search#/search" - Then element having xpath "//*a[title='Integration test third Version']" should not be present + Then I enter "itest" into input field having css "div[data-ng-search-form] div.gn-form-any input" + Then I click on element having css "div[data-ng-search-form] button.btn-primary i.fa-search" + Then element having xpath "//*a[title='itest-third Version']" should not be present diff --git a/integration-test/src/test/resources/features/098.cleanup.feature b/integration-test/src/test/resources/features/098.cleanup.feature index 0a24bfc8f22..0adfe8a3d77 100644 --- a/integration-test/src/test/resources/features/098.cleanup.feature +++ b/integration-test/src/test/resources/features/098.cleanup.feature @@ -5,27 +5,31 @@ Feature: Cleanup Draft Tests # Login as admin Given I login as admin/admin and navigate to catalog.edit#/board - # Remove all records - Then I click on element having css "div.gn-editor-board button.dropdown-toggle" + Scenario: Remove all records + Then I wait 3 seconds for element having css "div.gn-editor-board .gn-select button.dropdown-toggle" to display + Then I click on element having css "div.gn-editor-board .gn-select button.dropdown-toggle" Then I click on element having xpath "//a[@data-ng-click='selectAll()']" Then I click on element having css "div.gn-selection-actions" Then I click on element having css "div.gn-selection-actions i.fa-times" Then I accept alert And I wait 3 seconds for element having css "div.alert-warning" to display - # Remove editors + Scenario: Remove editors When I navigate to "{endPointToTest}/srv/eng/admin.console#/organization" Then I click on link having partial text "Edi Thor" Then I click on element having css "button.btn-danger" And I accept alert - And I wait for 1 sec + #Need to refresh because DOM elements still not garbage collected (chrome) + Then I refresh page Then I click on link having partial text "Revi Ewer" Then I click on element having css "button.btn-danger" And I accept alert - And I wait for 1 sec + #Need to refresh because DOM elements still not garbage collected (chrome) + Then I refresh page Then I click on link having partial text "Edi Thor" Then I click on element having css "button.btn-danger" And I accept alert + And I wait for 1 sec - # Logout + Scenario: Logout And I sign out diff --git a/listeners/pom.xml b/listeners/pom.xml new file mode 100644 index 00000000000..a47ed7a9b70 --- /dev/null +++ b/listeners/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + geonetwork + org.geonetwork-opensource + 3.7.0-SNAPSHOT + + + GeoNetwork Events + jar + listeners + Contains all listeners from events. + http://geonetwork-opensource.org + + + ${project.groupId} + core + ${project.version} + + + diff --git a/core/src/main/java/org/fao/geonet/listeners/history/AttachmentAddedListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/AttachmentAddedListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/AttachmentAddedListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/AttachmentAddedListener.java index 6c353845f62..e8df7be7bb4 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/AttachmentAddedListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/AttachmentAddedListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.AttachmentAddedEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/AttachmentDeletedListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/AttachmentDeletedListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/AttachmentDeletedListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/AttachmentDeletedListener.java index ea3faff6278..e94a9680efc 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/AttachmentDeletedListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/AttachmentDeletedListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.AttachmentDeletedEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/GenericMetadataEventListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/GenericMetadataEventListener.java similarity index 98% rename from core/src/main/java/org/fao/geonet/listeners/history/GenericMetadataEventListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/GenericMetadataEventListener.java index d73f7ba6320..c0fc58fc2fb 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/GenericMetadataEventListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/GenericMetadataEventListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.ISODate; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordCategoryChangeListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordCategoryChangeListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordCategoryChangeListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordCategoryChangeListener.java index a186210012b..3412db6657a 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordCategoryChangeListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordCategoryChangeListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordCategoryChangeEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordCreatedListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordCreatedListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordCreatedListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordCreatedListener.java index f1b9d54b664..99909f079a6 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordCreatedListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordCreatedListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordCreateEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordDeletedListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordDeletedListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordDeletedListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordDeletedListener.java index 76f9955e3cf..df69e3c688f 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordDeletedListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordDeletedListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordDeletedEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordGroupOwnerChangeListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordGroupOwnerChangeListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordGroupOwnerChangeListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordGroupOwnerChangeListener.java index 23336fdda96..bebd54bfa7c 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordGroupOwnerChangeListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordGroupOwnerChangeListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordGroupOwnerChangeEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordImportedListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordImportedListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordImportedListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordImportedListener.java index 6789f395e0e..b3569c018c1 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordImportedListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordImportedListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordImportedEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordOwnerChangeListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordOwnerChangeListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordOwnerChangeListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordOwnerChangeListener.java index d7936216ea5..434f8b5d519 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordOwnerChangeListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordOwnerChangeListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordOwnerChangeEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordPrivilegesChangeListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordPrivilegesChangeListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordPrivilegesChangeListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordPrivilegesChangeListener.java index 03738378eae..1662c04f5d6 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordPrivilegesChangeListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordPrivilegesChangeListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordPrivilegesChangeEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordProcessingChangeListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordProcessingChangeListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordProcessingChangeListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordProcessingChangeListener.java index c71f1b89e6f..811eb0dc833 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordProcessingChangeListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordProcessingChangeListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordProcessingChangeEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordUpdatedListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordUpdatedListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordUpdatedListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordUpdatedListener.java index c0d2b57ca5f..bcda0cf43d8 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordUpdatedListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordUpdatedListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordUpdatedEvent; diff --git a/core/src/main/java/org/fao/geonet/listeners/history/RecordValidationTriggeredListener.java b/listeners/src/main/java/org/fao/geonet/listener/history/RecordValidationTriggeredListener.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/history/RecordValidationTriggeredListener.java rename to listeners/src/main/java/org/fao/geonet/listener/history/RecordValidationTriggeredListener.java index 3d51ba0c603..a34d961f160 100644 --- a/core/src/main/java/org/fao/geonet/listeners/history/RecordValidationTriggeredListener.java +++ b/listeners/src/main/java/org/fao/geonet/listener/history/RecordValidationTriggeredListener.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.history; +package org.fao.geonet.listener.history; import org.fao.geonet.domain.StatusValue; import org.fao.geonet.events.history.RecordValidationTriggeredEvent; diff --git a/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/ApprovePublishedRecord.java b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/ApprovePublishedRecord.java new file mode 100644 index 00000000000..acab1535643 --- /dev/null +++ b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/ApprovePublishedRecord.java @@ -0,0 +1,110 @@ +/* + * 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.listener.metadata.draft; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.MetadataStatus; +import org.fao.geonet.domain.MetadataStatusId; +import org.fao.geonet.domain.StatusValue; +import org.fao.geonet.events.md.MetadataPublished; +import org.fao.geonet.kernel.datamanager.IMetadataStatus; +import org.fao.geonet.repository.StatusValueRepository; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; + +import jeeves.server.context.ServiceContext; + +/** + * When a record with workflow enabled gets published, status will automatically + * change to approved + * + * @author delawen + */ +@Component +public class ApprovePublishedRecord implements ApplicationListener { + + @Autowired + private IMetadataStatus metadataStatus; + + @Autowired + private DraftUtilities draftUtilities; + + @Autowired + private StatusValueRepository statusValueRepository; + + @Override + @Transactional + public void onApplicationEvent(MetadataPublished event) { + } + + @TransactionalEventListener(fallbackExecution = true) + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false) + @Modifying(clearAutomatically = true) + public void doAfterCommit(MetadataPublished event) { + + Log.debug(Geonet.DATA_MANAGER, "Metadata with id " + event.getMd().getId() + " published."); + + try { + // Only do something if the workflow is enabled + MetadataStatus previousStatus = metadataStatus.getStatus(event.getMd().getId()); + if (previousStatus != null) { + draftUtilities.replaceMetadataWithDraft(event.getMd()); + if (!Integer.valueOf(StatusValue.Status.APPROVED).equals(previousStatus.getId().getStatusId())) { + changeToApproved(event.getMd(), previousStatus); + } + } + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, "Error upgrading workflow of " + event.getMd(), e); + } + } + + private void changeToApproved(AbstractMetadata md, MetadataStatus previousStatus) + throws NumberFormatException, Exception { + // This status should be associated to original record, not draft + MetadataStatus status = new MetadataStatus(); + status.setChangeMessage("Record published."); + status.setPreviousState(previousStatus.getCurrentState()); + status.setStatusValue(statusValueRepository.findOne(Integer.valueOf(StatusValue.Status.APPROVED))); + + MetadataStatusId mdStatusId = new MetadataStatusId(); + mdStatusId.setStatusId(Integer.valueOf(StatusValue.Status.APPROVED)); + mdStatusId.setMetadataId(md.getId()); + mdStatusId.setChangeDate(new ISODate()); + mdStatusId.setUserId(ServiceContext.get().getUserSession().getUserIdAsInt()); + status.setId(mdStatusId); + + metadataStatus.setStatusExt(status); + + Log.trace(Geonet.DATA_MANAGER, "Metadata with id " + md.getId() + " automatically approved due to publishing."); + } + +} diff --git a/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/ApproveRecord.java b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/ApproveRecord.java new file mode 100644 index 00000000000..295654ec02f --- /dev/null +++ b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/ApproveRecord.java @@ -0,0 +1,159 @@ +/* + * 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.listener.metadata.draft; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.domain.MetadataStatus; +import org.fao.geonet.domain.MetadataStatusId; +import org.fao.geonet.domain.StatusValue; +import org.fao.geonet.events.md.MetadataStatusChanged; +import org.fao.geonet.kernel.datamanager.IMetadataStatus; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.repository.MetadataRepository; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +/** + * When a record gets a status change, check if there is a draft associated to + * it. If there is, act accordingly (replacing record with draft and/or removing + * draft). + * + * @author delawen + */ +@Component +public class ApproveRecord implements ApplicationListener { + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + + @Autowired + private MetadataRepository metadataRepository; + + @Autowired + private IMetadataStatus metadataStatus; + + @Autowired + private DraftUtilities draftUtilities; + + @Override + public void onApplicationEvent(MetadataStatusChanged event) { + } + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + public void doAfterCommit(MetadataStatusChanged event) { + try { + Log.trace(Geonet.DATA_MANAGER, "Status changed for metadata with id " + event.getMd().getId()); + + // Handle draft accordingly to the status change + // If there is no draft involved, these operations do nothing + StatusValue status = event.getStatus(); + switch (String.valueOf(status.getId())) { + case StatusValue.Status.DRAFT: + case StatusValue.Status.SUBMITTED: + if (event.getMd() instanceof Metadata) { + Log.trace(Geonet.DATA_MANAGER, + "Replacing contents of record (ID=" + event.getMd().getId() + ") with draft, if exists."); + draftUtilities.replaceMetadataWithDraft(event.getMd()); + } + break; + case StatusValue.Status.RETIRED: + case StatusValue.Status.REJECTED: + try { + Log.trace(Geonet.DATA_MANAGER, + "Removing draft from record (ID=" + event.getMd().getId() + "), if exists."); + removeDraft(event.getMd()); + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, "Error upgrading status", e); + + } + break; + case StatusValue.Status.APPROVED: + try { + Log.trace(Geonet.DATA_MANAGER, "Replacing contents of approved record (ID=" + event.getMd().getId() + + ") with draft, if exists."); + approveWithDraft(event); + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, "Error upgrading status", e); + + } + break; + } + } catch (Throwable e) { + Log.error(Geonet.DATA_MANAGER, "Error changing workflow status of " + event.getMd(), e); + } + } + + private void removeDraft(AbstractMetadata md) throws Exception { + + if (!(md instanceof MetadataDraft)) { + md = metadataDraftRepository.findOneByUuid(md.getUuid()); + } + + if (md != null) { + draftUtilities.removeDraft((MetadataDraft) md); + } + } + + private AbstractMetadata approveWithDraft(MetadataStatusChanged event) throws NumberFormatException, Exception { + AbstractMetadata md = event.getMd(); + AbstractMetadata draft = null; + + if (md instanceof MetadataDraft) { + draft = md; + md = metadataRepository.findOneByUuid(draft.getUuid()); + + // This status should be associated to original record, not draft + MetadataStatus status = new MetadataStatus(); + status.setChangeMessage(event.getMessage()); + status.setStatusValue(event.getStatus()); + + MetadataStatusId mdStatusId = new MetadataStatusId(); + mdStatusId.setStatusId(event.getStatus().getId()); + mdStatusId.setMetadataId(md.getId()); + mdStatusId.setChangeDate(new ISODate()); + mdStatusId.setUserId(event.getUser()); + status.setId(mdStatusId); + + metadataStatus.setStatusExt(status); + + } else if (md instanceof Metadata) { + draft = metadataDraftRepository.findOneByUuid(md.getUuid()); + } + + if (draft != null) { + Log.trace(Geonet.DATA_MANAGER, "Approving record " + md.getId() + " which has a draft " + draft.getId()); + md = draftUtilities.replaceMetadataWithDraft(md, draft); + } + + return md; + } +} diff --git a/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftCleanup.java b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftCleanup.java new file mode 100644 index 00000000000..925e416303a --- /dev/null +++ b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftCleanup.java @@ -0,0 +1,76 @@ +/* + * 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.listener.metadata.draft; + +import java.util.List; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.events.md.MetadataRemove; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.repository.specification.MetadataSpecs; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; + +/** + * If an approved metadata gets removed, remove all draft associated to it. + *

      + * This doesn't need to be disabled if no draft is used, as it only removes + * drafts. + * + * @author delawen + */ +@Component +public class DraftCleanup { + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + + @Autowired + private DraftUtilities draftUtilities; + + @TransactionalEventListener + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void doAfterCommit(MetadataRemove event) { + Log.trace(Geonet.DATA_MANAGER, + "A metadata has been removed. Cleanup associated drafts of " + event.getSource()); + try { + List toRemove = metadataDraftRepository + .findAll((Specification) MetadataSpecs.hasMetadataUuid(event.getMd().getUuid())); + + for (MetadataDraft md : toRemove) { + draftUtilities.removeDraft(md); + } + } catch (Throwable e) { + Log.error(Geonet.DATA_MANAGER, "Couldn't clean up associated drafts of " + event.getSource(), e); + } + + Log.trace(Geonet.DATA_MANAGER, "Finished cleaning up of " + event.getSource()); + } +} diff --git a/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftCreated.java b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftCreated.java new file mode 100644 index 00000000000..83d5458e7a6 --- /dev/null +++ b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftCreated.java @@ -0,0 +1,81 @@ +/* + * 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.listener.metadata.draft; + +import java.util.Arrays; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.events.md.MetadataDraftAdd; +import org.fao.geonet.kernel.datamanager.IMetadataIndexer; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +/** + * If an approved metadata gets removed, remove all draft associated to it. + *

      + * This doesn't need to be disabled if no draft is used, as it only removes + * drafts. + * + * @author delawen + */ +@Component +public class DraftCreated implements ApplicationListener { + + @Autowired + private IMetadataUtils metadataUtils; + + @Autowired + private IMetadataIndexer metadataIndexer; + + @Override + public void onApplicationEvent(MetadataDraftAdd event) { + } + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION) + public void doAfterCommit(MetadataDraftAdd event) { + Log.trace(Geonet.DATA_MANAGER, "Reindexing non drafted versions of uuid " + event.getMd().getUuid()); + try { + for (AbstractMetadata md : metadataUtils.findAllByUuid(event.getMd().getUuid())) { + if (!(md instanceof MetadataDraft)) { + Log.trace(Geonet.DATA_MANAGER, "Reindexing " + md.getId()); + try { + metadataIndexer.indexMetadata(Arrays.asList(String.valueOf(md.getId()))); + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, e, e); + } + } + } + } catch (Throwable e) { + Log.error(Geonet.DATA_MANAGER, "Couldn't reindex the non drafted versions of " + event.getMd(), e); + } + } + +} diff --git a/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftRemoved.java b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftRemoved.java new file mode 100644 index 00000000000..bc957e84554 --- /dev/null +++ b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftRemoved.java @@ -0,0 +1,86 @@ +/* + * 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.listener.metadata.draft; + +import java.util.Arrays; +import java.util.List; + +import javax.transaction.Transactional; +import javax.transaction.Transactional.TxType; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.events.md.MetadataDraftRemove; +import org.fao.geonet.kernel.datamanager.IMetadataIndexer; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +/** + * If an approved metadata gets removed, remove all draft associated to it. + *

      + * This doesn't need to be disabled if no draft is used, as it only removes + * drafts. + * + * @author delawen + */ +@Component +public class DraftRemoved { + + @Autowired + private IMetadataUtils metadataUtils; + + @Autowired + private IMetadataIndexer metadataIndexer; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION, fallbackExecution = true) + public void doAfterCommit(MetadataDraftRemove event) { + Log.trace(Geonet.DATA_MANAGER, "Reindexing non drafted versions of uuid " + event.getMd().getUuid()); + + try { + for (AbstractMetadata md : getRecords(event)) { + if (!(md instanceof MetadataDraft)) { + Log.trace(Geonet.DATA_MANAGER, "Reindexing " + md.getId()); + try { + metadataIndexer.indexMetadata(Arrays.asList(String.valueOf(md.getId()))); + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, e, e); + } + } + } + } catch (Throwable e) { + Log.error(Geonet.DATA_MANAGER, "Couldn't reindex the non drafted versions of " + event.getMd(), e); + } + } + + @Transactional(value = TxType.REQUIRES_NEW) + private List getRecords(MetadataDraftRemove event) { + return metadataUtils.findAllByUuid(event.getMd().getUuid()); + } + +} diff --git a/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftUtilities.java b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftUtilities.java new file mode 100644 index 00000000000..b806e214003 --- /dev/null +++ b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/DraftUtilities.java @@ -0,0 +1,179 @@ +package org.fao.geonet.listener.metadata.draft; + +import java.util.List; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.domain.MetadataFileUpload; +import org.fao.geonet.domain.MetadataStatus; +import org.fao.geonet.domain.MetadataStatusId; +import org.fao.geonet.domain.MetadataStatusId_; +import org.fao.geonet.domain.MetadataStatus_; +import org.fao.geonet.domain.MetadataValidation; +import org.fao.geonet.kernel.XmlSerializer; +import org.fao.geonet.kernel.datamanager.IMetadataManager; +import org.fao.geonet.kernel.datamanager.IMetadataOperations; +import org.fao.geonet.kernel.datamanager.draft.DraftMetadataUtils; +import org.fao.geonet.kernel.search.SearchManager; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.repository.MetadataFileUploadRepository; +import org.fao.geonet.repository.MetadataRatingByIpRepository; +import org.fao.geonet.repository.MetadataStatusRepository; +import org.fao.geonet.repository.MetadataValidationRepository; +import org.fao.geonet.repository.SortUtils; +import org.fao.geonet.repository.specification.MetadataFileUploadSpecs; +import org.fao.geonet.utils.Log; +import org.jdom.Element; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import jeeves.server.context.ServiceContext; + +@Service +public class DraftUtilities { + + @Autowired + private IMetadataManager metadataManager; + + @Autowired + private MetadataValidationRepository metadataValidationRepository; + + @Autowired + private MetadataFileUploadRepository metadataFileUploadRepository; + + @Autowired + private SearchManager searchManager; + + @Autowired + private XmlSerializer xmlSerializer; + + @Autowired + private IMetadataOperations metadataOperations; + + @Autowired + private MetadataStatusRepository metadataStatusRepository; + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + + @Autowired + private MetadataRatingByIpRepository metadataRatingByIpRepository; + + @Autowired + private DraftMetadataUtils draftMetadataUtils; + + /** + * Replace the contents of the record with the ones on the draft, if exists, and + * remove the draft + * + * @param md + * @param draft + * @return + */ + public AbstractMetadata replaceMetadataWithDraft(AbstractMetadata md) { + AbstractMetadata draft = metadataDraftRepository.findOneByUuid(md.getUuid()); + + if (draft != null) { + return replaceMetadataWithDraft(md, draft); + } + + return md; + } + + /** + * Replace the contents of the record with the ones on the draft and remove the + * draft + * + * @param md + * @param draft + * @return + */ + public AbstractMetadata replaceMetadataWithDraft(AbstractMetadata md, AbstractMetadata draft) { + Log.trace(Geonet.DATA_MANAGER, "Found approved record with id " + md.getId()); + Log.trace(Geonet.DATA_MANAGER, "Found draft with id " + draft.getId()); + // Reassign metadata validations + List validations = metadataValidationRepository.findAllById_MetadataId(draft.getId()); + for (MetadataValidation mv : validations) { + mv.getId().setMetadataId(md.getId()); + metadataValidationRepository.save(mv); + } + + // Reassign metadata workflow statuses + List statuses = metadataStatusRepository.findAllById_MetadataId(draft.getId(), + SortUtils.createSort(MetadataStatus_.id, MetadataStatusId_.metadataId)); + for (MetadataStatus old : statuses) { + MetadataStatus st = new MetadataStatus(); + st.setChangeMessage(old.getChangeMessage()); + st.setCloseDate(old.getCloseDate()); + st.setCurrentState(old.getCurrentState()); + st.setOwner(old.getOwner()); + st.setPreviousState(old.getPreviousState()); + st.setStatusValue(old.getStatusValue()); + MetadataStatusId id = new MetadataStatusId(); + id.setChangeDate(old.getId().getChangeDate()); + id.setStatusId(old.getId().getStatusId()); + id.setUserId(old.getId().getUserId()); + id.setMetadataId(md.getId()); + st.setId(id); + metadataStatusRepository.save(st); + metadataStatusRepository.delete(old); + } + + // Reassign file uploads + draftMetadataUtils.cloneFiles(draft, md); + metadataFileUploadRepository.deleteAll(MetadataFileUploadSpecs.hasMetadataId(md.getId())); + List fileUploads = metadataFileUploadRepository + .findAll(MetadataFileUploadSpecs.hasMetadataId(draft.getId())); + for (MetadataFileUpload fu : fileUploads) { + fu.setMetadataId(md.getId()); + metadataFileUploadRepository.save(fu); + } + + try { + ServiceContext context = ServiceContext.get(); + Element xmlData = draft.getXmlData(false); + String changeDate = draft.getDataInfo().getChangeDate().getDateAndTime(); + + removeDraft((MetadataDraft) draft); + + // Copy contents + Log.trace(Geonet.DATA_MANAGER, "Update record with id " + md.getId()); + md = metadataManager.updateMetadata(context, String.valueOf(md.getId()), + xmlData, false, false, true, + context.getLanguage(), changeDate, true); + + Log.info(Geonet.DATA_MANAGER, "Record updated with draft contents: " + md.getId()); + + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, "Error upgrading from draft record with id " + md.getId(), e); + } + return md; + } + + public void removeDraft(MetadataDraft draft) { + + Integer id = draft.getId(); + if (!metadataDraftRepository.exists(id)) { + // We are being called after removing everything related to this record. + // Nothing to do here + return; + } + + Log.trace(Geonet.DATA_MANAGER, "Removing draft " + draft); + + try { + // Remove related data + metadataOperations.deleteMetadataOper(String.valueOf(id), false); + metadataRatingByIpRepository.deleteAllById_MetadataId(id); + metadataValidationRepository.deleteAllById_MetadataId(id); + metadataStatusRepository.deleteAllById_MetadataId(id); + + // --- remove metadata + xmlSerializer.delete(String.valueOf(id), ServiceContext.get()); + searchManager.delete(id + ""); + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, "Couldn't cleanup draft " + draft, e); + } + } +} diff --git a/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/UpdateOperations.java b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/UpdateOperations.java new file mode 100644 index 00000000000..259413f352a --- /dev/null +++ b/listeners/src/main/java/org/fao/geonet/listener/metadata/draft/UpdateOperations.java @@ -0,0 +1,137 @@ +/* + * 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.listener.metadata.draft; + +import java.util.Arrays; + +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.MetadataDraft; +import org.fao.geonet.domain.OperationAllowed; +import org.fao.geonet.domain.ReservedOperation; +import org.fao.geonet.events.md.sharing.MetadataShare; +import org.fao.geonet.events.md.sharing.MetadataShare.Type; +import org.fao.geonet.kernel.datamanager.IMetadataIndexer; +import org.fao.geonet.kernel.datamanager.IMetadataOperations; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.repository.GroupRepository; +import org.fao.geonet.repository.MetadataDraftRepository; +import org.fao.geonet.utils.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import jeeves.server.context.ServiceContext; + +/** + * When a record modify the privileges, cascade to draft + * + * @author delawen + */ +@Component +public class UpdateOperations implements ApplicationListener { + + @Autowired + private IMetadataUtils metadataUtils; + + @Autowired + private IMetadataIndexer metadataIndexer; + + @Autowired + private IMetadataOperations metadataOperations; + + @Autowired + private GroupRepository groupRepository; + + @Autowired + private MetadataDraftRepository metadataDraftRepository; + + @Override + public void onApplicationEvent(MetadataShare event) { + } + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + public void doAfterCommit(MetadataShare event) { + + try { + + Log.trace(Geonet.DATA_MANAGER, "UpdateOperationsListener: " + event.getRecord() + " op: " + + event.getOp().getId().getOperationId()); + AbstractMetadata md = metadataUtils.findOne(event.getRecord()); + + if (md == null) { + // The metadata is still being created, no need to check for draft. + // If we try to update now, it could lead us to concurrency issues + return; + } + + if (md instanceof MetadataDraft) { + Log.trace(Geonet.DATA_MANAGER, "Draft privileges are handled on approved record: " + event.getOp()); + } else { + MetadataDraft draft = metadataDraftRepository.findOneByUuid(md.getUuid()); + + if (draft != null) { + // Copy privileges from original metadata + OperationAllowed op = event.getOp(); + ServiceContext context = ServiceContext.get(); + + // Only interested in editing and reviewing privileges + // No one else should be able to see it + if (op.getId().getOperationId() == ReservedOperation.editing.getId()) { + Log.trace(Geonet.DATA_MANAGER, "Updating privileges on draft " + draft.getId()); + + // except for reserved groups + Group g = groupRepository.findOne(op.getId().getGroupId()); + if (!g.isReserved()) { + try { + if (event.getType() == Type.REMOVE) { + Log.trace(Geonet.DATA_MANAGER, "Removing editing on group " + + op.getId().getGroupId() + " for draft " + draft.getId()); + + metadataOperations.forceUnsetOperation(context, draft.getId(), + op.getId().getGroupId(), op.getId().getOperationId()); + } else { + Log.trace(Geonet.DATA_MANAGER, "Adding editing on group " + op.getId().getGroupId() + + " for draft " + draft.getId()); + + metadataOperations.forceSetOperation(context, draft.getId(), + op.getId().getGroupId(), op.getId().getOperationId()); + } + metadataIndexer.indexMetadata(Arrays.asList(String.valueOf(draft.getId()))); + } catch (Exception e) { + Log.error(Geonet.DATA_MANAGER, "Error cascading operation to draft", e); + } + } + } + + } + } + } catch (Throwable e) { + Log.error(Geonet.DATA_MANAGER, "Couldn't update the operations of the draft " + event.getRecord(), e); + } + } +} diff --git a/core/src/main/java/org/fao/geonet/listeners/security/CheckUsername.java b/listeners/src/main/java/org/fao/geonet/listener/security/CheckUsername.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/security/CheckUsername.java rename to listeners/src/main/java/org/fao/geonet/listener/security/CheckUsername.java index 9f7e3cd92c5..af561fb4756 100644 --- a/core/src/main/java/org/fao/geonet/listeners/security/CheckUsername.java +++ b/listeners/src/main/java/org/fao/geonet/listener/security/CheckUsername.java @@ -20,7 +20,7 @@ * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.security; +package org.fao.geonet.listener.security; import org.apache.commons.lang.StringUtils; import org.fao.geonet.domain.User; diff --git a/core/src/main/java/org/fao/geonet/listeners/security/CheckUsernameCreation.java b/listeners/src/main/java/org/fao/geonet/listener/security/CheckUsernameCreation.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/security/CheckUsernameCreation.java rename to listeners/src/main/java/org/fao/geonet/listener/security/CheckUsernameCreation.java index 7ca36460583..e3a6277fec9 100644 --- a/core/src/main/java/org/fao/geonet/listeners/security/CheckUsernameCreation.java +++ b/listeners/src/main/java/org/fao/geonet/listener/security/CheckUsernameCreation.java @@ -20,7 +20,7 @@ and United Nations Environment Programme (UNEP) Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.security; +package org.fao.geonet.listener.security; import org.fao.geonet.events.user.UserCreated; import org.springframework.context.ApplicationListener; diff --git a/core/src/main/java/org/fao/geonet/listeners/security/CheckUsernameUpdate.java b/listeners/src/main/java/org/fao/geonet/listener/security/CheckUsernameUpdate.java similarity index 97% rename from core/src/main/java/org/fao/geonet/listeners/security/CheckUsernameUpdate.java rename to listeners/src/main/java/org/fao/geonet/listener/security/CheckUsernameUpdate.java index b20000b7211..5d533a8f25b 100644 --- a/core/src/main/java/org/fao/geonet/listeners/security/CheckUsernameUpdate.java +++ b/listeners/src/main/java/org/fao/geonet/listener/security/CheckUsernameUpdate.java @@ -20,7 +20,7 @@ and United Nations Environment Programme (UNEP) Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.listeners.security; +package org.fao.geonet.listener.security; import org.fao.geonet.events.user.UserUpdated; import org.springframework.context.ApplicationListener; diff --git a/listeners/src/main/resources/config-spring-geonetwork-parent.xml b/listeners/src/main/resources/config-spring-geonetwork-parent.xml new file mode 100644 index 00000000000..9a7180f3d28 --- /dev/null +++ b/listeners/src/main/resources/config-spring-geonetwork-parent.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/listeners/src/main/resources/config-spring-geonetwork.xml b/listeners/src/main/resources/config-spring-geonetwork.xml new file mode 100644 index 00000000000..32e44c7abe9 --- /dev/null +++ b/listeners/src/main/resources/config-spring-geonetwork.xml @@ -0,0 +1,36 @@ + + + + + + + diff --git a/pom.xml b/pom.xml index 91be294123e..3d27c1ec93b 100644 --- a/pom.xml +++ b/pom.xml @@ -1235,6 +1235,7 @@ oaipmh events core + listeners schemas csw-server harvesters @@ -1305,8 +1306,9 @@ gn www-data www-data - 10 - + 30 + 10 + 10 diff --git a/services/src/main/java/org/fao/geonet/api/ApiUtils.java b/services/src/main/java/org/fao/geonet/api/ApiUtils.java index 1ef226ea1ed..5cf07258d91 100644 --- a/services/src/main/java/org/fao/geonet/api/ApiUtils.java +++ b/services/src/main/java/org/fao/geonet/api/ApiUtils.java @@ -54,6 +54,7 @@ import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.lib.Lib; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.utils.GeonetHttpRequestFactory; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.XmlRequest; @@ -61,6 +62,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.util.StringUtils; import com.google.common.collect.Sets; @@ -108,36 +110,39 @@ static public Set getUuidsParameterOrSelection(String[] uuids, String bu /** * Search if a record match the UUID on its UUID or an internal identifier */ - public static String getInternalId(String uuidOrInternalId) + public static String getInternalId(String uuidOrInternalId, Boolean approved) throws Exception { - String id; - DataManager dm = ApplicationContextHolder.get().getBean(DataManager.class); - id = dm.getMetadataId(uuidOrInternalId); - if (id == null) { - String checkingId = dm.getMetadataUuid(id); - if (checkingId == null) { - throw new ResourceNotFoundException(String.format( - "Record with UUID '%s' not found in this catalog", - uuidOrInternalId)); - } + IMetadataUtils metadataUtils = ApplicationContextHolder.get().getBean(IMetadataUtils.class); + String id = String.valueOf(metadataUtils.findOneByUuid(uuidOrInternalId).getId()); + + if(StringUtils.isEmpty(id)) { + //It wasn't a UUID + id = String.valueOf(metadataUtils.findOne(uuidOrInternalId).getId()); + } else if(approved) { + //It was a UUID, check if draft or approved version + id = String.valueOf(ApplicationContextHolder.get().getBean(MetadataRepository.class) + .findOneByUuid(uuidOrInternalId).getId()); + } + + if (StringUtils.isEmpty(id)) { + throw new ResourceNotFoundException(String.format( + "Record with UUID '%s' not found in this catalog", + uuidOrInternalId)); } return id; } + public static AbstractMetadata getRecord(String uuidOrInternalId) throws Exception { ApplicationContext appContext = ApplicationContextHolder.get(); IMetadataUtils metadataRepository = appContext.getBean(IMetadataUtils.class); AbstractMetadata metadata = null; - try { - metadata = metadataRepository.findOneByUuid(uuidOrInternalId); - } catch (IncorrectResultSizeDataAccessException e){ - Log.warning(Geonet.GEONETWORK, String.format( - "More than one record found with UUID '%s'. Error is '%s'.", - uuidOrInternalId, e.getMessage())); - } + + metadata = metadataRepository.findOneByUuid(uuidOrInternalId); if (metadata == null) { + Log.trace(Geonet.DATA_MANAGER, uuidOrInternalId + " not recognized as UUID. Trying ID."); try { metadata = metadataRepository.findOne(uuidOrInternalId); } catch (InvalidDataAccessApiUsageException e) { @@ -146,20 +151,21 @@ public static AbstractMetadata getRecord(String uuidOrInternalId) uuidOrInternalId)); } if (metadata == null) { + Log.trace(Geonet.DATA_MANAGER, "Record identified by " + uuidOrInternalId + " not found."); throw new ResourceNotFoundException(String.format( "Record with UUID '%s' not found in this catalog", uuidOrInternalId)); - } else { - return metadata; } - } else { - return metadata; } + + Log.trace(Geonet.DATA_MANAGER, "ApiUtils.getRecord(" + uuidOrInternalId + ") -> " + metadata); + + return metadata; } /** * Return the Jeeves user session. - * + *

      * If session is null, it's probably a bot due to {@link AllRequestsInterceptor#createSessionForAllButNotCrawlers(HttpServletRequest)}. * In such case return an exception. */ @@ -234,6 +240,34 @@ static public AbstractMetadata canEditRecord(String metadataUuid, HttpServletReq return metadata; } + /** + * Check if the current user can review this record. + */ + static public AbstractMetadata canReviewRecord(String metadataUuid, HttpServletRequest request) throws Exception { + ApplicationContext appContext = ApplicationContextHolder.get(); + AbstractMetadata metadata = getRecord(metadataUuid); + AccessManager accessManager = appContext.getBean(AccessManager.class); + if (!accessManager.canReview(createServiceContext(request), String.valueOf(metadata.getId()))) { + throw new SecurityException(String.format( + "You can't review or edit record with UUID %s", metadataUuid)); + } + return metadata; + } + + /** + * Check if the current user can change status of this record. + */ + static public AbstractMetadata canChangeStatusRecord(String metadataUuid, HttpServletRequest request) throws Exception { + ApplicationContext appContext = ApplicationContextHolder.get(); + AbstractMetadata metadata = getRecord(metadataUuid); + AccessManager accessManager = appContext.getBean(AccessManager.class); + if (!accessManager.canChangeStatus(createServiceContext(request), String.valueOf(metadata.getId()))) { + throw new SecurityException(String.format( + "You can't change status of record with UUID %s", metadataUuid)); + } + return metadata; + } + /** * Check if the current user can view this record. */ @@ -249,7 +283,6 @@ public static AbstractMetadata canViewRecord(String metadataUuid, HttpServletReq } /** - * * @param img * @param outFile * @throws IOException diff --git a/services/src/main/java/org/fao/geonet/api/categories/TagsApi.java b/services/src/main/java/org/fao/geonet/api/categories/TagsApi.java index 39a6f087658..33ac5e03b05 100644 --- a/services/src/main/java/org/fao/geonet/api/categories/TagsApi.java +++ b/services/src/main/java/org/fao/geonet/api/categories/TagsApi.java @@ -23,22 +23,22 @@ package org.fao.geonet.api.categories; -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 java.util.List; +import java.util.Map; + import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.api.API; import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.domain.Language; +import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataCategory; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.LanguageRepository; import org.fao.geonet.repository.MetadataCategoryRepository; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.specification.MetadataSpecs; import org.springframework.context.ApplicationContext; +import org.springframework.data.jpa.domain.Specification; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -51,8 +51,11 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; -import java.util.List; -import java.util.Map; +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; @RequestMapping(value = { "/api/tags", @@ -250,12 +253,12 @@ public ResponseEntity deleteTag( ApplicationContext appContext = ApplicationContextHolder.get(); MetadataCategoryRepository categoryRepository = appContext.getBean(MetadataCategoryRepository.class); - MetadataRepository metadataRepository = - appContext.getBean(MetadataRepository.class); + IMetadataUtils metadataRepository = + appContext.getBean(IMetadataUtils.class); MetadataCategory category = categoryRepository.findOne(tagIdentifier); if (category != null) { - long recordsCount = metadataRepository.count(MetadataSpecs.hasCategory(category)); + long recordsCount = metadataRepository.count((Specification)MetadataSpecs.hasCategory(category)); if (recordsCount > 0l) { throw new IllegalArgumentException(String.format( "Tag '%s' is assigned to %d records. Update records first in order to remove that tag.", 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 9ec82e6b298..e9a91cb8439 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 @@ -493,7 +493,7 @@ public void deleteGroup( OperationAllowedId_.metadataId); if (reindex.size() > 0 && force) { - operationAllowedRepo.deleteAllByIdAttribute(OperationAllowedId_.groupId, groupIdentifier); + operationAllowedRepo.deleteAllByGroupId(groupIdentifier); //--- reindex affected metadata DataManager dm = ApplicationContextHolder.get().getBean(DataManager.class); diff --git a/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java b/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java index 81b7efc4caf..04c7ad662f7 100644 --- a/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java +++ b/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java @@ -133,22 +133,24 @@ public SimpleMetadataProcessingReport validateRecords( final IMetadataUtils metadataRepository = applicationContext.getBean(IMetadataUtils.class); for (String uuid : records) { - AbstractMetadata record = metadataRepository.findOneByUuid(uuid); - if (record == null) { + if (!metadataRepository.existsMetadataUuid(uuid)) { report.incrementNullRecords(); - } else if (!accessMan.canEdit(serviceContext, String.valueOf(record.getId()))) { - report.addNotEditableMetadataId(record.getId()); - } else { - boolean isValid = validator.doValidate(record, serviceContext.getLanguage()); - if (isValid) { - report.addMetadataInfos(record.getId(), "Is valid"); - new RecordValidationTriggeredEvent(record.getId(), ApiUtils.getUserSession(request.getSession()).getUserIdAsInt(), "1").publish(applicationContext); + } + for (AbstractMetadata record : metadataRepository.findAllByUuid(uuid)) { + if (!accessMan.canEdit(serviceContext, String.valueOf(record.getId()))) { + report.addNotEditableMetadataId(record.getId()); } else { - report.addMetadataInfos(record.getId(), "Is invalid"); - new RecordValidationTriggeredEvent(record.getId(), ApiUtils.getUserSession(request.getSession()).getUserIdAsInt(), "0").publish(applicationContext); + boolean isValid = validator.doValidate(record, serviceContext.getLanguage()); + if (isValid) { + report.addMetadataInfos(record.getId(), "Is valid"); + new RecordValidationTriggeredEvent(record.getId(), ApiUtils.getUserSession(request.getSession()).getUserIdAsInt(), "1").publish(applicationContext); + } else { + report.addMetadataInfos(record.getId(), "Is invalid"); + new RecordValidationTriggeredEvent(record.getId(), ApiUtils.getUserSession(request.getSession()).getUserIdAsInt(), "0").publish(applicationContext); + } + report.addMetadataId(record.getId()); + report.incrementProcessedRecords(); } - report.addMetadataId(record.getId()); - report.incrementProcessedRecords(); } } diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java index 9ad92ca7fb3..a077fe45b65 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java @@ -23,14 +23,24 @@ package org.fao.geonet.api.records; -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.constants.Jeeves; -import jeeves.server.context.ServiceContext; -import jeeves.services.ReadWriteController; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_TAG; +import static org.fao.geonet.api.ApiParams.API_PARAM_RECORD_UUID; +import static org.fao.geonet.kernel.mef.MEFLib.Version.Constants.MEF_V1_ACCEPT_TYPE; +import static org.fao.geonet.kernel.mef.MEFLib.Version.Constants.MEF_V2_ACCEPT_TYPE; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.fao.geonet.ApplicationContextHolder; @@ -50,15 +60,17 @@ import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; import org.fao.geonet.kernel.SchemaManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.mef.MEFLib; +import org.fao.geonet.kernel.mef.MEFLib.Version.Constants; import org.fao.geonet.lib.Lib; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.jdom.Attribute; import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -71,22 +83,14 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_OPS; -import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_TAG; -import static org.fao.geonet.api.ApiParams.API_PARAM_RECORD_UUID; -import static org.fao.geonet.kernel.mef.MEFLib.Version.Constants.MEF_V1_ACCEPT_TYPE; -import static org.fao.geonet.kernel.mef.MEFLib.Version.Constants.MEF_V2_ACCEPT_TYPE; +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.constants.Jeeves; +import jeeves.server.context.ServiceContext; +import jeeves.services.ReadWriteController; @RequestMapping(value = { "/api/records", @@ -98,7 +102,7 @@ description = API_CLASS_RECORD_OPS) @Controller("records") @ReadWriteController -public class MetadataApi implements ApplicationContextAware { +public class MetadataApi { @Autowired SchemaManager _schemaManager; @@ -106,12 +110,6 @@ public class MetadataApi implements ApplicationContextAware { @Autowired LanguageUtils languageUtils; - private ApplicationContext context; - - public synchronized void setApplicationContext(ApplicationContext context) { - this.context = context; - } - @ApiOperation(value = "Get a metadata record", notes = "Depending on the accept header the appropriate formatter is used. " + @@ -172,11 +170,11 @@ public String getRecord( || accept.contains("application/pdf")) { return "forward:" + (metadataUuid + "/formatters/" + defaultFormatter); } else if (accept.contains(MediaType.APPLICATION_XML_VALUE) - || accept.contains(MediaType.APPLICATION_JSON_VALUE)) { + || accept.contains(MediaType.APPLICATION_JSON_VALUE)) { return "forward:" + (metadataUuid + "/formatters/xml"); } else if (accept.contains("application/zip") - || accept.contains(MEF_V1_ACCEPT_TYPE) - || accept.contains(MEF_V2_ACCEPT_TYPE)) { + || accept.contains(MEF_V1_ACCEPT_TYPE) + || accept.contains(MEF_V2_ACCEPT_TYPE)) { return "forward:" + (metadataUuid + "/formatters/zip"); } else { // FIXME this else is never reached because any of the accepted medias match one of the previous if conditions. @@ -229,6 +227,10 @@ Object getRecordAsXML( required = false) @RequestParam(required = false, defaultValue = "false") boolean attachment, + @ApiParam(value = "Download the approved version", + required = false, defaultValue = "true") + @RequestParam(required = false, defaultValue = "true") + boolean approved, @RequestHeader( value = HttpHeaders.ACCEPT, defaultValue = MediaType.APPLICATION_XML_VALUE @@ -240,14 +242,14 @@ Object getRecordAsXML( throws Exception { ApplicationContext appContext = ApplicationContextHolder.get(); DataManager dataManager = appContext.getBean(DataManager.class); + MetadataRepository mdRepository = appContext.getBean(MetadataRepository.class); AbstractMetadata metadata; try { metadata = ApiUtils.canViewRecord(metadataUuid, request); } catch (ResourceNotFoundException e) { Log.debug(API.LOG_MODULE_NAME, e.getMessage(), e); throw e; - } - catch (Exception e) { + } catch (Exception e) { Log.debug(API.LOG_MODULE_NAME, e.getMessage(), e); throw new NotAllowedException(ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW); } @@ -270,10 +272,19 @@ Object getRecordAsXML( boolean withValidationErrors = false, keepXlinkAttributes = false, forEditing = false; - Element xml = withInfo ? - dataManager.getMetadata(context, - metadata.getId() + "", forEditing, withValidationErrors, keepXlinkAttributes) : - dataManager.getMetadataNoInfo(context, metadata.getId() + ""); + + String mdId = String.valueOf(metadata.getId()); + + //Here we just care if we need the approved version explicitly. + //ApiUtils.canViewRecord already filtered draft for non editors. + if (approved) { + mdId = String.valueOf(mdRepository.findOneByUuid(metadata.getUuid()).getId()); + } + + Element xml = withInfo ? + dataManager.getMetadata(context, mdId, forEditing, + withValidationErrors, keepXlinkAttributes) : + dataManager.getMetadataNoInfo(context, mdId + ""); if (addSchemaLocation) { Attribute schemaLocAtt = _schemaManager.getSchemaLocation( @@ -294,7 +305,7 @@ Object getRecordAsXML( boolean isJson = acceptHeader.contains(MediaType.APPLICATION_JSON_VALUE); - String mode = (attachment)?"attachment":"inline"; + String mode = (attachment) ? "attachment" : "inline"; response.setHeader("Content-Disposition", String.format( mode + "; filename=\"%s.%s\"", metadata.getUuid(), @@ -364,6 +375,10 @@ void getRecordAsZip( required = false, defaultValue = "true") boolean addSchemaLocation, + @ApiParam(value = "Download the approved version", + required = false) + @RequestParam(required = false, defaultValue = "true") + boolean approved, @RequestHeader( value = HttpHeaders.ACCEPT, defaultValue = "application/x-gn-mef-2-zip" @@ -375,6 +390,8 @@ void getRecordAsZip( throws Exception { ApplicationContext appContext = ApplicationContextHolder.get(); GeonetworkDataDirectory dataDirectory = appContext.getBean(GeonetworkDataDirectory.class); + IMetadataUtils mdUtils = appContext.getBean(IMetadataUtils.class); + MetadataRepository mdRepo = appContext.getBean(MetadataRepository.class); AbstractMetadata metadata; try { @@ -390,10 +407,20 @@ void getRecordAsZip( if (version == MEFLib.Version.V1) { // This parameter is deprecated in v2. boolean skipUUID = false; + + Integer id = -1; + + if (approved) { + id = mdRepo.findOneByUuid(metadataUuid).getId(); + } else { + id = mdUtils.findOneByUuid(metadataUuid).getId(); + } + file = MEFLib.doExport( - context, metadataUuid, format.toString(), + context, id, format.toString(), skipUUID, withXLinksResolved, withXLinkAttribute, addSchemaLocation ); + response.setContentType(MEFLib.Version.Constants.MEF_V1_ACCEPT_TYPE); } else { Set tmpUuid = new HashSet(); tmpUuid.add(metadataUuid); @@ -432,14 +459,15 @@ void getRecordAsZip( Log.info(Geonet.MEF, "Building MEF2 file with " + tmpUuid.size() + " records."); - file = MEFLib.doMEF2Export(context, tmpUuid, format.toString(), false, stylePath, withXLinksResolved, withXLinkAttribute, false, addSchemaLocation); + file = MEFLib.doMEF2Export(context, tmpUuid, format.toString(), false, stylePath, withXLinksResolved, withXLinkAttribute, false, addSchemaLocation, approved); + + response.setContentType(MEFLib.Version.Constants.MEF_V2_ACCEPT_TYPE); } response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format( "inline; filename=\"%s.zip\"", metadata.getUuid() )); response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(Files.size(file))); - response.setContentType(acceptHeader); FileUtils.copyFile(file.toFile(), response.getOutputStream()); } @@ -484,7 +512,7 @@ public RelatedResponse getRelated( HttpServletRequest request) throws Exception { AbstractMetadata md; - try{ + try { md = ApiUtils.canViewRecord(metadataUuid, request); } catch (SecurityException e) { Log.debug(API.LOG_MODULE_NAME, e.getMessage(), e); @@ -529,7 +557,7 @@ public RelatedResponse getRelated( @ApiResponse(code = 403, message = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW) }) @ResponseBody - public FeatureResponse getFeatureCatalog ( + public FeatureResponse getFeatureCatalog( @ApiParam( value = API_PARAM_RECORD_UUID, required = true) @@ -576,9 +604,9 @@ public FeatureResponse getFeatureCatalog ( private boolean isIncludedAttributeTable(RelatedResponse.Fcat fcat) { return fcat != null && fcat.getItem() != null - && fcat.getItem().size()>0 - && fcat.getItem().get(0).getFeatureType()!=null - && fcat.getItem().get(0).getFeatureType().getAttributeTable()!=null - && fcat.getItem().get(0).getFeatureType().getAttributeTable().getElement()!=null; + && fcat.getItem().size() > 0 + && fcat.getItem().get(0).getFeatureType() != null + && fcat.getItem().get(0).getFeatureType().getAttributeTable() != null + && fcat.getItem().get(0).getFeatureType().getAttributeTable().getElement() != null; } } diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java index 60dc8df6224..9cfa4df328b 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java @@ -80,6 +80,7 @@ import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; import org.fao.geonet.kernel.SchemaManager; +import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.mef.Importer; import org.fao.geonet.kernel.mef.MEFLib; @@ -197,7 +198,7 @@ public SimpleMetadataProcessingReport deleteRecords( Set records = ApiUtils.getUuidsParameterOrSelection(uuids, bucket, ApiUtils.getUserSession(session)); - final IMetadataUtils metadataRepository = appContext.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = appContext.getBean(MetadataRepository.class); SimpleMetadataProcessingReport report = new SimpleMetadataProcessingReport(); for (String uuid : records) { AbstractMetadata metadata = metadataRepository.findOneByUuid(uuid); @@ -841,7 +842,7 @@ private Pair loadRecord(MetadataType metadataType, Element xmlE } if (extra != null) { - context.getBean(MetadataRepository.class).update(iId, new Updater() { + context.getBean(IMetadataManager.class).update(iId, new Updater() { @Override public void apply(@Nonnull Metadata metadata) { if (extra != null) { diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java index 7f6a0874e9b..a0eeb190d6c 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java @@ -53,8 +53,8 @@ import org.fao.geonet.api.records.model.SharingParameter; import org.fao.geonet.api.records.model.SharingResponse; import org.fao.geonet.api.tools.i18n.LanguageUtils; -import org.fao.geonet.domain.Group; import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; import org.fao.geonet.domain.Operation; import org.fao.geonet.domain.OperationAllowed; import org.fao.geonet.domain.OperationAllowedId; @@ -252,7 +252,7 @@ MetadataProcessingReport share( final ApplicationContext appContext = ApplicationContextHolder.get(); final DataManager dataMan = appContext.getBean(DataManager.class); final AccessManager accessMan = appContext.getBean(AccessManager.class); - final IMetadataUtils metadataRepository = appContext.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = appContext.getBean(MetadataRepository.class); UserSession us = ApiUtils.getUserSession(session); boolean isAdmin = Profile.Administrator == us.getProfile(); @@ -653,6 +653,9 @@ MetadataProcessingReport setGroupAndOwner( required = true ) Integer userIdentifier, + @ApiParam(value = "Use approved version or not", example = "true") + @RequestParam(required = false, defaultValue = "false") + Boolean approved, @ApiIgnore @ApiParam(hidden = true) HttpSession session, @@ -669,7 +672,7 @@ MetadataProcessingReport setGroupAndOwner( final DataManager dataManager = context.getBean(DataManager.class); final MetadataCategoryRepository categoryRepository = context.getBean(MetadataCategoryRepository.class); final AccessManager accessMan = context.getBean(AccessManager.class); - final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); ServiceContext serviceContext = ApiUtils.createServiceContext(request); @@ -677,7 +680,7 @@ MetadataProcessingReport setGroupAndOwner( for (String uuid : records) { updateOwnership(groupIdentifier, userIdentifier, report, dataManager, accessMan, metadataRepository, - serviceContext, listOfUpdatedRecords, uuid, session); + serviceContext, listOfUpdatedRecords, uuid, session, approved); } dataManager.flush(); dataManager.indexMetadata(listOfUpdatedRecords); @@ -730,6 +733,9 @@ MetadataProcessingReport setRecordOwnership( required = true ) Integer userIdentifier, + @ApiParam(value = "Use approved version or not", example = "true") + @RequestParam(required = false, defaultValue = "true") + Boolean approved, @ApiIgnore @ApiParam(hidden = true) HttpSession session, @@ -745,13 +751,13 @@ MetadataProcessingReport setRecordOwnership( final ApplicationContext context = ApplicationContextHolder.get(); final DataManager dataManager = context.getBean(DataManager.class); final AccessManager accessMan = context.getBean(AccessManager.class); - final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); ServiceContext serviceContext = ApiUtils.createServiceContext(request); List listOfUpdatedRecords = new ArrayList<>(); updateOwnership(groupIdentifier, userIdentifier, report, dataManager, accessMan, metadataRepository, - serviceContext, listOfUpdatedRecords, metadataUuid, session); + serviceContext, listOfUpdatedRecords, metadataUuid, session, approved); dataManager.flush(); dataManager.indexMetadata(String.valueOf(metadata.getId()), true, null); @@ -770,10 +776,10 @@ private void updateOwnership(Integer groupIdentifier, MetadataProcessingReport report, DataManager dataManager, AccessManager accessMan, - IMetadataUtils metadataRepository, + MetadataRepository metadataRepository, ServiceContext serviceContext, List listOfUpdatedRecords, String uuid, - HttpSession session) throws Exception { + HttpSession session, Boolean approved) throws Exception { AbstractMetadata metadata = metadataRepository.findOneByUuid(uuid); if (metadata == null) { report.incrementNullRecords(); @@ -816,7 +822,7 @@ private void updateOwnership(Integer groupIdentifier, } } - Long metadataId = Long.parseLong(ApiUtils.getInternalId(uuid)); + Long metadataId = Long.parseLong(ApiUtils.getInternalId(uuid, approved)); ApplicationContext context = ApplicationContextHolder.get(); if(!Objects.equals(groupIdentifier, sourceGrp)) { Group newGroup = context.getBean(GroupRepository.class).findOne(groupIdentifier); diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java index 85a5cc9f858..c80715fcb1b 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java @@ -113,7 +113,7 @@ public Set getTags( ) throws Exception { AbstractMetadata metadata = ApiUtils.canViewRecord(metadataUuid, request); ApplicationContext appContext = ApplicationContextHolder.get(); - return metadata.getMetadataCategories(); + return metadata.getCategories(); } @@ -156,11 +156,11 @@ public void updateTags( ) throws Exception { AbstractMetadata metadata = ApiUtils.canEditRecord(metadataUuid, request); ApplicationContext appContext = ApplicationContextHolder.get(); - Set before = metadata.getMetadataCategories(); + Set before = metadata.getCategories(); if (clear) { appContext.getBean(IMetadataManager.class).update( - metadata.getId(), entity -> entity.getMetadataCategories().clear()); + metadata.getId(), entity -> entity.getCategories().clear()); } DataManager dataManager = appContext.getBean(DataManager.class); @@ -181,7 +181,7 @@ public void updateTags( dataManager.indexMetadata(String.valueOf(metadata.getId()), true, null); metadata = ApiUtils.canEditRecord(metadataUuid, request); - Set after = metadata.getMetadataCategories(); + Set after = metadata.getCategories(); UserSession userSession = ApiUtils.getUserSession(request.getSession()); new RecordCategoryChangeEvent(metadata.getId(), userSession.getUserIdAsInt(), ObjectJSONUtils.convertObjectInJsonObject(before, RecordCategoryChangeEvent.FIELD), ObjectJSONUtils.convertObjectInJsonObject(after, RecordCategoryChangeEvent.FIELD)).publish(appContext);; @@ -217,11 +217,11 @@ public void deleteTags( ) throws Exception { AbstractMetadata metadata = ApiUtils.canEditRecord(metadataUuid, request); ApplicationContext appContext = ApplicationContextHolder.get(); - Set before = metadata.getMetadataCategories(); + Set before = metadata.getCategories(); if (id == null || id.length == 0) { appContext.getBean(IMetadataManager.class).update( - metadata.getId(), entity -> entity.getMetadataCategories().clear()); + metadata.getId(), entity -> entity.getCategories().clear()); } DataManager dataManager = appContext.getBean(DataManager.class); @@ -236,7 +236,7 @@ public void deleteTags( dataManager.indexMetadata(String.valueOf(metadata.getId()), true, null); metadata = ApiUtils.canEditRecord(metadataUuid, request); - Set after = metadata.getMetadataCategories(); + Set after = metadata.getCategories(); UserSession userSession = ApiUtils.getUserSession(request.getSession()); new RecordCategoryChangeEvent(metadata.getId(), userSession.getUserIdAsInt(), ObjectJSONUtils.convertObjectInJsonObject(before, RecordCategoryChangeEvent.FIELD), ObjectJSONUtils.convertObjectInJsonObject(after, RecordCategoryChangeEvent.FIELD)).publish(appContext);; @@ -301,13 +301,13 @@ public MetadataProcessingReport updateTags( final DataManager dataMan = context.getBean(DataManager.class); final MetadataCategoryRepository categoryRepository = context.getBean(MetadataCategoryRepository.class); final AccessManager accessMan = context.getBean(AccessManager.class); - final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); final IMetadataManager metadataManager = context.getBean(IMetadataManager.class); List listOfUpdatedRecords = new ArrayList<>(); for (String uuid : records) { AbstractMetadata info = metadataRepository.findOneByUuid(uuid); - Set before = info.getMetadataCategories(); + Set before = info.getCategories(); if (info == null) { report.incrementNullRecords(); } else if (!accessMan.canEdit( @@ -315,14 +315,14 @@ public MetadataProcessingReport updateTags( report.addNotEditableMetadataId(info.getId()); } else { if (clear) { - info.getMetadataCategories().clear(); + info.getCategories().clear(); } if (id != null) { for (int c : id) { final MetadataCategory category = categoryRepository.findOne(c); if (category != null) { - info.getMetadataCategories().add(category); + info.getCategories().add(category); listOfUpdatedRecords.add(String.valueOf(info.getId())); } else { report.addMetadataInfos(info.getId(), String.format( @@ -337,7 +337,7 @@ public MetadataProcessingReport updateTags( } info = metadataRepository.findOneByUuid(uuid); - Set after = info.getMetadataCategories(); + Set after = info.getCategories(); UserSession userSession = ApiUtils.getUserSession(request.getSession()); new RecordCategoryChangeEvent(info.getId(), userSession.getUserIdAsInt(), ObjectJSONUtils.convertObjectInJsonObject(before, RecordCategoryChangeEvent.FIELD), ObjectJSONUtils.convertObjectInJsonObject(after, RecordCategoryChangeEvent.FIELD)).publish(context);; @@ -401,26 +401,26 @@ public MetadataProcessingReport updateTags( final DataManager dataMan = context.getBean(DataManager.class); final MetadataCategoryRepository categoryRepository = context.getBean(MetadataCategoryRepository.class); final AccessManager accessMan = context.getBean(AccessManager.class); - final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); final IMetadataManager metadataManager = context.getBean(IMetadataManager.class); List listOfUpdatedRecords = new ArrayList<>(); for (String uuid : records) { AbstractMetadata info = metadataRepository.findOneByUuid(uuid); - Set before = info.getMetadataCategories(); + Set before = info.getCategories(); if (info == null) { report.incrementNullRecords(); } else if (!accessMan.canEdit( ApiUtils.createServiceContext(request), String.valueOf(info.getId()))) { report.addNotEditableMetadataId(info.getId()); } else { - info.getMetadataCategories().clear(); + info.getCategories().clear(); metadataManager.save(info); report.incrementProcessedRecords(); } info = metadataRepository.findOneByUuid(uuid); - Set after = info.getMetadataCategories(); + Set after = info.getCategories(); UserSession userSession = ApiUtils.getUserSession(request.getSession()); new RecordCategoryChangeEvent(info.getId(), userSession.getUserIdAsInt(), ObjectJSONUtils.convertObjectInJsonObject(before, RecordCategoryChangeEvent.FIELD), ObjectJSONUtils.convertObjectInJsonObject(after, RecordCategoryChangeEvent.FIELD)).publish(context);; } diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java b/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java index 8781eff7d6f..a71bf228e0f 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java @@ -344,6 +344,7 @@ private static Element getRecord(String uuid, ServiceContext context, DataManage } public static void backupRecord(AbstractMetadata metadata, ServiceContext context) { + Log.trace(Geonet.DATA_MANAGER, "Backing up record " + metadata.getId()); Path outDir = Lib.resource.getRemovedDir(metadata.getId()); Path outFile; try { @@ -358,7 +359,7 @@ public static void backupRecord(AbstractMetadata metadata, ServiceContext contex Path file = null; try { - file = MEFLib.doExport(context, metadata.getUuid(), "full", false, true, false, false); + file = MEFLib.doExport(context, metadata.getUuid(), "full", false, true, false, false, true); Files.createDirectories(outDir); try (InputStream is = IO.newInputStream(file); OutputStream os = Files.newOutputStream(outFile)) { diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java index 2392e4a8092..a35e2066f32 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java @@ -152,17 +152,20 @@ public MetadataProcessingReport enableVersionControlForRecords( final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); for (String uuid : records) { - AbstractMetadata metadata = metadataRepository.findOneByUuid(uuid); - if (metadata == null) { - report.incrementNullRecords(); - } else if (!accessMan.canEdit( - ApiUtils.createServiceContext(request), String.valueOf(metadata.getId()))) { - report.addNotEditableMetadataId(metadata.getId()); - } else { - dataMan.versionMetadata(ApiUtils.createServiceContext(request), - String.valueOf(metadata.getId()), metadata.getXmlData(false)); - report.incrementProcessedRecords(); - } + if(!metadataRepository.existsMetadataUuid(uuid)) { + report.incrementNullRecords(); + } else { + for(AbstractMetadata metadata : metadataRepository.findAllByUuid(uuid)) { + if (!accessMan.canEdit( + ApiUtils.createServiceContext(request), String.valueOf(metadata.getId()))) { + report.addNotEditableMetadataId(metadata.getId()); + } else { + dataMan.versionMetadata(ApiUtils.createServiceContext(request), + String.valueOf(metadata.getId()), metadata.getXmlData(false)); + report.incrementProcessedRecords(); + } + } + } } } catch (Exception exception) { report.addError(exception); diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java index dd1027ec4f9..62254ca6630 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java @@ -64,6 +64,7 @@ import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.datamanager.IMetadataStatus; +import org.fao.geonet.kernel.datamanager.IMetadataIndexer; import org.fao.geonet.kernel.metadata.StatusActions; import org.fao.geonet.kernel.metadata.StatusActionsFactory; import org.fao.geonet.kernel.search.LuceneSearcher; @@ -112,7 +113,7 @@ public class MetadataWorkflowApi { @Autowired LanguageUtils languageUtils; - + @ApiOperation( value = "Get record status history", @@ -306,13 +307,13 @@ public void setStatus( - AccessManager am = appContext.getBean(AccessManager.class); +// AccessManager am = appContext.getBean(AccessManager.class); //--- only allow the owner of the record to set its status - if (!am.isOwner(context, String.valueOf(metadata.getId()))) { - throw new SecurityException(String.format( - "Only the owner of the metadata can set the status of this record. User is not the owner of the metadata." - )); - } +// if (!am.isOwner(context, String.valueOf(metadata.getId()))) { +// throw new SecurityException(String.format( +// "Only the owner of the metadata can set the status. User is not the owner of the metadata" +// )); +// } //--- use StatusActionsFactory and StatusActions class to //--- change status and carry out behaviours for status changes @@ -327,7 +328,7 @@ public void setStatus( sa.onStatusChange(listOfStatusChange); //--- reindex metadata - DataManager dataManager = appContext.getBean(DataManager.class); + IMetadataIndexer dataManager = appContext.getBean(IMetadataIndexer.class); dataManager.indexMetadata(String.valueOf(metadata.getId()), true, null); } diff --git a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java index 6bba3b3e52f..1aa3998fb4e 100644 --- a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java @@ -125,7 +125,6 @@ public void setStore(Store store) { this.store = store; } - @SuppressWarnings("unchecked") @PostConstruct public void init() { if (appContext != null) { @@ -152,10 +151,11 @@ public List getResources() { public List getAllResources( @ApiParam(value = "The metadata UUID", required = true, example = "43d7c186-2187-4bcd-8843-41e575a5ef56") @PathVariable String metadataUuid, @ApiParam(value = "Sort by", example = "type") @RequestParam(required = false, defaultValue = "name") Sort sort, + @ApiParam(value = "Use approved version or not", example = "true") @RequestParam(required = false, defaultValue = "true") Boolean approved, @RequestParam(required = false, defaultValue = FilesystemStore.DEFAULT_FILTER) String filter, @ApiIgnore HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - List list = store.getResources(context, metadataUuid, sort, filter); + List list = store.getResources(context, metadataUuid, sort, filter, approved); return list; } @@ -167,11 +167,12 @@ public List getAllResources( @ResponseStatus(value = HttpStatus.NO_CONTENT) public void delResources( @ApiParam(value = "The metadata UUID", required = true, example = "43d7c186-2187-4bcd-8843-41e575a5ef56") @PathVariable String metadataUuid, + @ApiParam(value = "Use approved version or not", example = "true") @RequestParam(required = false, defaultValue = "false") Boolean approved, @ApiIgnore HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - store.delResource(context, metadataUuid); + store.delResource(context, metadataUuid, approved); - String metadataIdString = ApiUtils.getInternalId(metadataUuid); + String metadataIdString = ApiUtils.getInternalId(metadataUuid, approved); if (metadataIdString != null) { long metadataId = Long.parseLong(metadataIdString); UserSession userSession = ApiUtils.getUserSession(request.getSession()); @@ -191,11 +192,12 @@ public MetadataResource putResource( @ApiParam(value = "The metadata UUID", required = true, example = "43d7c186-2187-4bcd-8843-41e575a5ef56") @PathVariable String metadataUuid, @ApiParam(value = "The sharing policy", example = "public") @RequestParam(required = false, defaultValue = "public") MetadataResourceVisibility visibility, @ApiParam(value = "The file to upload") @RequestParam("file") MultipartFile file, + @ApiParam(value = "Use approved version or not", example = "true") @RequestParam(required = false, defaultValue = "false") Boolean approved, @ApiIgnore HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - MetadataResource resource = store.putResource(context, metadataUuid, file, visibility); + MetadataResource resource = store.putResource(context, metadataUuid, file, visibility, approved); - String metadataIdString = ApiUtils.getInternalId(metadataUuid); + String metadataIdString = ApiUtils.getInternalId(metadataUuid, approved); if (metadataIdString != null && file != null && !file.isEmpty()) { long metadataId = Long.parseLong(metadataIdString); UserSession userSession = ApiUtils.getUserSession(request.getSession()); @@ -217,11 +219,12 @@ public MetadataResource putResourceFromURL( @ApiParam(value = "The metadata UUID", required = true, example = "43d7c186-2187-4bcd-8843-41e575a5ef56") @PathVariable String metadataUuid, @ApiParam(value = "The sharing policy", example = "public") @RequestParam(required = false, defaultValue = "public") MetadataResourceVisibility visibility, @ApiParam(value = "The URL to load in the store") @RequestParam("url") URL url, - @ApiIgnore HttpServletRequest request) throws Exception { + @ApiParam(value = "Use approved version or not", example = "true") @RequestParam(required = false, defaultValue = "false") Boolean approved, + @ApiIgnore HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - MetadataResource resource = store.putResource(context, metadataUuid, url, visibility); + MetadataResource resource = store.putResource(context, metadataUuid, url, visibility, approved); - String metadataIdString = ApiUtils.getInternalId(metadataUuid); + String metadataIdString = ApiUtils.getInternalId(metadataUuid, approved); if (metadataIdString != null && url != null) { long metadataId = Long.parseLong(metadataIdString); UserSession userSession = ApiUtils.getUserSession(request.getSession()); @@ -243,11 +246,12 @@ public MetadataResource putResourceFromURL( public HttpEntity getResource( @ApiParam(value = "The metadata UUID", required = true, example = "43d7c186-2187-4bcd-8843-41e575a5ef56") @PathVariable String metadataUuid, @ApiParam(value = "The resource identifier (ie. filename)", required = true) @PathVariable String resourceId, + @ApiParam(value = "Use approved version or not", example = "true") @RequestParam(required = false, defaultValue = "true") Boolean approved, @ApiIgnore HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - Path file = store.getResource(context, metadataUuid, resourceId); - - // TODO: Check user privileges + Path file = store.getResource(context, metadataUuid, resourceId, approved); + + ApiUtils.canViewRecord(metadataUuid, request); MultiValueMap headers = new HttpHeaders(); headers.add("Content-Disposition", "inline; filename=\"" + file.getFileName() + "\""); @@ -268,9 +272,10 @@ public MetadataResource patchResource( @ApiParam(value = "The metadata UUID", required = true, example = "43d7c186-2187-4bcd-8843-41e575a5ef56") @PathVariable String metadataUuid, @ApiParam(value = "The resource identifier (ie. filename)", required = true) @PathVariable String resourceId, @ApiParam(value = "The visibility", required = true, example = "public") @RequestParam(required = true) MetadataResourceVisibility visibility, + @ApiParam(value = "Use approved version or not", example = "true") @RequestParam(required = false, defaultValue = "false") Boolean approved, @ApiIgnore HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - return store.patchResourceStatus(context, metadataUuid, resourceId, visibility); + return store.patchResourceStatus(context, metadataUuid, resourceId, visibility, approved); } @ApiOperation(value = "Delete a metadata resource", nickname = "deleteMetadataResource") @@ -282,11 +287,12 @@ public MetadataResource patchResource( public void delResource( @ApiParam(value = "The metadata UUID", required = true, example = "43d7c186-2187-4bcd-8843-41e575a5ef56") @PathVariable String metadataUuid, @ApiParam(value = "The resource identifier (ie. filename)", required = true) @PathVariable String resourceId, - @ApiIgnore HttpServletRequest request) throws Exception { + @ApiParam(value = "Use approved version or not", example = "true") @RequestParam(required = false, defaultValue = "false") Boolean approved, + @ApiIgnore HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - store.delResource(context, metadataUuid, resourceId); + store.delResource(context, metadataUuid, resourceId, approved); - String metadataIdString = ApiUtils.getInternalId(metadataUuid); + String metadataIdString = ApiUtils.getInternalId(metadataUuid, approved); if (metadataIdString != null) { long metadataId = Long.parseLong(metadataIdString); UserSession userSession = ApiUtils.getUserSession(request.getSession()); diff --git a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java index ffb047091a3..8d2bf946740 100644 --- a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java @@ -43,14 +43,15 @@ import org.fao.geonet.events.history.RecordUpdatedEvent; import org.fao.geonet.kernel.*; import org.fao.geonet.kernel.datamanager.IMetadataValidator; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.metadata.StatusActions; import org.fao.geonet.kernel.metadata.StatusActionsFactory; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.repository.MetadataValidationRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.specification.MetadataValidationSpecs; +import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; -import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.output.XMLOutputter; @@ -65,10 +66,12 @@ import java.io.IOException; import java.nio.file.Path; import java.sql.SQLException; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import jeeves.server.context.ServiceContext; @@ -142,7 +145,8 @@ public Element startEditing( @ApiParam(hidden = true) @RequestParam Map allRequestParams, - HttpServletRequest request + HttpServletRequest request, + HttpServletResponse response ) throws Exception { AbstractMetadata metadata = ApiUtils.canEditRecord(metadataUuid, request); @@ -152,8 +156,36 @@ public Element startEditing( ServiceContext context = ApiUtils.createServiceContext(request); ApplicationContext applicationContext = ApplicationContextHolder.get(); if (starteditingsession) { - DataManager dm = applicationContext.getBean(DataManager.class); - dm.startEditingSession(context, String.valueOf(metadata.getId())); + IMetadataUtils dm = applicationContext.getBean(IMetadataUtils.class); + Integer id2 = dm.startEditingSession(context, String.valueOf(metadata.getId())); + + //Maybe we are redirected to another metadata? + if(id2 != metadata.getId()) { + + StringBuilder sb = new StringBuilder("?"); + + Enumeration parameters = request.getParameterNames(); + + //As this editor will redirect, make sure there is something to go + // back that makes sense and prevent a loop: + boolean hasPreviousURL = false; + + while(parameters.hasMoreElements()) { + String key = parameters.nextElement(); + sb.append(key + "=" + request.getParameter(key) + "%26"); + if(key.equalsIgnoreCase("redirectUrl")) { + hasPreviousURL = true; + } + } + + if(!hasPreviousURL) { + sb.append("redirectUrl=catalog.edit"); + } + + Element el = new Element("script"); + el.setText("window.location.hash = decodeURIComponent(\"#/metadata/" + id2 + sb.toString() + "\")"); + return el; + } } Element elMd = new AjaxEditUtils(context) @@ -241,6 +273,8 @@ public Element saveEdits( @ApiParam(hidden = true) HttpSession httpSession ) throws Exception { + + Log.trace(Geonet.DATA_MANAGER, "Saving metadata editing with UUID " + metadataUuid); AbstractMetadata metadata = ApiUtils.canEditRecord(metadataUuid, request); ServiceContext context = ApiUtils.createServiceContext(request); AjaxEditUtils ajaxEditUtils = new AjaxEditUtils(context); @@ -251,12 +285,14 @@ public Element saveEdits( UserSession session = ApiUtils.getUserSession(httpSession); IMetadataValidator validator = applicationContext.getBean(IMetadataValidator.class); String id = String.valueOf(metadata.getId()); + Log.trace(Geonet.DATA_MANAGER, " > ID of the record to edit: " + id); String isTemplate = allRequestParams.get(Params.TEMPLATE); // boolean finished = config.getValue(Params.FINISHED, "no").equals("yes"); // boolean forget = config.getValue(Params.FORGET, "no").equals("yes"); // boolean commit = config.getValue(Params.START_EDITING_SESSION, "no").equals("yes"); // TODO: Use map only to avoid this conversion + Log.trace(Geonet.DATA_MANAGER, " > Getting parameters from request"); Element params = new Element ("request"); Map forwardedParams = new HashMap<>(); for (Map.Entry e : allRequestParams.entrySet()) { @@ -266,17 +302,24 @@ public Element saveEdits( } } + if(Log.isTraceEnabled(Geonet.DATA_MANAGER)) { + Log.trace(Geonet.DATA_MANAGER, " > Setting type of record " + + MetadataType.lookup(isTemplate)); + } int iLocalId = Integer.parseInt(id); + Log.trace(Geonet.DATA_MANAGER, " > Id is " + iLocalId); dataMan.setTemplateExt(iLocalId, MetadataType.lookup(isTemplate)); //--- use StatusActionsFactory and StatusActions class to possibly //--- change status as a result of this edit (use onEdit method) + Log.trace(Geonet.DATA_MANAGER, " > Trigger status actions based on this edit"); StatusActionsFactory saf = context.getBean(StatusActionsFactory.class); StatusActions sa = saf.createStatusActions(context); sa.onEdit(iLocalId, minor); Element beforeMetadata = dataMan.getMetadata(context, String.valueOf(metadata.getId()), false, false, false); if (StringUtils.isNotEmpty(data)) { + Log.trace(Geonet.DATA_MANAGER, " > Updating metadata through data manager"); Element md = Xml.loadString(data, false); String changeDate = null; boolean updateDateStamp = !minor; @@ -291,6 +334,7 @@ public Element saveEdits( String xmlAfter = outp.outputString(md); new RecordUpdatedEvent(Long.parseLong(id), session.getUserIdAsInt(), xmlBefore, xmlAfter).publish(applicationContext); } else { + Log.trace(Geonet.DATA_MANAGER, " > Updating contents"); ajaxEditUtils.updateContent(params, false, true); Element afterMetadata = dataMan.getMetadata(context, String.valueOf(metadata.getId()), false, false, false); @@ -317,6 +361,7 @@ public Element saveEdits( return null; } if (terminate) { + Log.trace(Geonet.DATA_MANAGER, " > Closing editor"); SettingManager sm = context.getBean(SettingManager.class); boolean forceValidationOnMdSave = sm.getValueAsBool("metadata/workflow/forceValidationOnMdSave"); @@ -357,6 +402,7 @@ public Element saveEdits( } if (reindex) { + Log.trace(Geonet.DATA_MANAGER, " > Reindexing record"); dataMan.indexMetadata(id, true, null); } diff --git a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java index 6baf5a40105..86a8f2449f0 100644 --- a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java @@ -93,6 +93,7 @@ import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.languages.IsoLanguagesMapper; import org.fao.geonet.lib.Lib; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.specification.OperationAllowedSpecs; import org.fao.geonet.util.XslUtil; @@ -106,7 +107,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Lazy; @@ -128,40 +128,6 @@ import org.xhtmlrenderer.pdf.ITextRenderer; import springfox.documentation.annotations.ApiIgnore; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.io.ByteStreams; - -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import jeeves.server.context.ServiceContext; -import jeeves.server.dispatchers.ServiceManager; -import springfox.documentation.annotations.ApiIgnore; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.DirectoryStream; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.Callable; - -import static com.google.common.io.Files.getNameWithoutExtension; -import static org.fao.geonet.api.ApiParams.API_PARAM_RECORD_UUID; -import static org.fao.geonet.api.records.formatters.FormatterConstants.SCHEMA_PLUGIN_FORMATTER_DIR; -import static org.springframework.data.jpa.domain.Specifications.where; /** * Allows a user to display a metadata with a particular formatters * @@ -285,10 +251,13 @@ public void getRecordFormattedBy( value = "output", required = false) FormatType formatType, + @ApiParam(value = "Download the approved version", + required = false, defaultValue = "true") + @RequestParam(required = false, defaultValue = "true") + boolean approved, @ApiIgnore final NativeWebRequest request, final HttpServletRequest servletRequest) throws Exception { - ApplicationContext applicationContext = ApplicationContextHolder.get(); Locale locale = languageUtils.parseAcceptLanguage(servletRequest.getLocales()); // TODO : @@ -318,6 +287,10 @@ public void getRecordFormattedBy( formatType, request.getNativeRequest(HttpServletRequest.class)); AbstractMetadata metadata = ApiUtils.canViewRecord(metadataUuid, servletRequest); + + if(approved) { + metadata = context.getBean(MetadataRepository.class).findOneByUuid(metadataUuid); + } Boolean hideWithheld = true; diff --git a/services/src/main/java/org/fao/geonet/api/regions/metadata/MetadataRegionSearchRequest.java b/services/src/main/java/org/fao/geonet/api/regions/metadata/MetadataRegionSearchRequest.java index deb260af406..de30d03251d 100644 --- a/services/src/main/java/org/fao/geonet/api/regions/metadata/MetadataRegionSearchRequest.java +++ b/services/src/main/java/org/fao/geonet/api/regions/metadata/MetadataRegionSearchRequest.java @@ -23,26 +23,23 @@ package org.fao.geonet.api.regions.metadata; -import com.google.common.base.Optional; -import com.google.common.collect.Lists; - -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.geom.GeometryFactory; - -import jeeves.server.context.ServiceContext; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; import org.fao.geonet.GeonetContext; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.ISODate; import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.region.Region; import org.fao.geonet.kernel.region.Request; import org.fao.geonet.kernel.search.SearchManager; import org.fao.geonet.kernel.search.spatial.SpatialIndexWriter; import org.fao.geonet.lib.Lib; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.Utils; import org.fao.geonet.services.region.MetadataRegion; import org.fao.geonet.utils.Xml; @@ -50,11 +47,13 @@ import org.jdom.Element; import org.jdom.filter.Filter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.vividsolutions.jts.geom.Envelope; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +import jeeves.server.context.ServiceContext; public class MetadataRegionSearchRequest extends Request { @@ -203,7 +202,7 @@ private Element findMetadata(Id id, boolean includeEditData) throws Exception { final DataManager dataManager = context.getBean(DataManager.class); String mdId = id.getMdId(context.getBean(SearchManager.class), dataManager); try { - if (context.getBean(MetadataRepository.class).exists(Integer.parseInt(mdId))) { + if (context.getBean(IMetadataUtils.class).exists(Integer.parseInt(mdId))) { Lib.resource.checkPrivilege(context, mdId, ReservedOperation.view); return dataManager.getMetadata(context, mdId, includeEditData, false, true); diff --git a/services/src/main/java/org/fao/geonet/api/registries/DirectoryApi.java b/services/src/main/java/org/fao/geonet/api/registries/DirectoryApi.java index a8cbab08d76..fcf33f25769 100644 --- a/services/src/main/java/org/fao/geonet/api/registries/DirectoryApi.java +++ b/services/src/main/java/org/fao/geonet/api/registries/DirectoryApi.java @@ -65,6 +65,7 @@ import org.fao.geonet.kernel.mef.MEFLib; import org.fao.geonet.kernel.schema.MetadataSchema; import org.fao.geonet.kernel.setting.SettingManager; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.metadata.BatchOpsMetadataReindexer; import org.fao.geonet.utils.Xml; import org.geotools.GML; @@ -269,7 +270,7 @@ private ResponseEntity collectEntries( // List of identifier to check for duplicates Set listOfEntries = new HashSet<>(); Set listOfEntriesInternalId = new HashSet<>(); - final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); final int user = context.getUserSession().getUserIdAsInt(); final String siteId = context.getBean(SettingManager.class).getSiteId(); diff --git a/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java b/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java index 6b8fbe81114..88d44080c94 100644 --- a/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java +++ b/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java @@ -19,9 +19,9 @@ import org.fao.geonet.kernel.search.MetaSearcher; import org.fao.geonet.kernel.search.SearchManager; import org.fao.geonet.kernel.search.SearcherType; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.subtemplate.Get; import org.fao.geonet.util.Sha1Encoder; -import org.fao.geonet.util.XslUtil; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.jdom.Attribute; @@ -162,7 +162,7 @@ private static CollectResults collectEntries(ServiceContext context, CollectResults collectResults = new CollectResults(record); Map> namespaceList = new HashMap>(); - IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); if (Log.isDebugEnabled(LOGGER)) { Log.debug(LOGGER, String.format( diff --git a/services/src/main/java/org/fao/geonet/api/selections/UserSelectionsApi.java b/services/src/main/java/org/fao/geonet/api/selections/UserSelectionsApi.java index 204eae853c0..a0546e7fce3 100644 --- a/services/src/main/java/org/fao/geonet/api/selections/UserSelectionsApi.java +++ b/services/src/main/java/org/fao/geonet/api/selections/UserSelectionsApi.java @@ -351,11 +351,10 @@ ResponseEntity addToUserSelection( } UserSavedSelectionRepository umsRepository = appContext.getBean(UserSavedSelectionRepository.class); - IMetadataUtils mdRepository = appContext.getBean(IMetadataUtils.class); + IMetadataUtils metadataUtils = appContext.getBean(IMetadataUtils.class); for (String u : uuid) { // Check record exist - AbstractMetadata md = mdRepository.findOneByUuid(u); - if (md != null) { + if (metadataUtils.existsMetadataUuid(u)) { UserSavedSelection e = new UserSavedSelection(selection, user, u); try { umsRepository.save(e); diff --git a/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java b/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java index 2a609c9c995..b36113cf87a 100644 --- a/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java +++ b/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java @@ -49,7 +49,6 @@ import org.fao.geonet.api.userfeedback.service.IUserFeedbackService; import org.fao.geonet.api.users.recaptcha.RecaptchaChecker; import org.fao.geonet.domain.AbstractMetadata; -import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.userfeedback.RatingCriteria; import org.fao.geonet.domain.userfeedback.RatingsSetting; import org.fao.geonet.domain.userfeedback.UserFeedback; @@ -616,7 +615,7 @@ public ResponseEntity sendEmailToContact( toAddress.add(to); if (isNotBlank(metadataEmail)) { //Check metadata email belongs to metadata security!! - Metadata md = metadataRepository.findOneByUuid(metadataUuid); + AbstractMetadata md = metadataRepository.findOneByUuid(metadataUuid); if(md.getData().indexOf(metadataEmail) > 0) { toAddress.add(metadataEmail); } diff --git a/services/src/main/java/org/fao/geonet/api/userfeedback/service/UserFeedbackDatabaseService.java b/services/src/main/java/org/fao/geonet/api/userfeedback/service/UserFeedbackDatabaseService.java index 82403eae849..1a1d30d3afd 100644 --- a/services/src/main/java/org/fao/geonet/api/userfeedback/service/UserFeedbackDatabaseService.java +++ b/services/src/main/java/org/fao/geonet/api/userfeedback/service/UserFeedbackDatabaseService.java @@ -28,13 +28,13 @@ import org.apache.jcs.access.exception.ObjectNotFoundException; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.api.userfeedback.UserFeedbackUtils; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.ISODate; import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.User; import org.fao.geonet.domain.userfeedback.Rating; import org.fao.geonet.domain.userfeedback.UserFeedback; import org.fao.geonet.domain.userfeedback.UserFeedback.UserRatingStatus; -import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.UserRepository; @@ -230,8 +230,8 @@ public void saveUserFeedback(UserFeedback userFeedback, String ip) throws Except final User approver = userRepository.findOneByUsername(userFeedback.getApprover().getUsername()); userFeedback.setApprover(approver); } - final Metadata metadata = metadataRepository.findOneByUuid(userFeedback.getMetadata().getUuid()); - userFeedback.setMetadata(metadata); + final AbstractMetadata metadata = metadataRepository.findOneByUuid(userFeedback.getMetadata().getUuid()); + userFeedback.setMetadata((Metadata)metadata); if (userFeedback.getCreationDate() == null) { userFeedback.setCreationDate(new ISODate(System.currentTimeMillis()).toDate()); diff --git a/services/src/main/java/org/fao/geonet/api/users/UsersApi.java b/services/src/main/java/org/fao/geonet/api/users/UsersApi.java index 4e5905f3afb..f3c47b919da 100644 --- a/services/src/main/java/org/fao/geonet/api/users/UsersApi.java +++ b/services/src/main/java/org/fao/geonet/api/users/UsersApi.java @@ -37,6 +37,8 @@ import org.fao.geonet.domain.*; import org.fao.geonet.exceptions.UserNotFoundEx; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.GroupRepository; import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.SortUtils; @@ -228,12 +230,12 @@ public ResponseEntity deleteUser( // elsewhere in the GeoNetwork database - an exception is thrown if // this is the case if (dataManager.isUserMetadataOwner(userIdentifier)) { - MetadataRepository metadataRepository = ApplicationContextHolder.get().getBean(MetadataRepository.class); - final List allUserRecords = metadataRepository.findAll(MetadataSpecs.isOwnedByUser(userIdentifier)); + IMetadataUtils metadataRepository = ApplicationContextHolder.get().getBean(IMetadataUtils.class); + final long numUserRecords = metadataRepository.count(MetadataSpecs.isOwnedByUser(userIdentifier)); throw new IllegalArgumentException( String.format( "Cannot delete a user that is also metadata owner of %d record(s) (can be records, templates, subtemplates). Change owner of those records or remove them first.", - allUserRecords.size())); + numUserRecords)); } if (dataManager.isUserMetadataStatus(userIdentifier)) { diff --git a/services/src/main/java/org/fao/geonet/guiservices/metadata/GetByOwner.java b/services/src/main/java/org/fao/geonet/guiservices/metadata/GetByOwner.java index 8296b0c7283..d336f86340b 100644 --- a/services/src/main/java/org/fao/geonet/guiservices/metadata/GetByOwner.java +++ b/services/src/main/java/org/fao/geonet/guiservices/metadata/GetByOwner.java @@ -23,34 +23,39 @@ package org.fao.geonet.guiservices.metadata; -import com.google.common.base.Function; -import com.google.common.collect.Lists; +import static org.springframework.data.jpa.domain.Specifications.where; -import org.fao.geonet.domain.*; -import org.fao.geonet.exceptions.OperationNotAllowedEx; +import java.nio.file.Path; +import java.util.List; -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.fao.geonet.GeonetContext; import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Metadata; +import org.fao.geonet.domain.MetadataDataInfo_; +import org.fao.geonet.domain.Metadata_; +import org.fao.geonet.domain.Profile; +import org.fao.geonet.domain.UserGroup; +import org.fao.geonet.exceptions.OperationNotAllowedEx; import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.repository.MetadataRepository; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.UserGroupRepository; import org.fao.geonet.repository.specification.MetadataSpecs; import org.fao.geonet.repository.specification.UserGroupSpecs; import org.jdom.Element; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import java.nio.file.Path; -import java.util.List; +import com.google.common.base.Function; +import com.google.common.collect.Lists; -import static org.springframework.data.jpa.domain.Specifications.*; +import jeeves.interfaces.Service; +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; /** * Retrieves the metadata owned by a user. Depending on user profile: @@ -98,7 +103,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { Specifications spec; // if the user is an admin, return all metadata if (userProfile == Profile.Administrator) { - spec = where(MetadataSpecs.isHarvested(false)); + spec = where((Specification)MetadataSpecs.isHarvested(false)); } else if (userProfile == Profile.Reviewer || userProfile == Profile.UserAdmin) { final List groups = context.getBean(UserGroupRepository.class).findAll(UserGroupSpecs.hasUserId(ownerId)); List groupIds = Lists.transform(groups, new Function() { @@ -108,10 +113,10 @@ public Integer apply(@Nonnull UserGroup input) { return input.getId().getGroupId(); } }); - spec = where(MetadataSpecs.isHarvested(false)).and(MetadataSpecs.isOwnedByOneOfFollowingGroups(groupIds)); + spec = where((Specification)MetadataSpecs.isHarvested(false)).and((Specification)MetadataSpecs.isOwnedByOneOfFollowingGroups(groupIds)); // if the user is a reviewer, return all metadata of the user's groups } else if (userProfile == Profile.Editor) { - spec = where(MetadataSpecs.isOwnedByUser(ownerId)).and(MetadataSpecs.isHarvested(false)); + spec = where((Specification)MetadataSpecs.isOwnedByUser(ownerId)).and((Specification)MetadataSpecs.isHarvested(false)); // if the user is an editor, return metadata owned by this user } else { throw new OperationNotAllowedEx("Unauthorized user " + ownerId + " attempted to list editable metadata "); @@ -131,10 +136,11 @@ public Integer apply(@Nonnull UserGroup input) { throw new IllegalArgumentException("Unknown sortBy parameter: " + sortBy); } - List metadataList = context.getBean(MetadataRepository.class).findAll(spec, order); + List metadataList = + context.getBean(IMetadataUtils.class).findAll((Specification)spec, order); _response = new Element("response"); - for (Metadata rec : metadataList) { + for (AbstractMetadata rec : metadataList) { String id = "" + rec.getId(); boolean forEditing = false, withValidationErrors = false, keepXlinkAttributes = false; Element md = gc.getBean(DataManager.class).getMetadata(context, id, forEditing, withValidationErrors, keepXlinkAttributes); diff --git a/services/src/main/java/org/fao/geonet/guiservices/metadata/GetRelated.java b/services/src/main/java/org/fao/geonet/guiservices/metadata/GetRelated.java index 34f1a96b922..1d4e0098949 100644 --- a/services/src/main/java/org/fao/geonet/guiservices/metadata/GetRelated.java +++ b/services/src/main/java/org/fao/geonet/guiservices/metadata/GetRelated.java @@ -41,7 +41,7 @@ import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; import org.fao.geonet.kernel.RelatedMetadata; -import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.Utils; import org.fao.geonet.utils.Xml; import org.jdom.Element; @@ -157,7 +157,7 @@ public HttpEntity exec(@PathVariable String lang, ConfigurableApplicationContext appContext = ApplicationContextHolder.get(); ServiceManager serviceManager = appContext.getBean(ServiceManager.class); GeonetworkDataDirectory dataDirectory = appContext.getBean(GeonetworkDataDirectory.class); - IMetadataUtils metadataRepository = appContext.getBean(IMetadataUtils.class); + MetadataRepository metadataRepository = appContext.getBean(MetadataRepository.class); final ServiceContext context = serviceManager.createServiceContext("xml.relation", lang, request); diff --git a/services/src/main/java/org/fao/geonet/guiservices/metadata/Sitemap.java b/services/src/main/java/org/fao/geonet/guiservices/metadata/Sitemap.java index 613450a9598..458f000d8d6 100644 --- a/services/src/main/java/org/fao/geonet/guiservices/metadata/Sitemap.java +++ b/services/src/main/java/org/fao/geonet/guiservices/metadata/Sitemap.java @@ -28,6 +28,7 @@ import org.fao.geonet.Util; import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataDataInfo_; import org.fao.geonet.domain.Metadata_; import org.fao.geonet.domain.OperationAllowed; @@ -41,6 +42,7 @@ import org.jdom.Element; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; import jeeves.interfaces.Service; @@ -99,7 +101,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { OperationAllowedId_.metadataId); Sort sortByChangeDateDesc = new Sort(Sort.Direction.DESC, Metadata_.dataInfo.getName() + "." + MetadataDataInfo_.changeDate.getName()); - long metadatataCount = metadataRepository.count(MetadataSpecs.hasMetadataIdIn(list)); + long metadatataCount = metadataRepository.count((Specification)MetadataSpecs.hasMetadataIdIn(list)); long pages = (long) Math.ceil((double) metadatataCount / _maxItemsPage); int doc = Util.getParam(params, "doc", 0); diff --git a/services/src/main/java/org/fao/geonet/services/feedback/AddLimitations.java b/services/src/main/java/org/fao/geonet/services/feedback/AddLimitations.java index b768f4c0a3a..557a97f1c9b 100644 --- a/services/src/main/java/org/fao/geonet/services/feedback/AddLimitations.java +++ b/services/src/main/java/org/fao/geonet/services/feedback/AddLimitations.java @@ -37,8 +37,8 @@ import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.domain.User; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.lib.Lib; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.UserRepository; import org.fao.geonet.services.Utils; import org.fao.geonet.utils.BinaryFile; @@ -103,7 +103,7 @@ public Element exec(Element params, final ServiceContext context) throws Excepti Lib.resource.checkPrivilege(context, id, ReservedOperation.download); //--- get metadata info - AbstractMetadata info = context.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata info = context.getBean(IMetadataUtils.class).findOne(id); if (info == null) throw new IllegalArgumentException("Metadata not found --> " + id); diff --git a/services/src/main/java/org/fao/geonet/services/feedback/Insert.java b/services/src/main/java/org/fao/geonet/services/feedback/Insert.java index fa9b0a9e95d..9ff85173450 100644 --- a/services/src/main/java/org/fao/geonet/services/feedback/Insert.java +++ b/services/src/main/java/org/fao/geonet/services/feedback/Insert.java @@ -32,18 +32,14 @@ import org.fao.geonet.constants.Geonet; import org.fao.geonet.constants.Params; import org.fao.geonet.domain.AbstractMetadata; -import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.setting.SettingManager; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.util.MailUtil; import org.jdom.Element; import jeeves.interfaces.Service; import jeeves.server.ServiceConfig; import jeeves.server.context.ServiceContext; -import org.jdom.Element; - -import javax.naming.OperationNotSupportedException; -import java.nio.file.Path; /** * Stores the feedback from a user into the database and sends an e-mail @@ -85,7 +81,7 @@ public Element exec(Element params, final ServiceContext context) if (metadataEmail != null) { //Check metadata email belongs to metadata //security!! - AbstractMetadata md = gc.getBean(IMetadataUtils.class).findOneByUuid(uuid); + AbstractMetadata md = gc.getBean(MetadataRepository.class).findOneByUuid(uuid); if(md.getData().indexOf(metadataEmail) > 0) { toAddress.add(metadataEmail); } diff --git a/services/src/main/java/org/fao/geonet/services/group/Remove.java b/services/src/main/java/org/fao/geonet/services/group/Remove.java index a8ae531145c..ae2e46c8770 100644 --- a/services/src/main/java/org/fao/geonet/services/group/Remove.java +++ b/services/src/main/java/org/fao/geonet/services/group/Remove.java @@ -74,7 +74,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw Integer iId = Integer.valueOf(id); List reindex = operationAllowedRepo.findAllIds(OperationAllowedSpecs.hasGroupId(iId), OperationAllowedId_.metadataId); - operationAllowedRepo.deleteAllByIdAttribute(OperationAllowedId_.groupId, iId); + operationAllowedRepo.deleteAllByGroupId(iId); userGroupRepo.deleteAllByIdAttribute(UserGroupId_.groupId, Arrays.asList(iId)); groupRepo.delete(iId); //--- reindex affected metadata diff --git a/services/src/main/java/org/fao/geonet/services/mef/Export.java b/services/src/main/java/org/fao/geonet/services/mef/Export.java index 901f67a3902..6bf6c577b5b 100644 --- a/services/src/main/java/org/fao/geonet/services/mef/Export.java +++ b/services/src/main/java/org/fao/geonet/services/mef/Export.java @@ -91,6 +91,7 @@ public Element exec(Element params, ServiceContext context) boolean resolveXlink = Boolean.parseBoolean(Util.getParam(params, "resolveXlink", "true")); boolean removeXlinkAttribute = Boolean.parseBoolean(Util.getParam(params, "removeXlinkAttribute", "true")); boolean addSchemaLocation = Boolean.parseBoolean(Util.getParam(params, "addSchemaLocation", "true")); + boolean approved = Boolean.parseBoolean(Util.getParam(params, "approved", "true")); String relatedMetadataRecord = Util .getParam(params, "relation", "true"); @@ -116,7 +117,7 @@ public Element exec(Element params, ServiceContext context) // Uuid parameter MUST be set and add to selection manager before // export. if (version == null) { - file = MEFLib.doExport(context, uuid, format, skipUUID, resolveXlink, removeXlinkAttribute, addSchemaLocation); + file = MEFLib.doExport(context, uuid, format, skipUUID, resolveXlink, removeXlinkAttribute, addSchemaLocation, approved); } else { // MEF version 2 support multiple metadata record by file. @@ -165,7 +166,7 @@ public Element exec(Element params, ServiceContext context) Log.info(Geonet.MEF, "Building MEF2 file with " + uuids.size() + " records."); - file = MEFLib.doMEF2Export(context, uuids, format, false, stylePath, resolveXlink, removeXlinkAttribute, false, addSchemaLocation); + file = MEFLib.doMEF2Export(context, uuids, format, false, stylePath, resolveXlink, removeXlinkAttribute, false, addSchemaLocation, approved); } // -- Reset selection manager diff --git a/services/src/main/java/org/fao/geonet/services/metadata/BatchDelete.java b/services/src/main/java/org/fao/geonet/services/metadata/BatchDelete.java index e561ca10302..bd2ed7d6474 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/BatchDelete.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/BatchDelete.java @@ -95,30 +95,33 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw context.debug("Deleting metadata with uuid:" + uuid); } - AbstractMetadata info = metadataRepository.findOneByUuid(uuid); - if (info == null) { + if (!metadataRepository.existsMetadataUuid(uuid)) { notFound.add(uuid); - } else if (!accessMan.isOwner(context, String.valueOf(info.getId()))) { - notOwner.add(uuid); - } else { - String idString = String.valueOf(info.getId()); - - //--- backup metadata in 'removed' folder - if (backupFile && - info.getDataInfo().getType() != MetadataType.SUB_TEMPLATE && - info.getDataInfo().getType() != MetadataType.TEMPLATE_OF_SUB_TEMPLATE) { - backupFile(context, idString, info.getUuid(), MEFLib.doExport(context, info.getUuid(), "full", false, true, false, false)); - } - - //--- remove the metadata directory - Path pb = Lib.resource.getMetadataDir(context.getBean(GeonetworkDataDirectory.class), idString); - IO.deleteFileOrDirectory(pb); - - //--- delete metadata and return status - dataMan.deleteMetadata(context, idString); - if (context.isDebugEnabled()) - context.debug(" Metadata with id " + idString + " deleted."); - metadata.add(uuid); + } + + for(AbstractMetadata info : metadataRepository.findAllByUuid(uuid)) { + if (!accessMan.isOwner(context, String.valueOf(info.getId()))) { + notOwner.add(uuid); + } else { + String idString = String.valueOf(info.getId()); + + //--- backup metadata in 'removed' folder + if (backupFile && + info.getDataInfo().getType() != MetadataType.SUB_TEMPLATE && + info.getDataInfo().getType() != MetadataType.TEMPLATE_OF_SUB_TEMPLATE) { + backupFile(context, idString, info.getUuid(), MEFLib.doExport(context, info.getUuid(), "full", false, true, false, false, true)); + } + + //--- remove the metadata directory + Path pb = Lib.resource.getMetadataDir(context.getBean(GeonetworkDataDirectory.class), idString); + IO.deleteFileOrDirectory(pb); + + //--- delete metadata and return status + dataMan.deleteMetadata(context, idString); + if (context.isDebugEnabled()) + context.debug(" Metadata with id " + idString + " deleted."); + metadata.add(uuid); + } } } diff --git a/services/src/main/java/org/fao/geonet/services/metadata/BatchNewOwner.java b/services/src/main/java/org/fao/geonet/services/metadata/BatchNewOwner.java index 0b8ba0670da..1a535af22fa 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/BatchNewOwner.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/BatchNewOwner.java @@ -41,7 +41,7 @@ import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.SelectionManager; -import org.fao.geonet.repository.MetadataRepository; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; @@ -115,7 +115,7 @@ private NewOwnerResult setNewOwner(ServiceContext context, String targetUsr, Str //--- check existence and access AbstractMetadata info = null; if (id != null) { - info = context.getBean(MetadataRepository.class).findOne(id); + info = context.getBean(IMetadataUtils.class).findOne(id); } if (info == null) { if (id != null) { diff --git a/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdateCategories.java b/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdateCategories.java index 7068f0f7a73..587851e6259 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdateCategories.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdateCategories.java @@ -40,8 +40,8 @@ import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.SelectionManager; import org.fao.geonet.kernel.datamanager.IMetadataManager; -import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.MetadataCategoryRepository; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.NotInReadOnlyModeService; import org.jdom.Element; @@ -101,7 +101,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw // --- check access - final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); final IMetadataManager metadataManager = context.getBean(IMetadataManager.class); AbstractMetadata info = metadataRepository.findOne(id); if (info == null) { @@ -112,7 +112,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw // --- remove old operations if (!"replace".equals(mode)) { - info.getMetadataCategories().clear(); + info.getCategories().clear(); } // --- set new ones @@ -126,7 +126,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw if (name.startsWith("_") && !name.equals(Params.CONTENT_TYPE)) { final MetadataCategory category = categoryRepository.findOne(Integer.valueOf(name.substring(1))); if (category != null) { - info.getMetadataCategories().add(category); + info.getCategories().add(category); } else { context.warning("Unable to find category with name: " + name.substring(1)); } diff --git a/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdatePrivileges.java b/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdatePrivileges.java index 5b678998e3e..5c3bab5bb5f 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdatePrivileges.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/BatchUpdatePrivileges.java @@ -42,7 +42,7 @@ import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.SelectionManager; -import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.NotInReadOnlyModeService; import org.jdom.Element; @@ -91,7 +91,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw //--- check access - AbstractMetadata info = context.getBean(IMetadataUtils.class).findOneByUuid(uuid); + AbstractMetadata info = context.getBean(MetadataRepository.class).findOneByUuid(uuid); if (info == null) { notFound.add(uuid); } else if (!accessMan.isOwner(context, String.valueOf(info.getId()))) { diff --git a/services/src/main/java/org/fao/geonet/services/metadata/Convert.java b/services/src/main/java/org/fao/geonet/services/metadata/Convert.java index fde25d3ab7d..84b17c27861 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/Convert.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/Convert.java @@ -33,9 +33,9 @@ import org.fao.geonet.domain.ISODate; import org.fao.geonet.exceptions.MetadataNotFoundEx; import org.fao.geonet.kernel.SchemaManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.oaipmh.Lib; import org.fao.geonet.kernel.setting.SettingManager; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.Utils; import org.jdom.Element; @@ -79,7 +79,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { String styleSheet = Util.getParam(params, Params.STYLESHEET); //--- get metadata info and create an env that works with oai translators - final AbstractMetadata metadata = context.getBean(MetadataRepository.class).findOne(id); + final AbstractMetadata metadata = context.getBean(IMetadataUtils.class).findOne(id); Path schemaDir = sm.getSchemaDir(metadata.getDataInfo().getSchemaId()); final String baseUrl = context.getBaseUrl(); final ISODate changeDate = metadata.getDataInfo().getChangeDate(); diff --git a/services/src/main/java/org/fao/geonet/services/metadata/Delete.java b/services/src/main/java/org/fao/geonet/services/metadata/Delete.java index a4cf0149dc8..4b16d7b582a 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/Delete.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/Delete.java @@ -35,9 +35,9 @@ import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.mef.MEFLib; import org.fao.geonet.lib.Lib; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.Utils; import org.fao.geonet.utils.IO; import org.jdom.Element; @@ -75,7 +75,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw //----------------------------------------------------------------------- //--- check access - AbstractMetadata metadata = context.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata metadata = context.getBean(IMetadataUtils.class).findOne(id); if (metadata == null) throw new IllegalArgumentException("Metadata with identifier " + id + " not found."); @@ -87,7 +87,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw //--- backup metadata in 'removed' folder if (metadata.getDataInfo().getType() != MetadataType.SUB_TEMPLATE && backupFile) - backupFile(context, id, metadata.getUuid(), MEFLib.doExport(context, metadata.getUuid(), "full", false, true, false, false)); + backupFile(context, id, metadata.getUuid(), MEFLib.doExport(context, metadata.getUuid(), "full", false, true, false, false, true)); //----------------------------------------------------------------------- //--- remove the metadata directory including the public and private directories. diff --git a/services/src/main/java/org/fao/geonet/services/metadata/GetAdminOper.java b/services/src/main/java/org/fao/geonet/services/metadata/GetAdminOper.java index 610343a5c32..c71ea2e378e 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/GetAdminOper.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/GetAdminOper.java @@ -36,6 +36,7 @@ import org.fao.geonet.domain.UserGroup; import org.fao.geonet.exceptions.MetadataNotFoundEx; import org.fao.geonet.kernel.AccessManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.GroupRepository; import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; @@ -83,7 +84,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { //----------------------------------------------------------------------- //--- check access - AbstractMetadata info = context.getBean(MetadataRepository.class).findOne(metadataId); + AbstractMetadata info = context.getBean(IMetadataUtils.class).findOne(metadataId); if (info == null) throw new MetadataNotFoundEx(metadataId); diff --git a/services/src/main/java/org/fao/geonet/services/metadata/GetCategories.java b/services/src/main/java/org/fao/geonet/services/metadata/GetCategories.java index 31dd54c7bb7..eaccf1b0149 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/GetCategories.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/GetCategories.java @@ -89,7 +89,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { HashSet hsMetadataCat = new HashSet(); - for (MetadataCategory cat : metadata.getMetadataCategories()) { + for (MetadataCategory cat : metadata.getCategories()) { hsMetadataCat.add(cat.getId() + ""); } diff --git a/services/src/main/java/org/fao/geonet/services/metadata/GetSuggestion.java b/services/src/main/java/org/fao/geonet/services/metadata/GetSuggestion.java index 892da3f46da..70578cec4b8 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/GetSuggestion.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/GetSuggestion.java @@ -34,9 +34,9 @@ import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.schema.MetadataSchema; import org.fao.geonet.kernel.setting.SettingManager; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.Utils; import org.fao.geonet.utils.Xml; import org.jdom.Element; @@ -97,7 +97,7 @@ public Element exec(Element params, ServiceContext context) // Retrieve metadata record String id = Utils.getIdentifierFromParameters(params, context); - AbstractMetadata mdInfo = gc.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata mdInfo = gc.getBean(IMetadataUtils.class).findOne(id); boolean forEditing = false, withValidationErrors = false, keepXlinkAttributes = false; Element md = gc.getBean(DataManager.class).getMetadata(context, id, forEditing, withValidationErrors, keepXlinkAttributes); diff --git a/services/src/main/java/org/fao/geonet/services/metadata/Insert.java b/services/src/main/java/org/fao/geonet/services/metadata/Insert.java index 494dece56a6..ed6f5f4c6a7 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/Insert.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/Insert.java @@ -23,11 +23,13 @@ package org.fao.geonet.services.metadata; -import com.google.common.collect.Maps; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; -import jeeves.constants.Jeeves; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; +import javax.annotation.Nonnull; import org.fao.geonet.GeonetContext; import org.fao.geonet.Util; @@ -38,23 +40,21 @@ import org.fao.geonet.domain.MetadataType; import org.fao.geonet.exceptions.BadParameterEx; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.mef.Importer; import org.fao.geonet.kernel.mef.MEFLib; import org.fao.geonet.kernel.setting.SettingManager; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.Updater; import org.fao.geonet.services.NotInReadOnlyModeService; import org.fao.geonet.utils.FilePathChecker; import org.fao.geonet.utils.Xml; import org.jdom.Element; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import com.google.common.collect.Maps; -import javax.annotation.Nonnull; +import jeeves.constants.Jeeves; +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; /** * Inserts a new metadata to the system (data is validated). @@ -162,7 +162,7 @@ public Element serviceSpecificExec(Element params, final ServiceContext context) final boolean hasCategory = !category.equals("_none_") && !category.trim().isEmpty(); if (hasCategory || extra != null) { - context.getBean(MetadataRepository.class).update(iId, new Updater() { + context.getBean(IMetadataManager.class).update(iId, new Updater() { @Override public void apply(@Nonnull Metadata metadata) { if (hasCategory) { diff --git a/services/src/main/java/org/fao/geonet/services/metadata/Rate.java b/services/src/main/java/org/fao/geonet/services/metadata/Rate.java index b603edd8320..8257ac69b75 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/Rate.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/Rate.java @@ -38,13 +38,13 @@ import org.fao.geonet.exceptions.BadServerResponseEx; import org.fao.geonet.exceptions.MetadataNotFoundEx; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.harvest.HarvestManager; import org.fao.geonet.kernel.harvest.harvester.AbstractHarvester; import org.fao.geonet.kernel.harvest.harvester.geonet.GeonetHarvester; import org.fao.geonet.kernel.harvest.harvester.geonet.GeonetParams; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.lib.Lib; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.NotInReadOnlyModeService; import org.fao.geonet.services.Utils; import org.fao.geonet.utils.GeonetHttpRequestFactory; @@ -136,7 +136,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw //-------------------------------------------------------------------------- private String getHarvestingUuid(ServiceContext context, String id) throws Exception { - final AbstractMetadata metadata = context.getBean(MetadataRepository.class).findOne(id); + final AbstractMetadata metadata = context.getBean(IMetadataUtils.class).findOne(id); //--- if we don't have any metadata, just return diff --git a/services/src/main/java/org/fao/geonet/services/metadata/Show.java b/services/src/main/java/org/fao/geonet/services/metadata/Show.java index 4296915426f..4cee52a0fc5 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/Show.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/Show.java @@ -35,8 +35,8 @@ import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.SchemaManager; import org.fao.geonet.kernel.XmlSerializer; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.lib.Lib; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.Utils; import org.fao.geonet.utils.Xml; import org.jdom.Attribute; @@ -151,7 +151,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { // that is in the GeoNetwork schema identification and if there isn't one // of those then build one pointing to the XSD in GeoNetwork - AbstractMetadata info = context.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata info = context.getBean(IMetadataUtils.class).findOne(id); Attribute schemaLocAtt = sm.getSchemaLocation(info.getDataInfo().getSchemaId(), context); if (schemaLocAtt != null) { diff --git a/services/src/main/java/org/fao/geonet/services/metadata/UpdateAdminOper.java b/services/src/main/java/org/fao/geonet/services/metadata/UpdateAdminOper.java index 757ddfb1b90..7d3e3af4db0 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/UpdateAdminOper.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/UpdateAdminOper.java @@ -38,15 +38,14 @@ import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.exceptions.MetadataNotFoundEx; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataIndexer; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.datamanager.IMetadataValidator; import org.fao.geonet.kernel.setting.SettingManager; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.MetadataValidationRepository; import org.fao.geonet.repository.specification.MetadataValidationSpecs; import org.fao.geonet.services.NotInReadOnlyModeService; import org.fao.geonet.services.Utils; -import org.jdom.Document; import org.jdom.Element; import jeeves.constants.Jeeves; @@ -54,98 +53,99 @@ import jeeves.server.UserSession; import jeeves.server.context.ServiceContext; - /** * Stores all operations allowed for a metadata for each groups. * * In order to set a value for a group use __. * - * By default, all operations are removed and then added according to the parameter. In order to set - * or unset existing operations, add the update parameter with value true and set the off/on status - * for each operations (eg. __=. + * By default, all operations are removed and then added according to the + * parameter. In order to set or unset existing operations, add the update + * parameter with value true and set the off/on status for each operations (eg. + * __=. * * Called by the metadata.admin service (ie. privileges panel). * - * Sample URL: http://localhost:8080/geonetwork/srv/eng/metadata.admin?update=true&id=13962&_1_0=off&_1_1=off&_1_5=off&_1_6=off + * Sample URL: + * http://localhost:8080/geonetwork/srv/eng/metadata.admin?update=true&id=13962&_1_0=off&_1_1=off&_1_5=off&_1_6=off */ @Deprecated public class UpdateAdminOper extends NotInReadOnlyModeService { - //-------------------------------------------------------------------------- - //--- - //--- Init - //--- - //-------------------------------------------------------------------------- - - public void init(Path appPath, ServiceConfig params) throws Exception { - } - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - - public Element serviceSpecificExec(Element params, ServiceContext context) throws Exception { - GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME); - DataManager dm = gc.getBean(DataManager.class); - UserSession us = context.getUserSession(); - - String id = Utils.getIdentifierFromParameters(params, context); - boolean update = Util.getParam(params, Params.UPDATEONLY, "false").equals("true"); + // -------------------------------------------------------------------------- + // --- + // --- Init + // --- + // -------------------------------------------------------------------------- + + public void init(Path appPath, ServiceConfig params) throws Exception { + } + + // -------------------------------------------------------------------------- + // --- + // --- Service + // --- + // -------------------------------------------------------------------------- + + public Element serviceSpecificExec(Element params, ServiceContext context) throws Exception { + GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME); + DataManager dm = gc.getBean(DataManager.class); + UserSession us = context.getUserSession(); + + String id = Utils.getIdentifierFromParameters(params, context); + boolean update = Util.getParam(params, Params.UPDATEONLY, "false").equals("true"); SettingManager sm = context.getBean(SettingManager.class); boolean allowPublishInvalidMd = sm.getValueAsBool("metadata/workflow/allowPublishInvalidMd"); - //----------------------------------------------------------------------- - //--- check access + // ----------------------------------------------------------------------- + // --- check access - AbstractMetadata info = context.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata info = context.getBean(IMetadataUtils.class).findOne(id); - if (info == null) - throw new MetadataNotFoundEx(id); + if (info == null) + throw new MetadataNotFoundEx(id); - //----------------------------------------------------------------------- - //--- remove old operations + // ----------------------------------------------------------------------- + // --- remove old operations - boolean skip = false; + boolean skip = false; - //--- in case of owner, privileges for groups 0,1 and GUEST are disabled - //--- and are not sent to the server. So we cannot remove them + // --- in case of owner, privileges for groups 0,1 and GUEST are disabled + // --- and are not sent to the server. So we cannot remove them - boolean isAdmin = Profile.Administrator == us.getProfile(); - boolean isReviewer = Profile.Reviewer == us.getProfile(); + boolean isAdmin = Profile.Administrator == us.getProfile(); + boolean isReviewer = Profile.Reviewer == us.getProfile(); + if (us.getUserIdAsInt() == info.getSourceInfo().getOwner() && !isAdmin && !isReviewer) { + skip = true; + } - if (us.getUserIdAsInt() == info.getSourceInfo().getOwner() && !isAdmin && !isReviewer) { - skip = true; - } - - if (!update) { - dm.deleteMetadataOper(context, id, skip); - } + if (!update) { + dm.deleteMetadataOper(context, id, skip); + } - //----------------------------------------------------------------------- - //--- set new ones + // ----------------------------------------------------------------------- + // --- set new ones - @SuppressWarnings("unchecked") - List list = params.getChildren(); + @SuppressWarnings("unchecked") + List list = params.getChildren(); - Pattern opParamPatter = Pattern.compile("_([0-9]+)_([0-9]+)"); - for (Element el : list) { - String name = el.getName(); - Matcher matcher = opParamPatter.matcher(name); - if (matcher.matches()) { - String groupId = matcher.group(1); - String operId = matcher.group(2); + Pattern opParamPatter = Pattern.compile("_([0-9]+)_([0-9]+)"); + for (Element el : list) { + String name = el.getName(); + Matcher matcher = opParamPatter.matcher(name); + if (matcher.matches()) { + String groupId = matcher.group(1); + String operId = matcher.group(2); - // Never set editing for reserved group - if (Integer.parseInt(operId) == ReservedOperation.editing.getId() && - ReservedGroup.isReserved(Integer.valueOf(groupId))) { - continue; - } + // Never set editing for reserved group + if (Integer.parseInt(operId) == ReservedOperation.editing.getId() + && ReservedGroup.isReserved(Integer.valueOf(groupId))) { + continue; + } if (!update) { - // For privileges to ALL group, check if it's allowed or not to publish invalid metadata + // For privileges to ALL group, check if it's allowed or not to publish invalid + // metadata if (groupId.equals(ReservedGroup.all.getId() + "") && (!allowPublishInvalidMd)) { if (!canPublishToAllGroup(context, dm, Integer.parseInt(id))) { continue; @@ -157,7 +157,8 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw boolean publish = "on".equals(el.getTextTrim()); if (publish) { - // For privileges to ALL group, check if it's allowed or not to publish invalid metadata + // For privileges to ALL group, check if it's allowed or not to publish invalid + // metadata if (groupId.equals(ReservedGroup.all.getId() + "") && (!allowPublishInvalidMd)) { if (!canPublishToAllGroup(context, dm, Integer.parseInt(id))) { continue; @@ -172,39 +173,40 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw } } - //--- index metadata - dm.indexMetadata(id, true, null); + // --- index metadata + dm.indexMetadata(id, true, null); - //--- return id for showing + // --- return id for showing return new Element(Jeeves.Elem.RESPONSE).addContent(new Element(Geonet.Elem.ID).setText(id)); } /** - * For privileges to ALL group, check if it's allowed or not to publish invalid metadata. + * For privileges to ALL group, check if it's allowed or not to publish invalid + * metadata. * * @param context * @param dm * @param mdId * @return - * @throws Exception - */ + * @throws Exception + */ private boolean canPublishToAllGroup(ServiceContext context, DataManager dm, int mdId) throws Exception { - IMetadataUtils metadataUtils = context.getBean(IMetadataUtils.class); + IMetadataUtils metadataUtils = context.getBean(IMetadataUtils.class); MetadataValidationRepository metadataValidationRepository = context.getBean(MetadataValidationRepository.class); IMetadataValidator validator = context.getBean(IMetadataValidator.class); - - boolean hasValidation = - (metadataValidationRepository.count(MetadataValidationSpecs.hasMetadataId(mdId)) > 0); + IMetadataIndexer indexer = context.getBean(IMetadataIndexer.class); + + boolean hasValidation = (metadataValidationRepository.count(MetadataValidationSpecs.hasMetadataId(mdId)) > 0); if (!hasValidation) { AbstractMetadata metadata = metadataUtils.findOne(mdId); validator.doValidate(metadata, context.getLanguage()); - dm.indexMetadata(mdId + "", true, null); + indexer.indexMetadata(mdId + "", true, null); } - boolean isInvalid = - (metadataValidationRepository.count(MetadataValidationSpecs.isInvalidAndRequiredForMetadata(mdId)) > 0); + boolean isInvalid = (metadataValidationRepository + .count(MetadataValidationSpecs.isInvalidAndRequiredForMetadata(mdId)) > 0); return !isInvalid; } diff --git a/services/src/main/java/org/fao/geonet/services/metadata/UpdateCategories.java b/services/src/main/java/org/fao/geonet/services/metadata/UpdateCategories.java index f987781cc85..4af0538c173 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/UpdateCategories.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/UpdateCategories.java @@ -24,24 +24,24 @@ package org.fao.geonet.services.metadata; -import jeeves.constants.Jeeves; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; +import java.nio.file.Path; +import java.util.List; + +import javax.annotation.Nonnull; import org.fao.geonet.GeonetContext; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.Metadata; import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.repository.MetadataRepository; +import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.repository.Updater; import org.fao.geonet.services.NotInReadOnlyModeService; import org.fao.geonet.services.Utils; import org.jdom.Element; -import javax.annotation.Nonnull; - -import java.nio.file.Path; -import java.util.List; +import jeeves.constants.Jeeves; +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; /** * Stores all operations allowed for a metadata. Called by the metadata.admin service. @@ -77,7 +77,7 @@ public Element serviceSpecificExec(Element params, ServiceContext context) throw throw new IllegalArgumentException("Metadata not found --> " + id); //--- remove old operations - context.getBean(MetadataRepository.class).update(iLocalId, new Updater() { + context.getBean(IMetadataManager.class).update(iLocalId, new Updater() { @Override public void apply(@Nonnull Metadata entity) { entity.getMetadataCategories().clear(); diff --git a/services/src/main/java/org/fao/geonet/services/metadata/ValidationService.java b/services/src/main/java/org/fao/geonet/services/metadata/ValidationService.java index b8f833a6a65..b19879f3421 100644 --- a/services/src/main/java/org/fao/geonet/services/metadata/ValidationService.java +++ b/services/src/main/java/org/fao/geonet/services/metadata/ValidationService.java @@ -36,7 +36,6 @@ import org.fao.geonet.kernel.SelectionManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.datamanager.IMetadataValidator; -import org.jdom.Document; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.http.MediaType; @@ -88,7 +87,6 @@ StatusResponse validateRecords(@RequestParam(required = false) String[] uuid) th this.report = new HashMap<>(); this.report.put("records", new HashSet()); this.report.put("validRecords", new HashSet()); - this.report.put("notFoundRecords", new HashSet()); this.report.put("notOwnerRecords", new HashSet()); final Set setOfUuidsToValidate; @@ -121,23 +119,22 @@ private void validateRecords(ServiceContext serviceContext, Set setOfUuidsToValidate) throws Exception { - DataManager dataMan = context.getBean(DataManager.class); IMetadataValidator validator = context.getBean(IMetadataValidator.class); AccessManager accessMan = context.getBean(AccessManager.class); final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); for (String uuid : setOfUuidsToValidate) { - AbstractMetadata record = metadataRepository.findOneByUuid(uuid); - if (record == null) { - this.report.get("notFoundRecords").add(record.getId()); - } else if (!accessMan.isOwner(serviceContext, String.valueOf(record.getId()))) { - this.report.get("notOwnerRecords").add(record.getId()); - } else { - boolean isValid = validator.doValidate(record, serviceContext.getLanguage()); - if (isValid) { - this.report.get("validRecords").add(record.getId()); - } - this.report.get("records").add(record.getId()); + for(AbstractMetadata record : metadataRepository.findAllByUuid(uuid)) { + if (!accessMan.isOwner(serviceContext, String.valueOf(record.getId()))) { + this.report.get("notOwnerRecords").add(record.getId()); + } else { + boolean isValid = validator.doValidate(record, serviceContext.getLanguage()); + if (isValid) { + this.report.get("validRecords").add(record.getId()); + } + this.report.get("records").add(record.getId()); + + } } } } diff --git a/services/src/main/java/org/fao/geonet/services/reports/ReportInternalMetadata.java b/services/src/main/java/org/fao/geonet/services/reports/ReportInternalMetadata.java index 6fd20f1867f..0e41b48e2b4 100644 --- a/services/src/main/java/org/fao/geonet/services/reports/ReportInternalMetadata.java +++ b/services/src/main/java/org/fao/geonet/services/reports/ReportInternalMetadata.java @@ -23,22 +23,26 @@ package org.fao.geonet.services.reports; -import jeeves.constants.Jeeves; -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; import org.fao.geonet.Util; -import org.fao.geonet.domain.*; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.ReservedOperation; +import org.fao.geonet.domain.User; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.repository.GroupRepository; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.UserRepository; import org.fao.geonet.repository.specification.OperationAllowedSpecs; import org.jdom.Element; -import java.nio.file.Path; -import java.util.List; -import java.util.Set; +import jeeves.constants.Jeeves; +import jeeves.interfaces.Service; +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; /** * Service to return the updated metadata during a period. @@ -84,14 +88,14 @@ public Element exec(Element params, ServiceContext context) // Retrieve metadata //final Sort sort = new Sort(Sort.Direction.DESC, SortUtils.createPath(Metadata_.dataInfo, MetadataDataInfo_.changeDate)); - final List records = context.getBean(MetadataRepository.class).getMetadataReports(). + final List records = context.getBean(IMetadataUtils.class).getMetadataReports(). getInternalMetadata(beginDateIso, endDateIso, groupList, OperationAllowedSpecs.isPublic(ReservedOperation.view)); // Process metadata results for the report Element response = new Element(Jeeves.Elem.RESPONSE); // Process the records - for (Metadata metadata : records) { + for (AbstractMetadata metadata : records) { User userOwner = context.getBean(UserRepository.class).findOne(metadata.getSourceInfo().getOwner()); Group groupOwner = context.getBean(GroupRepository.class).findOne(metadata.getSourceInfo().getGroupOwner()); diff --git a/services/src/main/java/org/fao/geonet/services/resources/DownloadArchive.java b/services/src/main/java/org/fao/geonet/services/resources/DownloadArchive.java index 0362d1c2006..35db972014b 100644 --- a/services/src/main/java/org/fao/geonet/services/resources/DownloadArchive.java +++ b/services/src/main/java/org/fao/geonet/services/resources/DownloadArchive.java @@ -43,19 +43,19 @@ import org.fao.geonet.ZipUtil; import org.fao.geonet.constants.Geonet; import org.fao.geonet.constants.Params; -import org.fao.geonet.domain.Group; import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Group; import org.fao.geonet.domain.OperationAllowed; import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.domain.User; import org.fao.geonet.exceptions.MetadataNotFoundEx; import org.fao.geonet.exceptions.ResourceNotFoundEx; import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.mef.MEFLib; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.lib.Lib; import org.fao.geonet.repository.GroupRepository; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.UserRepository; import org.fao.geonet.services.Utils; @@ -170,7 +170,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { } //--- get metadata info - AbstractMetadata info = context.getBean(MetadataRepository.class).findOne(id); + AbstractMetadata info = context.getBean(IMetadataUtils.class).findOne(id); // set up zip output stream Path zFile = Files.createTempFile(username + "_" + info.getUuid(), ".zip"); @@ -270,7 +270,7 @@ public Element exec(Element params, ServiceContext context) throws Exception { //--- export the metadata as a partial mef/zip file and add that to the zip //--- stream FIXME: some refactoring required here to avoid metadata //--- being read yet again(!) from the database by the MEF exporter - Path outmef = MEFLib.doExport(context, info.getUuid(), MEFLib.Format.PARTIAL.toString(), false, true, true, true); + Path outmef = MEFLib.doExport(context, info.getUuid(), MEFLib.Format.PARTIAL.toString(), false, true, true, true, true); final Path toPath = zipFs.getPath("metadata.zip"); Files.copy(outmef, toPath); } diff --git a/services/src/main/java/org/fao/geonet/services/resources/Upload.java b/services/src/main/java/org/fao/geonet/services/resources/Upload.java index 1c00f12816b..19ccdd13116 100644 --- a/services/src/main/java/org/fao/geonet/services/resources/Upload.java +++ b/services/src/main/java/org/fao/geonet/services/resources/Upload.java @@ -28,8 +28,8 @@ import org.fao.geonet.Util; import org.fao.geonet.constants.Params; import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.lib.Lib; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.services.Utils; import org.fao.geonet.services.resources.handlers.IResourceUploadHandler; import org.jdom.Element; @@ -78,7 +78,7 @@ public Element exec(Element params, ServiceContext context) username = "unknown (this shouldn't happen?)"; - final AbstractMetadata metadata = context.getBean(MetadataRepository.class).findOne(id); + final AbstractMetadata metadata = context.getBean(IMetadataUtils.class).findOne(id); String mdUuid = metadata.getUuid(); diff --git a/services/src/main/java/org/fao/geonet/services/subtemplate/Get.java b/services/src/main/java/org/fao/geonet/services/subtemplate/Get.java index a5ce02645ce..5a46d08afd2 100644 --- a/services/src/main/java/org/fao/geonet/services/subtemplate/Get.java +++ b/services/src/main/java/org/fao/geonet/services/subtemplate/Get.java @@ -33,6 +33,7 @@ import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.MetadataType; import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.jdom.Attribute; @@ -78,7 +79,7 @@ public Element exec(Element params, ServiceContext context) String uuid = Util.getParam(params, Params.UUID); // Retrieve template - final IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); + final MetadataRepository metadataRepository = context.getBean(MetadataRepository.class); final AbstractMetadata metadata = metadataRepository.findOneByUuid(uuid); if (metadata == null) { diff --git a/services/src/main/java/org/fao/geonet/services/subtemplate/GetTypes.java b/services/src/main/java/org/fao/geonet/services/subtemplate/GetTypes.java index b57781d9b07..1fc06b724d3 100644 --- a/services/src/main/java/org/fao/geonet/services/subtemplate/GetTypes.java +++ b/services/src/main/java/org/fao/geonet/services/subtemplate/GetTypes.java @@ -22,26 +22,28 @@ //============================================================================== package org.fao.geonet.services.subtemplate; -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; +import static org.fao.geonet.repository.specification.MetadataSpecs.hasType; + +import java.nio.file.Path; +import java.util.List; import org.fao.geonet.GeonetContext; import org.fao.geonet.api.standards.StandardsUtils; import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataDataInfo_; import org.fao.geonet.domain.MetadataType; import org.fao.geonet.domain.Metadata_; import org.fao.geonet.kernel.SchemaManager; -import org.fao.geonet.repository.MetadataRepository; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.jdom.Element; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; -import java.nio.file.Path; -import java.util.List; - -import static org.fao.geonet.repository.specification.MetadataSpecs.hasType; +import jeeves.interfaces.Service; +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; @Deprecated public class GetTypes implements Service { @@ -56,10 +58,11 @@ public Element exec(Element params, ServiceContext context) final Sort sort = new Sort(Sort.Direction.ASC, Metadata_.dataInfo.getName() + "." + MetadataDataInfo_.root.getName()); - final List metadatas = context.getBean(MetadataRepository.class).findAll(hasType(MetadataType.SUB_TEMPLATE), sort); + final List metadatas = + context.getBean(IMetadataUtils.class).findAll((Specification)hasType(MetadataType.SUB_TEMPLATE), sort); Element subTemplateTypes = new Element("getTypes"); - for (Metadata metadata : metadatas) { + for (AbstractMetadata metadata : metadatas) { subTemplateTypes.addContent(new Element("type").setText(metadata.getDataInfo().getRoot())); subTemplateTypes.addContent(new Element("schemaId").setText(metadata.getDataInfo().getSchemaId())); } diff --git a/services/src/main/resources/config-spring-geonetwork.xml b/services/src/main/resources/config-spring-geonetwork.xml index 3c644a6b258..6adfa7c2b2f 100644 --- a/services/src/main/resources/config-spring-geonetwork.xml +++ b/services/src/main/resources/config-spring-geonetwork.xml @@ -35,7 +35,11 @@ file-encoding="UTF-8" ignore-unresolvable="true" /> - + + + + + diff --git a/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java b/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java index 0a5d830aad7..533373cf7c8 100644 --- a/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java @@ -72,9 +72,10 @@ import org.fao.geonet.kernel.SchemaManager; import org.fao.geonet.kernel.SpringLocalServiceInvoker; import org.fao.geonet.kernel.UpdateDatestamp; -import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.kernel.mef.MEFLib; import org.fao.geonet.kernel.mef.MEFLibIntegrationTest; import org.fao.geonet.lib.Lib; +import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.services.AbstractServiceIntegrationTest; import org.fao.geonet.utils.Xml; @@ -113,7 +114,7 @@ public class MetadataApiTest extends AbstractServiceIntegrationTest { @PersistenceContext private EntityManager _entityManager; - @Autowired private IMetadataUtils metadataRepository; + @Autowired private MetadataRepository metadataRepository; private String uuid; private int id; @@ -440,7 +441,7 @@ public void getRecordAsZip() throws Exception { .accept("application/zip")) .andDo(print()) .andExpect(status().isOk()) - .andExpect(content().contentType("application/zip")) + .andExpect(content().contentType(MEF_V2_ACCEPT_TYPE)) .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, equalTo(String.format("inline; filename=\"%s.%s\"", this.uuid, "zip")))) .andExpect(content().string(startsWith(zipMagicNumber))); diff --git a/services/src/test/java/org/fao/geonet/services/category/RemoveTest.java b/services/src/test/java/org/fao/geonet/services/category/RemoveTest.java index 280340c41ac..dcf093d3490 100644 --- a/services/src/test/java/org/fao/geonet/services/category/RemoveTest.java +++ b/services/src/test/java/org/fao/geonet/services/category/RemoveTest.java @@ -62,7 +62,7 @@ public void testExec() throws Exception { assertEquals(beforeCount, _categoryRepository.count()); AbstractMetadata entity = MetadataRepositoryTest.newMetadata(inc); - entity.getMetadataCategories().add(category); + entity.getCategories().add(category); entity = metadataManager.save(entity); ServiceContext context = createServiceContext(); @@ -72,6 +72,6 @@ public void testExec() throws Exception { assertEquals(beforeCount - 1, _categoryRepository.count()); assertEquals(1, metadataUtils.count()); entity = metadataUtils.findOne(entity.getId()); - assertTrue(entity.getMetadataCategories().isEmpty()); + assertTrue(entity.getCategories().isEmpty()); } } diff --git a/services/src/test/java/org/fao/geonet/services/main/SearchSuggestionIntegrationTest.java b/services/src/test/java/org/fao/geonet/services/main/SearchSuggestionIntegrationTest.java index 61fca582c8f..654c2eeaae9 100644 --- a/services/src/test/java/org/fao/geonet/services/main/SearchSuggestionIntegrationTest.java +++ b/services/src/test/java/org/fao/geonet/services/main/SearchSuggestionIntegrationTest.java @@ -23,38 +23,53 @@ package org.fao.geonet.services.main; -import com.google.common.base.Function; -import com.google.common.collect.Lists; +import static org.fao.geonet.domain.Pair.read; +import static org.fao.geonet.services.main.SearchSuggestion.ATT_FREQ; +import static org.fao.geonet.services.main.SearchSuggestion.ATT_TERM; +import static org.fao.geonet.services.main.SearchSuggestion.CONFIG_PARAM_DEFAULT_SEARCH_FIELD; +import static org.fao.geonet.services.main.SearchSuggestion.CONFIG_PARAM_MAX_NUMBER_OF_TERMS; +import static org.fao.geonet.services.main.SearchSuggestion.CONFIG_PARAM_THRESHOLD; +import static org.fao.geonet.services.main.SearchSuggestion.ELEM_ITEM; +import static org.fao.geonet.services.main.SearchSuggestion.INDEX_TERM_VALUES; +import static org.fao.geonet.services.main.SearchSuggestion.PARAM_FIELD; +import static org.fao.geonet.services.main.SearchSuggestion.PARAM_MAX_NUMBER_OF_TERMS; +import static org.fao.geonet.services.main.SearchSuggestion.PARAM_ORIGIN; +import static org.fao.geonet.services.main.SearchSuggestion.PARAM_Q; +import static org.fao.geonet.services.main.SearchSuggestion.PARAM_SORT_BY; +import static org.fao.geonet.services.main.SearchSuggestion.PARAM_THRESHOLD; +import static org.fao.geonet.services.main.SearchSuggestion.RECORDS_FIELD_VALUES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataType; +import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.mef.MEFLibIntegrationTest; -import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.specification.MetadataSpecs; import org.fao.geonet.services.AbstractServiceIntegrationTest; +import org.fao.geonet.services.main.SearchSuggestion.SORT_BY_OPTION; import org.fao.geonet.utils.IO; import org.jdom.Element; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.annotation.Nonnull; - -import javax.annotation.Nullable; +import com.google.common.base.Function; +import com.google.common.collect.Lists; -import static org.fao.geonet.domain.Pair.read; -import static org.fao.geonet.services.main.SearchSuggestion.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; /** * Test the SearchSuggestion Service Created by Jesse on 2/4/14. @@ -84,7 +99,7 @@ public void importSampleMetadata() throws Exception { importMetadata.getMefFilesToLoad().add("mef2-example-2md.zip"); importMetadata.invoke(); - assertEquals(4, context.getBean(MetadataRepository.class).count(MetadataSpecs.hasType(MetadataType.METADATA))); + assertEquals(4, context.getBean(IMetadataUtils.class).count((Specification)MetadataSpecs.hasType(MetadataType.METADATA))); } @Test diff --git a/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js b/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js index f1c9413cbbd..644ce644a0a 100644 --- a/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js +++ b/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js @@ -293,7 +293,7 @@ module.value('gnHttpServices', { mdGetPDFSelection: 'pdf.selection.search', // TODO: CHANGE mdGetRDF: 'rdf.metadata.get', - mdGetMEF: 'mef.export', + mdGetMEF: 'mef.export', // Deprecated service mdGetXML19139: 'xml_iso19139', csv: 'csv.search', @@ -738,6 +738,14 @@ } else if (s[0] === 'overview') { images.big = s[1]; } + + //Is it a draft? + if( s[1].indexOf("/api/records/") >= 0 + && s[1].indexOf("/api/records/")< s[1].indexOf("/attachments/")) { + s[1] += "?approved=" + (this.draft != 'y'); + } + + images.list[insertFn]({url: s[1], label: s[2]}); } } diff --git a/web-ui/src/main/resources/catalog/components/common/map/mapService.js b/web-ui/src/main/resources/catalog/components/common/map/mapService.js index fa5b1958789..7fea5665b29 100644 --- a/web-ui/src/main/resources/catalog/components/common/map/mapService.js +++ b/web-ui/src/main/resources/catalog/components/common/map/mapService.js @@ -1937,6 +1937,7 @@ return gnSearchManagerService.gnSearch({ uuid: layer.get('metadataUuid'), fast: 'index', + _draft: 'n or e', _content_type: 'json' }).then(function(data) { if (data.metadata.length == 1) { diff --git a/web-ui/src/main/resources/catalog/components/edit/directoryassociatedmd/DirectoryAssociatedMdDirective.js b/web-ui/src/main/resources/catalog/components/edit/directoryassociatedmd/DirectoryAssociatedMdDirective.js index e529a9bfe1c..fb84cd6fb62 100644 --- a/web-ui/src/main/resources/catalog/components/edit/directoryassociatedmd/DirectoryAssociatedMdDirective.js +++ b/web-ui/src/main/resources/catalog/components/edit/directoryassociatedmd/DirectoryAssociatedMdDirective.js @@ -26,9 +26,11 @@ goog.require('gn_mdtypewidget'); + goog.require('gn_draftvalidationwidget'); var module = angular.module('gn_directoryassociatedmd_directive', [ - 'gn_mdtypewidget' + 'gn_mdtypewidget', + 'gn_draftvalidationwidget' ]); module.directive('gnDirectoryAssociatedMd', [ diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js index e6b34514922..bc8071fdd8c 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js @@ -248,11 +248,32 @@ src.lUrl = src.url[lang] || src.url[mdLanguage] || src.url[Object.keys(src.url)[0]]; + + //Is it a draft? + if(src.lUrl.indexOf("/api/records/") >= 0 + && src.lUrl.indexOf("/api/records/")< src.lUrl.indexOf("/attachments/")) { + if(src.lUrl.indexOf("?") > 0) { + src.lUrl += "&approved=false"; + } else { + src.lUrl += "?approved=false"; + } + } + }); angular.forEach(data.thumbnails, function(img) { img.lUrl = img.url[lang] || img.url[mdLanguage] || img.url[Object.keys(img.url)[0]]; + + //Is it a draft? + if(img.lUrl.indexOf("/api/records/") >= 0 + && img.lUrl.indexOf("/api/records/")< img.lUrl.indexOf("/attachments/")) { + if(img.lUrl.indexOf("?") > 0) { + img.lUrl += "&approved=false"; + } else { + img.lUrl += "?approved=false"; + } + } }); if (data.siblings) { for (var i = 0; i < data.siblings.length; i++) { diff --git a/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js b/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js index 509380526d3..c6e73a23614 100644 --- a/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js +++ b/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js @@ -35,7 +35,8 @@ metadataUuid + '/attachments', { params: { filter: filter, - _random: Math.floor(Math.random() * 10000) + _random: Math.floor(Math.random() * 10000), + approved: 'false' } }); }, diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js index f8faa983141..1e620810aa0 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js +++ b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js @@ -125,8 +125,12 @@ * Export one metadata to RDF format. * @param {string} uuid */ - this.metadataRDF = function(uuid) { + this.metadataRDF = function(uuid, approved) { var url = gnHttp.getService('mdGetRDF') + '?uuid=' + uuid; + + url += angular.isDefined(approved) ? + '&approved=' + approved : ''; + location.replace(url); }; @@ -135,12 +139,15 @@ * one metadata, else export the whole selection. * @param {string} uuid */ - this.metadataMEF = function(uuid, bucket) { + this.metadataMEF = function(uuid, bucket, approved) { + var url = gnHttp.getService('mdGetMEF') + '?version=2'; url += angular.isDefined(uuid) ? '&uuid=' + uuid : '&format=full'; url += angular.isDefined(bucket) ? '&bucket=' + bucket : ''; + url += angular.isDefined(approved) ? + '&approved=' + approved : ''; location.replace(url); }; @@ -288,10 +295,19 @@ * @return {*} */ this.publish = function(md, bucket, flag, scope) { - scope.$broadcast('operationOnSelectionStart'); if (md) { flag = md.isPublished() ? 'off' : 'on'; + } + + //Warn about possible workflow changes on batch changes + // or when record is not approved + if((!md || md.mdStatus != 2) && flag === 'on') { + if(!confirm($translate.instant('warnPublishDraft'))){ + return; + } } + + scope.$broadcast('operationOnSelectionStart'); var onOrOff = flag === 'on'; return gnShareService.publish( diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js b/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js index 6c3367cfc9d..2f04ef8ccac 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js +++ b/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js @@ -48,11 +48,11 @@ * config.js */ module.service('gnRelatedService', ['$http', '$q', function($http, $q) { - this.get = function(uuid, types) { + this.get = function(uuidOrId, types) { var canceller = $q.defer(); var request = $http({ method: 'get', - url: '../api/records/' + uuid + '/related?' + + url: '../api/records/' + uuidOrId + '/related?' + (types ? 'type=' + types.split('|').join('&type=') : ''), @@ -138,13 +138,13 @@ scope.updateRelations = function() { scope.relations = null; - if (scope.uuid) { + if (scope.id) { scope.relationFound = false; if (controller) { controller.startGnRelatedRequest(elem); } (promise = gnRelatedService.get( - scope.uuid, scope.types) + scope.id, scope.types) ).then(function(data) { angular.forEach(data, function(value, idx) { if (!value) { return; } @@ -200,12 +200,12 @@ scope.config = gnRelatedResources; scope.$watchCollection('md', function(n, o) { - if (n && n !== o || angular.isUndefined(scope.uuid)) { + if (n && n !== o || angular.isUndefined(scope.id)) { if (promise && angular.isFunction(promise.abort)) { promise.abort(); } if (scope.md != null) { - scope.uuid = scope.md.getUuid(); + scope.id = scope.md.getId(); } scope.updateRelations(); } diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/partials/statusupdater.html b/web-ui/src/main/resources/catalog/components/metadataactions/partials/statusupdater.html index 7cb317b5035..9d49fc7140a 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/partials/statusupdater.html +++ b/web-ui/src/main/resources/catalog/components/metadataactions/partials/statusupdater.html @@ -1,4 +1,4 @@ -
      +
      @@ -31,7 +31,8 @@
      diff --git a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js index 656d386e9d5..da4c9a61fe3 100644 --- a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js +++ b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js @@ -93,7 +93,7 @@ // Data needs improvements // See https://github.com/geonetwork/core-geonetwork/issues/723 gnAlertService.addAlert({ - msg: reason.data, + msg: reason.data.description, type: 'danger' }); }); @@ -123,14 +123,20 @@ }; $scope.format = function(f) { + var showApproved = $scope.mdView.current.record.draft != 'y'; $scope.usingFormatter = f !== undefined; $scope.currentFormatter = f; - if (f) { - $('#gn-metadata-display').find('*').remove(); + if (f) { + var gn_metadata_display = $('#gn-metadata-display', $('.gn-md-view:visible')); + + gn_metadata_display.find('*').remove(); gnMdFormatter.getFormatterUrl(f.url, $scope).then(function(url) { $http.get(url, { headers: { Accept: 'text/html' + }, + params: { + approved : showApproved } }).then( function(response,status) { @@ -140,7 +146,7 @@ var snippet = response.data.replace( '', ''); - $('#gn-metadata-display').find('*').remove(); + gn_metadata_display.find('*').remove(); $scope.compileScope.$destroy(); @@ -148,14 +154,14 @@ $scope.compileScope = $scope.$new(); var content = $compile(snippet)($scope.compileScope); - $('#gn-metadata-display').append(content); + gn_metadata_display.append(content); // activate the tabs in the full view $scope.activateTabs(); } }, function(data) { - $('#gn-metadata-display').append("
      "+$translate.instant("metadataViewLoadError")+"
      "); + gn_metadata_display.append("
      "+$translate.instant("metadataViewLoadError")+"
      "); }); }); }; diff --git a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewService.js b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewService.js index ae2ae7c1e85..6ab945f8692 100644 --- a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewService.js +++ b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewService.js @@ -126,41 +126,75 @@ */ this.initMdView = function() { var that = this; - var loadMdView = function() { + var loadMdView = function(event, newUrl, oldUrl) { gnMdViewObj.loadDetailsFinished = false; var uuid = gnSearchLocation.getUuid(); if (uuid) { if (!gnMdViewObj.current.record || - gnMdViewObj.current.record.getUuid() !== uuid) { + gnMdViewObj.current.record.getUuid() !== uuid || + newUrl !== oldUrl) { + + //Check if we want the draft version + var getDraft = window.location.hash.indexOf("/metadraf/") > 0; + var foundMd = false; // Check if the md is in current search - if (angular.isArray(gnMdViewObj.records)) { + if (angular.isArray(gnMdViewObj.records) + && !getDraft) { for (var i = 0; i < gnMdViewObj.records.length; i++) { var md = gnMdViewObj.records[i]; if (md.getUuid() === uuid) { + foundMd = true; that.feedMd(i, md, gnMdViewObj.records); - return; } } - } + } - // get a new search to pick the md - gnMdViewObj.current.record = null; - gnSearchManagerService.gnSearch({ - _uuid_OR__id: uuid, - _isTemplate: 'y or n', - fast: 'index', - _content_type: 'json' - }).then(function(data) { - if (data.metadata.length == 1) { - data.metadata[0] = new Metadata(data.metadata[0]); - that.feedMd(0, undefined, data.metadata); - } else { - gnMdViewObj.loadDetailsFinished = true; - } - }, function(error) { - gnMdViewObj.loadDetailsFinished = true; - }); + if (!foundMd){ + // get a new search to pick the md + gnMdViewObj.current.record = null; + gnSearchManagerService.gnSearch({ + uuid: uuid, + _isTemplate: 'y or n', + _draft: 'y or n or e', + fast: 'index', + _content_type: 'json' + }).then(function(data) { + if (data.metadata.length > 0) { + //If trying to show a draft that is not a draft, correct url: + if(data.metadata.length == 1 && + window.location.hash.indexOf("/metadraf/") > 0) { + window.location.hash = + window.location.hash.replace("/metadraf/", "/metadata/"); + //Now the location change event handles this + return; + } + + //If returned more than one, maybe we are looking for the draft + var i = 0; + data.metadata.forEach(function (md, index) { + if(getDraft + && md.draft == 'y') { + //This will only happen if the draft exists + //and the user can see it + i = index; + } + }); + + data.metadata[i] = new Metadata(data.metadata[i]); + + //Keep the search results (gnMdViewObj.records) + //and the trace of where in the search result we are + that.feedMd(gnMdViewObj.current.index, + data.metadata[i], gnMdViewObj.records); + } else { + gnMdViewObj.loadDetailsFinished = true; + } + }, function(error) { + gnMdViewObj.loadDetailsFinished = true; + }); + } + } else { gnMdViewObj.loadDetailsFinished = true; } @@ -170,7 +204,9 @@ gnMdViewObj.current.record = null; } }; - loadMdView(); // To manage uuid on page loading + + loadMdView(); + // To manage uuid on page loading $rootScope.$on('$locationChangeSuccess', loadMdView); }; diff --git a/web-ui/src/main/resources/catalog/components/search/resultsview/partials/viewtemplates/editor.html b/web-ui/src/main/resources/catalog/components/search/resultsview/partials/viewtemplates/editor.html index 9f4df482b5b..ad86ea9b429 100644 --- a/web-ui/src/main/resources/catalog/components/search/resultsview/partials/viewtemplates/editor.html +++ b/web-ui/src/main/resources/catalog/components/search/resultsview/partials/viewtemplates/editor.html @@ -10,34 +10,43 @@ -
      - {{md.title || md.defaultTitle}} - {{md.title || md.defaultTitle}} -
      -
      - owner: - {{::md.getOwnername()}} -   - updatedOn - +
      +
      + {{md.title || md.defaultTitle}} + {{md.title || md.defaultTitle}} +
      + owner: + {{::md.getOwnername()}} + · + updatedOn + + · + {{('status-' + md.mdStatus) | translate}} + + {{'status-no-status' | translate}} +
      +
      - {{('status-' + md.mdStatus) | translate}}
      +
      -
      -
      + +
      diff --git a/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js b/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js index 69b9afc8b4d..127fa1f06b1 100644 --- a/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js +++ b/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js @@ -42,6 +42,7 @@ this.SEARCHPAGES = /\/search|\/board/; this.MAP = '/map'; this.METADATA = '/metadata/'; + this.DRAFT = '/metadraf/'; this.HOME = '/home'; var state = {}; @@ -65,7 +66,8 @@ this.isMdView = function(path) { var p = path || $location.path(); - return p.indexOf(this.METADATA) == 0; + return p.indexOf(this.METADATA) == 0 + || p.indexOf(this.DRAFT) == 0; }; this.isMap = function() { @@ -82,7 +84,11 @@ }; this.setUuid = function(uuid) { - $location.path(this.METADATA + uuid); + if($location.path().indexOf(this.DRAFT) == 0) { + $location.path(this.DRAFT + uuid); + } else { + $location.path(this.METADATA + uuid); + } this.removeParams(); }; diff --git a/web-ui/src/main/resources/catalog/components/utility/DraftValidationWidget.js b/web-ui/src/main/resources/catalog/components/utility/DraftValidationWidget.js new file mode 100644 index 00000000000..c00e52f9aa8 --- /dev/null +++ b/web-ui/src/main/resources/catalog/components/utility/DraftValidationWidget.js @@ -0,0 +1,61 @@ +/* + * 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 + */ + +(function() { + goog.provide('gn_draftvalidationwidget'); + + var module = angular.module('gn_draftvalidationwidget', []); + + module.directive('gnDraftValidationWidget', ['gnSearchManagerService', + function(gnSearchManagerService) { + return { + restrict: 'E', + scope: { + metadata: '=' + }, + link: function(scope) { + gnSearchManagerService.gnSearch({ + uuid: scope.metadata["geonet:info"].uuid, + _isTemplate: 'y or n', + _draft: 'y', + fast: 'index', + _content_type: 'json' + }).then(function(data) { + var i = 0; + data.metadata.forEach(function (md, index) { + if(md.draft == 'y') { + // This will only happen if the draft exists + // and the user can see it + scope.draft = data.metadata[i]; + } + }); + }); + + }, + templateUrl: '../../catalog/components/utility/' + + 'partials/draftvalidationwidget.html' + }; + } + ]); + +})(); diff --git a/web-ui/src/main/resources/catalog/components/utility/partials/draftvalidationwidget.html b/web-ui/src/main/resources/catalog/components/utility/partials/draftvalidationwidget.html new file mode 100644 index 00000000000..bec80b25757 --- /dev/null +++ b/web-ui/src/main/resources/catalog/components/utility/partials/draftvalidationwidget.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/web-ui/src/main/resources/catalog/js/GnSearchModule.js b/web-ui/src/main/resources/catalog/js/GnSearchModule.js index 798b7326d6a..e2607001ae9 100644 --- a/web-ui/src/main/resources/catalog/js/GnSearchModule.js +++ b/web-ui/src/main/resources/catalog/js/GnSearchModule.js @@ -51,7 +51,7 @@ module.config(['$LOCALES', function($LOCALES) { $LOCALES.push('search'); $LOCALES.push('/../api/0.1/tools/i18n/db?' + - 'type=MetadataCategory&type=Operation&type=Group'); + 'type=MetadataCategory&type=Operation&type=Group&type=StatusValue'); $LOCALES.push('/../api/0.1/standards/iso19139/' + 'codelists/gmd%3AMD_TopicCategoryCode'); $LOCALES.push('/../api/0.1/standards/iso19139/' + diff --git a/web-ui/src/main/resources/catalog/js/edit/DirectoryController.js b/web-ui/src/main/resources/catalog/js/edit/DirectoryController.js index ec8dfe28990..6f300703d28 100644 --- a/web-ui/src/main/resources/catalog/js/edit/DirectoryController.js +++ b/web-ui/src/main/resources/catalog/js/edit/DirectoryController.js @@ -32,13 +32,15 @@ goog.require('gn_directoryassociatedmd'); goog.require('gn_facets'); goog.require('gn_mdtypewidget'); + goog.require('gn_draftvalidationwidget'); var module = angular.module('gn_directory_controller', [ 'gn_catalog_service', 'gn_facets', 'gn_directoryassociatedmd', 'pascalprecht.translate', - 'gn_mdtypewidget' + 'gn_mdtypewidget', + 'gn_draftvalidationwidget' ]); /** diff --git a/web-ui/src/main/resources/catalog/js/edit/EditorController.js b/web-ui/src/main/resources/catalog/js/edit/EditorController.js index 5c3c10bb475..64d56356f30 100644 --- a/web-ui/src/main/resources/catalog/js/edit/EditorController.js +++ b/web-ui/src/main/resources/catalog/js/edit/EditorController.js @@ -166,6 +166,7 @@ _id: $routeParams.id, _content_type: 'json', _isTemplate: 'y or n or s', + _draft: 'y or n or e', fast: 'index' }).then(function(data) { $scope.metadataFound = data.count !== '0'; diff --git a/web-ui/src/main/resources/catalog/locales/en-admin.json b/web-ui/src/main/resources/catalog/locales/en-admin.json index dd31910dbae..bf3552675b2 100644 --- a/web-ui/src/main/resources/catalog/locales/en-admin.json +++ b/web-ui/src/main/resources/catalog/locales/en-admin.json @@ -1370,6 +1370,8 @@ "ui-fluidLayout-help": "If enabled, the layout of the application has a full width container, when disabled the layout has a fixed width and centered container", "ui-fluidEditorLayout": "Fluid container for the Editor", "ui-fluidEditorLayout-help": "If enabled, the layout of the application has a full width container, when disabled the layout has a fixed width and centered container", + "styleVariable-gnMenubarBackgroundHover": "Background color on hover", + "styleVariable-gnMenubarColorActive": "Color when active", "metadata/pdfReport": "Metadata selection - pdf report", "metadata/pdfReport/coverPdf": "Cover pdf", "metadata/pdfReport/coverPdf-help": "URL of the cover pdf for the PDF report. If not defined, no cover page is added to the report.", diff --git a/web-ui/src/main/resources/catalog/locales/en-core.json b/web-ui/src/main/resources/catalog/locales/en-core.json index fe29f8d38ac..f0cf1f47cda 100644 --- a/web-ui/src/main/resources/catalog/locales/en-core.json +++ b/web-ui/src/main/resources/catalog/locales/en-core.json @@ -172,7 +172,6 @@ "orgName": "Contact for the resource", "orgNames": "Contact for the resource", "orgNamesTree": "Contact for the resource", - "off": "Off", "topicCat" : "Topic", "topicCats" : "Topics", "organisation": "Organisation", @@ -322,7 +321,6 @@ "no": "No", "publishedForGroup": "Visible by", "_op0": "Visible by", - "status": "Status", "mdStatus": "Workflow progress", "_status": "Status", "_schema": "Standard", @@ -445,5 +443,8 @@ "metadataPublishedError": "Error occurred while publishing metadata.", "metadataUnpublished": "Metadata un-published.", "metadataUnpublishedError": "Error occurred while un-publishing metadata.", - "categoriesUpdated": "Categories updated." + "categoriesUpdated": "Categories updated.", + "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", + "canceWorkingCopy": "Cancel working copy", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?" } diff --git a/web-ui/src/main/resources/catalog/locales/en-editor.json b/web-ui/src/main/resources/catalog/locales/en-editor.json index 4ce89e0642a..3dcf8989ee7 100644 --- a/web-ui/src/main/resources/catalog/locales/en-editor.json +++ b/web-ui/src/main/resources/catalog/locales/en-editor.json @@ -249,6 +249,8 @@ "saveMetadataError": "Error while saving metadata. You should reload the editor.", "saveMetadataSuccess": "All changes saved.", "saveMetadata": "Save metadata", + "saveDraft": "Save draft", + "draftCanNoBeATemplateOrMinorEdit": "A draft cannot be a template and cannot use minor edit mode.", "saveTemplate": "Save template", "saveAsMetadata": "Save as metadata", "saveAsTemplate": "Save as template", @@ -382,5 +384,7 @@ "accessManager": "Access rights", "accessManagerTitle": "Access rights by records", "accessManagerChooseGroups": "Choose one or more groups", - "accessManagerChooseOperations": "Choose one or more operation types" + "accessManagerChooseOperations": "Choose one or more operation types", + "status-no-status": "Workflow not active", + "draft": "Working copy" } diff --git a/web-ui/src/main/resources/catalog/locales/en-search.json b/web-ui/src/main/resources/catalog/locales/en-search.json index 367a2fbe3c1..7c7d9d88076 100644 --- a/web-ui/src/main/resources/catalog/locales/en-search.json +++ b/web-ui/src/main/resources/catalog/locales/en-search.json @@ -490,5 +490,7 @@ "userSearchUpdateUrl": "Update", "userSearchFeaturedDisplayInHomePage": "Home page", "userSearchUpdated": "User search saved", - "userSearchUpdatedError": "Error saving user search" + "userSearchUpdatedError": "Error saving user search", + "seeDraft": "This record has a working copy version. Click here to see it.", + "seeNoDraft": "This record has an approved and published version. Click here to see it." } diff --git a/web-ui/src/main/resources/catalog/style/gn.less b/web-ui/src/main/resources/catalog/style/gn.less index b3e9fbaeccd..e9603e6e6d6 100644 --- a/web-ui/src/main/resources/catalog/style/gn.less +++ b/web-ui/src/main/resources/catalog/style/gn.less @@ -632,6 +632,45 @@ i.fa-times.delete:hover { border-color: darken(@brand-danger, 5%); color: @brand-danger; } + +// Same as before but with workflow statuses + .gn-workflow-status { + border: 2px solid darken(@brand-info, 5%); + position: absolute; + top: 95px; + left: 115px; + border-radius: 6px; + padding: 2px; + -ms-transform: rotate(30deg); + -webkit-transform: rotate(30deg); + transform: rotate(30deg); + color: @brand-info; + font-weight: bold; + font-size: 80%; + background-color: rgba(255, 255, 255 , 0.7); + border-color: darken(@brand-success, 5%); + color: @brand-success; + } + .gn-workflow-status-mdview { + position: relative; + top: 0; + left: 0; + display: inline-block; + font-size: 100%; + border-radius: 20px; + padding: 8px 12px; + font-weight: normal; + font-size: 80%; + transform: none; + border-color: darken(@brand-success, 5%); + color: @brand-success; + } + /* Decide here which status should be displayed + or not and with which colors */ + .gn-workflow-status-1 { + border-color: darken(@brand-danger, 5%); + color: @brand-danger; + } } // Language specific layout diff --git a/web-ui/src/main/resources/catalog/style/gn_editor.less b/web-ui/src/main/resources/catalog/style/gn_editor.less index 50c5713c28f..9e043dcc64a 100644 --- a/web-ui/src/main/resources/catalog/style/gn_editor.less +++ b/web-ui/src/main/resources/catalog/style/gn_editor.less @@ -883,6 +883,91 @@ gn-directory-associated-md { } } +// md type widget (used only in editor for now) +gn-md-type-widget { + display: inline-block; + & > div { + border-radius: 1000px; + width: 24px; + height: 24px; + line-height: 24px; + text-align: center; + font-size: 14px; + position: relative; + color: white; + margin-top: -0.2em; + margin-bottom: -0.4em; + background-color: @gray-light; + overflow: hidden; + + &.valid-status-1 { + background-color: @gray-light; + } + &.valid-status0 { + background-color: @btn-danger-bg; + } + &.valid-status1 { + background-color: @btn-success-bg; + } + &.md-type-t { + background-color: @btn-primary-bg; + } + + &::after { + content: ''; + background-color: rgba(255, 255, 255, 0.25); + position: absolute; + top: 0px; + height: 50%; + left: 0px; + right: 0px; + } + } +} + + +// md type widget (used only in editor for now) +gn-draft-validation-widget { + display: inline-block; + small small { + border-radius: 1000px; + width: 24px; + height: 24px; + line-height: 24px; + text-align: center; + font-size: 14px; + position: relative; + color: white; + margin-top: -0.2em; + margin-bottom: -0.4em; + background-color: @gray-light; + overflow: hidden; + + &.valid-status-1 { + background-color: @gray-light; + } + &.valid-status0 { + background-color: @btn-danger-bg; + } + &.valid-status1 { + background-color: @btn-success-bg; + } + &.md-type-t { + background-color: @btn-primary-bg; + } + + &::after { + content: ''; + background-color: rgba(255, 255, 255, 0.25); + position: absolute; + top: 0px; + height: 50%; + left: 0px; + right: 0px; + } + } +} + .gn-editor-board { .table.gn-results-editor td { gn-md-type-widget { diff --git a/web-ui/src/main/resources/catalog/templates/admin/settings/cssstyle.html b/web-ui/src/main/resources/catalog/templates/admin/settings/cssstyle.html index 7a1f31d9dff..5db5127ad41 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/settings/cssstyle.html +++ b/web-ui/src/main/resources/catalog/templates/admin/settings/cssstyle.html @@ -83,11 +83,23 @@ data-ng-model="gnCssStyle.gnMenubarBackgroundActive" color-picker-format="'hex'" color-picker-alpha="false">
      +
      + + +
      +
      + + +