UI for Import/Export of entities
parent
6fd4e177cf
commit
8bddae0711
|
|
@ -402,8 +402,9 @@ public class ImportExport {
|
|||
|
||||
private ImportManifest createImportManifest(final String path) {
|
||||
|
||||
final String manifestPath = String.format("imports/%s/ccm-export.json",
|
||||
path);
|
||||
final String manifestPath = String.format(
|
||||
"imports/%s/ccm-export.json", path
|
||||
);
|
||||
|
||||
try (final InputStream inputStream = ccmFiles
|
||||
.createInputStream(manifestPath)) {
|
||||
|
|
@ -412,24 +413,33 @@ public class ImportExport {
|
|||
final JsonObject manifestJson = reader.readObject();
|
||||
|
||||
if (!manifestJson.containsKey("created")) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The manifest file \"%s\" is malformed. "
|
||||
+ "Key \"created\" is missing.",
|
||||
manifestPath));
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"The manifest file \"%s\" is malformed. "
|
||||
+ "Key \"created\" is missing.",
|
||||
manifestPath
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!manifestJson.containsKey("onServer")) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The manifest file \"%s\" is malformed. "
|
||||
+ "Key \"onServer\" is missing.",
|
||||
manifestPath));
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"The manifest file \"%s\" is malformed. "
|
||||
+ "Key \"onServer\" is missing.",
|
||||
manifestPath
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!manifestJson.containsKey("types")) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The manifest file \"%s\" is malformed. "
|
||||
+ "Key \"types\" is missing.",
|
||||
manifestPath));
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"The manifest file \"%s\" is malformed. "
|
||||
+ "Key \"types\" is missing.",
|
||||
manifestPath
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final LocalDateTime created = LocalDateTime
|
||||
|
|
@ -446,9 +456,11 @@ public class ImportExport {
|
|||
}
|
||||
|
||||
return new ImportManifest(
|
||||
path,
|
||||
Date.from(created.atZone(ZoneId.of("UTC")).toInstant()),
|
||||
onServer,
|
||||
types);
|
||||
types
|
||||
);
|
||||
} catch (IOException
|
||||
| FileAccessException
|
||||
| FileDoesNotExistException
|
||||
|
|
|
|||
|
|
@ -29,19 +29,30 @@ import java.util.List;
|
|||
*/
|
||||
public class ImportManifest {
|
||||
|
||||
private final String importName;
|
||||
|
||||
private final Date created;
|
||||
|
||||
private final String onServer;
|
||||
|
||||
private final List<String> types;
|
||||
|
||||
public ImportManifest(final Date created,
|
||||
final String onServer,
|
||||
final List<String> types) {
|
||||
|
||||
public ImportManifest(
|
||||
final String importName,
|
||||
final Date created,
|
||||
final String onServer,
|
||||
final List<String> types
|
||||
) {
|
||||
this.importName = importName;
|
||||
this.created = created;
|
||||
this.onServer = onServer;
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
public String getImportName() {
|
||||
return importName;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return new Date(created.getTime());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,12 @@ import org.libreccm.imexport.AbstractEntityImExporter;
|
|||
import org.libreccm.imexport.EntityImExporterTreeNode;
|
||||
import org.libreccm.imexport.Exportable;
|
||||
import org.libreccm.imexport.ImportExport;
|
||||
import org.libreccm.imexport.ImportManifest;
|
||||
import org.libreccm.security.AuthorizationRequired;
|
||||
import org.libreccm.security.RequiresPrivilege;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
|
@ -129,15 +132,6 @@ public class ImExportController {
|
|||
|
||||
taskManager.exportEntities(exportTypes, exportName);
|
||||
|
||||
// models.put(
|
||||
// "exportEntities",
|
||||
// exportNodes
|
||||
// .stream()
|
||||
// .map(node -> node.getEntityImExporter().getEntityClass().getName())
|
||||
// .sorted()
|
||||
// .collect(Collectors.joining("\n"))
|
||||
// );
|
||||
// return "org/libreccm/ui/admin/imexport/exporting.xhtml";
|
||||
return "redirect:imexport";
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +140,33 @@ public class ImExportController {
|
|||
@AuthorizationRequired
|
||||
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||
public String importEntities() {
|
||||
throw new NotFoundException();
|
||||
models.put(
|
||||
"importArchives",
|
||||
importExport
|
||||
.listAvailableImportArchivies()
|
||||
.stream()
|
||||
.map(this::buildImportOption)
|
||||
.sorted()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
ImportOption::getImportName,
|
||||
ImportOption::getLabel
|
||||
)
|
||||
)
|
||||
);
|
||||
return "org/libreccm/ui/admin/imexport/import.xhtml";
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/import")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||
public String importEntities(
|
||||
@FormParam("archive") final String importArchive
|
||||
) {
|
||||
taskManager.importEntities(importArchive);
|
||||
|
||||
return "redirect:imexport";
|
||||
}
|
||||
|
||||
private String noDuplicateKeys(final String str1, final String str2) {
|
||||
|
|
@ -176,4 +196,19 @@ public class ImExportController {
|
|||
}
|
||||
}
|
||||
|
||||
private ImportOption buildImportOption(final ImportManifest manifest) {
|
||||
return new ImportOption(
|
||||
manifest.getImportName(),
|
||||
String.format(
|
||||
"%s from server %s created on %s with types %s",
|
||||
manifest.getImportName(),
|
||||
manifest.getOnServer(),
|
||||
DateTimeFormatter.ISO_DATE_TIME.withZone(
|
||||
ZoneOffset.systemDefault()
|
||||
).format(manifest.getCreated().toInstant()),
|
||||
manifest.getTypes().stream().collect(Collectors.joining(", "))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,7 @@ import org.libreccm.imexport.Exportable;
|
|||
import org.libreccm.imexport.ImportExport;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.ejb.AsyncResult;
|
||||
import javax.ejb.Asynchronous;
|
||||
import javax.ejb.Singleton;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.ejb.TransactionAttribute;
|
||||
import javax.ejb.TransactionAttributeType;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.event.ObservesAsync;
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -45,22 +38,6 @@ public class ImExportTasks {
|
|||
@Inject
|
||||
private ImportExport importExport;
|
||||
|
||||
// @Asynchronous
|
||||
// @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
|
||||
// public Future<?> startExport(
|
||||
// final Collection<Exportable> entities,
|
||||
// final String exportName
|
||||
// ) {
|
||||
// importExport.exportEntities(entities, exportName);
|
||||
// return new AsyncResult<>(null);
|
||||
// }
|
||||
//
|
||||
// @Asynchronous
|
||||
// @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
|
||||
// public Future<?> startImport(final String importName) {
|
||||
// importExport.importEntities(importName);
|
||||
// return new AsyncResult<>(null);
|
||||
// }
|
||||
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public ExportTask exportEntities(@ObservesAsync final ExportTask task) {
|
||||
|
|
@ -73,7 +50,7 @@ public class ImExportTasks {
|
|||
}
|
||||
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public void importEntitites(@ObservesAsync final ImExportTaskStatus task) {
|
||||
public void importEntitites(@ObservesAsync final ImportTask task) {
|
||||
final String importName = task.getName();
|
||||
|
||||
importExport.importEntities(importName);
|
||||
|
|
|
|||
|
|
@ -63,12 +63,6 @@ public class ImportExportTaskManager {
|
|||
@Inject
|
||||
private Event<ImportTask> importTaskSender;
|
||||
|
||||
// @Inject
|
||||
// private ImExportTasks imExportTasks;
|
||||
|
||||
// private SortedSet<ImExportTaskStatus> exportTasks;
|
||||
//
|
||||
// private SortedSet<ImExportTaskStatus> importTasks;
|
||||
private final SortedSet<ExportTaskStatus> exportTasks;
|
||||
|
||||
private final SortedSet<ImportTaskStatus> importTasks;
|
||||
|
|
@ -111,9 +105,6 @@ public class ImportExportTaskManager {
|
|||
final ExportTaskStatus taskStatus = new ExportTaskStatus();
|
||||
taskStatus.setName(exportName);
|
||||
taskStatus.setStarted(LocalDateTime.now());
|
||||
// final Future<?> status = imExportTasks.startExport(
|
||||
// entities, exportName
|
||||
// );
|
||||
exportTaskSender.fireAsync(
|
||||
new ExportTask(exportName, LocalDate.now(), entities, taskStatus)
|
||||
).handle((task , ex) -> handleExportTaskResult(task, ex, taskStatus));
|
||||
|
|
@ -122,24 +113,15 @@ public class ImportExportTaskManager {
|
|||
exportTasks.add(taskStatus);
|
||||
}
|
||||
|
||||
// public void exportEntities(
|
||||
// final Collection<Exportable> entities, final String exportName
|
||||
// ) {
|
||||
// final ImExportTaskStatus task = new ImExportTaskStatus();
|
||||
// task.setName(exportName);
|
||||
// task.setStarted(LocalDate.now());
|
||||
// final Future<?> status = startExport(entities, exportName);
|
||||
// task.setStatus(status);
|
||||
// exportTasks.add(task);
|
||||
// }
|
||||
public void importEntities(final String importName) {
|
||||
// final ImExportTaskStatus task = new ImExportTaskStatus();
|
||||
// task.setName(importName);
|
||||
// task.setStarted(LocalDateTime.now());
|
||||
// final Future<?> status = imExportTasks.startImport(importName);
|
||||
// task.setStatus(status);
|
||||
// importTasks.add(task);
|
||||
throw new UnsupportedOperationException();
|
||||
final ImportTaskStatus taskStatus = new ImportTaskStatus();
|
||||
taskStatus.setStarted(LocalDateTime.now());
|
||||
importTaskSender.fireAsync(
|
||||
new ImportTask(importName, LocalDate.now(), taskStatus)
|
||||
).handle((task, ex) -> handleImportTaskResult(task, ex, taskStatus));
|
||||
|
||||
taskStatus.setStatus(ImExportTaskStatusEnum.RUNNING);
|
||||
importTasks.add(taskStatus);
|
||||
}
|
||||
|
||||
@Schedule(hour = "*", minute = "*/5", persistent = false)
|
||||
|
|
@ -180,4 +162,17 @@ public class ImportExportTaskManager {
|
|||
return task;
|
||||
}
|
||||
|
||||
private Object handleImportTaskResult(
|
||||
final ImportTask task, final Throwable ex, final ImportTaskStatus status
|
||||
) {
|
||||
if (ex == null) {
|
||||
status.setStatus(ImExportTaskStatusEnum.FINISHED);
|
||||
} else {
|
||||
status.setStatus(ImExportTaskStatusEnum.ERROR);
|
||||
status.setException(ex);
|
||||
LOGGER.error("Import Task {} failed", task);
|
||||
LOGGER.error("with exception: ", ex);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2020 LibreCCM Foundation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package org.libreccm.ui.admin.imexport;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public class ImportOption implements Comparable<ImportOption> {
|
||||
|
||||
private final String importName;
|
||||
|
||||
private final String label;
|
||||
|
||||
public ImportOption(
|
||||
final String importName,
|
||||
final String label
|
||||
) {
|
||||
this.importName = importName;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getImportName() {
|
||||
return importName;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final ImportOption other) {
|
||||
return importName.compareTo(
|
||||
Objects.requireNonNull(other).getImportName()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -30,9 +30,16 @@ public class ImportTask {
|
|||
|
||||
private final LocalDate started;
|
||||
|
||||
public ImportTask(final String name, final LocalDate started) {
|
||||
private final ImportTaskStatus status;
|
||||
|
||||
public ImportTask(
|
||||
final String name,
|
||||
final LocalDate started,
|
||||
final ImportTaskStatus status
|
||||
) {
|
||||
this.name = name;
|
||||
this.started = started;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
|
@ -43,4 +50,8 @@ public class ImportTask {
|
|||
return started;
|
||||
}
|
||||
|
||||
public ImportTaskStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import java.time.ZoneId;
|
|||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -35,12 +34,15 @@ public class ImportTaskStatus implements Comparable<ImportTaskStatus> {
|
|||
|
||||
private LocalDateTime started;
|
||||
|
||||
private CompletionStage<ImportTask> status;
|
||||
private ImExportTaskStatusEnum status;
|
||||
|
||||
private Throwable exception;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
protected void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
|
@ -53,14 +55,56 @@ public class ImportTaskStatus implements Comparable<ImportTaskStatus> {
|
|||
this.started = started;
|
||||
}
|
||||
|
||||
public CompletionStage<ImportTask> getStatus() {
|
||||
public ImExportTaskStatusEnum getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
protected void setStatus(final CompletionStage<ImportTask> status) {
|
||||
protected void setStatus(final ImExportTaskStatusEnum status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Throwable getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
protected void setException(final Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 53 * hash + Objects.hashCode(name);
|
||||
hash = 53 * hash + Objects.hashCode(started);
|
||||
hash = 53 * hash + Objects.hashCode(status);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof ImExportTaskStatus)) {
|
||||
return false;
|
||||
}
|
||||
final ImportTaskStatus other = (ImportTaskStatus) obj;
|
||||
if (!other.canEqual(this)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(name, other.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(started, other.getStarted())) {
|
||||
return false;
|
||||
}
|
||||
return status == other.getStatus();
|
||||
}
|
||||
|
||||
|
||||
public boolean canEqual(final Object obj) {
|
||||
return obj instanceof ImExportTaskStatus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,6 @@
|
|||
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
|
||||
<cc:interface shortDescription="Component for a single checkbox">
|
||||
<cc:attribute default="false"
|
||||
name="checked"
|
||||
shortDescription="Is the checkbox selected?"
|
||||
required="false"
|
||||
type="boolean" />
|
||||
<cc:attribute name="help"
|
||||
required="true"
|
||||
shortDescription="A short description of the input field"
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
|
||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
|
||||
<ui:composition template="/WEB-INF/views/org/libreccm/ui/admin/ccm-admin.xhtml">
|
||||
|
||||
<ui:param name="activePage" value="imexport" />
|
||||
<ui:param name="title" value="#{AdminMessages['imexport.label']}" />
|
||||
|
||||
<ui:define name="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="#{mvc.uri('ImExportController#getImExportDashboard')}">
|
||||
#{AdminMessages['imexport.label']}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">
|
||||
#{AdminMessages['imexport.export.label']}
|
||||
</li>
|
||||
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="main">
|
||||
<div class="container">
|
||||
<h1>#{AdminMessages['imexport.export.label']}</h1>
|
||||
|
||||
<pre>#{exportEntities}</pre>
|
||||
</div>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
</html>
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
|
||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
|
||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
|
||||
<ui:composition template="/WEB-INF/views/org/libreccm/ui/admin/ccm-admin.xhtml">
|
||||
|
||||
<ui:param name="activePage" value="imexport" />
|
||||
<ui:param name="title" value="#{AdminMessages['imexport.label']}" />
|
||||
|
||||
<ui:define name="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="#{mvc.uri('ImExportController#getImExportDashboard')}">
|
||||
#{AdminMessages['imexport.label']}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">
|
||||
#{AdminMessages['imexport.import.label']}
|
||||
</li>
|
||||
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="main">
|
||||
<div class="container">
|
||||
<h1>#{AdminMessages['imexport.import.label']}</h1>
|
||||
|
||||
<form action="#{mvc.uri('ImExportController#importEntities')}"
|
||||
aria-described="import-help"
|
||||
method="post">
|
||||
<p id="import-help">#{AdminMessages['imexport.import.help']}</p>
|
||||
|
||||
<bootstrap:formGroupRadio help="#{AdminMessages['imexport.import.archives.help']}"
|
||||
inputId="importarchives"
|
||||
label="#{AdminMessages['imexport.import.archives.label']}"
|
||||
name="archive"
|
||||
options="#{importArchives}" />
|
||||
<a class="btn btn-warning"
|
||||
href="#{mvc.uri('ImExportController#getImExportDashboard')}">
|
||||
#{AdminMessages['imexport.import.cancel']}
|
||||
</a>
|
||||
<button class="btn btn-success" type="submit">
|
||||
#{AdminMessages['imexport.import.submit']}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
</html>
|
||||
|
|
@ -556,3 +556,9 @@ imexport.activeimports.table.columns.actions.heading=Actions
|
|||
imexport.activeimports.table.columns.status.finished=Finished
|
||||
imexport.activeimports.table.columns.status.running=In progress
|
||||
imexport.activeimports.table.columns.actions.button_label=Cancel
|
||||
imexport.import.label=Import
|
||||
imexport.import.help=Select one of the available import archives
|
||||
imexport.import.archives.help=Select the archive to import
|
||||
imexport.import.archives.label=Available import archives
|
||||
imexport.import.cancel=Cancel
|
||||
imexport.import.submit=Start Import
|
||||
|
|
|
|||
|
|
@ -556,3 +556,9 @@ imexport.activeimports.table.columns.actions.heading=Aktionen
|
|||
imexport.activeimports.table.columns.status.finished=Abgeschlossen
|
||||
imexport.activeimports.table.columns.status.running=In Arbeit
|
||||
imexport.activeimports.table.columns.actions.button_label=Abbrechen
|
||||
imexport.import.label=Import
|
||||
imexport.import.help=W\u00e4hlen Sie das zu importierende Archiv
|
||||
imexport.import.archives.help=W\u00e4hlen Sie das zu importierende Archiv
|
||||
imexport.import.archives.label=Verf\u00fcgbare Import-Archive
|
||||
imexport.import.cancel=Abbrechen
|
||||
imexport.import.submit=Import starten
|
||||
|
|
|
|||
Loading…
Reference in New Issue