Abstraction for storing binary asset data, and default implementation using java.sql.Blob and Streams.
parent
31015b0739
commit
c8382b6660
|
|
@ -96,7 +96,8 @@ import java.util.Properties;
|
|||
)
|
||||
},
|
||||
configurations = {
|
||||
org.librecms.CMSConfig.class
|
||||
org.librecms.CMSConfig.class,
|
||||
org.librecms.assets.BinaryAssetConfig.class
|
||||
},
|
||||
pageModelComponentModels = {
|
||||
@PageModelComponentModel(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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.librecms.assets;
|
||||
|
||||
import org.libreccm.core.UnexpectedErrorException;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.context.Dependent;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@Dependent
|
||||
public class BinaryAssetBlobDataProvider implements BinaryAssetDataProvider {
|
||||
|
||||
@Resource(lookup = "java:/comp/env/jdbc/libreccm/db")
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public InputStream retrieveData(final BinaryAsset asset) {
|
||||
Objects.requireNonNull(asset, "Can't retrieve data from null.");
|
||||
try ( Connection connection = dataSource.getConnection()) {
|
||||
final PreparedStatement stmt = connection
|
||||
.prepareStatement(
|
||||
"SELECT data FROM binary_assets WHERE object_id = ?"
|
||||
);
|
||||
stmt.setLong(1, asset.getObjectId());
|
||||
|
||||
try (ResultSet resultSet = stmt.executeQuery()) {
|
||||
final Blob blob = resultSet.getBlob("data");
|
||||
return blob.getBinaryStream();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new UnexpectedErrorException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData(final BinaryAsset asset, final InputStream stream) {
|
||||
Objects.requireNonNull(asset, "Can't save data to null.");
|
||||
try ( Connection connection = dataSource.getConnection()) {
|
||||
final PreparedStatement stmt = connection
|
||||
.prepareStatement(
|
||||
"UPDATE binary_assets SET data = ? WHERE object_id = ?"
|
||||
);
|
||||
stmt.setBlob(1, stream);
|
||||
stmt.setLong(2, asset.getObjectId());
|
||||
} catch (SQLException ex) {
|
||||
throw new UnexpectedErrorException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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.librecms.assets;
|
||||
|
||||
import org.libreccm.configuration.Configuration;
|
||||
import org.libreccm.configuration.Setting;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Configuration parameters for binary assets.
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@Configuration
|
||||
public final class BinaryAssetConfig {
|
||||
|
||||
/**
|
||||
* Sets the implementation of {@link BinaryAssetDataProvider} to use.
|
||||
*/
|
||||
@Setting
|
||||
private String binaryAssetDataProvider = BinaryAssetBlobDataProvider.class
|
||||
.getName();
|
||||
|
||||
public String getBinaryAssetDataProvider() {
|
||||
return binaryAssetDataProvider;
|
||||
}
|
||||
|
||||
public void setBinaryAssetDataProvider(final String binaryAssetDataProvider) {
|
||||
this.binaryAssetDataProvider = binaryAssetDataProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 67 * hash + Objects.hashCode(binaryAssetDataProvider);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof BinaryAssetConfig)) {
|
||||
return false;
|
||||
}
|
||||
final BinaryAssetConfig other = (BinaryAssetConfig) obj;
|
||||
return Objects.equals(
|
||||
binaryAssetDataProvider,
|
||||
other.binaryAssetDataProvider
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"%s{ "
|
||||
+ "binaryAssetDataProvider = %s"
|
||||
+ " }",
|
||||
binaryAssetDataProvider
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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.librecms.assets;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public interface BinaryAssetDataProvider {
|
||||
|
||||
InputStream retrieveData(BinaryAsset asset);
|
||||
|
||||
void saveData(BinaryAsset asset, InputStream stream);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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.librecms.assets;
|
||||
|
||||
import org.libreccm.configuration.ConfigurationManager;
|
||||
import org.libreccm.core.UnexpectedErrorException;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.enterprise.inject.Any;
|
||||
import javax.enterprise.inject.Instance;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@RequestScoped
|
||||
public class BinaryAssetDataService {
|
||||
|
||||
@Inject
|
||||
private ConfigurationManager confManager;
|
||||
|
||||
@Inject
|
||||
@Any
|
||||
private Instance<BinaryAssetDataProvider> dataProvider;
|
||||
|
||||
public InputStream retrieveData(final BinaryAsset asset) {
|
||||
Objects.requireNonNull(asset, "Can't retrieve data from null.");
|
||||
|
||||
final BinaryAssetDataProvider dataProvider = getDataProvider();
|
||||
|
||||
return dataProvider.retrieveData(asset);
|
||||
}
|
||||
|
||||
public void saveData(final BinaryAsset asset, final InputStream stream) {
|
||||
Objects.requireNonNull(asset, "Can't save data to null.");
|
||||
|
||||
final BinaryAssetDataProvider dataProvider = getDataProvider();
|
||||
dataProvider.saveData(asset, stream);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private BinaryAssetDataProvider getDataProvider() {
|
||||
final BinaryAssetConfig config = confManager.findConfiguration(
|
||||
BinaryAssetConfig.class
|
||||
);
|
||||
|
||||
final Class<? extends BinaryAssetDataProvider> clazz;
|
||||
try {
|
||||
clazz = (Class<? extends BinaryAssetDataProvider>) Class.forName(
|
||||
config.getBinaryAssetDataProvider()
|
||||
);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new UnexpectedErrorException(ex);
|
||||
}
|
||||
|
||||
final Instance<? extends BinaryAssetDataProvider> selectedInstance
|
||||
= dataProvider.select(clazz);
|
||||
|
||||
if (selectedInstance.isResolvable()) {
|
||||
return selectedInstance.get();
|
||||
} else {
|
||||
throw new UnexpectedErrorException(
|
||||
String.format(
|
||||
"The configured implementation of %s could not be resolved.",
|
||||
BinaryAssetDataProvider.class.getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -319,7 +319,6 @@ public class Images {
|
|||
LOGGER.error(ex);
|
||||
return Response.serverError().build();
|
||||
}
|
||||
|
||||
return Response
|
||||
.ok(outputStream.toByteArray(), mimeType)
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ public class CmsAssetEditSteps implements MvcAssetEditSteps {
|
|||
public Set<Class<?>> getResourceClasses() {
|
||||
final Set<Class<?>> classes = new HashSet<>();
|
||||
|
||||
classes.add(FileAssetEditStepDownload.class);
|
||||
classes.add(SideNoteEditStepResources.class);
|
||||
|
||||
return classes;
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
|
|||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
||||
import org.libreccm.l10n.GlobalizationHelper;
|
||||
import org.libreccm.security.AuthorizationRequired;
|
||||
import org.librecms.assets.BinaryAssetDataService;
|
||||
import org.librecms.assets.FileAsset;
|
||||
import org.librecms.contentsection.AssetRepository;
|
||||
import org.librecms.ui.contentsections.AssetPermissionsChecker;
|
||||
import org.librecms.ui.contentsections.ContentSectionNotFoundException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
|
@ -55,6 +55,7 @@ import javax.ws.rs.Path;
|
|||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -84,6 +85,9 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
|
|||
@Inject
|
||||
private AssetRepository assetRepo;
|
||||
|
||||
@Inject
|
||||
private BinaryAssetDataService dataService;
|
||||
|
||||
@Inject
|
||||
private GlobalizationHelper globalizationHelper;
|
||||
|
||||
|
|
@ -312,6 +316,9 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@POST
|
||||
@Path("/upload")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
|
|
@ -348,19 +355,10 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
|
|||
.getHeaders();
|
||||
fileName = getFileName(headers);
|
||||
contentType = getContentType(headers);
|
||||
final byte[] bytes = new byte[1024];
|
||||
try (InputStream inputStream = inputPart.getBody(
|
||||
InputStream.class, null
|
||||
dataService.saveData(
|
||||
fileAsset,
|
||||
inputPart.getBody(InputStream.class, null)
|
||||
);
|
||||
ByteArrayOutputStream fileDataOutputStream
|
||||
= new ByteArrayOutputStream()) {
|
||||
while (inputStream.read(bytes) != -1) {
|
||||
fileDataOutputStream.writeBytes(bytes);
|
||||
}
|
||||
|
||||
fileAsset.setData(fileDataOutputStream.toByteArray());
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error(
|
||||
"Failed to upload file for FileAsset {}:", assetPath
|
||||
|
|
@ -370,6 +368,33 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
|
|||
models.put("uploadFailed", true);
|
||||
return buildRedirectPathForStep();
|
||||
}
|
||||
|
||||
// final MultivaluedMap<String, String> headers = inputPart
|
||||
// .getHeaders();
|
||||
// fileName = getFileName(headers);
|
||||
// contentType = getContentType(headers);
|
||||
// final byte[] bytes = new byte[1024];
|
||||
// try (InputStream inputStream = inputPart.getBody(
|
||||
// InputStream.class, null
|
||||
// );
|
||||
// ByteArrayOutputStream fileDataOutputStream
|
||||
// = new ByteArrayOutputStream()) {
|
||||
// while (inputStream.read(bytes) != -1) {
|
||||
// fileDataOutputStream.writeBytes(bytes);
|
||||
// }
|
||||
//
|
||||
// fileAsset.setData(fileDataOutputStream.toByteArray());
|
||||
// }
|
||||
//
|
||||
// } catch (IOException ex) {
|
||||
// LOGGER.error(
|
||||
// "Failed to upload file for FileAsset {}:", assetPath
|
||||
// );
|
||||
// LOGGER.error(ex);
|
||||
//
|
||||
// models.put("uploadFailed", true);
|
||||
// return buildRedirectPathForStep();
|
||||
// }
|
||||
}
|
||||
|
||||
fileAsset.setFileName(fileName);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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.librecms.ui.contentsections.assets;
|
||||
|
||||
import org.libreccm.security.AuthorizationRequired;
|
||||
import org.librecms.assets.BinaryAssetDataService;
|
||||
import org.librecms.assets.FileAsset;
|
||||
import org.librecms.contentsection.Asset;
|
||||
import org.librecms.contentsection.AssetRepository;
|
||||
import org.librecms.contentsection.ContentSection;
|
||||
import org.librecms.ui.contentsections.ContentSectionNotFoundException;
|
||||
import org.librecms.ui.contentsections.ContentSectionsUi;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.internet.ContentDisposition;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@RequestScoped
|
||||
@Path(MvcAssetEditSteps.PATH_PREFIX + "fileasset-edit-download")
|
||||
|
||||
public class FileAssetEditStepDownload {
|
||||
|
||||
@Inject
|
||||
private AssetRepository assetRepo;
|
||||
|
||||
@Inject
|
||||
private BinaryAssetDataService dataService;
|
||||
|
||||
@Inject
|
||||
private ContentSectionsUi sectionsUi;
|
||||
|
||||
@GET
|
||||
@Path("/")
|
||||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public Response downloadFile(
|
||||
@PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
|
||||
final String sectionIdentifier,
|
||||
@PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
|
||||
final String assetPath
|
||||
) {
|
||||
|
||||
final ContentSection contentSection = sectionsUi
|
||||
.findContentSection(sectionIdentifier)
|
||||
.orElseThrow(
|
||||
() -> new WebApplicationException(
|
||||
Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(
|
||||
String.format(
|
||||
"ContentSection %s not found.",
|
||||
sectionIdentifier
|
||||
)
|
||||
).build()
|
||||
)
|
||||
);
|
||||
|
||||
final Asset asset = assetRepo
|
||||
.findByPath(contentSection, assetPath)
|
||||
.orElseThrow(
|
||||
() -> new WebApplicationException(
|
||||
Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(
|
||||
String.format(
|
||||
"No asset for path %s found in section %s.",
|
||||
assetPath,
|
||||
contentSection.getLabel()
|
||||
)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
);
|
||||
|
||||
if (!(asset instanceof FileAsset)) {
|
||||
throw new WebApplicationException(
|
||||
Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(
|
||||
String.format(
|
||||
"No asset for path %s found in section %s.",
|
||||
assetPath,
|
||||
contentSection.getLabel()
|
||||
)
|
||||
)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
final FileAsset fileAsset = (FileAsset) asset;
|
||||
try ( InputStream dataInputStream = dataService.retrieveData(fileAsset)) {
|
||||
|
||||
final StreamingOutput output = new StreamingOutput() {
|
||||
|
||||
@Override
|
||||
public void write(final OutputStream outputStream)
|
||||
throws IOException, WebApplicationException {
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
while ((length = dataInputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return Response
|
||||
.ok()
|
||||
.entity(output)
|
||||
.header("Content-Type", fileAsset.getMimeType())
|
||||
.header(
|
||||
"Content-Disposition",
|
||||
String.format(
|
||||
"attachment; filename=\"%s\"",
|
||||
fileAsset.getFileName()
|
||||
)
|
||||
)
|
||||
.build();
|
||||
|
||||
} catch (IOException ex) {
|
||||
throw new WebApplicationException(
|
||||
ex,
|
||||
Response.Status.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// private class FileAssetOutput implements StreamingOutput {
|
||||
//
|
||||
// @Override
|
||||
// public void write(final OutputStream outputStream)
|
||||
// throws IOException, WebApplicationException {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
|
@ -52,14 +52,19 @@
|
|||
|
||||
<h3>#{CmsAssetsStepsDefaultMessagesBundle['fileasset.editstep.file.title']}</h3>
|
||||
<c:if test="#{MvcAssetEditStepModel.canEdit}">
|
||||
<div class="text-right">
|
||||
<div class="text-left">
|
||||
<button class="btn btn-primary"
|
||||
data-toggle="modal"
|
||||
data-target="#file-upload-dialog"
|
||||
type="button">
|
||||
<bootstrap:svgIcon icon="upload" />
|
||||
<span class="sr-only">#{CmsAssetsStepsDefaultMessagesBundle['fileasset.editstep.file.upload.button.label']}</span>
|
||||
<span>#{CmsAssetsStepsDefaultMessagesBundle['fileasset.editstep.file.upload.button.label']}</span>
|
||||
</button>
|
||||
<a class="btn btn-primary"
|
||||
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSelectedAssetModel.assetPath}/@fileasset-edit-download">
|
||||
<bootstrap:svgIcon icon="download" />
|
||||
<span>#{CmsAssetsStepsDefaultMessagesBundle['fileasset.editstep.file.download.button.label']}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div aria-hidden="true"
|
||||
aria-labelledby="file-upload-dialog-title"
|
||||
|
|
|
|||
|
|
@ -387,3 +387,4 @@ createform.externalvideoasset.url.help=The URL of the external video. The video
|
|||
createform.externalvideoasset.url.label=URL
|
||||
createform.externalvideoasset.description.help=A short description of the contents of the video.
|
||||
createform.externalvideoasset.description.label=Description
|
||||
fileasset.editstep.file.download.button.label=Download File
|
||||
|
|
|
|||
|
|
@ -387,3 +387,4 @@ createform.externalvideoasset.url.help=Die URL der externe Video-Datei. Das Vide
|
|||
createform.externalvideoasset.url.label=URL
|
||||
createform.externalvideoasset.description.help=Eine kurze Beschreibung des Inhaltes des Videos.
|
||||
createform.externalvideoasset.description.label=Beschreibung
|
||||
fileasset.editstep.file.download.button.label=Datei herunterladen
|
||||
|
|
|
|||
Loading…
Reference in New Issue