Base class for create step of publication assets.
parent
fc0efffe4d
commit
c654460b0a
|
|
@ -1,8 +1,3 @@
|
||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.scientificcms.publications.assets;
|
package org.scientificcms.publications.assets;
|
||||||
|
|
||||||
import org.hibernate.envers.Audited;
|
import org.hibernate.envers.Audited;
|
||||||
|
|
@ -41,7 +36,9 @@ import static org.scientificcms.publications.SciPublicationsConstants.*;
|
||||||
createStep = CollectedVolumeAssetCreateStep.class,
|
createStep = CollectedVolumeAssetCreateStep.class,
|
||||||
editStep = CollectedVolumeAssetEditStep.class
|
editStep = CollectedVolumeAssetEditStep.class
|
||||||
)
|
)
|
||||||
public class CollectedVolumeAsset extends Asset {
|
public class CollectedVolumeAsset
|
||||||
|
extends Asset
|
||||||
|
implements PublicationAsset<CollectedVolume> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
@ -53,6 +50,16 @@ public class CollectedVolumeAsset extends Asset {
|
||||||
@JoinColumn(name = "COLLECTED_VOLUME_ID")
|
@JoinColumn(name = "COLLECTED_VOLUME_ID")
|
||||||
private CollectedVolume collectedVolume;
|
private CollectedVolume collectedVolume;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CollectedVolume getPublication() {
|
||||||
|
return collectedVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPublication(final CollectedVolume publication) {
|
||||||
|
this.collectedVolume = publication;
|
||||||
|
}
|
||||||
|
|
||||||
public CollectedVolume getCollectedVolume() {
|
public CollectedVolume getCollectedVolume() {
|
||||||
return collectedVolume;
|
return collectedVolume;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,18 +36,30 @@ import static org.scientificcms.publications.SciPublicationsConstants.*;
|
||||||
createStep = ProceedingsAssetCreateStep.class,
|
createStep = ProceedingsAssetCreateStep.class,
|
||||||
editStep = ProceedingsAssetEditStep.class
|
editStep = ProceedingsAssetEditStep.class
|
||||||
)
|
)
|
||||||
public class ProceedingsAsset extends Asset {
|
public class ProceedingsAsset
|
||||||
|
extends Asset
|
||||||
|
implements PublicationAsset<Proceedings> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@OneToOne(cascade = {CascadeType.DETACH,
|
@OneToOne(cascade = {CascadeType.DETACH,
|
||||||
CascadeType.MERGE,
|
CascadeType.MERGE,
|
||||||
CascadeType.PERSIST,
|
CascadeType.PERSIST,
|
||||||
CascadeType.REFRESH
|
CascadeType.REFRESH
|
||||||
})
|
})
|
||||||
@JoinColumn(name = "PROCEEDINGS_ID")
|
@JoinColumn(name = "PROCEEDINGS_ID")
|
||||||
private Proceedings proceedings;
|
private Proceedings proceedings;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Proceedings getPublication() {
|
||||||
|
return proceedings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPublication(final Proceedings publication) {
|
||||||
|
this.proceedings = publication;
|
||||||
|
}
|
||||||
|
|
||||||
public Proceedings getProceedings() {
|
public Proceedings getProceedings() {
|
||||||
return proceedings;
|
return proceedings;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.scientificcms.publications.assets;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.Asset;
|
||||||
|
import org.scientificcms.publications.Publication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for {@link Asset} wrapping a {@link Publication}.To avoid an
|
||||||
|
* additional layer of inheritence is the JPA entitites, this was defined as
|
||||||
|
* interface instead of a class.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
* @param <T> The type of publication wrapped by the implementing {@link Asset}.
|
||||||
|
*/
|
||||||
|
public interface PublicationAsset<T extends Publication> {
|
||||||
|
|
||||||
|
T getPublication();
|
||||||
|
|
||||||
|
void setPublication(T publication);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
package org.scientificcms.publications.ui.assets;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.Asset;
|
||||||
|
import org.librecms.contentsection.AssetRepository;
|
||||||
|
import org.librecms.ui.contentsections.assets.AbstractMvcAssetCreateStep;
|
||||||
|
import org.scientificcms.publications.Publication;
|
||||||
|
import org.scientificcms.publications.PublicationRepository;
|
||||||
|
import org.scientificcms.publications.assets.PublicationAsset;
|
||||||
|
import org.scientificcms.publications.ui.SciPublicationsUiConstants;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
* @param <A> Type of {@link Asset}. Must implement {@link PublicationAsset}
|
||||||
|
* @param <P> Type of {@link Publication}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AbstractPublicationAssetCreateStep<A extends Asset & PublicationAsset<P>, P extends Publication>
|
||||||
|
extends AbstractMvcAssetCreateStep<A> {
|
||||||
|
|
||||||
|
private static final String FORM_PARAMS_YEAR_OF_PUBLICATION
|
||||||
|
= "yearOfPublication";
|
||||||
|
|
||||||
|
private static final String FORM_PARAMS_SHORT_DESCRIPTION
|
||||||
|
= "shortDescription";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AssetRepository assetRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PublicationRepository publicationRepo;
|
||||||
|
|
||||||
|
private int yearOfPublication;
|
||||||
|
|
||||||
|
private String shortDescription;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBundle() {
|
||||||
|
return SciPublicationsUiConstants.BUNDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getYearOfPublication() {
|
||||||
|
return yearOfPublication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShortDescription() {
|
||||||
|
return shortDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the publication instance.
|
||||||
|
*
|
||||||
|
* @return The publication instance.
|
||||||
|
*/
|
||||||
|
protected abstract P createPublication();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
protected String setAssetProperties(
|
||||||
|
final A publicationAsset,
|
||||||
|
final Map<String, String[]> formParams
|
||||||
|
) {
|
||||||
|
final P publication = createPublication();
|
||||||
|
final Locale locale = new Locale(getInitialLocale());
|
||||||
|
publication.getTitle().putValue(
|
||||||
|
locale,
|
||||||
|
publicationAsset.getTitle().getValue(locale)
|
||||||
|
);
|
||||||
|
|
||||||
|
shortDescription = Optional
|
||||||
|
.ofNullable(formParams.get(FORM_PARAMS_SHORT_DESCRIPTION))
|
||||||
|
.filter(value -> value.length > 0)
|
||||||
|
.map(value -> value[0])
|
||||||
|
.filter(value -> !value.isBlank())
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (shortDescription != null) {
|
||||||
|
publication.getShortDescription().putValue(
|
||||||
|
locale,
|
||||||
|
shortDescription
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String yearOfPublicationParam = Optional
|
||||||
|
.ofNullable(formParams.get(FORM_PARAMS_YEAR_OF_PUBLICATION))
|
||||||
|
.filter(value -> value.length > 0)
|
||||||
|
.map(value -> value[0])
|
||||||
|
.filter(value -> value.matches("\\d*"))
|
||||||
|
.orElse(null);
|
||||||
|
if (yearOfPublicationParam != null) {
|
||||||
|
publication.setYearOfPublication(
|
||||||
|
Integer.parseInt(yearOfPublicationParam)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
publicationRepo.save(publication);
|
||||||
|
|
||||||
|
publicationAsset.setPublication(publication);
|
||||||
|
assetRepo.save(publicationAsset);
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"redirect:/%s/assets/%s/%s/@%s",
|
||||||
|
getContentSectionLabel(),
|
||||||
|
getFolderPath(),
|
||||||
|
getName(),
|
||||||
|
getEditStepName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns name (the last path fragment) of the edit step, without the
|
||||||
|
* leading <code>@</code>. For example, <code>collectedvolume-edit</code>.
|
||||||
|
*
|
||||||
|
* @return The last path fragment of the edit step.
|
||||||
|
*/
|
||||||
|
protected abstract String getEditStepName();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,12 @@
|
||||||
package org.scientificcms.publications.ui.assets;
|
package org.scientificcms.publications.ui.assets;
|
||||||
|
|
||||||
import org.libreccm.l10n.GlobalizationHelper;
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
import org.librecms.ui.contentsections.assets.AbstractMvcAssetCreateStep;
|
|
||||||
import org.scientificcms.publications.CollectedVolume;
|
import org.scientificcms.publications.CollectedVolume;
|
||||||
import org.scientificcms.publications.PublicationRepository;
|
|
||||||
import org.scientificcms.publications.assets.CollectedVolumeAsset;
|
import org.scientificcms.publications.assets.CollectedVolumeAsset;
|
||||||
import org.scientificcms.publications.ui.SciPublicationsUiConstants;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.transaction.Transactional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -23,24 +15,11 @@ import javax.transaction.Transactional;
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
@Named("SciCmsCollectedVolumeAssetCreateStep")
|
@Named("SciCmsCollectedVolumeAssetCreateStep")
|
||||||
public class CollectedVolumeAssetCreateStep
|
public class CollectedVolumeAssetCreateStep
|
||||||
extends AbstractMvcAssetCreateStep<CollectedVolumeAsset> {
|
extends AbstractPublicationAssetCreateStep<CollectedVolumeAsset, CollectedVolume> {
|
||||||
|
|
||||||
private static final String FORM_PARAMS_YEAR_OF_PUBLICATION
|
|
||||||
= "yearOfPublication";
|
|
||||||
|
|
||||||
private static final String FORM_PARAMS_SHORT_DESCRIPTION
|
|
||||||
= "shortDescription";
|
|
||||||
|
|
||||||
private int yearOfPublication;
|
|
||||||
|
|
||||||
private String shortDescription;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private GlobalizationHelper globalizationHelper;
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private PublicationRepository publicationRepo;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String showCreateStep() {
|
public String showCreateStep() {
|
||||||
return "org/scientificcms/assets/collectedvolume/ui/create-collectedvolume.xhtml";
|
return "org/scientificcms/assets/collectedvolume/ui/create-collectedvolume.xhtml";
|
||||||
|
|
@ -60,73 +39,19 @@ public class CollectedVolumeAssetCreateStep
|
||||||
.getText("collectedvolume.description");
|
.getText("collectedvolume.description");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getBundle() {
|
|
||||||
return SciPublicationsUiConstants.BUNDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<CollectedVolumeAsset> getAssetClass() {
|
protected Class<CollectedVolumeAsset> getAssetClass() {
|
||||||
return CollectedVolumeAsset.class;
|
return CollectedVolumeAsset.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getYearOfPublication() {
|
@Override
|
||||||
return yearOfPublication;
|
protected CollectedVolume createPublication() {
|
||||||
}
|
return new CollectedVolume();
|
||||||
|
|
||||||
public String getShortDescription() {
|
|
||||||
return shortDescription;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
protected String getEditStepName() {
|
||||||
protected String setAssetProperties(
|
return "collectedvolume-edit";
|
||||||
final CollectedVolumeAsset collectedVolumeAsset,
|
|
||||||
final Map<String, String[]> formParams
|
|
||||||
) {
|
|
||||||
final CollectedVolume collectedVolume = new CollectedVolume();
|
|
||||||
final Locale locale = new Locale(getInitialLocale());
|
|
||||||
collectedVolume.getTitle().putValue(
|
|
||||||
locale,
|
|
||||||
collectedVolumeAsset.getTitle().getValue(locale)
|
|
||||||
);
|
|
||||||
|
|
||||||
shortDescription = Optional
|
|
||||||
.ofNullable(formParams.get(FORM_PARAMS_SHORT_DESCRIPTION))
|
|
||||||
.filter(value -> value.length > 0)
|
|
||||||
.map(value -> value[0])
|
|
||||||
.filter(value -> !value.isBlank())
|
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
if (shortDescription != null) {
|
|
||||||
collectedVolume.getShortDescription().putValue(
|
|
||||||
locale,
|
|
||||||
shortDescription
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String yearOfPublicationParam = Optional
|
|
||||||
.ofNullable(formParams.get(FORM_PARAMS_YEAR_OF_PUBLICATION))
|
|
||||||
.filter(value -> value.length > 0)
|
|
||||||
.map(value -> value[0])
|
|
||||||
.filter(value -> value.matches("\\d*"))
|
|
||||||
.orElse(null);
|
|
||||||
if (yearOfPublicationParam != null) {
|
|
||||||
collectedVolume.setYearOfPublication(
|
|
||||||
Integer.parseInt(yearOfPublicationParam)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
publicationRepo.save(collectedVolume);
|
|
||||||
|
|
||||||
collectedVolumeAsset.setCollectedVolume(collectedVolume);
|
|
||||||
|
|
||||||
return String.format(
|
|
||||||
"redirect:/%s/assets/%s/%s/@collectedvolume-edit",
|
|
||||||
getContentSectionLabel(),
|
|
||||||
getFolderPath(),
|
|
||||||
getName()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,12 @@
|
||||||
package org.scientificcms.publications.ui.assets;
|
package org.scientificcms.publications.ui.assets;
|
||||||
|
|
||||||
import org.libreccm.l10n.GlobalizationHelper;
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
import org.librecms.ui.contentsections.assets.AbstractMvcAssetCreateStep;
|
|
||||||
import org.scientificcms.publications.Proceedings;
|
import org.scientificcms.publications.Proceedings;
|
||||||
import org.scientificcms.publications.PublicationRepository;
|
|
||||||
import org.scientificcms.publications.assets.ProceedingsAsset;
|
import org.scientificcms.publications.assets.ProceedingsAsset;
|
||||||
import org.scientificcms.publications.ui.SciPublicationsUiConstants;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.transaction.Transactional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -23,24 +15,11 @@ import javax.transaction.Transactional;
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
@Named("SciCmsProceedingsAssetCreateStep")
|
@Named("SciCmsProceedingsAssetCreateStep")
|
||||||
public class ProceedingsAssetCreateStep
|
public class ProceedingsAssetCreateStep
|
||||||
extends AbstractMvcAssetCreateStep<ProceedingsAsset> {
|
extends AbstractPublicationAssetCreateStep<ProceedingsAsset, Proceedings> {
|
||||||
|
|
||||||
private static final String FORM_PARAMS_YEAR_OF_PUBLICATION
|
|
||||||
= "yearOfPublication";
|
|
||||||
|
|
||||||
private static final String FORM_PARAMS_SHORT_DESCRIPTION
|
|
||||||
= "shortDescription";
|
|
||||||
|
|
||||||
private int yearOfPublication;
|
|
||||||
|
|
||||||
private String shortDescription;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private GlobalizationHelper globalizationHelper;
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private PublicationRepository publicationRepo;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String showCreateStep() {
|
public String showCreateStep() {
|
||||||
return "org/scientificcms/assets/proceedings/ui/create-proceedings.xhtml";
|
return "org/scientificcms/assets/proceedings/ui/create-proceedings.xhtml";
|
||||||
|
|
@ -52,80 +31,27 @@ public class ProceedingsAssetCreateStep
|
||||||
.getLocalizedTextsUtil(getBundle())
|
.getLocalizedTextsUtil(getBundle())
|
||||||
.getText("proceedings.label");
|
.getText("proceedings.label");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return globalizationHelper
|
return globalizationHelper
|
||||||
.getLocalizedTextsUtil(getBundle())
|
.getLocalizedTextsUtil(getBundle())
|
||||||
.getText("proceedings.description");
|
.getText("proceedings.description");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBundle() {
|
protected Proceedings createPublication() {
|
||||||
return SciPublicationsUiConstants.BUNDLE;
|
return new Proceedings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getEditStepName() {
|
||||||
|
return "proceedings-edit";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<ProceedingsAsset> getAssetClass() {
|
public Class<ProceedingsAsset> getAssetClass() {
|
||||||
return ProceedingsAsset.class;
|
return ProceedingsAsset.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getYearOfPublication() {
|
|
||||||
return yearOfPublication;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getShortDescription() {
|
|
||||||
return shortDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
|
||||||
protected String setAssetProperties(
|
|
||||||
final ProceedingsAsset proceedingsAsset,
|
|
||||||
final Map<String, String[]> formParams
|
|
||||||
) {
|
|
||||||
final Proceedings proceedings = new Proceedings();
|
|
||||||
final Locale locale = new Locale(getInitialLocale());
|
|
||||||
proceedings.getTitle().putValue(
|
|
||||||
locale,
|
|
||||||
proceedingsAsset.getTitle().getValue(locale)
|
|
||||||
);
|
|
||||||
|
|
||||||
shortDescription = Optional
|
|
||||||
.ofNullable(formParams.get(FORM_PARAMS_SHORT_DESCRIPTION))
|
|
||||||
.filter(value -> value.length > 0)
|
|
||||||
.map(value -> value[0])
|
|
||||||
.filter(value -> !value.isBlank())
|
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
if (shortDescription != null) {
|
|
||||||
proceedings.getShortDescription().putValue(
|
|
||||||
locale,
|
|
||||||
shortDescription
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String yearOfPublicationParam = Optional
|
|
||||||
.ofNullable(formParams.get(FORM_PARAMS_YEAR_OF_PUBLICATION))
|
|
||||||
.filter(value -> value.length > 0)
|
|
||||||
.map(value -> value[0])
|
|
||||||
.filter(value -> value.matches("\\d*"))
|
|
||||||
.orElse(null);
|
|
||||||
if (yearOfPublicationParam != null) {
|
|
||||||
proceedings.setYearOfPublication(
|
|
||||||
Integer.parseInt(yearOfPublicationParam)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
publicationRepo.save(proceedings);
|
|
||||||
|
|
||||||
proceedingsAsset.setProceedings(proceedings);
|
|
||||||
|
|
||||||
return String.format(
|
|
||||||
"redirect:/%s/assets/%s/%s/@proceedings-edit",
|
|
||||||
getContentSectionLabel(),
|
|
||||||
getFolderPath(),
|
|
||||||
getName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue