diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/ConfigFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/ConfigFacade.java
index f9c3e4e4d6f..79d9d189024 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/ConfigFacade.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/ConfigFacade.java
@@ -19,6 +19,7 @@
import de.symeda.sormas.api.externaljournal.PatientDiaryConfig;
import de.symeda.sormas.api.externaljournal.SymptomJournalConfig;
import de.symeda.sormas.api.geo.GeoLatLon;
+import de.symeda.sormas.api.news.eios.EiosConfig;
import de.symeda.sormas.api.sormastosormas.SormasToSormasConfig;
@Remote
@@ -167,4 +168,8 @@ public interface ConfigFacade {
long getMinimumEmancipatedAge();
long getMinimumAdultAge();
+
+ EiosConfig getEIOSConfig();
+
+ String getEiosBoardIds();
}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/FacadeProvider.java b/sormas-api/src/main/java/de/symeda/sormas/api/FacadeProvider.java
index b530e4fd4da..4560448447f 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/FacadeProvider.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/FacadeProvider.java
@@ -83,6 +83,7 @@
import de.symeda.sormas.api.infrastructure.region.RegionFacade;
import de.symeda.sormas.api.infrastructure.subcontinent.SubcontinentFacade;
import de.symeda.sormas.api.manualmessagelog.ManualMessageLogFacade;
+import de.symeda.sormas.api.news.NewsFacade;
import de.symeda.sormas.api.outbreak.OutbreakFacade;
import de.symeda.sormas.api.person.PersonFacade;
import de.symeda.sormas.api.report.AggregateReportFacade;
@@ -559,6 +560,10 @@ public static SurveyTokenFacade getSurveyTokenFacade() {
return get().lookupEjbRemote(SurveyTokenFacade.class);
}
+ public static NewsFacade getNewsFacade() {
+ return get().lookupEjbRemote(NewsFacade.class);
+ }
+
@SuppressWarnings("unchecked")
public
P lookupEjbRemote(Class
clazz) {
try {
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/feature/FeatureType.java b/sormas-api/src/main/java/de/symeda/sormas/api/feature/FeatureType.java
index 7cb2d730131..fda24ae7f6d 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/feature/FeatureType.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/feature/FeatureType.java
@@ -332,7 +332,8 @@ public enum FeatureType {
CASE_SURVEILANCE,
CONTACT_TRACING },
null,
- ImmutableMap.of(FeatureTypeProperty.S2S_SHARING, Boolean.FALSE));
+ ImmutableMap.of(FeatureTypeProperty.S2S_SHARING, Boolean.FALSE)),
+ NEWS_FEATURE(true, false, null, null, null);
public static final FeatureType[] SURVEILLANCE_FEATURE_TYPES = {
FeatureType.CASE_SURVEILANCE,
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
index 345bc885f33..be627f13d1b 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
@@ -1170,6 +1170,8 @@ public interface Captions {
String countryActiveCountries = "countryActiveCountries";
String countryAllCountries = "countryAllCountries";
String countryArchivedCountries = "countryArchivedCountries";
+ String createEvent = "createEvent";
+ String createNew = "createNew";
String createSymptomJournalAccountButton = "createSymptomJournalAccountButton";
String creationDate = "creationDate";
String CustomizableEnum_hasDetails = "CustomizableEnum.hasDetails";
@@ -1921,6 +1923,7 @@ public interface Captions {
String HealthConditions_otherConditions = "HealthConditions.otherConditions";
String HealthConditions_sickleCellDisease = "HealthConditions.sickleCellDisease";
String HealthConditions_tuberculosis = "HealthConditions.tuberculosis";
+ String hideDescription = "hideDescription";
String humanSampleViewType = "humanSampleViewType";
String Immunization = "Immunization";
String Immunization_additionalDetails = "Immunization.additionalDetails";
@@ -2116,6 +2119,22 @@ public interface Captions {
String moreActions = "moreActions";
String name = "name";
String nationalHealthId = "nationalHealthId";
+ String news = "news";
+ String News_community = "News.community";
+ String News_description = "News.description";
+ String News_disease = "News.disease";
+ String News_district = "News.district";
+ String News_link = "News.link";
+ String News_newsDate = "News.newsDate";
+ String News_region = "News.region";
+ String News_riskLevel = "News.riskLevel";
+ String News_status = "News.status";
+ String News_title = "News.title";
+ String newsAction = "newsAction";
+ String NewsCriteria_endDate = "NewsCriteria.endDate";
+ String NewsCriteria_startDate = "NewsCriteria.startDate";
+ String newsList = "newsList";
+ String newsUpdate = "newsUpdate";
String notAvailableShort = "notAvailableShort";
String notificationType = "notificationType";
String notificationType_caption = "notificationType.caption";
@@ -2128,6 +2147,7 @@ public interface Captions {
String openInPatientDiaryButton = "openInPatientDiaryButton";
String openInSymptomJournalButton = "openInSymptomJournalButton";
String openLinkedCaseToImmunizationButton = "openLinkedCaseToImmunizationButton";
+ String openLinkInTab = "openLinkInTab";
String options = "options";
String otherDeletionReason = "otherDeletionReason";
String outbreakAffectedDistricts = "outbreakAffectedDistricts";
@@ -2588,6 +2608,7 @@ public interface Captions {
String selfReportProcess = "selfReportProcess";
String selfReportSelfReportsList = "selfReportSelfReportsList";
String sex = "sex";
+ String showDescription = "showDescription";
String showPlacesOnMap = "showPlacesOnMap";
String singleDayEventDate = "singleDayEventDate";
String singleDayEventEvolutionDate = "singleDayEventEvolutionDate";
@@ -3068,6 +3089,7 @@ public interface Captions {
String treatmentOpenPrescription = "treatmentOpenPrescription";
String unassigned = "unassigned";
String unknown = "unknown";
+ String updateNews = "updateNews";
String User = "User";
String User_active = "User.active";
String User_address = "User.address";
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java
index 0cfea2d8e6e..39ae3b784ea 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java
@@ -326,6 +326,7 @@ public interface Strings {
String errorDocumentGeneration = "errorDocumentGeneration";
String errorDocumentGenerationMultipleDiseasses = "errorDocumentGenerationMultipleDiseasses";
String errorDocumentTemplateWorkflowChangeNotAllowed = "errorDocumentTemplateWorkflowChangeNotAllowed";
+ String errorEiosRequestToken = "errorEiosRequestToken";
String errorEntityNotEditable = "errorEntityNotEditable";
String errorEntityOutdated = "errorEntityOutdated";
String errorEnvironmentSampleNoDispatchRight = "errorEnvironmentSampleNoDispatchRight";
@@ -1598,6 +1599,10 @@ public interface Strings {
String mmhg = "mmhg";
String month = "month";
String nameOf = "nameOf";
+ String newsData = "newsData";
+ String newsEndDate = "newsEndDate";
+ String newsFilterText = "newsFilterText";
+ String newsStartDate = "newsStartDate";
String no = "no";
String none = "none";
String notAnswered = "notAnswered";
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsBodyResponse.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsBodyResponse.java
new file mode 100644
index 00000000000..1ce09e3d941
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsBodyResponse.java
@@ -0,0 +1,29 @@
+package de.symeda.sormas.api.news;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class NewsBodyResponse {
+
+ @JsonProperty("data")
+ private List newsList;
+ @JsonProperty("meta")
+ private NewsMetaResponseDto newsMetaResponse;
+
+ public List getNewsList() {
+ return newsList;
+ }
+
+ public void setNewsList(List newsList) {
+ this.newsList = newsList;
+ }
+
+ public NewsMetaResponseDto getNewsMetaResponse() {
+ return newsMetaResponse;
+ }
+
+ public void setNewsMetaResponse(NewsMetaResponseDto newsMetaResponse) {
+ this.newsMetaResponse = newsMetaResponse;
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsConfig.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsConfig.java
new file mode 100644
index 00000000000..62d0a749eba
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsConfig.java
@@ -0,0 +1,26 @@
+package de.symeda.sormas.api.news;
+
+import de.symeda.sormas.api.audit.AuditedClass;
+
+@AuditedClass
+public class NewsConfig {
+
+ private String baseUrl;
+ private String authToken;
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ public String getAuthToken() {
+ return authToken;
+ }
+
+ public void setAuthToken(String authToken) {
+ this.authToken = authToken;
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsCriteria.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsCriteria.java
new file mode 100644
index 00000000000..d537132c7ff
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsCriteria.java
@@ -0,0 +1,107 @@
+package de.symeda.sormas.api.news;
+
+import java.util.Date;
+
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.api.infrastructure.community.CommunityReferenceDto;
+import de.symeda.sormas.api.infrastructure.district.DistrictReferenceDto;
+import de.symeda.sormas.api.infrastructure.region.RegionReferenceDto;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+import de.symeda.sormas.api.utils.IgnoreForUrl;
+import de.symeda.sormas.api.utils.criteria.BaseCriteria;
+
+public class NewsCriteria extends BaseCriteria {
+
+ public static final String I18N_PREFIX = "NewsCriteria";
+ public static final String REGION = "region";
+ public static final String DISTRICT = "district";
+ public static final String COMMUNITY = "community";
+ public final static String RISK_LEVE = "riskLevel";
+ public final static String STATUS = "status";
+ public final static String START_DATE = "startDate";
+ public final static String END_DATE = "endDate";
+ public final static String IS_USER_LEVEL_FILER = "onlyInMyJurisdiction";
+ public final static String NEWS_LIKE = "newsLike";
+ private RegionReferenceDto region;
+ private DistrictReferenceDto district;
+ private CommunityReferenceDto community;
+ private RiskLevel riskLevel;
+ private NewsStatus status;
+ private Date startDate;
+ private Date endDate;
+ private Boolean onlyInMyJurisdiction;
+ private String newsLike;
+
+ public RegionReferenceDto getRegion() {
+ return region;
+ }
+
+ public void setRegion(RegionReferenceDto region) {
+ this.region = region;
+ }
+
+ public DistrictReferenceDto getDistrict() {
+ return district;
+ }
+
+ public void setDistrict(DistrictReferenceDto district) {
+ this.district = district;
+ }
+
+ public CommunityReferenceDto getCommunity() {
+ return community;
+ }
+
+ public void setCommunity(CommunityReferenceDto community) {
+ this.community = community;
+ }
+
+ public RiskLevel getRiskLevel() {
+ return riskLevel;
+ }
+
+ public void setRiskLevel(RiskLevel riskLevel) {
+ this.riskLevel = riskLevel;
+ }
+
+ public NewsStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(NewsStatus status) {
+ this.status = status;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public Boolean getOnlyInMyJurisdiction() {
+ return onlyInMyJurisdiction;
+ }
+
+ public void setOnlyInMyJurisdiction(Boolean onlyInMyJurisdiction) {
+ this.onlyInMyJurisdiction = onlyInMyJurisdiction;
+ }
+
+ @IgnoreForUrl
+ public String getNewsLike() {
+ return newsLike;
+ }
+
+ public void setNewsLike(String newsLike) {
+ this.newsLike = newsLike;
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsDto.java
new file mode 100644
index 00000000000..2fdf672dfec
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsDto.java
@@ -0,0 +1,170 @@
+package de.symeda.sormas.api.news;
+
+import java.util.Date;
+
+import de.symeda.sormas.api.Disease;
+import de.symeda.sormas.api.EntityDto;
+import de.symeda.sormas.api.audit.AuditedClass;
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.api.infrastructure.community.CommunityReferenceDto;
+import de.symeda.sormas.api.infrastructure.district.DistrictReferenceDto;
+import de.symeda.sormas.api.infrastructure.region.RegionReferenceDto;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+import de.symeda.sormas.api.utils.DataHelper;
+
+@AuditedClass
+public class NewsDto extends EntityDto {
+
+ public static final String I18N_PREFIX = "News";
+ public static final String TITLE = "title";
+ public static final String LINK = "link";
+ public static final String DESCRIPTION = "description";
+ public static final String NEWS_DATE = "newsDate";
+ public static final String RISK_LEVEL = "riskLevel";
+ public static final String STATUS = "status";
+ public static final String REGION = "region";
+ public static final String DISTRICT = "district";
+ public static final String COMMUNITY = "community";
+ public static final String DISEASE = "disease";
+ private Long eiosId;
+ private String title;
+ private String eiosUrl;
+ private String link;
+ private String description;
+ private Date newsDate;
+ private RegionReferenceDto region;
+ private DistrictReferenceDto district;
+ private CommunityReferenceDto community;
+ private RiskLevel riskLevel;
+
+ private NewsStatus status;
+ private Disease disease;
+ private Disease communicableDisease;
+ private String otherNewsCategory;
+
+ public static NewsDto build() {
+ NewsDto dto = new NewsDto();
+ dto.setUuid(DataHelper.createUuid());
+ dto.setNewsDate(new Date());
+ return dto;
+ }
+
+ public Long getEiosId() {
+ return eiosId;
+ }
+
+ public void setEiosId(Long eiosId) {
+ this.eiosId = eiosId;
+ }
+
+ public String getLink() {
+ return link;
+ }
+
+ public void setLink(String link) {
+ this.link = link;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getEiosUrl() {
+ return eiosUrl;
+ }
+
+ public void setEiosUrl(String eiosUrl) {
+ this.eiosUrl = eiosUrl;
+ }
+
+ public Date getNewsDate() {
+ return newsDate;
+ }
+
+ public void setNewsDate(Date newsDate) {
+ this.newsDate = newsDate;
+ }
+
+ public RegionReferenceDto getRegion() {
+ return region;
+ }
+
+ public void setRegion(RegionReferenceDto region) {
+ this.region = region;
+ }
+
+ public DistrictReferenceDto getDistrict() {
+ return district;
+ }
+
+ public void setDistrict(DistrictReferenceDto district) {
+ this.district = district;
+ }
+
+ public CommunityReferenceDto getCommunity() {
+ return community;
+ }
+
+ public void setCommunity(CommunityReferenceDto community) {
+ this.community = community;
+ }
+
+ public RiskLevel getRiskLevel() {
+ return riskLevel;
+ }
+
+ public void setRiskLevel(RiskLevel riskLevel) {
+ this.riskLevel = riskLevel;
+ }
+
+ public NewsStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(NewsStatus status) {
+ this.status = status;
+ }
+
+ public Disease getDisease() {
+ return disease;
+ }
+
+ public void setDisease(Disease disease) {
+ this.disease = disease;
+ }
+
+ public Disease getCommunicableDisease() {
+ return communicableDisease;
+ }
+
+ public void setCommunicableDisease(Disease communicableDisease) {
+ this.communicableDisease = communicableDisease;
+ }
+
+ public String getOtherNewsCategory() {
+ return otherNewsCategory;
+ }
+
+ public void setOtherNewsCategory(String otherNewsCategory) {
+ this.otherNewsCategory = otherNewsCategory;
+ }
+
+ @Override
+ public String toString() {
+ return "EiosArticleDto{" + "eiosId=" + eiosId + ", title='" + title + '\'' + ", eiosUrl='" + eiosUrl + '\'' + ", link='" + link + '\''
+ + ", description='" + description + '\'' + ", processedOnDate=" + newsDate + ", region=" + region + ", district=" + district
+ + ", community=" + community + ", riskLevel=" + riskLevel + ", status=" + status + '}';
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsFacade.java
new file mode 100644
index 00000000000..5f05e66a368
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsFacade.java
@@ -0,0 +1,20 @@
+package de.symeda.sormas.api.news;
+
+import java.util.List;
+
+import javax.ejb.Remote;
+
+import de.symeda.sormas.api.BaseFacade;
+import de.symeda.sormas.api.EditPermissionFacade;
+import de.symeda.sormas.api.common.Page;
+import de.symeda.sormas.api.utils.SortProperty;
+
+@Remote
+public interface NewsFacade extends BaseFacade, EditPermissionFacade {
+
+ void markApprove(NewsReferenceDto newsRef);
+
+ void markUnUseful(NewsReferenceDto newsRef);
+
+ Page getNewsPage(NewsCriteria newsCriteria, int offset, int size, List sortProperties);
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsIndexDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsIndexDto.java
new file mode 100644
index 00000000000..329547cec7a
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsIndexDto.java
@@ -0,0 +1,201 @@
+package de.symeda.sormas.api.news;
+
+import java.util.Date;
+
+import de.symeda.sormas.api.Disease;
+import de.symeda.sormas.api.EntityDto;
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+
+public class NewsIndexDto extends EntityDto {
+
+ public static final String I18N_PREFIX = "News";
+ public static final String NEWS_LINK = "newsLink";
+ public static final String TITLE = "title";
+ public static final String DESCRIPTION = "description";
+ public static final String REGION = "region";
+ public static final String DISTRICT = "district";
+ public static final String NEWS_DATE = "newsDate";
+ public static final String COMMUNITY = "community";
+ public static final String RISK_LEVEL = "riskLevel";
+ public static final String STATUS = "status";
+ public static final String DISEASE = "disease";
+
+ long id;
+ private Date newsDate;
+
+ private String newsLink;
+
+ private String title;
+
+ private String description;
+
+ private String categories;
+
+ private String area;
+
+ private String region;
+
+ private String district;
+
+ private String community;
+
+ private String village;
+
+ private String newsSource;
+
+ private RiskLevel riskLevel;
+
+ private NewsStatus status;
+ private Disease disease;
+
+ public NewsIndexDto() {
+ }
+
+ public NewsIndexDto(
+ Long id,
+ String uuid,
+ String title,
+ String link,
+ String description,
+ String region,
+ String district,
+ String community,
+ Date newsDate,
+ RiskLevel riskLevel,
+ NewsStatus status,
+ Disease disease) {
+ this.id = id;
+ setUuid(uuid);
+ this.title = title;
+ this.newsLink = link;
+ this.description = description;
+ this.region = region;
+ this.district = district;
+ this.community = community;
+ this.newsDate = newsDate;
+ this.riskLevel = riskLevel;
+ this.status = status;
+ this.disease = disease;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Date getNewsDate() {
+ return newsDate;
+ }
+
+ public void setNewsDate(Date newsDate) {
+ this.newsDate = newsDate;
+ }
+
+ public String getNewsLink() {
+ return newsLink;
+ }
+
+ public void setNewsLink(String newsLink) {
+ this.newsLink = newsLink;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCategories() {
+ return categories;
+ }
+
+ public void setCategories(String categories) {
+ this.categories = categories;
+ }
+
+ public String getArea() {
+ return area;
+ }
+
+ public void setArea(String area) {
+ this.area = area;
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public String getDistrict() {
+ return district;
+ }
+
+ public void setDistrict(String district) {
+ this.district = district;
+ }
+
+ public String getCommunity() {
+ return community;
+ }
+
+ public void setCommunity(String community) {
+ this.community = community;
+ }
+
+ public String getVillage() {
+ return village;
+ }
+
+ public void setVillage(String village) {
+ this.village = village;
+ }
+
+ public String getNewsSource() {
+ return newsSource;
+ }
+
+ public void setNewsSource(String newsSource) {
+ this.newsSource = newsSource;
+ }
+
+ public RiskLevel getRiskLevel() {
+ return riskLevel;
+ }
+
+ public void setRiskLevel(RiskLevel riskLevel) {
+ this.riskLevel = riskLevel;
+ }
+
+ public NewsStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(NewsStatus status) {
+ this.status = status;
+ }
+
+ public Disease getDisease() {
+ return disease;
+ }
+
+ public void setDisease(Disease disease) {
+ this.disease = disease;
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsMetaResponseDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsMetaResponseDto.java
new file mode 100644
index 00000000000..74181e00e58
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsMetaResponseDto.java
@@ -0,0 +1,74 @@
+package de.symeda.sormas.api.news;
+
+public class NewsMetaResponseDto {
+
+ String path;
+
+ int perPage;
+
+ int to;
+
+ int total;
+
+ int currentPage;
+
+ int from;
+
+ int lastPage;
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public int getPerPage() {
+ return perPage;
+ }
+
+ public void setPerPage(int perPage) {
+ this.perPage = perPage;
+ }
+
+ public int getTo() {
+ return to;
+ }
+
+ public void setTo(int to) {
+ this.to = to;
+ }
+
+ public int getTotal() {
+ return total;
+ }
+
+ public void setTotal(int total) {
+ this.total = total;
+ }
+
+ public int getCurrentPage() {
+ return currentPage;
+ }
+
+ public void setCurrentPage(int currentPage) {
+ this.currentPage = currentPage;
+ }
+
+ public int getFrom() {
+ return from;
+ }
+
+ public void setFrom(int from) {
+ this.from = from;
+ }
+
+ public int getLastPage() {
+ return lastPage;
+ }
+
+ public void setLastPage(int lastPage) {
+ this.lastPage = lastPage;
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsReferenceDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsReferenceDto.java
new file mode 100644
index 00000000000..ba1118467c7
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/NewsReferenceDto.java
@@ -0,0 +1,18 @@
+package de.symeda.sormas.api.news;
+
+import de.symeda.sormas.api.ReferenceDto;
+
+public class NewsReferenceDto extends ReferenceDto {
+
+ public NewsReferenceDto() {
+
+ }
+
+ public NewsReferenceDto(String uuid) {
+ super(uuid);
+ }
+
+ public NewsReferenceDto(String uuid, String caption) {
+ super(uuid, caption);
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/ArticleDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/ArticleDto.java
new file mode 100644
index 00000000000..06a8ee301a6
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/ArticleDto.java
@@ -0,0 +1,137 @@
+package de.symeda.sormas.api.news.eios;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import de.symeda.sormas.api.EntityDto;
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.api.infrastructure.community.CommunityReferenceDto;
+import de.symeda.sormas.api.infrastructure.district.DistrictReferenceDto;
+import de.symeda.sormas.api.infrastructure.region.RegionReferenceDto;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ArticleDto extends EntityDto {
+
+ public static final String I18N_PREFIX = "News";
+ public static final String TITLE = "title";
+ public static final String LINK = "link";
+ public static final String DESCRIPTION = "description";
+ public static final String PROCESSED_ON_DATE = "processedOnDate";
+ public static final String RISK_LEVEL = "riskLevel";
+ public static final String STATUS = "status";
+ public static final String REGION = "region";
+ public static final String DISTRICT = "district";
+ public static final String COMMUNITY = "community";
+ @JsonProperty("id")
+ private Long eiosId;
+ private String title;
+ private String eiosUrl;
+ private String link;
+ private String description;
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
+ private Date processedOnDate;
+ private RegionReferenceDto region;
+ private DistrictReferenceDto district;
+ private CommunityReferenceDto community;
+ private RiskLevel riskLevel;
+
+ private NewsStatus status;
+
+ public Long getEiosId() {
+ return eiosId;
+ }
+
+ public void setEiosId(Long eiosId) {
+ this.eiosId = eiosId;
+ }
+
+ public String getLink() {
+ return link;
+ }
+
+ public void setLink(String link) {
+ this.link = link;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getEiosUrl() {
+ return eiosUrl;
+ }
+
+ public void setEiosUrl(String eiosUrl) {
+ this.eiosUrl = eiosUrl;
+ }
+
+ public Date getProcessedOnDate() {
+ return processedOnDate;
+ }
+
+ public void setProcessedOnDate(Date processedOnDate) {
+ this.processedOnDate = processedOnDate;
+ }
+
+ public RegionReferenceDto getRegion() {
+ return region;
+ }
+
+ public void setRegion(RegionReferenceDto region) {
+ this.region = region;
+ }
+
+ public DistrictReferenceDto getDistrict() {
+ return district;
+ }
+
+ public void setDistrict(DistrictReferenceDto district) {
+ this.district = district;
+ }
+
+ public CommunityReferenceDto getCommunity() {
+ return community;
+ }
+
+ public void setCommunity(CommunityReferenceDto community) {
+ this.community = community;
+ }
+
+ public RiskLevel getRiskLevel() {
+ return riskLevel;
+ }
+
+ public void setRiskLevel(RiskLevel riskLevel) {
+ this.riskLevel = riskLevel;
+ }
+
+ public NewsStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(NewsStatus status) {
+ this.status = status;
+ }
+
+ @Override
+ public String toString() {
+ return "EiosArticleDto{" + "eiosId=" + eiosId + ", title='" + title + '\'' + ", eiosUrl='" + eiosUrl + '\'' + ", link='" + link + '\''
+ + ", description='" + description + '\'' + ", processedOnDate=" + processedOnDate + ", region=" + region + ", district=" + district
+ + ", community=" + community + ", riskLevel=" + riskLevel + ", status=" + status + '}';
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosAritcleReferenceDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosAritcleReferenceDto.java
new file mode 100644
index 00000000000..53d45224745
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosAritcleReferenceDto.java
@@ -0,0 +1,6 @@
+package de.symeda.sormas.api.news.eios;
+
+import de.symeda.sormas.api.ReferenceDto;
+
+public class EiosAritcleReferenceDto extends ReferenceDto {
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticleCriteria.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticleCriteria.java
new file mode 100644
index 00000000000..e36aa9a0508
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticleCriteria.java
@@ -0,0 +1,52 @@
+package de.symeda.sormas.api.news.eios;
+
+import de.symeda.sormas.api.utils.criteria.BaseCriteria;
+
+public class EiosArticleCriteria extends BaseCriteria {
+
+ private Long timespan;
+ private Long untilTimespan;
+ private Integer start;
+ private Integer limit;
+ private Long boardId;
+
+ public Long getTimespan() {
+ return timespan;
+ }
+
+ public void setTimespan(Long timespan) {
+ this.timespan = timespan;
+ }
+
+ public Long getUntilTimespan() {
+ return untilTimespan;
+ }
+
+ public void setUntilTimespan(Long untilTimespan) {
+ this.untilTimespan = untilTimespan;
+ }
+
+ public Integer getStart() {
+ return start;
+ }
+
+ public void setStart(Integer start) {
+ this.start = start;
+ }
+
+ public Integer getLimit() {
+ return limit;
+ }
+
+ public void setLimit(Integer limit) {
+ this.limit = limit;
+ }
+
+ public Long getBoardId() {
+ return boardId;
+ }
+
+ public void setBoardId(Long boardId) {
+ this.boardId = boardId;
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticleSourceDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticleSourceDto.java
new file mode 100644
index 00000000000..d87d8763809
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticleSourceDto.java
@@ -0,0 +1,213 @@
+package de.symeda.sormas.api.news.eios;
+
+public class EiosArticleSourceDto {
+
+ private Long sourceId;
+ private String id;
+ private String name;
+ private String language;
+ private String country;
+ private String countryIso;
+ private String subject;
+ private String url;
+ private String languageCode;
+ private Object sourceTags;
+ private String category;
+ private String region;
+ private String type;
+ private String period;
+ private Long frequency;
+ private String state;
+ private Object feeds;
+ private String lastUpdateDate;
+ private String continentCode;
+ private String whoRegionCode;
+ private String continent;
+ private String whoRegion;
+ private Object restrictionType;
+
+ public Long getSourceId() {
+ return sourceId;
+ }
+
+ public void setSourceId(Long sourceId) {
+ this.sourceId = sourceId;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public String getCountryIso() {
+ return countryIso;
+ }
+
+ public void setCountryIso(String countryIso) {
+ this.countryIso = countryIso;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getLanguageCode() {
+ return languageCode;
+ }
+
+ public void setLanguageCode(String languageCode) {
+ this.languageCode = languageCode;
+ }
+
+ public Object getSourceTags() {
+ return sourceTags;
+ }
+
+ public void setSourceTags(Object sourceTags) {
+ this.sourceTags = sourceTags;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getPeriod() {
+ return period;
+ }
+
+ public void setPeriod(String period) {
+ this.period = period;
+ }
+
+ public Long getFrequency() {
+ return frequency;
+ }
+
+ public void setFrequency(Long frequency) {
+ this.frequency = frequency;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public Object getFeeds() {
+ return feeds;
+ }
+
+ public void setFeeds(Object feeds) {
+ this.feeds = feeds;
+ }
+
+ public String getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public void setLastUpdateDate(String lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ public String getContinentCode() {
+ return continentCode;
+ }
+
+ public void setContinentCode(String continentCode) {
+ this.continentCode = continentCode;
+ }
+
+ public String getWhoRegionCode() {
+ return whoRegionCode;
+ }
+
+ public void setWhoRegionCode(String whoRegionCode) {
+ this.whoRegionCode = whoRegionCode;
+ }
+
+ public String getContinent() {
+ return continent;
+ }
+
+ public void setContinent(String continent) {
+ this.continent = continent;
+ }
+
+ public String getWhoRegion() {
+ return whoRegion;
+ }
+
+ public void setWhoRegion(String whoRegion) {
+ this.whoRegion = whoRegion;
+ }
+
+ public Object getRestrictionType() {
+ return restrictionType;
+ }
+
+ public void setRestrictionType(Object restrictionType) {
+ this.restrictionType = restrictionType;
+ }
+
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticlesResponse.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticlesResponse.java
new file mode 100644
index 00000000000..2cb5b369770
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosArticlesResponse.java
@@ -0,0 +1,40 @@
+package de.symeda.sormas.api.news.eios;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EiosArticlesResponse {
+
+ @JsonProperty("result")
+ private List eiosArticlesDto;
+ private Long count;
+ private Boolean isCountAccurate;
+
+ public List getArticles() {
+ return eiosArticlesDto;
+ }
+
+ public void setArticles(List articleDtos) {
+ this.eiosArticlesDto = articleDtos;
+ }
+
+ public Long getCount() {
+ return count;
+ }
+
+ public void setCount(Long count) {
+ this.count = count;
+ }
+
+ public Boolean getIsCountAccurate() {
+ return isCountAccurate;
+ }
+
+ public void setIsCountAccurate(Boolean isCountAccurate) {
+ this.isCountAccurate = isCountAccurate;
+ }
+
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosConfig.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosConfig.java
new file mode 100644
index 00000000000..3ed21a3a4f3
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosConfig.java
@@ -0,0 +1,53 @@
+package de.symeda.sormas.api.news.eios;
+
+import de.symeda.sormas.api.audit.AuditedClass;
+
+@AuditedClass
+public class EiosConfig {
+
+ private String eiosUrl;
+ private String oidUrl;
+ private String oidcClientId;
+ private String oidcClientSecret;
+ private String oidScope;
+
+ public String getEiosUrl() {
+ return eiosUrl;
+ }
+
+ public void setEiosUrl(String eiosUrl) {
+ this.eiosUrl = eiosUrl;
+ }
+
+ public String getOidUrl() {
+ return oidUrl;
+ }
+
+ public void setOidUrl(String oidUrl) {
+ this.oidUrl = oidUrl;
+ }
+
+ public String getOidcClientId() {
+ return oidcClientId;
+ }
+
+ public void setOidcClientId(String oidcClientId) {
+ this.oidcClientId = oidcClientId;
+ }
+
+ public String getOidcClientSecret() {
+ return oidcClientSecret;
+ }
+
+ public void setOidcClientSecret(String oidcClientSecret) {
+ this.oidcClientSecret = oidcClientSecret;
+ }
+
+ public String getOidScope() {
+ return oidScope;
+ }
+
+ public void setOidScope(String oidScope) {
+ this.oidScope = oidScope;
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosFacade.java
new file mode 100644
index 00000000000..6bcdb73bcf9
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosFacade.java
@@ -0,0 +1,8 @@
+package de.symeda.sormas.api.news.eios;
+
+import javax.ejb.Remote;
+
+@Remote
+public interface EiosFacade {
+
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosSourceReferenceDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosSourceReferenceDto.java
new file mode 100644
index 00000000000..d22dbe1876e
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/EiosSourceReferenceDto.java
@@ -0,0 +1,6 @@
+package de.symeda.sormas.api.news.eios;
+
+import de.symeda.sormas.api.ReferenceDto;
+
+public class EiosSourceReferenceDto extends ReferenceDto {
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/GeoData.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/GeoData.java
new file mode 100644
index 00000000000..a53126d968b
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/GeoData.java
@@ -0,0 +1,24 @@
+package de.symeda.sormas.api.news.eios;
+
+public class GeoData {
+
+ private Float latitude;
+ private Float longitude;
+
+ public Float getLatitude() {
+ return latitude;
+ }
+
+ public void setLatitude(Float latitude) {
+ this.latitude = latitude;
+ }
+
+ public Float getLongitude() {
+ return longitude;
+ }
+
+ public void setLongitude(Float longitude) {
+ this.longitude = longitude;
+ }
+
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/Location.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/Location.java
new file mode 100644
index 00000000000..42916ca016e
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/Location.java
@@ -0,0 +1,107 @@
+package de.symeda.sormas.api.news.eios;
+
+import java.util.List;
+
+public class Location {
+
+ private GeoData geoData;
+ private List areas;
+ private List areaFullName;
+ private String areaId;
+ private String iso2;
+ private String countryName;
+ private Long emmAreaId;
+ private String trigger;
+ private Boolean hasMaxEmmScore;
+ private String whoRegionCode;
+ private String continentCode;
+
+ public GeoData getGeoData() {
+ return geoData;
+ }
+
+ public void setGeoData(GeoData geoData) {
+ this.geoData = geoData;
+ }
+
+ public List getAreas() {
+ return areas;
+ }
+
+ public void setAreas(List areas) {
+ this.areas = areas;
+ }
+
+ public List getAreaFullName() {
+ return areaFullName;
+ }
+
+ public void setAreaFullName(List areaFullName) {
+ this.areaFullName = areaFullName;
+ }
+
+ public String getAreaId() {
+ return areaId;
+ }
+
+ public void setAreaId(String areaId) {
+ this.areaId = areaId;
+ }
+
+ public String getIso2() {
+ return iso2;
+ }
+
+ public void setIso2(String iso2) {
+ this.iso2 = iso2;
+ }
+
+ public String getCountryName() {
+ return countryName;
+ }
+
+ public void setCountryName(String countryName) {
+ this.countryName = countryName;
+ }
+
+ public Long getEmmAreaId() {
+ return emmAreaId;
+ }
+
+ public void setEmmAreaId(Long emmAreaId) {
+ this.emmAreaId = emmAreaId;
+ }
+
+ public String getTrigger() {
+ return trigger;
+ }
+
+ public void setTrigger(String trigger) {
+ this.trigger = trigger;
+ }
+
+ public Boolean getHasMaxEmmScore() {
+ return hasMaxEmmScore;
+ }
+
+ public void setHasMaxEmmScore(Boolean hasMaxEmmScore) {
+ this.hasMaxEmmScore = hasMaxEmmScore;
+ }
+
+ public String getWhoRegionCode() {
+ return whoRegionCode;
+ }
+
+ public void setWhoRegionCode(String whoRegionCode) {
+ this.whoRegionCode = whoRegionCode;
+ }
+
+ public String getContinentCode() {
+ return continentCode;
+ }
+
+ public void setContinentCode(String continentCode) {
+ this.continentCode = continentCode;
+ }
+
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/NewsStatus.java b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/NewsStatus.java
new file mode 100644
index 00000000000..aee42ba8d11
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/news/eios/NewsStatus.java
@@ -0,0 +1,15 @@
+package de.symeda.sormas.api.news.eios;
+
+import de.symeda.sormas.api.i18n.I18nProperties;
+
+public enum NewsStatus {
+
+ PENDING,
+ UNUSEFUL,
+ APPROVED;
+
+ @Override
+ public String toString() {
+ return I18nProperties.getEnumCaption(this);
+ }
+}
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/DefaultUserRole.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/DefaultUserRole.java
index b87b3dac0a7..2e869403d98 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/user/DefaultUserRole.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/DefaultUserRole.java
@@ -17,7 +17,234 @@
*******************************************************************************/
package de.symeda.sormas.api.user;
-import static de.symeda.sormas.api.user.UserRight.*;
+import static de.symeda.sormas.api.user.UserRight.ACTION_CREATE;
+import static de.symeda.sormas.api.user.UserRight.ACTION_DELETE;
+import static de.symeda.sormas.api.user.UserRight.ACTION_EDIT;
+import static de.symeda.sormas.api.user.UserRight.ADDITIONAL_TEST_CREATE;
+import static de.symeda.sormas.api.user.UserRight.ADDITIONAL_TEST_DELETE;
+import static de.symeda.sormas.api.user.UserRight.ADDITIONAL_TEST_EDIT;
+import static de.symeda.sormas.api.user.UserRight.ADDITIONAL_TEST_VIEW;
+import static de.symeda.sormas.api.user.UserRight.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_CREATE;
+import static de.symeda.sormas.api.user.UserRight.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_DELETE;
+import static de.symeda.sormas.api.user.UserRight.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_EDIT;
+import static de.symeda.sormas.api.user.UserRight.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_VIEW;
+import static de.symeda.sormas.api.user.UserRight.AGGREGATE_REPORT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.AGGREGATE_REPORT_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.AGGREGATE_REPORT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.BAG_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_DELETE;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_EDIT;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_FORM_DATA_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_FORM_DATA_DELETE;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_FORM_DATA_EDIT;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_FORM_DATA_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_FORM_DATA_VIEW;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_FORM_DATA_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_VIEW;
+import static de.symeda.sormas.api.user.UserRight.CAMPAIGN_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.CASE_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.CASE_CHANGE_DISEASE;
+import static de.symeda.sormas.api.user.UserRight.CASE_CHANGE_EPID_NUMBER;
+import static de.symeda.sormas.api.user.UserRight.CASE_CLASSIFY;
+import static de.symeda.sormas.api.user.UserRight.CASE_CLINICIAN_VIEW;
+import static de.symeda.sormas.api.user.UserRight.CASE_CREATE;
+import static de.symeda.sormas.api.user.UserRight.CASE_DELETE;
+import static de.symeda.sormas.api.user.UserRight.CASE_EDIT;
+import static de.symeda.sormas.api.user.UserRight.CASE_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.CASE_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.CASE_INVESTIGATE;
+import static de.symeda.sormas.api.user.UserRight.CASE_MERGE;
+import static de.symeda.sormas.api.user.UserRight.CASE_REFER_FROM_POE;
+import static de.symeda.sormas.api.user.UserRight.CASE_RESPONSIBLE;
+import static de.symeda.sormas.api.user.UserRight.CASE_SHARE;
+import static de.symeda.sormas.api.user.UserRight.CASE_TRANSFER;
+import static de.symeda.sormas.api.user.UserRight.CASE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.CASE_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.CLINICAL_COURSE_EDIT;
+import static de.symeda.sormas.api.user.UserRight.CLINICAL_COURSE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.CLINICAL_VISIT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.CLINICAL_VISIT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.CLINICAL_VISIT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_CONVERT;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_MERGE;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_REASSIGN_CASE;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_RESPONSIBLE;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.CONTACT_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.CUSTOMIZABLE_ENUM_MANAGEMENT;
+import static de.symeda.sormas.api.user.UserRight.DASHBOARD_ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_VIEW;
+import static de.symeda.sormas.api.user.UserRight.DASHBOARD_CAMPAIGNS_VIEW;
+import static de.symeda.sormas.api.user.UserRight.DASHBOARD_CONTACT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.DASHBOARD_CONTACT_VIEW_TRANSMISSION_CHAINS;
+import static de.symeda.sormas.api.user.UserRight.DASHBOARD_SAMPLES_VIEW;
+import static de.symeda.sormas.api.user.UserRight.DASHBOARD_SURVEILLANCE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.DATABASE_EXPORT_ACCESS;
+import static de.symeda.sormas.api.user.UserRight.DEV_MODE;
+import static de.symeda.sormas.api.user.UserRight.DOCUMENT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.DOCUMENT_TEMPLATE_MANAGEMENT;
+import static de.symeda.sormas.api.user.UserRight.DOCUMENT_UPLOAD;
+import static de.symeda.sormas.api.user.UserRight.DOCUMENT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.EDIT_NEWS;
+import static de.symeda.sormas.api.user.UserRight.EMAIL_TEMPLATE_MANAGEMENT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_PATHOGEN_TEST_CREATE;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_PATHOGEN_TEST_DELETE;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_PATHOGEN_TEST_EDIT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_CREATE;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_DELETE;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_EDIT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_EDIT_DISPATCH;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_EDIT_RECEIVAL;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_SAMPLE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.ENVIRONMENT_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.EVENTGROUP_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.EVENTGROUP_CREATE;
+import static de.symeda.sormas.api.user.UserRight.EVENTGROUP_DELETE;
+import static de.symeda.sormas.api.user.UserRight.EVENTGROUP_EDIT;
+import static de.symeda.sormas.api.user.UserRight.EVENTGROUP_LINK;
+import static de.symeda.sormas.api.user.UserRight.EVENTGROUP_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.EVENTPARTICIPANT_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.EVENTPARTICIPANT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.EVENTPARTICIPANT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.EVENTPARTICIPANT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.EVENTPARTICIPANT_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.EVENTPARTICIPANT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.EVENTPARTICIPANT_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.EVENT_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.EVENT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.EVENT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.EVENT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.EVENT_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.EVENT_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.EVENT_RESPONSIBLE;
+import static de.symeda.sormas.api.user.UserRight.EVENT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.EVENT_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.EXPORT_DATA_PROTECTION_DATA;
+import static de.symeda.sormas.api.user.UserRight.EXTERNAL_EMAIL_ATTACH_DOCUMENTS;
+import static de.symeda.sormas.api.user.UserRight.EXTERNAL_EMAIL_SEND;
+import static de.symeda.sormas.api.user.UserRight.EXTERNAL_MESSAGE_DELETE;
+import static de.symeda.sormas.api.user.UserRight.EXTERNAL_MESSAGE_PROCESS;
+import static de.symeda.sormas.api.user.UserRight.EXTERNAL_MESSAGE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.EXTERNAL_VISITS;
+import static de.symeda.sormas.api.user.UserRight.GRANT_SPECIAL_CASE_ACCESS;
+import static de.symeda.sormas.api.user.UserRight.IMMUNIZATION_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.IMMUNIZATION_CREATE;
+import static de.symeda.sormas.api.user.UserRight.IMMUNIZATION_DELETE;
+import static de.symeda.sormas.api.user.UserRight.IMMUNIZATION_EDIT;
+import static de.symeda.sormas.api.user.UserRight.IMMUNIZATION_VIEW;
+import static de.symeda.sormas.api.user.UserRight.IMMUNIZATION_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.INFRASTRUCTURE_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.INFRASTRUCTURE_CREATE;
+import static de.symeda.sormas.api.user.UserRight.INFRASTRUCTURE_EDIT;
+import static de.symeda.sormas.api.user.UserRight.INFRASTRUCTURE_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.INFRASTRUCTURE_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.INFRASTRUCTURE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.INFRASTRUCTURE_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.LINE_LISTING_CONFIGURE;
+import static de.symeda.sormas.api.user.UserRight.MANAGE_EXTERNAL_SYMPTOM_JOURNAL;
+import static de.symeda.sormas.api.user.UserRight.MANAGE_PUBLIC_EXPORT_CONFIGURATION;
+import static de.symeda.sormas.api.user.UserRight.OUTBREAK_EDIT;
+import static de.symeda.sormas.api.user.UserRight.OUTBREAK_VIEW;
+import static de.symeda.sormas.api.user.UserRight.PATHOGEN_TEST_CREATE;
+import static de.symeda.sormas.api.user.UserRight.PATHOGEN_TEST_DELETE;
+import static de.symeda.sormas.api.user.UserRight.PATHOGEN_TEST_EDIT;
+import static de.symeda.sormas.api.user.UserRight.PERFORM_BULK_OPERATIONS;
+import static de.symeda.sormas.api.user.UserRight.PERFORM_BULK_OPERATIONS_PSEUDONYM;
+import static de.symeda.sormas.api.user.UserRight.PERSON_CONTACT_DETAILS_DELETE;
+import static de.symeda.sormas.api.user.UserRight.PERSON_DELETE;
+import static de.symeda.sormas.api.user.UserRight.PERSON_EDIT;
+import static de.symeda.sormas.api.user.UserRight.PERSON_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.PERSON_MERGE;
+import static de.symeda.sormas.api.user.UserRight.PERSON_VIEW;
+import static de.symeda.sormas.api.user.UserRight.POPULATION_MANAGE;
+import static de.symeda.sormas.api.user.UserRight.PORT_HEALTH_INFO_EDIT;
+import static de.symeda.sormas.api.user.UserRight.PORT_HEALTH_INFO_VIEW;
+import static de.symeda.sormas.api.user.UserRight.PRESCRIPTION_CREATE;
+import static de.symeda.sormas.api.user.UserRight.PRESCRIPTION_DELETE;
+import static de.symeda.sormas.api.user.UserRight.PRESCRIPTION_EDIT;
+import static de.symeda.sormas.api.user.UserRight.QUARANTINE_ORDER_CREATE;
+import static de.symeda.sormas.api.user.UserRight.SAMPLE_CREATE;
+import static de.symeda.sormas.api.user.UserRight.SAMPLE_DELETE;
+import static de.symeda.sormas.api.user.UserRight.SAMPLE_EDIT;
+import static de.symeda.sormas.api.user.UserRight.SAMPLE_EDIT_NOT_OWNED;
+import static de.symeda.sormas.api.user.UserRight.SAMPLE_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.SAMPLE_TRANSFER;
+import static de.symeda.sormas.api.user.UserRight.SAMPLE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.SEE_PERSONAL_DATA_IN_JURISDICTION;
+import static de.symeda.sormas.api.user.UserRight.SEE_PERSONAL_DATA_OUTSIDE_JURISDICTION;
+import static de.symeda.sormas.api.user.UserRight.SEE_SENSITIVE_DATA_IN_JURISDICTION;
+import static de.symeda.sormas.api.user.UserRight.SEE_SENSITIVE_DATA_OUTSIDE_JURISDICTION;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_PROCESS;
+import static de.symeda.sormas.api.user.UserRight.SELF_REPORT_VIEW;
+import static de.symeda.sormas.api.user.UserRight.SEND_MANUAL_EXTERNAL_MESSAGES;
+import static de.symeda.sormas.api.user.UserRight.SORMAS_REST;
+import static de.symeda.sormas.api.user.UserRight.SORMAS_UI;
+import static de.symeda.sormas.api.user.UserRight.STATISTICS_ACCESS;
+import static de.symeda.sormas.api.user.UserRight.STATISTICS_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_CREATE;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_DELETE;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_EDIT;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_TOKEN_CREATE;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_TOKEN_DELETE;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_TOKEN_EDIT;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_TOKEN_IMPORT;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_TOKEN_VIEW;
+import static de.symeda.sormas.api.user.UserRight.SURVEY_VIEW;
+import static de.symeda.sormas.api.user.UserRight.TASK_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.TASK_ASSIGN;
+import static de.symeda.sormas.api.user.UserRight.TASK_CREATE;
+import static de.symeda.sormas.api.user.UserRight.TASK_DELETE;
+import static de.symeda.sormas.api.user.UserRight.TASK_EDIT;
+import static de.symeda.sormas.api.user.UserRight.TASK_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.TASK_VIEW;
+import static de.symeda.sormas.api.user.UserRight.TASK_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.THERAPY_VIEW;
+import static de.symeda.sormas.api.user.UserRight.TRAVEL_ENTRY_ARCHIVE;
+import static de.symeda.sormas.api.user.UserRight.TRAVEL_ENTRY_CREATE;
+import static de.symeda.sormas.api.user.UserRight.TRAVEL_ENTRY_DELETE;
+import static de.symeda.sormas.api.user.UserRight.TRAVEL_ENTRY_EDIT;
+import static de.symeda.sormas.api.user.UserRight.TRAVEL_ENTRY_MANAGEMENT_ACCESS;
+import static de.symeda.sormas.api.user.UserRight.TRAVEL_ENTRY_VIEW;
+import static de.symeda.sormas.api.user.UserRight.TRAVEL_ENTRY_VIEW_ARCHIVED;
+import static de.symeda.sormas.api.user.UserRight.TREATMENT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.TREATMENT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.TREATMENT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.USER_CREATE;
+import static de.symeda.sormas.api.user.UserRight.USER_EDIT;
+import static de.symeda.sormas.api.user.UserRight.USER_ROLE_DELETE;
+import static de.symeda.sormas.api.user.UserRight.USER_ROLE_EDIT;
+import static de.symeda.sormas.api.user.UserRight.USER_ROLE_VIEW;
+import static de.symeda.sormas.api.user.UserRight.USER_VIEW;
+import static de.symeda.sormas.api.user.UserRight.VIEW_NEWS;
+import static de.symeda.sormas.api.user.UserRight.VISIT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.VISIT_DELETE;
+import static de.symeda.sormas.api.user.UserRight.VISIT_EDIT;
+import static de.symeda.sormas.api.user.UserRight.VISIT_EXPORT;
+import static de.symeda.sormas.api.user.UserRight.WEEKLYREPORT_CREATE;
+import static de.symeda.sormas.api.user.UserRight.WEEKLYREPORT_VIEW;
import java.util.Arrays;
import java.util.Collection;
@@ -1469,7 +1696,9 @@ public Set getDefaultUserRights() {
EXTERNAL_EMAIL_SEND,
EXTERNAL_EMAIL_ATTACH_DOCUMENTS,
SORMAS_REST,
- SORMAS_UI));
+ SORMAS_UI,
+ EDIT_NEWS,
+ VIEW_NEWS));
break;
case POE_INFORMANT:
userRights.addAll(
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRight.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRight.java
index 99f2d8b3a4f..bfef3500994 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRight.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRight.java
@@ -310,7 +310,9 @@ public enum UserRight {
EMAIL_TEMPLATE_MANAGEMENT(UserRightGroup.EXTERNAL_EMAILS),
EXTERNAL_EMAIL_SEND(UserRightGroup.EXTERNAL_EMAILS),
EXTERNAL_EMAIL_ATTACH_DOCUMENTS(UserRightGroup.EXTERNAL_EMAILS, UserRight._EXTERNAL_EMAIL_SEND),
- CUSTOMIZABLE_ENUM_MANAGEMENT(UserRightGroup.CONFIGURATION);
+ CUSTOMIZABLE_ENUM_MANAGEMENT(UserRightGroup.CONFIGURATION),
+ VIEW_NEWS(UserRightGroup.NEWS),
+ EDIT_NEWS(UserRightGroup.NEWS, UserRight._VIEW_NEWS);
//@formatter:on
@@ -551,7 +553,8 @@ public enum UserRight {
public static final String _EXTERNAL_EMAIL_SEND = "EXTERNAL_EMAIL_SEND";
public static final String _EXTERNAL_EMAIL_ATTACH_DOCUMENTS = "EXTERNAL_EMAIL_ATTACH_DOCUMENTS";
public static final String _CUSTOMIZABLE_ENUM_MANAGEMENT = "CUSTOMIZABLE_ENUM_MANAGEMENT";
-
+ public static final String _VIEW_NEWS = "VIEW_NEWS";
+ public static final String _EDIT_NEWS = "EDIT_NEWS";
private static final Map> userRightDependencies = buildUserRightDependencies();
private final UserRightGroup userRightGroup;
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRightGroup.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRightGroup.java
index 9e7ba605031..8ca13ada8c9 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRightGroup.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserRightGroup.java
@@ -49,7 +49,8 @@ public enum UserRightGroup {
EXPORT,
CONFIGURATION,
- EXTERNAL;
+ EXTERNAL,
+ NEWS;
@Override
public String toString() {
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/utils/criteria/BaseCriteria.java b/sormas-api/src/main/java/de/symeda/sormas/api/utils/criteria/BaseCriteria.java
index 54d7647b5e2..80e3b7726d6 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/utils/criteria/BaseCriteria.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/utils/criteria/BaseCriteria.java
@@ -81,6 +81,8 @@ public String toUrlParams() {
stringValue = (String) value;
} else if (Integer.class.isAssignableFrom(type)) {
stringValue = String.valueOf(value);
+ } else if (Long.class.isAssignableFrom(type)) {
+ stringValue = String.valueOf(value);
} else if (EpiWeek.class.isAssignableFrom(type)) {
stringValue = ((EpiWeek) value).toUrlString();
} else if (CriteriaDateType.class.isAssignableFrom(type)) {
diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties
index e18afc5c795..c67bf7c1ef8 100644
--- a/sormas-api/src/main/resources/captions.properties
+++ b/sormas-api/src/main/resources/captions.properties
@@ -3394,6 +3394,29 @@ selfReportAllActiveAndArchivedEnvironments=All active and archived self reports
selfReportDeletedEnvironments=Deleted self reports
selfReportSelfReportsList=Self reports list
selfReportProcess=Process
+#News
+news=News
+createNew=Create New
+createEvent=Create Event
+openLinkInTab=Open Link In Tab
+updateNews=Update News
+NewsCriteria.startDate=Start Date
+NewsCriteria.endDate=End Date
+News.title=Title
+News.link=Link
+News.description=Description
+News.newsDate=News Date
+News.region=Region
+News.district=District
+News.community=Community
+News.riskLevel=Risk Level
+News.status=Status
+News.disease=Disease
+newsList=News List
+newsAction=Action
+showDescription=Show Description
+hideDescription=Hide Description
+newsUpdate=News Updated
confirmChangesField=Field:
confirmChangesValue=Will be changed to:
diff --git a/sormas-api/src/main/resources/enum.properties b/sormas-api/src/main/resources/enum.properties
index 9bfa9001b0a..8f5f9021008 100644
--- a/sormas-api/src/main/resources/enum.properties
+++ b/sormas-api/src/main/resources/enum.properties
@@ -1627,6 +1627,8 @@ UserRight.SURVEY_TOKEN_CREATE=Create survey tokens
UserRight.SURVEY_TOKEN_EDIT=Edit survey tokens
UserRight.SURVEY_TOKEN_DELETE=Delete survey tokens
UserRight.SURVEY_TOKEN_IMPORT=Import survey tokens
+UserRight.VIEW_NEWS=View news
+UserRight.EDIT_NEWS=Edit news
# UserRight descriptions
UserRight.Desc.CASE_ARCHIVE = Able to archive cases
@@ -2329,26 +2331,6 @@ WeatherCondition.CLOUDY = Cloudy
WeatherCondition.RAINING = Raining
WeatherCondition.WINDY = Windy
-#Pathogen Customizable enum
-Pathogen.CAMPYLOBACTER_JEJUNI = Campylobacter jejuni
-Pathogen.ESCHERICHIA_COLI = Escherichia coli
-Pathogen.SALMONELLA_SPP = Salmonella spp.
-Pathogen.SHIGELLA_SPP = Shigella spp.
-Pathogen.VIBRIO_CHOLERAE = Vibrio cholerae
-Pathogen.YERSINIA_SPP = Yersinia spp.
-Pathogen.SARS_COV_2 = SARS-CoV-2
-Pathogen.ADENOVIRUS = Adenovirus
-Pathogen.ASTROVIRUS = Astrovirus
-Pathogen.COXSACKIE_VIRUS = Coxsackie virus
-Pathogen.ECHOVIRUS = Echovirus
-Pathogen.HEPATITIS_A_VIRUS = Hepatitis A virus
-Pathogen.HEPATITIS_E_VIRUS = Hepatitis E virus
-Pathogen.HUMAN_CALICIVIRUS = Human calicivirus
-Pathogen.POLIO_VIRUS = Polio virus 2
-Pathogen.REOVIRUS = Reovirus
-Pathogen.ROTAVIRUS = Rotavirus
-Pathogen.TT_HEPATITIS = TT hepatitis
-Pathogen.OTHER = Other
# SelfReportType
SelfReportType.CASE=Case
SelfReportType.CONTACT=Contact
@@ -2360,6 +2342,10 @@ SelfReportInvestigationStatus.REJECTED=Rejected
# SelfReportProcessingStatus
SelfReportProcessingStatus.UNPROCESSED=Unprocessed
SelfReportProcessingStatus.PROCESSED=Processed
+# News
+NewsStatus.PENDING=Pending
+NewsStatus.UNUSEFUL=Unuseful
+NewsStatus.APPROVED=Approved
# AefiAgeGroup
AefiAgeGroup.ZERO_TO_ONE = 0 < 1 year
diff --git a/sormas-api/src/main/resources/strings.properties b/sormas-api/src/main/resources/strings.properties
index 1dbc349d737..461bd51377c 100644
--- a/sormas-api/src/main/resources/strings.properties
+++ b/sormas-api/src/main/resources/strings.properties
@@ -1901,4 +1901,10 @@ confirmationSelfReportLinkContactsByCaseReference=Some contacts without a case h
headingSelfReportCaseReportWithSameReferenceFound=Case self report with same case reference found
confirmationSelfReportCaseReportWithSameReferenceFound=There is a case self report with the same case reference number as the processed self report.It is recommended to process case reports first.Do you want to continue processing this self report?
headingSelfReportCaseWithSameReferenceNumberFound=Case with same reference number found
-confirmationSelfReportLinkContactToCaseWithSameReferenceNumber=There is a case with the same reference number as the contact foundDo you want to link this contact to that case?
\ No newline at end of file
+confirmationSelfReportLinkContactToCaseWithSameReferenceNumber=There is a case with the same reference number as the contact foundDo you want to link this contact to that case?
+newsData=News Data
+newsFilterText=title, description
+newsStartDate=Start Date
+newsEndDate=End Date
+errorEiosRequestToken=Error requesting token from EIOS
+
diff --git a/sormas-api/src/test/java/de/symeda/sormas/api/i18n/I18nConstantGenerator.java b/sormas-api/src/test/java/de/symeda/sormas/api/i18n/I18nConstantGenerator.java
index 87b29880c55..12b853732a8 100644
--- a/sormas-api/src/test/java/de/symeda/sormas/api/i18n/I18nConstantGenerator.java
+++ b/sormas-api/src/test/java/de/symeda/sormas/api/i18n/I18nConstantGenerator.java
@@ -22,7 +22,7 @@
/**
* Generates Constants out of the corresponding property files.
- *
+ *
* @see Captions
* @see Strings
* @see Validations
@@ -30,7 +30,6 @@
public class I18nConstantGenerator {
private static final String FILE_PATH_PATTERN = "src/main/java/de/symeda/sormas/api/i18n/%s.java";
-
private final String propertiesFileName;
private final String outputClassName;
private final String outputClassFilePath;
@@ -78,7 +77,7 @@ public boolean isIgnoreChildren() {
private void generateI18nConstantClass() throws IOException {
- Path path = Paths.get(outputClassFilePath);
+ Path path = Paths.get("sormas-api\\" + outputClassFilePath);
String sep = determineLineSeparator(path);
try (Writer writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
@@ -180,7 +179,7 @@ public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis();
// Check if this program is started with the module directory as working directory.
- Path path = Paths.get(FILE_PATH_PATTERN.split("/")[0]);
+ Path path = Paths.get("sormas-api\\" + FILE_PATH_PATTERN.split("\\\\")[0]);
if (!Files.exists(path)) {
throw new IOException(
String.format(
diff --git a/sormas-app/app/build.gradle b/sormas-app/app/build.gradle
index 1a8455112be..c79e9ab11b5 100644
--- a/sormas-app/app/build.gradle
+++ b/sormas-app/app/build.gradle
@@ -134,7 +134,7 @@ dependencies {
implementation 'androidx.paging:paging-runtime:3.1.1'
implementation 'androidx.work:work-runtime-ktx:2.8.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
- implementation 'me.dm7.barcodescanner:zxing:1.9.13'
+ implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'io.crowdcode.sormas.lbds:lbds-android-messaging:1.4.8'
implementation 'org.slf4j:slf4j-api:2.0.7'
// Align versions of all Kotlin components
diff --git a/sormas-app/app/src/main/AndroidManifest.xml b/sormas-app/app/src/main/AndroidManifest.xml
index 18d3de04c70..2ea3a9ed596 100644
--- a/sormas-app/app/src/main/AndroidManifest.xml
+++ b/sormas-app/app/src/main/AndroidManifest.xml
@@ -489,7 +489,14 @@
-
+
+
{
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ startActivity(browserIntent);
+ });
+ }
+
+ private void showProgressBar() {
+ binding.progressBar.setVisibility(View.VISIBLE);
+ }
+
+ private void hideProgressBar() {
+ binding.progressBar.setVisibility(View.GONE);
+ }
+
+ private class CustomWebViewClient extends WebViewClient {
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ hideProgressBar();
+ super.onPageStarted(view, url, favicon);
+ }
+ }
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/event/edit/EventNewActivity.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/event/edit/EventNewActivity.java
index bb275c23ca8..8cca1cf955e 100644
--- a/sormas-app/app/src/main/java/de/symeda/sormas/app/event/edit/EventNewActivity.java
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/event/edit/EventNewActivity.java
@@ -22,9 +22,7 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
-
import androidx.annotation.NonNull;
-
import de.symeda.sormas.api.caze.CaseReferenceDto;
import de.symeda.sormas.api.event.EventStatus;
import de.symeda.sormas.api.utils.ValidationException;
@@ -66,6 +64,10 @@ public static Bundler buildBundleWithCase(String caseUuid) {
return BaseEditActivity.buildBundle(null).setCaseUuid(caseUuid);
}
+ public static void startActivity(Context fromActivity, Bundler bundler) {
+ BaseEditActivity.startActivity(fromActivity, EventNewActivity.class, bundler);
+ }
+
@Override
protected Event queryRootEntity(String recordUuid) {
throw new UnsupportedOperationException();
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/news/News.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/News.java
new file mode 100644
index 00000000000..b345c31fb9c
--- /dev/null
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/News.java
@@ -0,0 +1,14 @@
+package de.symeda.sormas.app.news;
+
+import java.util.Date;
+
+import de.symeda.sormas.api.news.NewsIndexDto;
+import de.symeda.sormas.app.backend.common.HasLocalChangeDate;
+
+public class News extends NewsIndexDto implements HasLocalChangeDate {
+
+ @Override
+ public Date getLocalChangeDate() {
+ return getNewsDate();
+ }
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsFilterCriteria.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsFilterCriteria.java
new file mode 100644
index 00000000000..491dc480ea2
--- /dev/null
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsFilterCriteria.java
@@ -0,0 +1,85 @@
+package de.symeda.sormas.app.news;
+
+import java.util.Date;
+
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+import de.symeda.sormas.app.backend.region.Community;
+import de.symeda.sormas.app.backend.region.District;
+import de.symeda.sormas.app.backend.region.Region;
+
+public class NewsFilterCriteria {
+
+ private Region region;
+ private District district;
+ private Community community;
+ private RiskLevel riskLevel;
+ private NewsStatus status;
+ private Date startDate;
+ private Date endDate;
+ private Boolean onlyInMyJurisdiction;
+
+ public Region getRegion() {
+ return region;
+ }
+
+ public void setRegion(Region region) {
+ this.region = region;
+ }
+
+ public District getDistrict() {
+ return district;
+ }
+
+ public void setDistrict(District district) {
+ this.district = district;
+ }
+
+ public Community getCommunity() {
+ return community;
+ }
+
+ public void setCommunity(Community community) {
+ this.community = community;
+ }
+
+ public RiskLevel getRiskLevel() {
+ return riskLevel;
+ }
+
+ public void setRiskLevel(RiskLevel riskLevel) {
+ this.riskLevel = riskLevel;
+ }
+
+ public NewsStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(NewsStatus status) {
+ this.status = status;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public Boolean getOnlyInMyJurisdiction() {
+ return onlyInMyJurisdiction;
+ }
+
+ public void setOnlyInMyJurisdiction(Boolean onlyInMyJurisdiction) {
+ this.onlyInMyJurisdiction = onlyInMyJurisdiction;
+ }
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListActivity.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListActivity.java
new file mode 100644
index 00000000000..982466a8a88
--- /dev/null
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListActivity.java
@@ -0,0 +1,102 @@
+package de.symeda.sormas.app.news;
+
+import java.util.List;
+
+import android.os.Bundle;
+import android.view.View;
+import androidx.databinding.DataBindingUtil;
+import androidx.lifecycle.ViewModelProvider;
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.app.PagedBaseListActivity;
+import de.symeda.sormas.app.PagedBaseListFragment;
+import de.symeda.sormas.app.R;
+import de.symeda.sormas.app.component.Item;
+import de.symeda.sormas.app.component.menu.PageMenuItem;
+import de.symeda.sormas.app.databinding.FilterNewsListLayoutBinding;
+import de.symeda.sormas.app.util.DataUtils;
+import de.symeda.sormas.app.util.InfrastructureDaoHelper;
+import de.symeda.sormas.app.util.InfrastructureFieldsDependencyHandler;
+
+public class NewsListActivity extends PagedBaseListActivity {
+
+ NewsListViewModel viewModel;
+ FilterNewsListLayoutBinding filterBinding;
+ Boolean isFirstCreate = true;
+
+ @Override
+ public void onCreate(Bundle saveInstanceState) {
+ super.onCreate(saveInstanceState);
+ adapter = new NewsListAdapter(getContext());
+ viewModel = new ViewModelProvider(this).get(NewsListViewModel.class);
+ viewModel.setContext(this);
+ viewModel.getNewsList().observe(this, news -> {
+ adapter.submitList(news);
+ hidePreloader();
+ });
+ filterBinding.setCriteria(viewModel.getNewsFilterCriteria());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (isFirstCreate) {
+ isFirstCreate = false;
+ showPreloader();
+ }
+ }
+
+ @Override
+ protected int getActivityTitle() {
+ return R.string.heading_activityNews;
+ }
+
+ @Override
+ public void addFiltersToPageMenu() {
+ View newsListFilterView = getLayoutInflater().inflate(R.layout.filter_news_list_layout, null);
+ filterBinding = DataBindingUtil.bind(newsListFilterView);
+ pageMenu.addFilter(newsListFilterView);
+
+ runOnUiThread(() -> {
+ List- initialRegions = InfrastructureDaoHelper.loadRegionsByServerCountry();
+ InfrastructureFieldsDependencyHandler.instance.initializeRegionFields(
+ filterBinding.regionFilter,
+ initialRegions,
+ null,
+ filterBinding.districtFilter,
+ List.of(),
+ null,
+ filterBinding.communityFilter,
+ List.of(),
+ null);
+ });
+ filterBinding.priorityFilter.initializeSpinner(DataUtils.getEnumItems(RiskLevel.class));
+ filterBinding.startDateFilter.initializeDateField(getSupportFragmentManager());
+ filterBinding.endDateFilter.initializeDateField(getSupportFragmentManager());
+ filterBinding.applyFilters.setOnClickListener(e -> {
+ showPreloader();
+ pageMenu.hideAll();
+ viewModel.notifyCriteriaUpdated();
+ });
+ filterBinding.resetFilters.setOnClickListener(e -> resetFilter());
+ }
+
+ private void resetFilter() {
+ showPreloader();
+ pageMenu.hideAll();
+ viewModel.getNewsFilterCriteria().setRegion(null);
+ viewModel.getNewsFilterCriteria().setDistrict(null);
+ viewModel.getNewsFilterCriteria().setCommunity(null);
+ viewModel.getNewsFilterCriteria().setRiskLevel(null);
+ viewModel.getNewsFilterCriteria().setStartDate(null);
+ viewModel.getNewsFilterCriteria().setEndDate(null);
+ filterBinding.invalidateAll();
+ viewModel.notifyCriteriaUpdated();
+ }
+
+ @Override
+ protected PagedBaseListFragment buildListFragment(PageMenuItem menuItem) {
+ NewsListFragment newsListFragment = NewsListFragment.newInstance();
+ newsListFragment.setResetFilterCallBack(this::resetFilter);
+ return newsListFragment;
+ }
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListAdapter.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListAdapter.java
new file mode 100644
index 00000000000..bba3f98aa3b
--- /dev/null
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListAdapter.java
@@ -0,0 +1,70 @@
+package de.symeda.sormas.app.news;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import de.symeda.sormas.api.event.EventSourceType;
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.api.user.UserRight;
+import de.symeda.sormas.app.R;
+import de.symeda.sormas.app.backend.common.DatabaseHelper;
+import de.symeda.sormas.app.backend.config.ConfigProvider;
+import de.symeda.sormas.app.backend.event.Event;
+import de.symeda.sormas.app.core.adapter.databinding.BindingPagedListAdapter;
+import de.symeda.sormas.app.core.adapter.databinding.BindingViewHolder;
+import de.symeda.sormas.app.databinding.RowNewsListItemLayoutBinding;
+import de.symeda.sormas.app.event.edit.EventNewActivity;
+import de.symeda.sormas.app.util.Bundler;
+
+public class NewsListAdapter extends BindingPagedListAdapter {
+
+ private final Context context;
+
+ public NewsListAdapter(Context context) {
+ super(R.layout.row_news_list_item_layout);
+ this.context = context;
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+ super.onBindViewHolder(holder, position);
+ if (holder instanceof BindingViewHolder) {
+ BindingViewHolder pageHolder = (BindingViewHolder) holder;
+ pageHolder.setOnListItemClickListener(this.mOnListItemClickListener);
+ News data = pageHolder.getData();
+ setColorInPriorityButton(pageHolder.binding.newsDataActionPriority, data.getRiskLevel());
+ if (ConfigProvider.hasUserRight(UserRight.EVENT_CREATE)) {
+ pageHolder.binding.newsDataPriorityContainer.setOnClickListener(l -> createNewEvent(data));
+ }
+ }
+ }
+
+ private void setColorInPriorityButton(View pageHolder, RiskLevel riskLevel) {
+ if (context != null) {
+ if (riskLevel == RiskLevel.HIGH) {
+ pageHolder.setBackground(context.getDrawable(R.drawable.background_legend_high_priority));
+ } else if (riskLevel == RiskLevel.MODERATE) {
+ pageHolder.setBackground(context.getDrawable(R.drawable.background_legend_normal_priority));
+ } else if (riskLevel == RiskLevel.LOW) {
+ pageHolder.setBackground(context.getDrawable(R.drawable.background_legend_low_priority));
+ }
+ }
+ }
+
+ private void createNewEvent(News news) {
+ Bundle bundle = new Bundle();
+ Event event = DatabaseHelper.getEventDao().build();
+ event.setEventTitle(news.getTitle());
+ event.setEventDesc(news.getDescription());
+ event.setRiskLevel(news.getRiskLevel());
+ event.setStartDate(news.getCreationDate());
+ event.setSrcType(EventSourceType.MEDIA_NEWS);
+ event.setSrcMediaWebsite(news.getNewsLink());
+ event.setSrcMediaName(news.getNewsSource());
+ bundle.putSerializable(Event.I18N_PREFIX, event);
+ Bundler bundler = new Bundler(bundle);
+ EventNewActivity.startActivity(context, bundler);
+ }
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListFragment.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListFragment.java
new file mode 100644
index 00000000000..ad901aec7f9
--- /dev/null
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListFragment.java
@@ -0,0 +1,71 @@
+package de.symeda.sormas.app.news;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import de.symeda.sormas.app.CustomWebView;
+import de.symeda.sormas.app.PagedBaseListFragment;
+import de.symeda.sormas.app.R;
+import de.symeda.sormas.app.core.adapter.databinding.OnListItemClickListener;
+import de.symeda.sormas.app.util.Callback;
+
+public class NewsListFragment extends PagedBaseListFragment implements OnListItemClickListener {
+
+ private LinearLayoutManager linearLayoutManager;
+ private RecyclerView recyclerViewForList;
+ private Callback resetFilterCallBack;
+
+ public static NewsListFragment newInstance() {
+ return newInstance(NewsListFragment.class, null);
+ }
+
+ public void setResetFilterCallBack(Callback resetFilterCallBack) {
+ this.resetFilterCallBack = resetFilterCallBack;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ linearLayoutManager = new LinearLayoutManager(getActivity(), RecyclerView.VERTICAL, false);
+ recyclerViewForList = view.findViewById(R.id.recyclerViewForList);
+
+ return view;
+ }
+
+ @Override
+ public NewsListAdapter getNewListAdapter() {
+ return (NewsListAdapter) ((NewsListActivity) getActivity()).getAdapter();
+ }
+
+ @Override
+ public void onListItemClick(View view, int position, Object item) {
+ News news = (News) item;
+ CustomWebView.startActivity(getContext(), news.getNewsLink());
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ recyclerViewForList.setAdapter(getListAdapter());
+ recyclerViewForList.setLayoutManager(linearLayoutManager);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ final SwipeRefreshLayout swiperefresh = this.getView().findViewById(R.id.swiperefresh);
+ if (swiperefresh != null) {
+ swiperefresh.setOnRefreshListener(() -> {
+ swiperefresh.setRefreshing(false);
+ if (resetFilterCallBack != null) {
+ resetFilterCallBack.call();
+ }
+ });
+ }
+ }
+
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListViewModel.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListViewModel.java
new file mode 100644
index 00000000000..ad9a24fb85c
--- /dev/null
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/news/NewsListViewModel.java
@@ -0,0 +1,160 @@
+package de.symeda.sormas.app.news;
+
+import java.util.function.Consumer;
+
+import android.content.Context;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+import androidx.paging.DataSource;
+import androidx.paging.LivePagedListBuilder;
+import androidx.paging.PagedList;
+import androidx.paging.PositionalDataSource;
+import de.symeda.sormas.api.caze.CriteriaWithSorting;
+import de.symeda.sormas.api.common.Page;
+import de.symeda.sormas.api.infrastructure.community.CommunityReferenceDto;
+import de.symeda.sormas.api.infrastructure.district.DistrictReferenceDto;
+import de.symeda.sormas.api.infrastructure.region.RegionReferenceDto;
+import de.symeda.sormas.api.news.NewsCriteria;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+import de.symeda.sormas.app.core.async.AsyncTaskResult;
+import de.symeda.sormas.app.core.async.DefaultAsyncTask;
+import de.symeda.sormas.app.core.async.TaskResultHolder;
+import de.symeda.sormas.app.rest.RetroProvider;
+import retrofit2.Call;
+import retrofit2.Response;
+
+public class NewsListViewModel extends ViewModel {
+
+ private LiveData> newsList;
+ private NewsDataFactory newsDataFactory;
+ private static Context context;
+
+ private static final Integer NEWS_PAGE_SIZE = 16;
+
+ public NewsListViewModel() {
+ newsDataFactory = new NewsDataFactory();
+ newsDataFactory.setCriteria(new NewsFilterCriteria());
+ PagedList.Config config =
+ new PagedList.Config.Builder().setEnablePlaceholders(true).setInitialLoadSizeHint(NEWS_PAGE_SIZE).setPageSize(NEWS_PAGE_SIZE).build();
+ LivePagedListBuilder newsPageListBuilder = new LivePagedListBuilder(newsDataFactory, config);
+ newsList = newsPageListBuilder.build();
+ }
+
+ void notifyCriteriaUpdated() {
+ if (newsList.getValue() != null) {
+ newsList.getValue().getDataSource().invalidate();
+ if (!newsList.getValue().isEmpty()) {
+ newsList.getValue().loadAround(0);
+ }
+ }
+ }
+
+ public void setContext(Context context) {
+ NewsListViewModel.context = context;
+ }
+
+ public NewsFilterCriteria getNewsFilterCriteria() {
+ return newsDataFactory.getCriteria();
+ }
+
+ public LiveData> getNewsList() {
+ return newsList;
+ }
+
+ public static class NewsDataSource extends PositionalDataSource {
+
+ NewsFilterCriteria criteria;
+
+ public NewsDataSource(NewsFilterCriteria criteria) {
+ this.criteria = criteria;
+ }
+
+ @Override
+ public void loadInitial(@NonNull LoadInitialParams loadInitialParams, @NonNull LoadInitialCallback loadInitialCallback) {
+ loadNewsList(0, criteria, page -> loadInitialCallback.onResult(page.getElements(), 0, page.getTotalElementCount().intValue()));
+
+ }
+
+ @Override
+ public void loadRange(@NonNull LoadRangeParams loadRangeParams, @NonNull LoadRangeCallback loadRangeCallback) {
+ loadNewsList(loadRangeParams.startPosition, criteria, page -> loadRangeCallback.onResult(page.getElements()));
+ }
+
+ private void loadNewsList(int offset, NewsFilterCriteria criteria, Consumer> consumer) {
+
+ DefaultAsyncTask task = new DefaultAsyncTask(context) {
+
+ @Override
+ protected void doInBackground(TaskResultHolder resultHolder) throws Exception {
+ CriteriaWithSorting criteriaWithSorting = new CriteriaWithSorting<>();
+ criteriaWithSorting.setCaseCriteria(toNewCriteria(criteria));
+ RetroProvider.connect(context);
+ Call> pageCall = RetroProvider.getNewsFacade().pullNewsIndexList(criteriaWithSorting, offset, NEWS_PAGE_SIZE);
+ Response> response = pageCall.execute();
+ if (response.isSuccessful()) {
+ Page newsListPage = response.body();
+ Log.i("NewsDataSource", "loadNewsList: " + newsListPage);
+ consumer.accept(newsListPage);
+ } else {
+ Log.e("NewsDataSource", "loadNewsList: " + response.errorBody().string());
+ }
+ }
+
+ @Override
+ protected void onPostExecute(AsyncTaskResult taskResult) {
+ super.onPostExecute(taskResult);
+ RetroProvider.disconnect();
+ }
+
+ };
+ task.executeOnThreadPool();
+ }
+
+ private NewsCriteria toNewCriteria(NewsFilterCriteria criteria) {
+ NewsCriteria newCriteria = new NewsCriteria();
+ // Only receiving approved news
+ newCriteria.setStatus(NewsStatus.APPROVED);
+ if (criteria.getRegion() != null)
+ newCriteria.setRegion(new RegionReferenceDto(criteria.getRegion().getUuid()));
+ if (criteria.getDistrict() != null)
+ newCriteria.setDistrict(new DistrictReferenceDto(criteria.getDistrict().getUuid()));
+ if (criteria.getCommunity() != null)
+ newCriteria.setCommunity(new CommunityReferenceDto(criteria.getCommunity().getUuid()));
+ newCriteria.setRiskLevel(criteria.getRiskLevel());
+ newCriteria.setStartDate(criteria.getStartDate());
+ newCriteria.setEndDate(criteria.getEndDate());
+ return newCriteria;
+ }
+
+ }
+
+ public static class NewsDataFactory extends DataSource.Factory {
+
+ private MutableLiveData mutableLiveData;
+ private NewsFilterCriteria criteria;
+
+ public NewsDataFactory() {
+ this.mutableLiveData = new MutableLiveData<>();
+ }
+
+ @NonNull
+ @Override
+ public DataSource create() {
+ NewsDataSource dataSource = new NewsDataSource(criteria);
+ mutableLiveData.postValue(dataSource);
+ return dataSource;
+ }
+
+ public NewsFilterCriteria getCriteria() {
+ return criteria;
+ }
+
+ public void setCriteria(NewsFilterCriteria criteria) {
+ this.criteria = criteria;
+ }
+ }
+
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/NewsFacadeRetro.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/NewsFacadeRetro.java
new file mode 100644
index 00000000000..3d4fc6982c6
--- /dev/null
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/NewsFacadeRetro.java
@@ -0,0 +1,19 @@
+package de.symeda.sormas.app.rest;
+
+import de.symeda.sormas.api.caze.CriteriaWithSorting;
+import de.symeda.sormas.api.common.Page;
+import de.symeda.sormas.api.news.NewsCriteria;
+import de.symeda.sormas.app.news.News;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.POST;
+import retrofit2.http.Query;
+
+public interface NewsFacadeRetro {
+
+ @POST("news/indexList")
+ Call> pullNewsIndexList(
+ @Body CriteriaWithSorting criteriaWithSorting,
+ @Query("offset") int offset,
+ @Query("size") int size);
+}
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/RetroProvider.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/RetroProvider.java
index f1b0cec8e7d..8638c5808ac 100644
--- a/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/RetroProvider.java
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/RetroProvider.java
@@ -20,38 +20,15 @@
import java.util.Date;
import java.util.concurrent.TimeUnit;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonNull;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializer;
+import com.google.gson.*;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.util.Log;
-
import androidx.fragment.app.FragmentActivity;
-
-import de.symeda.sormas.api.caze.classification.ClassificationAllOfCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationAllSymptomsCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationAnyOfSymptomsCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationCaseCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationEpiDataCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationEventClusterCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationExposureCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationNoneOfCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationPathogenTestCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationPathogenTestNegativeResultCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationPathogenTestOtherPositiveResultCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationPathogenTestPositiveResultCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationPersonAgeBetweenYearsCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationSymptomsCriteriaDto;
-import de.symeda.sormas.api.caze.classification.ClassificationVaccinationDateNotInStartDateRangeDto;
-import de.symeda.sormas.api.caze.classification.ClassificationXOfCriteriaDto;
+import de.symeda.sormas.api.caze.classification.*;
import de.symeda.sormas.api.environment.WaterUse;
import de.symeda.sormas.api.environment.environmentsample.WeatherCondition;
import de.symeda.sormas.api.utils.CompatibilityCheckResponse;
@@ -70,11 +47,7 @@
import de.symeda.sormas.app.util.BiConsumer;
import de.symeda.sormas.app.util.Consumer;
import de.symeda.sormas.app.util.EnumMapKeySerializer;
-import okhttp3.Credentials;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.ResponseBody;
+import okhttp3.*;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
@@ -132,7 +105,7 @@ public final class RetroProvider {
private AggregateReportFacadeRetro aggregateReportFacadeRetro;
private EnvironmentFacadeRetro environmentFacadeRetro;
private EnvironmentSampleFacadeRetro environmentSampleFacadeRetro;
-
+ private NewsFacadeRetro newsFacadeRetro;
private RetroProvider(Context context) throws ServerConnectionException, ServerCommunicationException, ApiVersionException {
lastConnectionId = this.hashCode();
@@ -1013,6 +986,19 @@ public static EnvironmentSampleFacadeRetro getEnvironmentSampleFacade() throws N
return instance.environmentSampleFacadeRetro;
}
+ public static NewsFacadeRetro getNewsFacade() throws NoConnectionException {
+ if (instance == null)
+ throw new NoConnectionException();
+ if (instance.newsFacadeRetro == null) {
+ synchronized ((RetroProvider.class)) {
+ if (instance.newsFacadeRetro == null) {
+ instance.newsFacadeRetro = instance.retrofit.create(NewsFacadeRetro.class);
+ }
+ }
+ }
+ return instance.newsFacadeRetro;
+ }
+
public static void throwException(Response> response) throws ServerConnectionException, ServerCommunicationException {
if (ServerConnectionException.RelatedErrorCodes.contains(response.code())) {
diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/util/NavigationHelper.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/util/NavigationHelper.java
index 79cb6ba94ec..b01ffb74841 100644
--- a/sormas-app/app/src/main/java/de/symeda/sormas/app/util/NavigationHelper.java
+++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/util/NavigationHelper.java
@@ -17,11 +17,9 @@
import android.content.Context;
import android.content.Intent;
-
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NavUtils;
import androidx.core.app.TaskStackBuilder;
-
import de.symeda.sormas.api.task.TaskStatus;
import de.symeda.sormas.app.campaign.list.CampaignFormDataListActivity;
import de.symeda.sormas.app.caze.edit.CaseNewActivity;
@@ -32,6 +30,7 @@
import de.symeda.sormas.app.environmentsample.list.EnvironmentSampleListActivity;
import de.symeda.sormas.app.event.list.EventListActivity;
import de.symeda.sormas.app.immunization.list.ImmunizationListActivity;
+import de.symeda.sormas.app.news.NewsListActivity;
import de.symeda.sormas.app.report.ReportActivity;
import de.symeda.sormas.app.report.aggregate.AggregateReportsActivity;
import de.symeda.sormas.app.sample.list.SampleListActivity;
@@ -100,6 +99,11 @@ public static void goToReports(Context context) {
context.startActivity(intent);
}
+ public static void goToNews(Context context) {
+ Intent intent = new Intent(context, NewsListActivity.class);
+ context.startActivity(intent);
+ }
+
public static void goToSettings(Context context) {
Intent intent = new Intent(context, SettingsActivity.class);
context.startActivity(intent);
diff --git a/sormas-app/app/src/main/res/layout/activity_custom_web_view.xml b/sormas-app/app/src/main/res/layout/activity_custom_web_view.xml
new file mode 100644
index 00000000000..e2942be228f
--- /dev/null
+++ b/sormas-app/app/src/main/res/layout/activity_custom_web_view.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sormas-app/app/src/main/res/layout/activity_news_list.xml b/sormas-app/app/src/main/res/layout/activity_news_list.xml
new file mode 100644
index 00000000000..57efd279fa2
--- /dev/null
+++ b/sormas-app/app/src/main/res/layout/activity_news_list.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sormas-app/app/src/main/res/layout/filter_news_list_layout.xml b/sormas-app/app/src/main/res/layout/filter_news_list_layout.xml
new file mode 100644
index 00000000000..430dec669a6
--- /dev/null
+++ b/sormas-app/app/src/main/res/layout/filter_news_list_layout.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sormas-app/app/src/main/res/layout/row_news_list_item_layout.xml b/sormas-app/app/src/main/res/layout/row_news_list_item_layout.xml
new file mode 100644
index 00000000000..4f149e6f2f5
--- /dev/null
+++ b/sormas-app/app/src/main/res/layout/row_news_list_item_layout.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sormas-app/app/src/main/res/menu/drawer_main_menu.xml b/sormas-app/app/src/main/res/menu/drawer_main_menu.xml
index 712b36382f7..492a52e0f51 100644
--- a/sormas-app/app/src/main/res/menu/drawer_main_menu.xml
+++ b/sormas-app/app/src/main/res/menu/drawer_main_menu.xml
@@ -115,6 +115,11 @@
android:icon="@drawable/ic_drawer_assessment_blue_36dp"
android:orderInCategory="12"
/>
-
+
\ No newline at end of file
diff --git a/sormas-app/app/src/main/res/values/strings.xml b/sormas-app/app/src/main/res/values/strings.xml
index 0c33c478b52..c9fb121b035 100644
--- a/sormas-app/app/src/main/res/values/strings.xml
+++ b/sormas-app/app/src/main/res/values/strings.xml
@@ -548,6 +548,7 @@
Settings
Tasks
Aggregate
+ News
Already saving..
Attention: Duplicate reports have been found for the above criteria. Diseases marked with red already have reports.
@@ -722,5 +723,5 @@
Environment Name N/A
Use rapid event participant entry
Environment Media N/A
-
+ News
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/ConfigFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/ConfigFacadeEjb.java
index 3cbe5f489df..9fb3715cda8 100644
--- a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/ConfigFacadeEjb.java
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/ConfigFacadeEjb.java
@@ -47,6 +47,7 @@
import de.symeda.sormas.api.externaljournal.UserConfig;
import de.symeda.sormas.api.geo.GeoLatLon;
import de.symeda.sormas.api.i18n.I18nProperties;
+import de.symeda.sormas.api.news.eios.EiosConfig;
import de.symeda.sormas.api.person.PersonHelper;
import de.symeda.sormas.api.sormastosormas.SormasToSormasConfig;
import de.symeda.sormas.api.utils.CompatibilityCheckResponse;
@@ -195,6 +196,13 @@ public class ConfigFacadeEjb implements ConfigFacade {
public static final String MINIMUM_ADULT_AGE = "minimumAdultAge";
public static final int DEFAULT_MINIMUM_ADULT_AGE = 18;
+ public static final int DEFAULT_IMPOR_FILE_SIZE_LIMIT_MB = 20;
+ public static final String EIOS_URL = "eios.url";
+ public static final String EIOS_ODI_URL = "eios.odi.url";
+ public static final String EIOS_CLIENT_ID = "eios.clientId";
+ public static final String EIOS_CLIENT_SECRET = "eios.clientSecret";
+ public static final String EIOS_SCOPE = "eios.scope";
+ public static final String EIOS_BOARD_IDS = "eios.boardIds";
private final Logger logger = LoggerFactory.getLogger(getClass());
@Resource(lookup = "sormas/Properties")
@@ -835,6 +843,22 @@ public long getImportFileSizeLimitMb() {
return getLong(IMPORT_FILE_SIZE_LIMIT_MB, DEFAULT_IMPORT_FILE_SIZE_LIMIT_MB);
}
+ @Override
+ public EiosConfig getEIOSConfig() {
+ EiosConfig config = new EiosConfig();
+ config.setEiosUrl(getProperty(EIOS_URL, ""));
+ config.setOidUrl(getProperty(EIOS_ODI_URL, ""));
+ config.setOidcClientId(getProperty(EIOS_CLIENT_ID, ""));
+ config.setOidcClientSecret(getProperty(EIOS_CLIENT_SECRET, ""));
+ config.setOidScope(getProperty(EIOS_SCOPE, ""));
+ return config;
+ }
+
+ @Override
+ public String getEiosBoardIds() {
+ return getProperty(EIOS_BOARD_IDS, "");
+ }
+
@Override
public void setRequestContext(RequestContextTO requestContext) {
RequestContextHolder.setRequestContext(requestContext);
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/CronService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/CronService.java
index dd443441e71..a6d02bfe0fb 100644
--- a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/CronService.java
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/CronService.java
@@ -47,6 +47,7 @@
import de.symeda.sormas.backend.feature.FeatureConfigurationFacadeEjb.FeatureConfigurationFacadeEjbLocal;
import de.symeda.sormas.backend.immunization.ImmunizationFacadeEjb;
import de.symeda.sormas.backend.infrastructure.central.CentralInfraSyncFacade;
+import de.symeda.sormas.backend.news.EiosFacadeEjb;
import de.symeda.sormas.backend.report.WeeklyReportFacadeEjb.WeeklyReportFacadeEjbLocal;
import de.symeda.sormas.backend.sample.SampleService;
import de.symeda.sormas.backend.specialcaseaccess.SpecialCaseAccessFacadeEjb.SpecialCaseAccessFacadeEjbLocal;
@@ -100,6 +101,8 @@ public class CronService {
@EJB
private SampleService sampleService;
+ @EJB
+ private EiosFacadeEjb.EiosFacadeEjbLocal eiosFacade;
@Schedule(hour = "*", minute = "*/" + TASK_UPDATE_INTERVAL, second = "0", persistent = false)
public void sendNewAndDueTaskMessages() {
taskFacade.sendNewAndDueTaskMessages();
@@ -305,4 +308,11 @@ public void syncUsersFromAuthenticationProvider() {
public void sofDeleteOldNegativeSamples() {
sampleService.cleanupOldCovidSamples();
}
+
+ @Schedule(hour = "*/2", persistent = false)
+ public void fetchAndSaveBoardArticlesCron() {
+ if (featureConfigurationFacade.isFeatureEnabled(FeatureType.NEWS_FEATURE)) {
+ eiosFacade.fetchAndSaveBoardArticles();
+ }
+ }
}
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java
index b051759007e..ee62cdd3bb8 100644
--- a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java
@@ -71,6 +71,7 @@
import de.symeda.sormas.api.externaljournal.SymptomJournalConfig;
import de.symeda.sormas.api.externaljournal.UserConfig;
import de.symeda.sormas.api.feature.FeatureConfigurationDto;
+import de.symeda.sormas.api.feature.FeatureType;
import de.symeda.sormas.api.i18n.I18nProperties;
import de.symeda.sormas.api.infrastructure.country.CountryReferenceDto;
import de.symeda.sormas.api.infrastructure.facility.FacilityCriteria;
@@ -111,6 +112,7 @@
import de.symeda.sormas.backend.infrastructure.pointofentry.PointOfEntryService;
import de.symeda.sormas.backend.infrastructure.region.Region;
import de.symeda.sormas.backend.infrastructure.region.RegionService;
+import de.symeda.sormas.backend.news.EiosBoardConfigService;
import de.symeda.sormas.backend.sormastosormas.SormasToSormasFacadeEjb;
import de.symeda.sormas.backend.user.User;
import de.symeda.sormas.backend.user.UserRole;
@@ -193,6 +195,8 @@ public class StartupShutdownService {
private CustomizableEnumValueService customizableEnumValueService;
@EJB
private DocumentTemplateService documentTemplateService;
+ @EJB
+ private EiosBoardConfigService boardConfigService;
@Inject
private Event passwordResetEvent;
@@ -256,6 +260,7 @@ public void startup() {
configFacade.validateConfigUrls();
centralInfraSync.syncAll();
+ manageEiosConfig();
}
private void createDefaultInfrastructureData() {
@@ -1044,6 +1049,13 @@ private void createEntitiesForDocumentTemplates() {
});
}
+ private void manageEiosConfig() {
+ if (featureConfigurationFacade.isFeatureEnabled(FeatureType.NEWS_FEATURE)) {
+ String boardIds = configFacade.getEiosBoardIds();
+ boardConfigService.manageEiosConfigAtStartUp(boardIds);
+ }
+ }
+
@PreDestroy
public void shutdown() {
auditLogger.logApplicationStop();
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EIOSRestClient.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EIOSRestClient.java
new file mode 100644
index 00000000000..cdef753d134
--- /dev/null
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EIOSRestClient.java
@@ -0,0 +1,156 @@
+package de.symeda.sormas.backend.news;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.util.Collections;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.ResponseProcessingException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+
+import de.symeda.sormas.api.i18n.Strings;
+import de.symeda.sormas.api.news.eios.EiosConfig;
+import de.symeda.sormas.backend.sormastosormas.rest.auth.Oidc;
+import de.symeda.sormas.backend.util.ClientHelper;
+
+public class EIOSRestClient {
+
+ private final String eiosRestUrlTemplate;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EIOSRestClient.class);
+ private final ObjectMapper mapper;
+ private final EiosConfig eiosConfig;
+
+ public EIOSRestClient(EiosConfig eiosConfig) {
+ this.eiosConfig = eiosConfig;
+ this.eiosRestUrlTemplate = eiosConfig.getEiosUrl() + "/%s";
+ mapper = new ObjectMapper();
+ mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
+ mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ }
+
+ public T post(String endpoint, Object body, Class responseType) throws Exception {
+ return sendRequest(endpoint, body, responseType, HttpMethod.POST);
+
+ }
+
+ public T put(String endpoint, Object body, Class responseType) throws Exception {
+ return sendRequest(endpoint, body, responseType, HttpMethod.PUT);
+ }
+
+ public T get(String endpoint, Class responseType) throws Exception {
+ return sendRequest(endpoint, null, responseType, HttpMethod.GET);
+ }
+
+ private String buildAuthToken() throws Exception {
+ String authToken;
+ try {
+ authToken = Oidc.requestAccessToken(
+ eiosConfig.getOidUrl(),
+ eiosConfig.getOidcClientId(),
+ eiosConfig.getOidcClientSecret(),
+ Collections.singletonList(eiosConfig.getOidScope()));
+ } catch (Exception e) {
+ LOGGER.info("Could not requested access token {}", e);
+ throw new Exception(Strings.errorEiosRequestToken);
+ }
+ LOGGER.info("Successfully requested access token");
+ return String.format("Bearer %s", authToken);
+ }
+
+ private Invocation.Builder buildRestClient(String endpoint) throws Exception {
+
+ String host = "";
+ String authToken = buildAuthToken();
+
+ return ClientHelper.newBuilderWithProxy()
+ .register(JacksonJsonProvider.class)
+ .build()
+ .target(String.format(eiosRestUrlTemplate, endpoint))
+ .request()
+ .header("Authorization", authToken);
+ }
+
+ private T sendRequest(String endpoint, Object body, Class responseType, String method) throws Exception {
+ try {
+ Entity entity = null;
+ if (body != null) {
+ entity = Entity.entity(mapper.writeValueAsString(body), MediaType.APPLICATION_JSON_TYPE);
+ } else {
+ // no sender org id in the encrypted DTP, therefore, we pass it as query parameter
+
+ // safely append the parameter
+ endpoint = UriBuilder.fromUri(endpoint).build().toString();
+ }
+
+ Invocation.Builder invocation = buildRestClient(endpoint);
+
+ Response response;
+ switch (method) {
+ case HttpMethod.POST:
+ response = invocation.post(entity);
+ break;
+ case HttpMethod.PUT:
+ response = invocation.put(entity);
+ break;
+ case HttpMethod.GET:
+ response = invocation.get();
+ break;
+ default:
+ throw new Exception(Strings.errorSormasToSormasInvalidRequestMethod);
+ }
+ return handleResponse(response, responseType);
+ } catch (JsonProcessingException e) {
+ LOGGER.error("Unable to send data sormas", e);
+ throw e;
+ } catch (ResponseProcessingException e) {
+ LOGGER.error("Unable to process sormas response", e);
+ throw e;
+ } catch (ProcessingException e) {
+ LOGGER.error("Unable to send data to sormas", e);
+
+ String processingErrorStringProperty = Strings.errorSormasToSormasSend;
+ if (ConnectException.class.isAssignableFrom(e.getCause().getClass())) {
+ processingErrorStringProperty = Strings.errorSormasToSormasConnection;
+ }
+ throw e;
+ }
+ }
+
+ private T handleResponse(Response response, Class responseType) {
+ int statusCode = response.getStatus();
+ if (statusCode != HttpStatus.SC_NO_CONTENT && statusCode != HttpStatus.SC_OK) {
+ String errorMessage = response.readEntity(String.class);
+ try {
+ T responseObject = mapper.readValue(errorMessage, responseType);
+ return responseObject;
+ } catch (IOException e) {
+ // do nothing, keep the unparsed response as error message
+ }
+
+ if (statusCode != HttpStatus.SC_BAD_REQUEST) {
+ // don't log validation errors, will be displayed on the UI
+ LOGGER.error("Sending request failed: {}; {}", statusCode, errorMessage);
+ }
+ }
+ return responseType != null ? response.readEntity(responseType) : null;
+
+ }
+}
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosBoardConfig.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosBoardConfig.java
new file mode 100644
index 00000000000..df288dbf381
--- /dev/null
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosBoardConfig.java
@@ -0,0 +1,38 @@
+package de.symeda.sormas.backend.news;
+
+import javax.persistence.Entity;
+
+import de.symeda.sormas.backend.common.AbstractDomainObject;
+
+@Entity
+public class EiosBoardConfig extends AbstractDomainObject {
+
+ public static final String ENABLE = "enabled";
+ private Long boardId;
+ private Long startTimeStamp;
+ private Boolean enabled;
+
+ public Long getBoardId() {
+ return boardId;
+ }
+
+ public void setBoardId(Long boardId) {
+ this.boardId = boardId;
+ }
+
+ public Long getStartTimeStamp() {
+ return startTimeStamp;
+ }
+
+ public void setStartTimeStamp(Long startTimeStamp) {
+ this.startTimeStamp = startTimeStamp;
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosBoardConfigService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosBoardConfigService.java
new file mode 100644
index 00000000000..7fe0a66339a
--- /dev/null
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosBoardConfigService.java
@@ -0,0 +1,69 @@
+package de.symeda.sormas.backend.news;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.ejb.LocalBean;
+import javax.ejb.Stateless;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+
+import org.jetbrains.annotations.NotNull;
+
+import de.symeda.sormas.api.EntityDto;
+import de.symeda.sormas.backend.common.BaseAdoService;
+import de.symeda.sormas.backend.util.DtoHelper;
+
+@Stateless
+@LocalBean
+public class EiosBoardConfigService extends BaseAdoService {
+
+ public EiosBoardConfigService() {
+ super(EiosBoardConfig.class);
+ }
+
+ public void manageEiosConfigAtStartUp(String boardIds) {
+ List boardIdList =
+ Arrays.stream(boardIds.split(",")).filter(s -> !s.isEmpty()).map(String::trim).map(Long::parseLong).collect(Collectors.toList());
+ List currentConfigs = getAll();
+ enableBoard(boardIdList, currentConfigs);
+ disableBoard(boardIdList, currentConfigs);
+ }
+
+ public List getEnabledBoards() {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery cq = cb.createQuery(EiosBoardConfig.class);
+ Root boardConfigRoot = cq.from(EiosBoardConfig.class);
+ CriteriaQuery enabledBoard = cq.select(boardConfigRoot).where(cb.isTrue(boardConfigRoot.get(EiosBoardConfig.ENABLE)));
+ return em.createQuery(enabledBoard).getResultList();
+ }
+
+ private void disableBoard(List boardIdList, List currentConfigs) {
+ currentConfigs.stream().filter(c -> !boardIdList.contains(c.getBoardId())).forEach(c -> {
+ c.setEnabled(false);
+ ensurePersisted(c);
+ });
+ }
+
+ private void enableBoard(List boardIdList, List currentConfigs) {
+ for (Long boardId : boardIdList) {
+ EiosBoardConfig config =
+ currentConfigs.stream().filter(c -> c.getBoardId().equals(boardId)).findFirst().orElseGet(() -> createNewBoard(boardId));
+ config.setEnabled(true);
+ ensurePersisted(config);
+ }
+ }
+
+ @NotNull
+ private EiosBoardConfig createNewBoard(Long boardId) {
+ EiosBoardConfig newConfig = DtoHelper.fillOrBuildEntity(new EntityDto() {
+ }, new EiosBoardConfig(), EiosBoardConfig::new, false);
+ newConfig.setBoardId(boardId);
+ newConfig.setStartTimeStamp(new Date().getTime());
+ return newConfig;
+ }
+
+}
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosFacadeEjb.java
new file mode 100644
index 00000000000..bf2861316b3
--- /dev/null
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/EiosFacadeEjb.java
@@ -0,0 +1,152 @@
+package de.symeda.sormas.backend.news;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.ejb.EJB;
+import javax.ejb.LocalBean;
+import javax.ejb.Stateless;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import de.symeda.sormas.api.news.NewsDto;
+import de.symeda.sormas.api.news.eios.ArticleDto;
+import de.symeda.sormas.api.news.eios.EiosArticleCriteria;
+import de.symeda.sormas.api.news.eios.EiosArticlesResponse;
+import de.symeda.sormas.api.news.eios.EiosConfig;
+import de.symeda.sormas.api.news.eios.EiosFacade;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+import de.symeda.sormas.api.utils.DateHelper;
+import de.symeda.sormas.backend.common.ConfigFacadeEjb;
+import de.symeda.sormas.backend.util.DtoHelper;
+import de.symeda.sormas.backend.util.ModelConstants;
+
+@Stateless(name = "EiosFacade")
+public class EiosFacadeEjb implements EiosFacade {
+
+ private static final Integer MAX_FETCH_SIZE = 40;
+ private final Logger LOGGER = LoggerFactory.getLogger(EiosFacadeEjb.class);
+ @PersistenceContext(unitName = ModelConstants.PERSISTENCE_UNIT_NAME)
+ protected EntityManager em;
+ @EJB
+ private NewsService newsService;
+
+ @EJB
+ private ConfigFacadeEjb.ConfigFacadeEjbLocal configFacade;
+
+ @EJB
+ private EiosBoardConfigService eiosBoardConfigService;
+
+ public void save(@Valid @NotNull NewsDto newsDto) {
+ try {
+ News news = newsService.getByUuid(newsDto.getUuid());
+ news = fillOrBuildEntity(newsDto, news, true);
+ newsService.ensurePersisted(news);
+ News article = newsService.getByUuid(news.getUuid());
+ } catch (Exception exe) {
+ LOGGER.error("Error while saving {}", newsDto, exe);
+ }
+ }
+
+ public void saveArticle(@Valid @NotNull ArticleDto articleDto) {
+ try {
+ News existingNews = newsService.getByEiosId(articleDto.getEiosId());
+ existingNews = fillOrBuildEntity(articleDto, existingNews, false);
+ newsService.ensurePersisted(existingNews);
+ News article = newsService.getByEiosId(existingNews.getEiosId());
+ } catch (Exception exe) {
+ LOGGER.error("Error while saving {}", articleDto, exe);
+ }
+ }
+
+ public void fetchAndSaveBoardArticles() {
+ LOGGER.info("Started fetching article at: {}", new Date());
+ long currentTimespan = DateHelper.now();
+
+ EiosConfig eiosConfig = configFacade.getEIOSConfig();
+ List enabledBoards = eiosBoardConfigService.getEnabledBoards();
+ enabledBoards.forEach(eiosBoardConfig -> {
+ try {
+ EiosArticleCriteria criteria = createCriteriaFromBoardConfig(eiosBoardConfig);
+ criteria.setUntilTimespan(currentTimespan);
+ batchFetchBatchArticleAndSave(criteria, eiosConfig);
+ eiosBoardConfig.setStartTimeStamp(currentTimespan);
+ eiosBoardConfigService.ensurePersisted(eiosBoardConfig);
+ } catch (Exception e) {
+ LOGGER.error("Error while fetching and saving board articles", e);
+ }
+ });
+ }
+
+ private void batchFetchBatchArticleAndSave(EiosArticleCriteria criteria, EiosConfig eiosConfig) throws Exception {
+ long total = Integer.MAX_VALUE;
+ for (int offset = 0; offset < total; offset += MAX_FETCH_SIZE) {
+ criteria.setStart(offset);
+ criteria.setLimit(MAX_FETCH_SIZE);
+ EiosArticlesResponse eiosArticlesResponse = fetchEiosArticle(criteria, eiosConfig);
+ total = eiosArticlesResponse.getCount();
+ List newsDtos = eiosArticlesResponse.getArticles();
+ newsDtos.forEach(eiosArticleDto -> {
+ eiosArticleDto.setStatus(NewsStatus.PENDING);
+ saveArticle(eiosArticleDto);
+ });
+ }
+ }
+
+ public EiosArticlesResponse fetchEiosArticle(EiosArticleCriteria criteria, EiosConfig eiosConfig) throws Exception {
+ final String boardArticlePath = "GetBoardArticles";
+ final String url = boardArticlePath + "?" + criteria.toUrlParams();
+ EIOSRestClient eiosRestClient = new EIOSRestClient(eiosConfig);
+ EiosArticlesResponse eiosArticlesResponse = eiosRestClient.get(url, EiosArticlesResponse.class);
+ return eiosArticlesResponse;
+ }
+
+ private EiosArticleCriteria createCriteriaFromBoardConfig(EiosBoardConfig eiosBoardConfig) {
+ EiosArticleCriteria criteria = new EiosArticleCriteria();
+ criteria.setBoardId(eiosBoardConfig.getBoardId());
+ criteria.setTimespan(eiosBoardConfig.getStartTimeStamp());
+ return criteria;
+ }
+
+ public News fillOrBuildEntity(NewsDto source, News target, boolean checkChangeDate) {
+ if (source == null) {
+ return null;
+ }
+ target = DtoHelper.fillOrBuildEntity(source, target, News::new, checkChangeDate);
+ target.setTitle(source.getTitle());
+ target.setUrl(source.getLink());
+ target.setDescription(source.getDescription());
+ target.setEiosUrl(source.getEiosUrl());
+ target.setNewsDate(source.getNewsDate());
+ target.setRiskLevel(source.getRiskLevel());
+ target.setStatus(source.getStatus());
+ target.setEiosId(source.getEiosId());
+ return target;
+ }
+
+ public News fillOrBuildEntity(ArticleDto source, News target, boolean checkChangeDate) {
+ if (source == null) {
+ return null;
+ }
+ target = DtoHelper.fillOrBuildEntity(source, target, News::build, checkChangeDate);
+ target.setTitle(source.getTitle());
+ target.setUrl(source.getLink());
+ target.setDescription(source.getDescription());
+ target.setEiosUrl(source.getEiosUrl());
+ target.setNewsDate(source.getProcessedOnDate());
+ target.setRiskLevel(source.getRiskLevel());
+ target.setStatus(source.getStatus());
+ target.setEiosId(source.getEiosId());
+ return target;
+ }
+
+ @Stateless
+ @LocalBean
+ public static class EiosFacadeEjbLocal extends EiosFacadeEjb {
+ }
+}
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/news/News.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/News.java
new file mode 100644
index 00000000000..f2e15257ea4
--- /dev/null
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/News.java
@@ -0,0 +1,186 @@
+package de.symeda.sormas.backend.news;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.ManyToOne;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import de.symeda.sormas.api.Disease;
+import de.symeda.sormas.api.event.RiskLevel;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+import de.symeda.sormas.api.utils.FieldConstraints;
+import de.symeda.sormas.backend.common.AbstractDomainObject;
+import de.symeda.sormas.backend.infrastructure.community.Community;
+import de.symeda.sormas.backend.infrastructure.district.District;
+import de.symeda.sormas.backend.infrastructure.region.Region;
+
+@Entity(name = "news")
+public class News extends AbstractDomainObject {
+
+ public static final String TABLE_NAME = "News";
+ public static final String EIOS_ID = "eiosId";
+ public static final String TITLE = "title";
+ public static final String DESCRIPTION = "description";
+ public static final String URL = "url";
+ public static final String REGION = "region";
+ public static final String DISTRICT = "district";
+ public static final String COMMUNITY = "community";
+ public static final String NEWS_DATE = "newsDate";
+ public static final String RISK_LEVEL = "riskLevel";
+ public static final String STATUS = "status";
+ public static final String DISEASE = "disease";
+
+ private Long eiosId;
+ private String comments;
+ private String title;
+ private String url;
+ private String description;
+ private String eiosUrl;
+ private Date newsDate;
+ private Boolean isContentRestricted;
+ private Region region;
+ private District district;
+ private Community community;
+ private RiskLevel riskLevel;
+ private NewsStatus status;
+ private Disease disease;
+
+ public static News build() {
+ News news = new News();
+ news.setCreationDate(Timestamp.from(Instant.now()));
+ return news;
+ }
+
+ public Long getEiosId() {
+ return eiosId;
+ }
+
+ public void setEiosId(Long eiosId) {
+ this.eiosId = eiosId;
+ }
+
+ public String getComments() {
+ return comments;
+ }
+
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @Column(length = FieldConstraints.CHARACTER_LIMIT_DEFAULT)
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @Column(length = FieldConstraints.CHARACTER_LIMIT_BIG)
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @Column(length = FieldConstraints.CHARACTER_LIMIT_TEXT)
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Column(length = FieldConstraints.CHARACTER_LIMIT_DEFAULT)
+ public String getEiosUrl() {
+ return eiosUrl;
+ }
+
+ public void setEiosUrl(String eiosUrl) {
+ this.eiosUrl = eiosUrl;
+ }
+
+ @Temporal(TemporalType.TIMESTAMP)
+ public Date getNewsDate() {
+ return newsDate;
+ }
+
+ public void setNewsDate(Date processedOnDate) {
+ this.newsDate = processedOnDate;
+ }
+
+ public Boolean getContentRestricted() {
+ return isContentRestricted;
+ }
+
+ public void setContentRestricted(Boolean contentRestricted) {
+ isContentRestricted = contentRestricted;
+ }
+
+ @Enumerated(EnumType.STRING)
+ public NewsStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(NewsStatus status) {
+ this.status = status;
+ }
+
+ @ManyToOne
+ public Region getRegion() {
+ return region;
+ }
+
+ public void setRegion(Region region) {
+ this.region = region;
+ }
+
+ @ManyToOne
+
+ public District getDistrict() {
+ return district;
+ }
+
+ public void setDistrict(District district) {
+ this.district = district;
+ }
+
+ @ManyToOne
+
+ public Community getCommunity() {
+ return community;
+ }
+
+ public void setCommunity(Community community) {
+ this.community = community;
+ }
+
+ @Enumerated(EnumType.STRING)
+ public RiskLevel getRiskLevel() {
+ return riskLevel;
+ }
+
+ public void setRiskLevel(RiskLevel riskLevel) {
+ this.riskLevel = riskLevel;
+ }
+
+ @Enumerated(EnumType.STRING)
+ public Disease getDisease() {
+ return disease;
+ }
+
+ public void setDisease(Disease disease) {
+ this.disease = disease;
+ }
+
+}
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/news/NewsFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/NewsFacadeEjb.java
new file mode 100644
index 00000000000..95bb501ca07
--- /dev/null
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/news/NewsFacadeEjb.java
@@ -0,0 +1,320 @@
+package de.symeda.sormas.backend.news;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.ejb.EJB;
+import javax.ejb.LocalBean;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Tuple;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Expression;
+import javax.persistence.criteria.Order;
+import javax.persistence.criteria.Path;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Selection;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+import de.symeda.sormas.api.EditPermissionType;
+import de.symeda.sormas.api.common.Page;
+import de.symeda.sormas.api.news.NewsCriteria;
+import de.symeda.sormas.api.news.NewsDto;
+import de.symeda.sormas.api.news.NewsFacade;
+import de.symeda.sormas.api.news.NewsIndexDto;
+import de.symeda.sormas.api.news.NewsReferenceDto;
+import de.symeda.sormas.api.news.eios.NewsStatus;
+import de.symeda.sormas.api.user.UserRight;
+import de.symeda.sormas.api.utils.SortProperty;
+import de.symeda.sormas.api.utils.ValidationRuntimeException;
+import de.symeda.sormas.backend.common.AbstractBaseEjb;
+import de.symeda.sormas.backend.infrastructure.community.Community;
+import de.symeda.sormas.backend.infrastructure.community.CommunityFacadeEjb;
+import de.symeda.sormas.backend.infrastructure.community.CommunityService;
+import de.symeda.sormas.backend.infrastructure.district.District;
+import de.symeda.sormas.backend.infrastructure.district.DistrictFacadeEjb;
+import de.symeda.sormas.backend.infrastructure.district.DistrictService;
+import de.symeda.sormas.backend.infrastructure.region.Region;
+import de.symeda.sormas.backend.infrastructure.region.RegionFacadeEjb;
+import de.symeda.sormas.backend.infrastructure.region.RegionService;
+import de.symeda.sormas.backend.user.UserService;
+import de.symeda.sormas.backend.util.DtoHelper;
+import de.symeda.sormas.backend.util.ModelConstants;
+import de.symeda.sormas.backend.util.Pseudonymizer;
+import de.symeda.sormas.backend.util.QueryHelper;
+import de.symeda.sormas.backend.util.RightsAllowed;
+
+@Stateless(name = "NewsFacade")
+@RightsAllowed(UserRight._VIEW_NEWS)
+public class NewsFacadeEjb extends AbstractBaseEjb implements NewsFacade {
+
+ @PersistenceContext(unitName = ModelConstants.PERSISTENCE_UNIT_NAME)
+ protected EntityManager em;
+ @EJB
+ private EiosFacadeEjb.EiosFacadeEjbLocal eiosFacadeEjb;
+ @EJB
+ private UserService userService;
+
+ @EJB
+ private RegionService regionService;
+ @EJB
+ private DistrictService districtService;
+ @EJB
+ private CommunityService communityService;
+ @EJB
+ private NewsService newsService;
+
+ public NewsFacadeEjb() {
+
+ }
+
+ @Inject
+ public NewsFacadeEjb(NewsService newsService) {
+ super(News.class, NewsDto.class, newsService);
+ }
+
+ @Override
+ public List getIndexList(NewsCriteria criteria, Integer first, Integer max, List sortProperties) {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery cq = cb.createQuery(NewsIndexDto.class);
+ Root root = cq.from(News.class);
+ NewsJoin newsJoin = new NewsJoin(root);
+ cq.multiselect(
+ root.get(News.ID),
+ root.get(News.UUID),
+ root.get(News.TITLE),
+ root.get(News.URL),
+ root.get(News.DESCRIPTION),
+ newsJoin.getRegion().get(Region.NAME),
+ newsJoin.getDistrict().get(District.NAME),
+ newsJoin.getCommunity().get(Community.NAME),
+ root.get(News.NEWS_DATE),
+ root.get(News.RISK_LEVEL),
+ root.get(News.STATUS),
+ root.get(News.DISEASE));
+ List indexListIds = getIndexListIds(criteria, first, max, sortProperties);
+ sortBy(sortProperties, new NewsQueryContext(cb, cq, root));
+ cq.where(root.get(News.ID).in(indexListIds));
+ return em.createQuery(cq).getResultList();
+ }
+
+ @Override
+ public Page getNewsPage(NewsCriteria newsCriteria, int offset, int size, List sortProperties) {
+ List indexList = getIndexList(newsCriteria, offset, size, sortProperties);
+ long count = count(newsCriteria);
+ return new Page<>(indexList, offset, size, count);
+
+ }
+
+ private List getIndexListIds(NewsCriteria criteria, Integer first, Integer max, List sortProperties) {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery cq = cb.createTupleQuery();
+ Root root = cq.from(News.class);
+ List> selections = new ArrayList<>();
+ selections.add(root.get(News.ID));
+ cq.multiselect(selections);
+ NewsQueryContext queryContext = new NewsQueryContext(cb, cq, root);
+ Predicate filter = newsService.buildFilterFromCriteria(criteria, queryContext);
+ if (filter != null) {
+ cq.where(filter);
+ }
+ selections.addAll(sortBy(sortProperties, queryContext));
+ return QueryHelper.getResultList(em, cq, first, max).stream().map(t -> t.get(0, Long.class)).collect(Collectors.toList());
+ }
+
+ private List> sortBy(List sortProperties, NewsQueryContext queryContext) {
+ CriteriaBuilder cb = queryContext.getCriteriaBuilder();
+ CriteriaQuery> cq = queryContext.getQuery();
+ List> selections = new ArrayList<>();
+ if (sortProperties != null && !sortProperties.isEmpty()) {
+ Expression expression;
+ List order = new ArrayList<>(sortProperties.size());
+ for (SortProperty sortProperty : sortProperties) {
+ switch (sortProperty.propertyName) {
+ case News.UUID:
+ case News.NEWS_DATE:
+ case News.TITLE:
+ case News.RISK_LEVEL:
+ case News.STATUS:
+ case News.DESCRIPTION:
+ case News.DISEASE:
+ expression = queryContext.getRoot().get(sortProperty.propertyName);
+ break;
+ case News.REGION:
+ expression = queryContext.getJoins().getRegion().get(Region.NAME);
+ break;
+ case News.DISTRICT:
+ expression = queryContext.getJoins().getDistrict().get(District.NAME);
+ break;
+ case News.COMMUNITY:
+ expression = queryContext.getJoins().getCommunity().get(Community.NAME);
+ break;
+ default:
+ throw new IllegalArgumentException(sortProperty.propertyName);
+ }
+ order.add(sortProperty.ascending ? cb.asc(expression) : cb.desc(expression));
+ selections.add(expression);
+ }
+ cq.orderBy(order);
+ } else {
+ Path