no message
This commit is contained in:
parent
f34b39d4c1
commit
9b3f58f31a
|
@ -14,262 +14,261 @@ import com.fasterxml.jackson.annotation.ObjectIdGenerators;
|
|||
|
||||
|
||||
@Entity
|
||||
@Table(name="\"DMP\"")
|
||||
@Table(name = "\"DMP\"")
|
||||
@NamedEntityGraphs({
|
||||
@NamedEntityGraph(
|
||||
name = "organisationsAndResearchers",
|
||||
attributeNodes = {@NamedAttributeNode("organisations"),@NamedAttributeNode("researchers")}),
|
||||
@NamedEntityGraph(
|
||||
name = "fullyDetailed",
|
||||
attributeNodes = {
|
||||
@NamedAttributeNode("project"),@NamedAttributeNode("profile"),
|
||||
@NamedAttributeNode("users"),@NamedAttributeNode("organisations"),@NamedAttributeNode("researchers")})
|
||||
@NamedEntityGraph(
|
||||
name = "organisationsAndResearchers",
|
||||
attributeNodes = {@NamedAttributeNode("organisations"), @NamedAttributeNode("researchers")}),
|
||||
@NamedEntityGraph(
|
||||
name = "fullyDetailed",
|
||||
attributeNodes = {
|
||||
@NamedAttributeNode("project"), @NamedAttributeNode("profile"),
|
||||
@NamedAttributeNode("users"), @NamedAttributeNode("organisations"), @NamedAttributeNode("researchers")})
|
||||
})
|
||||
public class DMP implements Serializable,DataEntity<DMP> {
|
||||
public class DMP implements Serializable, DataEntity<DMP> {
|
||||
|
||||
public static Set<String> getHints() {
|
||||
return hints;
|
||||
}
|
||||
public static Set<String> getHints() {
|
||||
return hints;
|
||||
}
|
||||
|
||||
private static final Set<String> hints = new HashSet<>(Arrays.asList("organisationsAndResearchers", "fullyDetailed"));
|
||||
private static final long serialVersionUID = -8263056535208547615L;
|
||||
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private static final Set<String> hints = new HashSet<>(Arrays.asList("organisationsAndResearchers", "fullyDetailed"));
|
||||
private static final long serialVersionUID = -8263056535208547615L;
|
||||
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
@GenericGenerator(name = "uuid2", strategy = "uuid2")
|
||||
@Column(name = "\"ID\"", updatable = false, nullable = false, columnDefinition = "BINARY(16)")
|
||||
private UUID id;
|
||||
|
||||
@Type(type="org.hibernate.type.PostgresUUIDType")
|
||||
private UUID id;
|
||||
|
||||
@Type(type = "org.hibernate.type.PostgresUUIDType")
|
||||
@Column(name = "\"Previous\"")
|
||||
private UUID previous;
|
||||
|
||||
@Column(name = "\"Label\"")
|
||||
private String label;
|
||||
|
||||
@Column(name = "\"Version\"")
|
||||
private Integer version;
|
||||
private UUID previous;
|
||||
|
||||
@OneToMany(mappedBy = "dmp", fetch = FetchType.LAZY)
|
||||
private Set<Dataset> dataset;
|
||||
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@Column(name = "\"Label\"")
|
||||
private String label;
|
||||
|
||||
@Column(name = "\"Version\"")
|
||||
private Integer version;
|
||||
|
||||
@OneToMany(mappedBy = "dmp", fetch = FetchType.LAZY)
|
||||
private Set<Dataset> dataset;
|
||||
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "\"Project\"")
|
||||
private Project project;
|
||||
|
||||
|
||||
@Type(type="eu.eudat.typedefinition.XMLType")
|
||||
@Column(name = "\"AssociatedDmps\"", columnDefinition = "xml", nullable = true)
|
||||
private String associatedDmps;
|
||||
private Project project;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
|
||||
@Type(type = "eu.eudat.typedefinition.XMLType")
|
||||
@Column(name = "\"AssociatedDmps\"", columnDefinition = "xml", nullable = true)
|
||||
private String associatedDmps;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "\"Profile\"")
|
||||
private DMPProfile profile;
|
||||
|
||||
|
||||
private DMPProfile profile;
|
||||
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "\"Creator\"")
|
||||
private UserInfo creator;
|
||||
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name="\"DMPOrganisation\"",
|
||||
joinColumns={@JoinColumn(name="\"DMP\"", referencedColumnName="\"ID\"")},
|
||||
inverseJoinColumns={@JoinColumn(name="\"Organisation\"", referencedColumnName="\"ID\"")}
|
||||
)
|
||||
private UserInfo creator;
|
||||
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "\"DMPOrganisation\"",
|
||||
joinColumns = {@JoinColumn(name = "\"DMP\"", referencedColumnName = "\"ID\"")},
|
||||
inverseJoinColumns = {@JoinColumn(name = "\"Organisation\"", referencedColumnName = "\"ID\"")}
|
||||
)
|
||||
private Set<Organisation> organisations;
|
||||
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name="\"DMPResearcher\"",
|
||||
joinColumns={@JoinColumn(name="\"DMP\"", referencedColumnName="\"ID\"")},
|
||||
inverseJoinColumns={@JoinColumn(name="\"Researcher\"", referencedColumnName="\"ID\"")}
|
||||
)
|
||||
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "\"DMPResearcher\"",
|
||||
joinColumns = {@JoinColumn(name = "\"DMP\"", referencedColumnName = "\"ID\"")},
|
||||
inverseJoinColumns = {@JoinColumn(name = "\"Researcher\"", referencedColumnName = "\"ID\"")}
|
||||
)
|
||||
private Set<Researcher> researchers;
|
||||
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name="\"UserDMP\"",
|
||||
joinColumns={@JoinColumn(name="dmp", referencedColumnName="\"ID\"")},
|
||||
inverseJoinColumns={@JoinColumn(name="usr", referencedColumnName="id")}
|
||||
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "\"UserDMP\"",
|
||||
joinColumns = {@JoinColumn(name = "dmp", referencedColumnName = "\"ID\"")},
|
||||
inverseJoinColumns = {@JoinColumn(name = "usr", referencedColumnName = "id")}
|
||||
)
|
||||
private Set<UserInfo> users;
|
||||
|
||||
|
||||
@Column(name = "\"Status\"", nullable = false)
|
||||
private Short status;
|
||||
|
||||
|
||||
@Column(name = "\"Created\"")
|
||||
private Date created = null;
|
||||
|
||||
@Column(name = "\"Modified\"")
|
||||
private Date modified = new Date();
|
||||
|
||||
|
||||
@Column(name = "\"Description\"")
|
||||
private String description;
|
||||
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
|
||||
public UserInfo getCreator() {
|
||||
return creator;
|
||||
}
|
||||
@Column(name = "\"Status\"", nullable = false)
|
||||
private Short status;
|
||||
|
||||
|
||||
public void setCreator(UserInfo creator) {
|
||||
this.creator = creator;
|
||||
}
|
||||
@Column(name = "\"Created\"")
|
||||
private Date created = null;
|
||||
|
||||
@Column(name = "\"Modified\"")
|
||||
private Date modified = new Date();
|
||||
|
||||
|
||||
public Short getStatus() {
|
||||
return status;
|
||||
}
|
||||
@Column(name = "\"Description\"")
|
||||
private String description;
|
||||
|
||||
|
||||
public void setStatus(Short status) {
|
||||
this.status = status;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
public UserInfo getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
public void setCreator(UserInfo creator) {
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Set<UserInfo> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(Set<UserInfo> users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
public Short getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public UUID getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
public void setStatus(Short status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setPrevious(UUID previous) {
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return project;
|
||||
}
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setProject(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public String getAssociatedDmps() {
|
||||
return associatedDmps;
|
||||
}
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public void setAssociatedDmps(String associatedDmps) {
|
||||
this.associatedDmps = associatedDmps;
|
||||
}
|
||||
|
||||
public DMPProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
public Set<UserInfo> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setProfile(DMPProfile profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
public void setUsers(Set<UserInfo> users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public Set<Dataset> getDataset() {
|
||||
return dataset;
|
||||
}
|
||||
|
||||
public void setDataset(Set<Dataset> dataset) {
|
||||
this.dataset = dataset;
|
||||
}
|
||||
|
||||
public Set<Organisation> getOrganisations() {
|
||||
return organisations;
|
||||
}
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setOrganisations(Set<Organisation> organisations) {
|
||||
this.organisations = organisations;
|
||||
}
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Set<Researcher> getResearchers() {
|
||||
return researchers;
|
||||
}
|
||||
public UUID getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
public void setResearchers(Set<Researcher> researchers) {
|
||||
this.researchers = researchers;
|
||||
}
|
||||
public void setPrevious(UUID previous) {
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DMP entity) {
|
||||
this.setAssociatedDmps(entity.associatedDmps);
|
||||
this.label = entity.getLabel();
|
||||
this.status = entity.getStatus();
|
||||
this.created = entity.created;
|
||||
this.description = entity.getDescription();
|
||||
this.researchers = entity.getResearchers();
|
||||
this.organisations = entity.getOrganisations();
|
||||
this.users = entity.getUsers();
|
||||
}
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
public void setProject(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public String getAssociatedDmps() {
|
||||
return associatedDmps;
|
||||
}
|
||||
|
||||
public void setAssociatedDmps(String associatedDmps) {
|
||||
this.associatedDmps = associatedDmps;
|
||||
}
|
||||
|
||||
public DMPProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void setProfile(DMPProfile profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public Set<Dataset> getDataset() {
|
||||
return dataset;
|
||||
}
|
||||
|
||||
public void setDataset(Set<Dataset> dataset) {
|
||||
this.dataset = dataset;
|
||||
}
|
||||
|
||||
public Set<Organisation> getOrganisations() {
|
||||
return organisations;
|
||||
}
|
||||
|
||||
public void setOrganisations(Set<Organisation> organisations) {
|
||||
this.organisations = organisations;
|
||||
}
|
||||
|
||||
public Set<Researcher> getResearchers() {
|
||||
return researchers;
|
||||
}
|
||||
|
||||
public void setResearchers(Set<Researcher> researchers) {
|
||||
this.researchers = researchers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DMP entity) {
|
||||
this.setAssociatedDmps(entity.associatedDmps);
|
||||
this.label = entity.getLabel();
|
||||
this.status = entity.getStatus();
|
||||
this.created = entity.created;
|
||||
this.description = entity.getDescription();
|
||||
this.researchers = entity.getResearchers();
|
||||
this.organisations = entity.getOrganisations();
|
||||
if (entity.getUsers() != null) this.users = entity.getUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getKeys() {
|
||||
return new UUID[]{this.id == null ? null : this.id};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getKeys() {
|
||||
return new UUID[]{this.id == null ? null : this.id};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
@ -282,5 +281,5 @@ public class DMP implements Serializable,DataEntity<DMP> {
|
|||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ public class FieldSet implements DatabaseViewStyleDefinition,XmlSerializable<Fie
|
|||
private String extendedDescription;
|
||||
private Multiplicity multiplicity;
|
||||
private boolean hasCommentField;
|
||||
private String commentFieldValue;
|
||||
public List<Field> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
@ -85,12 +86,19 @@ public class FieldSet implements DatabaseViewStyleDefinition,XmlSerializable<Fie
|
|||
this.hasCommentField = hasCommentField;
|
||||
}
|
||||
|
||||
public String getCommentFieldValue() {
|
||||
return commentFieldValue;
|
||||
}
|
||||
|
||||
public void setCommentFieldValue(String commentFieldValue) {
|
||||
this.commentFieldValue = commentFieldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Element toXml(Document doc) {
|
||||
Element fieldSet = doc.createElement("fieldSet");
|
||||
fieldSet.setAttribute("id", this.id);
|
||||
fieldSet.setAttribute("ordinal", ""+this.ordinal);
|
||||
fieldSet.setAttribute("hasCommentField",""+this.hasCommentField);
|
||||
Element title = doc.createElement("title");
|
||||
title.setTextContent(this.title);
|
||||
|
||||
|
@ -104,12 +112,17 @@ public class FieldSet implements DatabaseViewStyleDefinition,XmlSerializable<Fie
|
|||
multiplicity.setAttribute("min", ""+this.multiplicity.getMin());
|
||||
multiplicity.setAttribute("max", ""+this.multiplicity.getMax());
|
||||
|
||||
Element commentField = doc.createElement("commentField");
|
||||
commentField.setAttribute("hasCommentField",""+this.hasCommentField);
|
||||
commentField.setAttribute("commentFieldValue",this.commentFieldValue);
|
||||
|
||||
|
||||
|
||||
Element fieldsElement = doc.createElement("fields");
|
||||
for(Field field : fields){
|
||||
fieldsElement.appendChild(field.toXml(doc));
|
||||
}
|
||||
fieldSet.appendChild(commentField);
|
||||
fieldSet.appendChild(fieldsElement);
|
||||
fieldSet.appendChild(multiplicity);
|
||||
fieldSet.appendChild(title);
|
||||
|
@ -123,14 +136,15 @@ public class FieldSet implements DatabaseViewStyleDefinition,XmlSerializable<Fie
|
|||
this.id = element.getAttribute("id");
|
||||
this.ordinal = Integer.parseInt(element.getAttribute("ordinal"));
|
||||
this.fields = new LinkedList();
|
||||
this.hasCommentField = Boolean.parseBoolean(element.getAttribute("hasCommentField"));
|
||||
Element title = (Element)XmlBuilder.getNodeFromListByTagName(element.getChildNodes(), "title");
|
||||
this.title = title.getTextContent();
|
||||
Element description = (Element)XmlBuilder.getNodeFromListByTagName(element.getChildNodes(), "description");
|
||||
this.description = description.getTextContent();
|
||||
Element extendedDescription = (Element)XmlBuilder.getNodeFromListByTagName(element.getChildNodes(), "extendedDescription");
|
||||
this.extendedDescription = extendedDescription.getTextContent();
|
||||
|
||||
Element commentField = (Element)XmlBuilder.getNodeFromListByTagName(element.getChildNodes(), "commentField");
|
||||
this.hasCommentField = Boolean.parseBoolean(commentField.getAttribute("hasCommentField"));
|
||||
this.commentFieldValue = commentField.getAttribute("commentFieldValue");
|
||||
Element fields = (Element)XmlBuilder.getNodeFromListByTagName(element.getChildNodes(), "fields");
|
||||
|
||||
if(fields!=null){
|
||||
|
|
|
@ -25,80 +25,81 @@ import eu.eudat.utilities.builders.DomainModelConverter;
|
|||
|
||||
public class DataManagementPlanManager {
|
||||
|
||||
public DataTableData<DataManagementPlanListingModel> getPaged(ApiContext apiContext, DataManagementPlanTableRequest dataManagementPlanTableRequest,Principal principal) throws IllegalAccessException, InstantiationException{
|
||||
UserInfo userInfo = apiContext.getDatabaseRepository().getUserInfoDao().find(principal.getId());
|
||||
QueryableList<DMP> items = apiContext.getDatabaseRepository().getDmpDao().getWithCriteria(dataManagementPlanTableRequest.getCriteria());
|
||||
QueryableList<DMP> authItems = apiContext.getDatabaseRepository().getDmpDao().getAuthenticated(items,userInfo);
|
||||
QueryableList<DMP> pagedItems = PaginationManager.applyPaging(authItems,dataManagementPlanTableRequest);
|
||||
public DataTableData<DataManagementPlanListingModel> getPaged(ApiContext apiContext, DataManagementPlanTableRequest dataManagementPlanTableRequest, Principal principal) throws IllegalAccessException, InstantiationException {
|
||||
UserInfo userInfo = apiContext.getDatabaseRepository().getUserInfoDao().find(principal.getId());
|
||||
QueryableList<DMP> items = apiContext.getDatabaseRepository().getDmpDao().getWithCriteria(dataManagementPlanTableRequest.getCriteria());
|
||||
QueryableList<DMP> authItems = apiContext.getDatabaseRepository().getDmpDao().getAuthenticated(items, userInfo);
|
||||
QueryableList<DMP> pagedItems = PaginationManager.applyPaging(authItems, dataManagementPlanTableRequest);
|
||||
|
||||
if(dataManagementPlanTableRequest.getWithHint())pagedItems.withHint("fullyDetailed");
|
||||
List<DataManagementPlanListingModel> datamanagementPlans = new DomainModelConverter<eu.eudat.entities.DMP, DataManagementPlanListingModel>().fromDataModel( pagedItems.toList(), DataManagementPlanListingModel.class);
|
||||
DataTableData<DataManagementPlanListingModel> dataTable = new DataTableData<DataManagementPlanListingModel>();
|
||||
dataTable.setData(datamanagementPlans);
|
||||
dataTable.setTotalCount(items.count());
|
||||
return dataTable;
|
||||
}
|
||||
|
||||
public eu.eudat.models.dmp.DataManagementPlan getSingle(DMPDao dmpsRepository, String id,Principal principal) throws InstantiationException, IllegalAccessException{
|
||||
DMP dataManagementPlanEntity = dmpsRepository.find(UUID.fromString(id));
|
||||
if(dataManagementPlanEntity.getUsers().stream().filter(userInfo -> userInfo.getId() == principal.getId()).collect(Collectors.toList()).size()==0)throw new UnauthorisedException();
|
||||
eu.eudat.models.dmp.DataManagementPlan datamanagementPlan = new eu.eudat.models.dmp.DataManagementPlan();
|
||||
datamanagementPlan.fromDataModel(dataManagementPlanEntity);
|
||||
return datamanagementPlan;
|
||||
}
|
||||
if (dataManagementPlanTableRequest.getWithHint()) pagedItems.withHint("fullyDetailed");
|
||||
List<DataManagementPlanListingModel> datamanagementPlans = new DomainModelConverter<eu.eudat.entities.DMP, DataManagementPlanListingModel>().fromDataModel(pagedItems.toList(), DataManagementPlanListingModel.class);
|
||||
DataTableData<DataManagementPlanListingModel> dataTable = new DataTableData<DataManagementPlanListingModel>();
|
||||
dataTable.setData(datamanagementPlans);
|
||||
dataTable.setTotalCount(items.count());
|
||||
return dataTable;
|
||||
}
|
||||
|
||||
public List<DataManagementPlan> getWithCriteria(DMPDao dmpsRepository, DataManagementPlanCriteriaRequest dataManagementPlanCriteria) throws IllegalAccessException, InstantiationException{
|
||||
QueryableList<DMP> items = dmpsRepository.getWithCriteria(dataManagementPlanCriteria.getCriteria());
|
||||
List<eu.eudat.models.dmp.DataManagementPlan> datamanagementPlans = new DomainModelConverter<eu.eudat.entities.DMP, eu.eudat.models.dmp.DataManagementPlan>().fromDataModel( items.toList(), eu.eudat.models.dmp.DataManagementPlan.class);
|
||||
return datamanagementPlans;
|
||||
}
|
||||
public eu.eudat.models.dmp.DataManagementPlan getSingle(DMPDao dmpsRepository, String id, Principal principal) throws InstantiationException, IllegalAccessException {
|
||||
DMP dataManagementPlanEntity = dmpsRepository.find(UUID.fromString(id));
|
||||
if (dataManagementPlanEntity.getCreator().getId()!=principal.getId()&&dataManagementPlanEntity.getUsers().stream().filter(userInfo -> userInfo.getId() == principal.getId()).collect(Collectors.toList()).size() == 0)
|
||||
throw new UnauthorisedException();
|
||||
eu.eudat.models.dmp.DataManagementPlan datamanagementPlan = new eu.eudat.models.dmp.DataManagementPlan();
|
||||
datamanagementPlan.fromDataModel(dataManagementPlanEntity);
|
||||
return datamanagementPlan;
|
||||
}
|
||||
|
||||
public static void createOrUpdate(ApiContext apiContext, DataManagementPlan dataManagementPlan, Principal principal){
|
||||
DMP newDmp = dataManagementPlan.toDataModel();
|
||||
createOrganisationsIfTheyDontExist(newDmp,apiContext.getDatabaseRepository().getOrganisationDao());
|
||||
createResearchersIfTheyDontExist(newDmp,apiContext.getDatabaseRepository().getResearcherDao());
|
||||
UserInfo user = apiContext.getDatabaseRepository().getUserInfoDao().find(principal.getId());
|
||||
createProjectIfItDoesntExist(newDmp,apiContext.getDatabaseRepository().getProjectDao(),user);
|
||||
newDmp.setCreator(user);
|
||||
apiContext.getDatabaseRepository().getDmpDao().createOrUpdate(newDmp);
|
||||
public List<DataManagementPlan> getWithCriteria(DMPDao dmpsRepository, DataManagementPlanCriteriaRequest dataManagementPlanCriteria) throws IllegalAccessException, InstantiationException {
|
||||
QueryableList<DMP> items = dmpsRepository.getWithCriteria(dataManagementPlanCriteria.getCriteria());
|
||||
List<eu.eudat.models.dmp.DataManagementPlan> datamanagementPlans = new DomainModelConverter<eu.eudat.entities.DMP, eu.eudat.models.dmp.DataManagementPlan>().fromDataModel(items.toList(), eu.eudat.models.dmp.DataManagementPlan.class);
|
||||
return datamanagementPlans;
|
||||
}
|
||||
|
||||
}
|
||||
public static void createOrUpdate(ApiContext apiContext, DataManagementPlan dataManagementPlan, Principal principal) {
|
||||
DMP newDmp = dataManagementPlan.toDataModel();
|
||||
createOrganisationsIfTheyDontExist(newDmp, apiContext.getDatabaseRepository().getOrganisationDao());
|
||||
createResearchersIfTheyDontExist(newDmp, apiContext.getDatabaseRepository().getResearcherDao());
|
||||
UserInfo user = apiContext.getDatabaseRepository().getUserInfoDao().find(principal.getId());
|
||||
createProjectIfItDoesntExist(newDmp, apiContext.getDatabaseRepository().getProjectDao(), user);
|
||||
newDmp.setCreator(user);
|
||||
apiContext.getDatabaseRepository().getDmpDao().createOrUpdate(newDmp);
|
||||
|
||||
private static void createResearchersIfTheyDontExist(DMP newDmp,ResearcherDao researcherRepository){
|
||||
if(newDmp.getResearchers()!=null&&!newDmp.getResearchers().isEmpty()){
|
||||
for(eu.eudat.entities.Researcher researcher : newDmp.getResearchers()){
|
||||
ResearcherCriteria criteria = new ResearcherCriteria();
|
||||
criteria.setLike(researcher.getReference());
|
||||
List<eu.eudat.entities.Researcher> entries = researcherRepository.listBy(criteria);
|
||||
if(entries!=null&&!entries.isEmpty())researcher.setId(entries.get(0).getId());
|
||||
else researcher = researcherRepository.create(researcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createOrganisationsIfTheyDontExist(DMP newDmp,OrganisationDao organisationRepository){
|
||||
if(newDmp.getOrganisations()!=null&&!newDmp.getOrganisations().isEmpty()){
|
||||
for(eu.eudat.entities.Organisation organisation: newDmp.getOrganisations()){
|
||||
OrganisationCriteria criteria = new OrganisationCriteria();
|
||||
criteria.setLike(organisation.getReference());
|
||||
List<eu.eudat.entities.Organisation> entries = organisationRepository.listBy(criteria);
|
||||
if(entries!=null&&!entries.isEmpty())organisation.setId(entries.get(0).getId());
|
||||
else organisation = organisationRepository.create(organisation);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void createResearchersIfTheyDontExist(DMP newDmp, ResearcherDao researcherRepository) {
|
||||
if (newDmp.getResearchers() != null && !newDmp.getResearchers().isEmpty()) {
|
||||
for (eu.eudat.entities.Researcher researcher : newDmp.getResearchers()) {
|
||||
ResearcherCriteria criteria = new ResearcherCriteria();
|
||||
criteria.setLike(researcher.getReference());
|
||||
List<eu.eudat.entities.Researcher> entries = researcherRepository.listBy(criteria);
|
||||
if (entries != null && !entries.isEmpty()) researcher.setId(entries.get(0).getId());
|
||||
else researcher = researcherRepository.create(researcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createProjectIfItDoesntExist(DMP newDmp,ProjectDao projectDao,UserInfo userInfo){
|
||||
if(newDmp.getProject()!=null){
|
||||
Project project = newDmp.getProject();
|
||||
ProjectCriteria criteria = new ProjectCriteria();
|
||||
criteria.setLike(project.getReference());
|
||||
List<eu.eudat.entities.Project> entries = projectDao.getWithCriteria(criteria).toList();
|
||||
if(entries!=null&&!entries.isEmpty())project.setId(entries.get(0).getId());
|
||||
else{
|
||||
project.setCreationUser(userInfo);
|
||||
projectDao.createOrUpdate(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void createOrganisationsIfTheyDontExist(DMP newDmp, OrganisationDao organisationRepository) {
|
||||
if (newDmp.getOrganisations() != null && !newDmp.getOrganisations().isEmpty()) {
|
||||
for (eu.eudat.entities.Organisation organisation : newDmp.getOrganisations()) {
|
||||
OrganisationCriteria criteria = new OrganisationCriteria();
|
||||
criteria.setLike(organisation.getReference());
|
||||
List<eu.eudat.entities.Organisation> entries = organisationRepository.listBy(criteria);
|
||||
if (entries != null && !entries.isEmpty()) organisation.setId(entries.get(0).getId());
|
||||
else organisation = organisationRepository.create(organisation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createProjectIfItDoesntExist(DMP newDmp, ProjectDao projectDao, UserInfo userInfo) {
|
||||
if (newDmp.getProject() != null) {
|
||||
Project project = newDmp.getProject();
|
||||
ProjectCriteria criteria = new ProjectCriteria();
|
||||
criteria.setLike(project.getReference());
|
||||
List<eu.eudat.entities.Project> entries = projectDao.getWithCriteria(criteria).toList();
|
||||
if (entries != null && !entries.isEmpty()) project.setId(entries.get(0).getId());
|
||||
else {
|
||||
project.setCreationUser(userInfo);
|
||||
projectDao.createOrUpdate(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public class FieldSet implements Comparable,PropertiesModelBuilder, ViewStyleDef
|
|||
private List<Field> fields;
|
||||
private List<FieldSet> multiplicityItems;
|
||||
private boolean hasCommentField;
|
||||
private String commentFieldValue;
|
||||
public List<Field> getFields() {
|
||||
Collections.sort(this.fields);
|
||||
return fields;
|
||||
|
@ -78,8 +79,6 @@ public class FieldSet implements Comparable,PropertiesModelBuilder, ViewStyleDef
|
|||
public void setMultiplicity(Multiplicity multiplicity) {
|
||||
this.multiplicity = multiplicity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<FieldSet> getMultiplicityItems() {
|
||||
return multiplicityItems;
|
||||
|
@ -93,12 +92,20 @@ public class FieldSet implements Comparable,PropertiesModelBuilder, ViewStyleDef
|
|||
this.ordinal = ordinal;
|
||||
}
|
||||
|
||||
public void setHasCommentField(boolean hasCommentField) {
|
||||
this.hasCommentField = hasCommentField;
|
||||
}
|
||||
|
||||
public boolean getHasCommentField() {
|
||||
return hasCommentField;
|
||||
}
|
||||
|
||||
public void setHasCommentField(boolean hasCommentField) {
|
||||
this.hasCommentField = hasCommentField;
|
||||
public String getCommentFieldValue() {
|
||||
return commentFieldValue;
|
||||
}
|
||||
|
||||
public void setCommentFieldValue(String commentFieldValue) {
|
||||
this.commentFieldValue = commentFieldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -109,6 +116,7 @@ public class FieldSet implements Comparable,PropertiesModelBuilder, ViewStyleDef
|
|||
item.setOrdinal(this.ordinal);
|
||||
item.setHasCommentField(this.hasCommentField);
|
||||
item.setMultiplicity(this.multiplicity);
|
||||
item.setCommentFieldValue(this.commentFieldValue);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -122,6 +130,7 @@ public class FieldSet implements Comparable,PropertiesModelBuilder, ViewStyleDef
|
|||
this.extendedDescription = item.getExtendedDescription();
|
||||
this.hasCommentField = item.getHasCommentField();
|
||||
this.multiplicity = item.getMultiplicity();
|
||||
this.commentFieldValue = item.getCommentFieldValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -47,7 +47,7 @@ public class LinkedInTokenValidator implements TokenValidator {
|
|||
LinkedInProfile linkedInProfile = linkedInService.profileOperations().getUserProfile();
|
||||
LoginProviderUser user = new LoginProviderUser();
|
||||
|
||||
if (user.getEmail() == null) throw new UnauthorisedException("Cannot login user.LinkedIn account did not provide email");
|
||||
if (linkedInProfile.getEmailAddress() == null) throw new UnauthorisedException("Cannot login user.LinkedIn account did not provide email");
|
||||
user.setEmail(linkedInProfile.getEmailAddress());
|
||||
user.setIsVerified(true); //TODO
|
||||
user.setName(linkedInProfile.getFirstName() + " " + linkedInProfile.getLastName());
|
||||
|
|
|
@ -80,7 +80,7 @@ public class AuthenticationService {
|
|||
credential.setCreationTime(new Date());
|
||||
credential.setStatus(1);
|
||||
credential.setLastUpdateTime(new Date());
|
||||
credential.setProvider((int) TokenValidatorFactoryImpl.LoginProvider.FACEBOOK.getValue());
|
||||
credential.setProvider((int) profile.getProvider().getValue());
|
||||
credential.setSecret(profile.getSecret());
|
||||
if(userInfo == null) {
|
||||
userInfo = new UserInfo();
|
||||
|
|
|
@ -19,7 +19,7 @@ configuration.externalUrls = file:///C:\\Users\\ikalyvas\\Documents\\Projects\\O
|
|||
spring.mail.default-encoding=UTF-8
|
||||
spring.mail.host=smtp.gmail.com
|
||||
spring.mail.username=kalivasioan@gmail.com
|
||||
spring.mail.password=A3b*1*92
|
||||
spring.mail.password=A3b*1*92giannis
|
||||
spring.mail.port=587
|
||||
spring.mail.protocol=smtp
|
||||
spring.mail.test-connection=false
|
||||
|
@ -38,7 +38,7 @@ google.login.clientId = 524432312250-sc9qsmtmbvlv05r44onl6l93ia3k9deo.apps.googl
|
|||
########################LINKEDIN LOGIN Properties#############################HiR4hQH9HNubKC5iKQy0l4mAZ
|
||||
linkedin.login.clientId = 86bl8vfk77clh9
|
||||
linkedin.login.clientSecret = 2OCO9e3wKylW05Tt
|
||||
linkedin.login.redirect_uri = 2OCO9e3wKylW05Tt
|
||||
linkedin.login.redirect_uri = http://localhost:4200/login/linkedin
|
||||
|
||||
########################LINKEDIN LOGIN Properties#############################
|
||||
twitter.login.clientId = HiR4hQH9HNubKC5iKQy0l4mAZ
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
<app-datasets-criteria-component></app-datasets-criteria-component>
|
||||
<mat-card class="mat-card">
|
||||
<mat-progress-bar *ngIf="dataSource?.isLoadingResults" mode="query"></mat-progress-bar>
|
||||
|
||||
<mat-card-header>
|
||||
<mat-progress-bar *ngIf="dataSource?.isLoadingResults" mode="query"></mat-progress-bar>
|
||||
</mat-card-header>
|
||||
<mat-table [dataSource]="dataSource" matSort>
|
||||
|
||||
<!-- Column Definition: Name -->
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
margin: 24px;
|
||||
}
|
||||
|
||||
.mat-progress-bar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mat-fab-bottom-right {
|
||||
top: auto !important;
|
||||
right: 20px !important;
|
||||
|
|
|
@ -11,10 +11,13 @@
|
|||
}
|
||||
|
||||
.project-editor {
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
padding: 3px;
|
||||
.mat-form-field-full-width{
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
padding: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.mat-card {
|
||||
margin: 16px 0;
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
<mat-card-title *ngIf="!isNew">{{'DATASET-EDITOR.TITLE.EDIT' | translate}} {{dataset.label}}</mat-card-title>
|
||||
<mat-card-content>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="{{'DATASET-EDITOR.FIELDS.NAME' | translate}}" type="text" name="label" formControlName="label"
|
||||
required>
|
||||
<mat-error *ngIf="formGroup.get('label').errors?.backendError">{{baseErrorModel.label}}</mat-error>
|
||||
<mat-error *ngIf="formGroup.get('label').errors?.required">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="{{'DATASET-EDITOR.FIELDS.URI' | translate}}" type="text" name="uri" formControlName="uri" required>
|
||||
<mat-error *ngIf="formGroup.get('uri').errors?.backendError">{{baseErrorModel.uri}}</mat-error>
|
||||
<mat-error *ngIf="formGroup.get('uri').errors?.required">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
<app-dmp-criteria-component></app-dmp-criteria-component>
|
||||
<mat-card class="mat-card">
|
||||
<mat-progress-bar *ngIf="dataSource?.isLoadingResults" mode="query"></mat-progress-bar>
|
||||
|
||||
<mat-card-header>
|
||||
<mat-progress-bar *ngIf="dataSource?.isLoadingResults" mode="query"></mat-progress-bar>
|
||||
</mat-card-header>
|
||||
<mat-table [dataSource]="dataSource" matSort>
|
||||
|
||||
|
||||
|
@ -49,7 +50,7 @@
|
|||
<mat-cell *matCellDef="let row">
|
||||
<mat-menu #actionsMenu="matMenu">
|
||||
<button mat-menu-item (click)="rowClick(row.id)"><mat-icon>mode_edit</mat-icon>{{'DMP-LISTING.ACTIONS.EDIT' | translate}}</button>
|
||||
<button mat-menu-item (click)="openShareDialog(row.id)"><mat-icon>insert_invitation</mat-icon>{{'DMP-LISTING.ACTIONS.INVITE' | translate}}</button>
|
||||
<button mat-menu-item (click)="openShareDialog(row.id,row.label)"><mat-icon>insert_invitation</mat-icon>{{'DMP-LISTING.ACTIONS.INVITE' | translate}}</button>
|
||||
</mat-menu>
|
||||
<button mat-icon-button [matMenuTriggerFor]="actionsMenu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
margin: 24px;
|
||||
}
|
||||
|
||||
.mat-progress-bar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mat-fab-bottom-right {
|
||||
top: auto !important;
|
||||
right: 20px !important;
|
||||
|
|
|
@ -27,7 +27,7 @@ export class DataManagementPlanListingComponent implements OnInit {
|
|||
@ViewChild(DataManagementPlanCriteriaComponent) criteria: DataManagementPlanCriteriaComponent;
|
||||
|
||||
dataSource: DataManagementPlanDataSource | null;
|
||||
displayedColumns: String[] = ['name', 'project', 'profile', 'researchers', 'organisations', 'version','actions'];
|
||||
displayedColumns: String[] = ['name', 'project', 'profile', 'researchers', 'organisations', 'version', 'actions'];
|
||||
|
||||
constructor(
|
||||
private dataManagementPlanService: DataManagementPlanService,
|
||||
|
@ -58,12 +58,13 @@ export class DataManagementPlanListingComponent implements OnInit {
|
|||
return defaultCriteria;
|
||||
}
|
||||
|
||||
openShareDialog(rowId: any) {
|
||||
openShareDialog(rowId: any, rowName: any) {
|
||||
let dialogRef = this.dialog.open(InvitationComponent, {
|
||||
height: '200px',
|
||||
width: '700px',
|
||||
data: {
|
||||
dmpId: rowId
|
||||
dmpId: rowId,
|
||||
dmpName: rowName
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<mat-card-title *ngIf="!isNew">{{formGroup.get('label').value}}</mat-card-title>
|
||||
<mat-card-content>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<input matInput placeholder="{{'DMP-EDITOR.FIELDS.NAME' | translate}}" type="text" name="label" formControlName="label" required>
|
||||
<mat-error *ngIf="formGroup.get('label').errors?.backendError">{{baseErrorModel.label}}</mat-error>
|
||||
<mat-error *ngIf="formGroup.get('label').errors?.required">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||
|
@ -18,7 +18,7 @@
|
|||
<mat-error *ngIf="formGroup.get('description').errors?.required">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<auto-complete class="full-width" placeholder="{{'DMP-EDITOR.FIELDS.PROJECT' | translate}}"
|
||||
<auto-complete class="mat-form-field-full-width" placeholder="{{'DMP-EDITOR.FIELDS.PROJECT' | translate}}"
|
||||
[configuration]="projectAutoCompleteConfiguration"
|
||||
titleKey="label"
|
||||
[control]="formGroup.get('project')"
|
||||
|
|
|
@ -11,10 +11,14 @@
|
|||
}
|
||||
|
||||
.data-management-plan-editor {
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
padding: 3px;
|
||||
|
||||
.mat-form-field-full-width{
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
padding: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.mat-card {
|
||||
margin: 16px 0;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<div>
|
||||
<form *ngIf="formGroup" (ngSubmit)="formSubmit()" [formGroup]="formGroup">
|
||||
<td-chips color="accent" [items]="filteredUsers" formControlName="users" placeholder="{{'INVITATION-EDITOR.TITLE' | translate}}"
|
||||
(inputChange)="filterUsers($event)" [requireMatch]="false">
|
||||
<form *ngIf="formGroup" [formGroup]="formGroup">
|
||||
<h1 mat-dialog-title>{{'INVITATION-EDITOR.TITLE' | translate}} {{data.dmpName}}</h1>
|
||||
<div mat-dialog-content>
|
||||
<td-chips color="accent" [items]="filteredUsers" formControlName="users" placeholder="{{'INVITATION-EDITOR.AUTOCOMPLETE-TITLE' | translate}}"
|
||||
(inputChange)="filterUsers($event)" requireMatch>
|
||||
<ng-template td-chip let-chip="chip">
|
||||
<div class="tc-grey-100 bgc-teal-700" td-chip-avatar>{{chip.name.substring(0, 1).toUpperCase()}}</div>
|
||||
{{chip.name}}
|
||||
|
@ -13,6 +14,10 @@
|
|||
</ng-template>
|
||||
<mat-progress-bar [style.height.px]="2" *ngIf="filteredUsersAsync" mode="indeterminate"></mat-progress-bar>
|
||||
</td-chips>
|
||||
<button mat-raised-button color="primary" (click)="send()" type="button">{{'INVITATION-EDITOR.ACTIONS.SEND-INVITATION' | translate}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<div layout="row" class="full-width text-right" align="end">
|
||||
<button mat-raised-button color="primary" (click)="send()" type="button">{{'INVITATION-EDITOR.ACTIONS.SEND-INVITATION' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
<app-projects-criteria-component></app-projects-criteria-component>
|
||||
<mat-card class="mat-card">
|
||||
<mat-progress-bar *ngIf="dataSource?.isLoadingResults" mode="query"></mat-progress-bar>
|
||||
<mat-card-header>
|
||||
<mat-progress-bar *ngIf="dataSource?.isLoadingResults" mode="query"></mat-progress-bar>
|
||||
</mat-card-header>
|
||||
|
||||
<mat-table [dataSource]="dataSource" matSort>
|
||||
|
||||
|
@ -41,9 +43,7 @@
|
|||
<mat-row *matRowDef="let row; columns: displayedColumns" (click)="rowClick(row.id)"></mat-row>
|
||||
|
||||
</mat-table>
|
||||
<mat-paginator #paginator
|
||||
[length]="dataSource?.totalCount"
|
||||
[pageSizeOptions]="[10, 25, 100]">
|
||||
<mat-paginator #paginator [length]="dataSource?.totalCount" [pageSizeOptions]="[10, 25, 100]">
|
||||
</mat-paginator>
|
||||
</mat-card>
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
margin: 24px;
|
||||
}
|
||||
|
||||
.mat-progress-bar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mat-fab-bottom-right {
|
||||
top: auto !important;
|
||||
|
|
|
@ -159,7 +159,8 @@
|
|||
}
|
||||
},
|
||||
"INVITATION-EDITOR": {
|
||||
"TITLE": "User/Email",
|
||||
"TITLE": "Send Invitations for ",
|
||||
"AUTOCOMPLETE-TITLE": "User/Email",
|
||||
"ACTIONS": {
|
||||
"SEND-INVITATION": "Send Invitations",
|
||||
"CANCEL": "Cancel"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue