diff --git a/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml b/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml index 849baaab..5135ee6e 100644 --- a/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml +++ b/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml @@ -34,7 +34,7 @@ false - + diff --git a/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml.orig b/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml.orig index 383bf590..44097756 100644 --- a/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml.orig +++ b/dist/app/re3gistry2/WEB-INF/classes/META-INF/persistence.xml.orig @@ -39,10 +39,6 @@ - - - - diff --git a/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties b/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties index 49363547..5022a5d5 100644 --- a/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties +++ b/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties @@ -33,7 +33,7 @@ application.persistenceunitname=Re3gistry2 # Application version -application.version=Version: 2.0.0 +application.version=Version: 2.4.0-rc.1 # Localization application.language.available=en @@ -45,6 +45,9 @@ application.language.defaultLocale=en # ECAS base URL (authetication method) application.ecas.baseurl = https://webgate.ec.europa.eu +# Cache +application.cache.disk.path=/path/to/cache + # Login type - ECAS | SHIRO application.login.type=SHIRO @@ -75,14 +78,14 @@ application.dateformat=yyyy-MM-dd application.releasenote.rss.path=/path/to/rss # Optional heading message -application.headingmessage.class= -application.headingmessage.text= +application.headingmessage.class=alert-warning +application.headingmessage.text=This is the production environment of the Re3gistry 2 software

Link to the Re3gistry - localhost Instance - frontend view # Application root taken from pom -application.rooturl=https://localhost/re3gistry2 +application.rooturl=https://registry-test.eu/re3gistry2 # Solr configurations -application.solr.isactive=true +application.solr.isactive=false application.solr.url=http://localhost:8983/solr/ application.solr.core=re3gistry2 application.solr.connectiontimeout=10000 @@ -102,7 +105,7 @@ application.solr.fieldtoindex.localid=label,description,definition # * propose to add RegItem Register and RegItem Registry # # - |ManageFieldMapping| with this permission a RegUser can: -# * add field mpping (between field and item) +# * add field mapping (between field and item) # * eidt firld mapping (e.g. change properties such mandatory, multivalued, ecc.) # # - |ManageField| with this permission a RegUser can: @@ -166,7 +169,7 @@ mail.text.body.bulkimport.success=Dear {name},

The bulk import has be mail.text.body.bulkimport.error=Dear {name},

The bulk import has been completed with some error(s) when importing the file.
{errors} Than retry to load the file. ### Webapp properties #### -web.application_root_url=https://localhost/re3gistry2 +web.application_root_url=https://registry-test.eu/re3gistry2 web.cdn_url=https://inspire.ec.europa.eu/cdn/1.0/ web.default_locale=en web.default_title=Re3gistry 2 - Reference codes management system diff --git a/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties.orig b/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties.orig index 1dbd8b16..578e423e 100644 --- a/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties.orig +++ b/dist/app/re3gistry2/WEB-INF/classes/configurations_files/configuration.properties.orig @@ -45,6 +45,9 @@ application.language.defaultLocale=en # ECAS base URL (authetication method) application.ecas.baseurl = https://webgate.ec.europa.eu +# Cache +application.cache.disk.path=/path/to/cache + # Login type - ECAS | SHIRO application.login.type=SHIRO @@ -103,7 +106,7 @@ application.solr.fieldtoindex.localid=label,description,definition # * propose to add RegItem Register and RegItem Registry # # - |ManageFieldMapping| with this permission a RegUser can: -# * add field mpping (between field and item) +# * add field mapping (between field and item) # * eidt firld mapping (e.g. change properties such mandatory, multivalued, ecc.) # # - |ManageField| with this permission a RegUser can: @@ -149,8 +152,8 @@ mail.smtp.host=smtphost mail.smtp.port=25 mail.smtp.auth=false mail.smtp.starttls.enable=true -mail.user= -mail.password= +mail.user=name +mail.password=password mail.sender=jrc-inspire-support@ec.europa.eu mail.template=%subject%
%subject%

%mailbody%


This email has been automatically generated, please do not reply to it. For more details please access the management interface.

Best regards,
INSPIRE Re3gistry Team
diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/AddRegister.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/AddRegister.class index f7afaee1..677d19d8 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/AddRegister.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/AddRegister.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/ControlBody.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/ControlBody.class index 20c609c5..35e33b7b 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/ControlBody.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/ControlBody.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/Install.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/Install.class index 17f11599..17b68644 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/Install.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/Install.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegisterManager.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegisterManager.class index 6bb6ae2c..cde77ae3 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegisterManager.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegisterManager.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManager.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManager.class index 244dc901..f5aac9d5 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManager.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManager.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.class index e1fb7a18..2706460e 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/CharacterEncodingFilter.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/CharacterEncodingFilter.class similarity index 58% rename from dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/CharacterEncodingFilter.class rename to dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/CharacterEncodingFilter.class index 1767687d..9f13ac85 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/CharacterEncodingFilter.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/CharacterEncodingFilter.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/LoadSalt.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/LoadSalt.class similarity index 57% rename from dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/LoadSalt.class rename to dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/LoadSalt.class index 1c673140..40217799 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/LoadSalt.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/LoadSalt.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/SendEmailFromAction.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/SendEmailFromAction.class new file mode 100644 index 00000000..1d3aab30 Binary files /dev/null and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/SendEmailFromAction.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/UpdateRSS.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/UpdateRSS.class similarity index 60% rename from dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/UpdateRSS.class rename to dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/UpdateRSS.class index f4a1706f..4ce3a02f 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/UpdateRSS.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/UpdateRSS.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/ValidateSalt.class b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/ValidateSalt.class similarity index 58% rename from dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/ValidateSalt.class rename to dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/ValidateSalt.class index ff31aaba..3ef5df60 100644 Binary files a/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/controller/ValidateSalt.class and b/dist/app/re3gistry2/WEB-INF/classes/eu/europa/ec/re3gistry2/web/utility/ValidateSalt.class differ diff --git a/dist/app/re3gistry2/WEB-INF/classes/localizations/LocalizationBundle_en.properties b/dist/app/re3gistry2/WEB-INF/classes/localizations/LocalizationBundle_en.properties index d9f0f598..2df722c3 100644 --- a/dist/app/re3gistry2/WEB-INF/classes/localizations/LocalizationBundle_en.properties +++ b/dist/app/re3gistry2/WEB-INF/classes/localizations/LocalizationBundle_en.properties @@ -533,6 +533,13 @@ installation.clean.registry.label.description=The label of the registry will be installation.clean.registry.content.summary=Content summary installation.clean.registry.content.summary.description=This text should describe shortly what would be available under your Re3gistry instance. It will be shown in the registry\u2019s landing page. +installation.workflow.title=Type of workflow +installation.workflow.description=Select the governance model you would like the registry to apply. +installation.workflow.iso19135.title=Following ISO 19135 +installation.workflow.iso19135.description=Recommended if you plan to receive contributions from different organisations and manage them following a formal and standardised governance process. +installation.workflow.simplified.title=Simplified workflow +installation.workflow.simplified.description=Recommended if the data management will be handled entirely internally in the same organisation, or within a small group of contributors, not requiring such formal process. + installation.finalstep.title=Installation progress installation.finalstep.description.1=The Re3gistry is in process of installation. installation.finalstep.description.2=You can close this tab if you wish. @@ -594,7 +601,8 @@ mail.text.subject.cache.success=Re3gistry - complete CACHE success mail.text.body.cache.success=The Re3gistry software has has complete the CACHE all items with success. You can start managing your registry content by signing in. mail.text.subject.cache.error=Re3gistry - complete CACHE error mail.text.body.cache.error=An error has occurred during the Re3gistry CACHE all items, please review your settings. - +mail.text.subject.item.publised={label} had been published +mail.text.body.item.publised= {label} had been published successfully. The changelog associated with this action is: {changelog} mail.text.subject.bulkimport.success=Re3gistry - bulk import {itemclass} success mail.text.subject.bulkimport.error=Re3gistry - bulk import {itemclass} error diff --git a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Base-1.0.jar b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Base-1.0.jar index 3d812cf5..cd9c418e 100644 Binary files a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Base-1.0.jar and b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Base-1.0.jar differ diff --git a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar index 869593f4..2a7b0e31 100644 Binary files a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar and b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar differ diff --git a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar index 444229a6..2f6300a0 100644 Binary files a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar and b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar differ diff --git a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar index 4c7e56b0..948e50f9 100644 Binary files a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar and b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar differ diff --git a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Migration-1.0.jar b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Migration-1.0.jar index 8f807d23..2927ed89 100644 Binary files a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Migration-1.0.jar and b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Migration-1.0.jar differ diff --git a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Model-1.0.jar b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Model-1.0.jar index 8bae4bbf..94583a9f 100644 Binary files a/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Model-1.0.jar and b/dist/app/re3gistry2/WEB-INF/lib/Re3gistry2Model-1.0.jar differ diff --git a/dist/app/re3gistry2/WEB-INF/lib/postgresql-42.2.18.jar b/dist/app/re3gistry2/WEB-INF/lib/postgresql-42.2.18.jar new file mode 100644 index 00000000..7a851472 Binary files /dev/null and b/dist/app/re3gistry2/WEB-INF/lib/postgresql-42.2.18.jar differ diff --git a/dist/app/re3gistry2/WEB-INF/lib/postgresql-9.4.1212.jar b/dist/app/re3gistry2/WEB-INF/lib/postgresql-9.4.1212.jar deleted file mode 100644 index b0de752d..00000000 Binary files a/dist/app/re3gistry2/WEB-INF/lib/postgresql-9.4.1212.jar and /dev/null differ diff --git a/dist/app/re3gistry2/WEB-INF/shiro.ini b/dist/app/re3gistry2/WEB-INF/shiro.ini index 10bb8768..8fc8f655 100644 --- a/dist/app/re3gistry2/WEB-INF/shiro.ini +++ b/dist/app/re3gistry2/WEB-INF/shiro.ini @@ -54,7 +54,7 @@ jdbcRealm.authenticationQuery = SELECT shiropassword, shirosalt FROM RegUser WHE # Connection ds = org.postgresql.jdbc2.optional.SimpleDataSource ds.serverName = DB_ADDRESS -ds.databaseName = re3gistry2_dev +ds.databaseName = re3gistry2_srv ds.user = DB_USERNAME ds.password = DB_PASSWORD jdbcRealm.dataSource=$ds diff --git a/dist/app/re3gistry2/WEB-INF/web.xml b/dist/app/re3gistry2/WEB-INF/web.xml index 60846299..96d904a0 100644 --- a/dist/app/re3gistry2/WEB-INF/web.xml +++ b/dist/app/re3gistry2/WEB-INF/web.xml @@ -35,18 +35,17 @@ - + loadSalt - eu.europa.ec.re3gistry2.web.controller.LoadSalt + eu.europa.ec.re3gistry2.web.utility.LoadSalt loadSalt * - validateSalt - eu.europa.ec.re3gistry2.web.controller.ValidateSalt + eu.europa.ec.re3gistry2.web.utility.ValidateSalt validateSalt diff --git a/dist/app/re3gistry2/jsp/addRegister.jsp b/dist/app/re3gistry2/jsp/addRegister.jsp index 9588eb57..5125dffc 100644 --- a/dist/app/re3gistry2/jsp/addRegister.jsp +++ b/dist/app/re3gistry2/jsp/addRegister.jsp @@ -62,7 +62,7 @@ <% // Setting system localization ResourceBundle localization = (ResourceBundle) request.getAttribute(BaseConstants.KEY_REQUEST_LOCALIZATION); - + boolean checWorkflowSimplifiedAdd = configuration.checkWorkflowSimplified(); // Initializing managers RegFieldManager regFieldManager = new RegFieldManager(entityManager); RegLocalizationManager regLocalizationManager = new RegLocalizationManager(entityManager); @@ -191,7 +191,7 @@ - + <% if (checWorkflowSimplifiedAdd==false) { %> <%-- Showing the register ownerr field and selector --%>
<% @@ -292,6 +292,7 @@
+ <% } %> <%-- If the user has the rights to insert a new RegItemclass, showing diff --git a/dist/app/re3gistry2/jsp/field.jsp b/dist/app/re3gistry2/jsp/field.jsp index 7b6748f1..6f08dc68 100644 --- a/dist/app/re3gistry2/jsp/field.jsp +++ b/dist/app/re3gistry2/jsp/field.jsp @@ -108,9 +108,9 @@ <% } %> - <% + <% String buttonCacheDisabled = (CacheHelper.checkCacheCompleteRunning()) ? " disabled" : ""; - %> + %>

${regItemclass.localid}

@@ -128,62 +128,60 @@ <%if (childItemClassSize != 0) {%> -
+ -
- - ${localization.getString("label.cache.class")} - + -
+ <%} else {%> -
+ -
+ -
- - ${localization.getString("label.cache.class")} - + -
+ <%}%> - - -
+ -
- ${localization.getString("label.remove")} + -
- - ${localization.getString("label.cache.class")} - + @@ -368,18 +366,18 @@ columnDefs: [{orderable: true, className: 'reorder', targets: 0, type: 'num-fmt' }], <% } %> "columns": [null, { "width": "16%" }, null, null, null, null, null, null, null], - 'drawCallback': function () { + 'drawCallback': function () { <%-- Init the update on checkbox (if the user has the right) --%> <% if (permissionManageFieldMapping) {%> - $(".cbUpdate").on('change', function(){ - var itemclassUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_ITEMCLASSUUID%>"); - var fieldUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_FIELDUUID%>"); - var checkboxType = $(this).data("type"); - var checked = $(this).is(":checked"); - $.get(".<%=WebConstants.PAGE_URINAME_FIELD%>?<%=BaseConstants.KEY_REQUEST_FIELDUUID%>=" + fieldUuid + "&<%=BaseConstants.KEY_REQUEST_ITEMCLASSUUID%>=" + itemclassUuid + "&<%=BaseConstants.KEY_REQUEST_CHECKBOXTYPE%>=" + checkboxType + "&<%=BaseConstants.KEY_REQUEST_CHECKED%>=" + checked, function(data) {}); - }); + $(".cbUpdate").on('change', function(){ + var itemclassUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_ITEMCLASSUUID%>"); + var fieldUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_FIELDUUID%>"); + var checkboxType = $(this).data("type"); + var checked = $(this).is(":checked"); + $.get(".<%=WebConstants.PAGE_URINAME_FIELD%>?<%=BaseConstants.KEY_REQUEST_FIELDUUID%>=" + fieldUuid + "&<%=BaseConstants.KEY_REQUEST_ITEMCLASSUUID%>=" + itemclassUuid + "&<%=BaseConstants.KEY_REQUEST_CHECKBOXTYPE%>=" + checkboxType + "&<%=BaseConstants.KEY_REQUEST_CHECKED%>=" + checked, function(data) {}); + }); <% } %> - } + } }); <%-- If the user has the rights, activating the update of the list order on drag & drop --%> @@ -458,12 +456,12 @@ <%@include file="includes/footer.inc.jsp" %> @@ -479,31 +477,31 @@ <% }%> diff --git a/dist/app/re3gistry2/jsp/includes/menu.inc.jsp b/dist/app/re3gistry2/jsp/includes/menu.inc.jsp index ce3861dd..23e6488d 100644 --- a/dist/app/re3gistry2/jsp/includes/menu.inc.jsp +++ b/dist/app/re3gistry2/jsp/includes/menu.inc.jsp @@ -51,6 +51,7 @@ <% // Getting the configuration boolean checInstallation = configuration.checkInstallation(); + boolean checWorkflowSimplified = configuration.checkWorkflowSimplified(); // Instantiating managers RegUserRegGroupMappingManager regUserRegGroupMappingManager = new RegUserRegGroupMappingManager(entityManager); @@ -87,6 +88,7 @@ ${checInstallation} <% if (checInstallation) { + %> @@ -102,17 +104,17 @@ <% } %> - <% if (partOfSubmittingOrganizations || partOfRegistryManager) { %> + <% if ((partOfSubmittingOrganizations || partOfRegistryManager) && (checWorkflowSimplified==false)) { %> <% } %> - <% if (partOfControlBody) { %> + <% if (partOfControlBody && (checWorkflowSimplified==false)) { %> <% } %> - <% if (partOfRegisterManager) { %> + <% if (partOfRegisterManager && (checWorkflowSimplified==false)) { %> diff --git a/dist/app/re3gistry2/jsp/install/step-3.jsp b/dist/app/re3gistry2/jsp/install/step-3.jsp index 287e0e99..b73cf5ca 100644 --- a/dist/app/re3gistry2/jsp/install/step-3.jsp +++ b/dist/app/re3gistry2/jsp/install/step-3.jsp @@ -108,6 +108,8 @@

${localization.getString("installation.profile.choose.description")}

+
+
+ +

${localization.getString("installation.workflow.description")}

+ +
+ +

${localization.getString("installation.workflow.iso19135.description")}

+
+
+ +

${localization.getString("installation.workflow.simplified.description")}

+
diff --git a/dist/app/re3gistry2/jsp/registryManager.jsp b/dist/app/re3gistry2/jsp/registryManager.jsp index e3be8bfa..007b987d 100644 --- a/dist/app/re3gistry2/jsp/registryManager.jsp +++ b/dist/app/re3gistry2/jsp/registryManager.jsp @@ -91,7 +91,11 @@
@@ -191,7 +195,7 @@ <%=tmp.getRegUser().getName() + " (" + tmp.getRegUser().getSsoreference() + ")"%> - <% + <% // Checking the status of the action boolean showActionPublish = false; @@ -200,6 +204,37 @@ showActionPublish = true; } %> + + <% + // Checking the status of the action + boolean showSubmit = false; + boolean showChangesRequest = false; + + // Check if the user is the owner of the action + if (Configuration.checkWorkflowSimplified()== false) { + + } + if (tmp.getRegUser().getSsoreference().equals(regUser.getSsoreference())) { + if (tmp.getRegStatus().getLocalid().equals(BaseConstants.KEY_STATUS_LOCALID_DRAFT)) { + showSubmit = true; + } + if (tmp.getRegStatus().getLocalid().equals(BaseConstants.KEY_STATUS_LOCALID_DRAFT) && (tmp.getChangeRequest() != null && tmp.getChangeRequest().length() > 0)) { + showChangesRequest = true; + } + } + + %> + <%if (showActionPublish==false) {%> + <%if (showSubmit && !(regAction == null && showChangesRequest)) {%> + ="<%=tmp.getUuid()%>"> ${localization.getString("label.submit")}
+ ${localization.getString("label.discardaction")} + <%}%> + + <%if (showChangesRequest && regAction == null) {%> + ="<%=tmp.getUuid()%>" href="?<%=WebConstants.DATA_PARAMETER_ACTIONUUID%>=<%=tmp.getUuid()%>"> ${localization.getString("submittingorganisations.label.readchangerequest")}
+ <%}%> + <%}%> + <%-- Action buttons --%> <%if (showActionPublish) {%> <% if (!CacheHelper.checkCacheCompleteRunning()) {%> @@ -502,7 +537,57 @@ %>
- + <%@include file="includes/footer.inc.jsp" %> <%@include file="includes/pageend.inc.jsp" %> @@ -526,6 +611,41 @@ "order": [[0, "desc"]] }); }); + + + + + <%-- Init the confirm message --%> + $('[data-toggle=confirmation]').confirmation({ + rootSelector: '[data-toggle=confirmation]' + }); + + $('.btn-submit-action').on('click', function () { + var actionUuid = $(this).data('<%=WebConstants.DATA_PARAMETER_ACTIONUUID%>'); + $.get(".<%=WebConstants.PAGE_URINAME_ACTIONDETAIL%>?<%=BaseConstants.KEY_REQUEST_ACTION_UUID%>=" + actionUuid, function (data) { + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_CHANGELOG%>').val(data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGELOG%>); + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_ISSUEREFERENCE%>').val(data.<%=BaseConstants.KEY_JSON_FIELDS_ISSUETRACKERLINK%>); + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_REACTIONLABEL%>').val(data.<%=BaseConstants.KEY_JSON_FIELDS_LABEL%>); + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_DIRECTPUBLISH%>').val(true); + $('#actionIdDisplay').text(actionUuid); + $('#submit_action #<%=BaseConstants.KEY_FORM_FIELD_NAME_ACTIONUUID%>').val(actionUuid); + if (data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGEREQUEST%> != '' && data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGEREQUEST%> != 'undefined' && data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGEREQUEST%> != null) { + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_REACTIONLABEL%>').attr('disabled', 'disabled'); + } + $('#submit_action').modal(); + }); + }); + + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_CHANGELOG%>').bind('input propertychange', function () { + + if (this.value.length) { + $('#submitButton').removeClass("d-none"); + } else { + $('#submitButton').addClass("d-none"); + } + }); + + diff --git a/dist/app/re3gistry2/jsp/registryManagerDataExport.jsp b/dist/app/re3gistry2/jsp/registryManagerDataExport.jsp index 6a5bf31a..66832437 100644 --- a/dist/app/re3gistry2/jsp/registryManagerDataExport.jsp +++ b/dist/app/re3gistry2/jsp/registryManagerDataExport.jsp @@ -90,7 +90,11 @@
diff --git a/dist/app/re3gistry2/jsp/registryManagerUsers.jsp b/dist/app/re3gistry2/jsp/registryManagerUsers.jsp index 1ff648e8..23285635 100644 --- a/dist/app/re3gistry2/jsp/registryManagerUsers.jsp +++ b/dist/app/re3gistry2/jsp/registryManagerUsers.jsp @@ -88,7 +88,11 @@
diff --git a/dist/app/re3gistry2/jsp/registryManagerUsersAdd.jsp b/dist/app/re3gistry2/jsp/registryManagerUsersAdd.jsp index fc2b9966..52b36550 100644 --- a/dist/app/re3gistry2/jsp/registryManagerUsersAdd.jsp +++ b/dist/app/re3gistry2/jsp/registryManagerUsersAdd.jsp @@ -84,7 +84,11 @@
@@ -196,10 +200,23 @@ List regGroups = (List) request.getAttribute(BaseConstants.KEY_REQUEST_REGGROUPS); %> <% + if (Configuration.checkWorkflowSimplified()) { + for (RegGroup tmp : regGroups) { + if (tmp.getLocalid().equals(BaseConstants.KEY_FIELD_MANDATORY_REGISTRYMANAGER)) { + %> + + + <%=tmp.getName()%> + + <% + } + } + } else { for (RegGroup tmp : regGroups) { boolean found = false; String foundUuid = null; // Check if the user is in the current group + %> @@ -208,6 +225,7 @@ <% } +} %> diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml b/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml index 849baaab..5135ee6e 100644 --- a/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml +++ b/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml @@ -34,7 +34,7 @@ false - + diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml.orig b/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml.orig index eaf27c89..44097756 100644 --- a/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml.orig +++ b/dist/app/re3gistry2restapi/WEB-INF/classes/META-INF/persistence.xml.orig @@ -39,10 +39,6 @@ - - - - diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/configurations_files/configuration.properties b/dist/app/re3gistry2restapi/WEB-INF/classes/configurations_files/configuration.properties index 8cb96708..0f33a9aa 100644 --- a/dist/app/re3gistry2restapi/WEB-INF/classes/configurations_files/configuration.properties +++ b/dist/app/re3gistry2restapi/WEB-INF/classes/configurations_files/configuration.properties @@ -45,3 +45,9 @@ application.cache.disk.maximum.space=25 # Status base URI application.status.maingroup=1 + +# XSD Schema to validate XML path +application.schemas.path=/schemas/2.0 + +# Legacy flag to manintain the old format of XSL +application.legacy.flag=ON diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ApiError.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ApiError.class deleted file mode 100644 index 8e4ed143..00000000 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ApiError.class and /dev/null differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.class index 8fe4712c..d4c0ae16 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheServlet.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheServlet.class index 8b66ea57..ebe7612b 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheServlet.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/CacheServlet.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ItemsServlet.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ItemsServlet.class index 2c04c6cb..a77d8134 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ItemsServlet.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ItemsServlet.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/StatusSupplier.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/StatusSupplier.class index 7c3d4c14..e30157f9 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/StatusSupplier.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/StatusSupplier.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/cache/CaffeineCache.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/cache/CaffeineCache.class deleted file mode 100644 index 8f510c66..00000000 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/cache/CaffeineCache.class and /dev/null differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/ATOMFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/ATOMFormatter.class new file mode 100644 index 00000000..afe086ba Binary files /dev/null and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/ATOMFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.class index 30374f46..67f21c41 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/Formatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/Formatter.class index cbacaa58..b09ecaf0 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/Formatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/Formatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.class index fa11787a..ff9ece69 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.class index 6f4e428e..da6afda5 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.class index b68e95d5..36da38d8 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.class index f6ba2c40..d8685fbf 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.class index daa499d8..ed960dfa 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.class index 22c2a468..e40708fc 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/XSDFormatter.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/XSDFormatter.class new file mode 100644 index 00000000..943fd8db Binary files /dev/null and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/format/XSDFormatter.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ApiError.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ApiError.class new file mode 100644 index 00000000..193e6255 Binary files /dev/null and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ApiError.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ApiResponse.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ApiResponse.class similarity index 63% rename from dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ApiResponse.class rename to dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ApiResponse.class index 791d307f..15cb5d40 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/ApiResponse.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ApiResponse.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.class b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.class index dd049008..f6025762 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.class and b/dist/app/re3gistry2restapi/WEB-INF/classes/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.class differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Base-1.0.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Base-1.0.jar index 3d812cf5..cd9c418e 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Base-1.0.jar and b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Base-1.0.jar differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar index 869593f4..2a7b0e31 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar and b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDInterface-1.0.jar differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar index 444229a6..2f6300a0 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar and b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2CRUDrdb-1.0.jar differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar index 4c7e56b0..948e50f9 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar and b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2JavaAPI-1.0.jar differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Model-1.0.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Model-1.0.jar index 8bae4bbf..94583a9f 100644 Binary files a/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Model-1.0.jar and b/dist/app/re3gistry2restapi/WEB-INF/lib/Re3gistry2Model-1.0.jar differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/postgresql-42.2.18.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/postgresql-42.2.18.jar new file mode 100644 index 00000000..7a851472 Binary files /dev/null and b/dist/app/re3gistry2restapi/WEB-INF/lib/postgresql-42.2.18.jar differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/postgresql-9.4.1212.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/postgresql-9.4.1212.jar deleted file mode 100644 index b0de752d..00000000 Binary files a/dist/app/re3gistry2restapi/WEB-INF/lib/postgresql-9.4.1212.jar and /dev/null differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/validation-api-1.1.0.Final.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/validation-api-1.1.0.Final.jar deleted file mode 100644 index de854038..00000000 Binary files a/dist/app/re3gistry2restapi/WEB-INF/lib/validation-api-1.1.0.Final.jar and /dev/null differ diff --git a/dist/app/re3gistry2restapi/WEB-INF/lib/validation-api-2.0.1.Final.jar b/dist/app/re3gistry2restapi/WEB-INF/lib/validation-api-2.0.1.Final.jar new file mode 100644 index 00000000..2368e10a Binary files /dev/null and b/dist/app/re3gistry2restapi/WEB-INF/lib/validation-api-2.0.1.Final.jar differ diff --git a/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/docker-compose.yml b/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/docker-compose.yml index 81951f0c..76b9138e 100644 --- a/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/docker-compose.yml +++ b/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/docker-compose.yml @@ -24,7 +24,7 @@ services: # Be sure file sources-main/resources-configurations_files/system.installed # is removed from the war file. If not, installation will be not detected. - ../../dist/customize-interface/example-profile-developer-docker/dump-docker-202205180929.sql:/docker-entrypoint-initdb.d/init_backup.sql - # login from example db: name@example.com password + # login example db: name@example.com password expose: - "5432" ports: @@ -66,8 +66,8 @@ services: - web volumes: - ../../dist/webapp/httpd.conf:/usr/local/apache2/conf/httpd.conf - - ../../dist/webapp/public_html:/var/www/host.docker.internal/public_html - ../../dist/webapp/httpd-vhosts.conf:/usr/local/apache2/conf/extra/httpd-vhosts.conf + - ../Re3gistry2ServiceWebapp/public_html:/var/www/host.docker.internal/public_html restart: always networks: - main @@ -80,10 +80,14 @@ services: - "8983:8983" # volumes: # - ../../../solr:/opt/solr/server/solr/re3gistry2 +# entrypoint: +# - docker-entrypoint.sh +# - solr-precreate +# - re3gistry entrypoint: - - docker-entrypoint.sh - - solr-precreate - - re3gistry + - bash + - "-c" + - "precreate-core re3gistry; precreate-core rortest; exec solr -f" mailserver: image: marcopas/docker-mailslurper container_name: mailserver diff --git a/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/pom.xml b/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/pom.xml index e4f0e383..0cc82123 100644 --- a/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/pom.xml +++ b/dist/customize-interface/example-profile-developer-docker/Re3gistry2-build-helper/pom.xml @@ -17,7 +17,7 @@ none CONFIG false - 9.4.1212 + 42.2.18 @@ -73,6 +73,10 @@ name password admin@example.com + true + /path/to/cache + /path/to/rss + ON diff --git a/dist/customize-interface/example-profile-developer-docker/Re3gistry2/src/main/webapp/WEB-INF/web.xml b/dist/customize-interface/example-profile-developer-docker/Re3gistry2/src/main/webapp/WEB-INF/web.xml index 3550f214..220ea618 100644 --- a/dist/customize-interface/example-profile-developer-docker/Re3gistry2/src/main/webapp/WEB-INF/web.xml +++ b/dist/customize-interface/example-profile-developer-docker/Re3gistry2/src/main/webapp/WEB-INF/web.xml @@ -37,7 +37,7 @@ loadSalt - eu.europa.ec.re3gistry2.web.controller.LoadSalt + eu.europa.ec.re3gistry2.web.utility.LoadSalt loadSalt @@ -46,7 +46,7 @@ validateSalt - eu.europa.ec.re3gistry2.web.controller.ValidateSalt + eu.europa.ec.re3gistry2.web.utility.ValidateSalt validateSalt diff --git a/dist/customize-interface/example-profile-developer-docker/Re3gistry2ServiceWebapp/httpd-vhosts.conf b/dist/customize-interface/example-profile-developer-docker/Re3gistry2ServiceWebapp/httpd-vhosts.conf index 86896be4..4f3d779b 100644 --- a/dist/customize-interface/example-profile-developer-docker/Re3gistry2ServiceWebapp/httpd-vhosts.conf +++ b/dist/customize-interface/example-profile-developer-docker/Re3gistry2ServiceWebapp/httpd-vhosts.conf @@ -47,6 +47,10 @@ loglevel debug rewrite:trace3 RewriteCond %{REQUEST_URI} !^/registry/js-ecl-v2 RewriteRule ^/(.*)/(.*)\.([a-z]{2})\.(jsonc|json|xml|atom|csv|rdf|iso19135xml|ror)$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=$3&uri=http://%{HTTP_HOST}/$1&format=$4 [P,L] +#Handling dynamic XSD +RewriteCond %{REQUEST_URI} ^/registry/schemas/2.0 +RewriteRule ^/registry/schemas/2.0/(.*)\.xsd$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?itemclass=$1&format=xsd [P,L] + ## Handling content negotiation: json RewriteCond %{REQUEST_URI} !^/registry/rest @@ -90,6 +94,9 @@ RewriteCond %{HTTP:Accept-Language} ([a-z]{2}) [OR] RewriteCond %{HTTP:Accept-Language} ^()$ RewriteRule ^/(.*) %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=%1&uri=http://%{HTTP_HOST}/$1&format=csv [P,L] +#Handling ROR format without specific language (defaults to ENG) + +RewriteRule ^/(.*)/((.*)\.(ror))$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?uri=http://%{HTTP_HOST}/$1&lang=en&format=ror [P,NE,L] ## Generic request to the registry RewriteCond %{REQUEST_URI} !^/cache-all-reg diff --git a/dist/webapp/apache-example-configuration.conf b/dist/webapp/apache-example-configuration.conf index f7cbde30..0af14ec2 100644 --- a/dist/webapp/apache-example-configuration.conf +++ b/dist/webapp/apache-example-configuration.conf @@ -40,6 +40,10 @@ RewriteCond %{REQUEST_URI} !^/registry/js RewriteRule ^/(.*)/(.*)\.([a-z]{2})\.(json|xml|atom|csv|rdf|iso19135xml|ror)$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=$3&uri=http://%{HTTP_HOST}/$1&format=$4 [P,L] + #Handling dynamic XSD + RewriteCond %{REQUEST_URI} ^/registry/schemas/2.0 + RewriteRule ^/registry/schemas/2.0/(.*)\.xsd$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?itemclass=$1&format=xsd [P,L] + ## Handling content negotiation: json RewriteCond %{REQUEST_URI} !^/registry/rest RewriteCond %{HTTP_ACCEPT} ^application/json$ @@ -82,6 +86,10 @@ RewriteCond %{HTTP:Accept-Language} ^()$ RewriteRule ^/(.*) %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=%1&uri=http://%{HTTP_HOST}/$1&format=csv [P,L] + #Handling ROR format without specific language (defaults to ENG) + + RewriteRule ^/(.*)/((.*)\.(ror))$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?uri=http://%{HTTP_HOST}/$1&lang=en&format=ror [P,NE,L] + ## Generic request to the registry RewriteCond %{REQUEST_URI} !^/re3gistry2 RewriteCond %{REQUEST_URI} !^/registry/searchapi diff --git a/dist/webapp/public_html/api.html b/dist/webapp/public_html/api.html index 078b0665..4b0ae6f1 100644 --- a/dist/webapp/public_html/api.html +++ b/dist/webapp/public_html/api.html @@ -29,17 +29,14 @@ - + - + --> @@ -318,7 +315,7 @@

@@ -452,5 +449,13 @@

',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:vn},Ln="show",xn="out",Pn={HIDE:"hide"+Tn,HIDDEN:"hidden"+Tn,SHOW:"show"+Tn,SHOWN:"shown"+Tn,INSERTED:"inserted"+Tn,CLICK:"click"+Tn,FOCUSIN:"focusin"+Tn,FOCUSOUT:"focusout"+Tn,MOUSEENTER:"mouseenter"+Tn,MOUSELEAVE:"mouseleave"+Tn},Hn="fade",jn="show",Rn=".tooltip-inner",Fn=".arrow",Mn="hover",Wn="focus",Un="click",Bn="manual",qn=function(){function i(t,e){if("undefined"==typeof be)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=p(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),p(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(p(this.getTipElement()).hasClass(jn))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),p.removeData(this.element,this.constructor.DATA_KEY),p(this.element).off(this.constructor.EVENT_KEY),p(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&p(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===p(this.element).css("display"))throw new Error("Please use show on visible elements");var t=p.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){p(this.element).trigger(t);var n=m.findShadowRoot(this.element),i=p.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=m.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&p(o).addClass(Hn);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();p(o).data(this.constructor.DATA_KEY,this),p.contains(this.element.ownerDocument.documentElement,this.tip)||p(o).appendTo(l),p(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new be(this.element,o,{placement:a,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Fn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),p(o).addClass(jn),"ontouchstart"in document.documentElement&&p(document.body).children().on("mouseover",null,p.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,p(e.element).trigger(e.constructor.Event.SHOWN),t===xn&&e._leave(null,e)};if(p(this.tip).hasClass(Hn)){var h=m.getTransitionDurationFromElement(this.tip);p(this.tip).one(m.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=p.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==Ln&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),p(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(p(this.element).trigger(i),!i.isDefaultPrevented()){if(p(n).removeClass(jn),"ontouchstart"in document.documentElement&&p(document.body).children().off("mouseover",null,p.noop),this._activeTrigger[Un]=!1,this._activeTrigger[Wn]=!1,this._activeTrigger[Mn]=!1,p(this.tip).hasClass(Hn)){var r=m.getTransitionDurationFromElement(n);p(n).one(m.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){p(this.getTipElement()).addClass(Dn+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(p(t.querySelectorAll(Rn)),this.getTitle()),p(t).removeClass(Hn+" "+jn)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=bn(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?p(e).parent().is(t)||t.empty().append(e):t.text(p(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:m.isElement(this.config.container)?p(this.config.container):p(document).find(this.config.container)},t._getAttachment=function(t){return Nn[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)p(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Bn){var e=t===Mn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Mn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;p(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),p(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||p(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),p(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Wn:Mn]=!0),p(e.getTipElement()).hasClass(jn)||e._hoverState===Ln?e._hoverState=Ln:(clearTimeout(e._timeout),e._hoverState=Ln,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Ln&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||p(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),p(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Wn:Mn]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=xn,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===xn&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=p(this.element).data();return Object.keys(e).forEach(function(t){-1!==An.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),m.typeCheckConfig(wn,t,this.constructor.DefaultType),t.sanitize&&(t.template=bn(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=p(this.getTipElement()),e=t.attr("class").match(In);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(p(t).removeClass(Hn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=p(this).data(Cn),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),p(this).data(Cn,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return kn}},{key:"NAME",get:function(){return wn}},{key:"DATA_KEY",get:function(){return Cn}},{key:"Event",get:function(){return Pn}},{key:"EVENT_KEY",get:function(){return Tn}},{key:"DefaultType",get:function(){return On}}]),i}();p.fn[wn]=qn._jQueryInterface,p.fn[wn].Constructor=qn,p.fn[wn].noConflict=function(){return p.fn[wn]=Sn,qn._jQueryInterface};var Kn="popover",Qn="bs.popover",Vn="."+Qn,Yn=p.fn[Kn],zn="bs-popover",Xn=new RegExp("(^|\\s)"+zn+"\\S+","g"),Gn=l({},qn.Default,{placement:"right",trigger:"click",content:"",template:''}),$n=l({},qn.DefaultType,{content:"(string|element|function)"}),Jn="fade",Zn="show",ti=".popover-header",ei=".popover-body",ni={HIDE:"hide"+Vn,HIDDEN:"hidden"+Vn,SHOW:"show"+Vn,SHOWN:"shown"+Vn,INSERTED:"inserted"+Vn,CLICK:"click"+Vn,FOCUSIN:"focusin"+Vn,FOCUSOUT:"focusout"+Vn,MOUSEENTER:"mouseenter"+Vn,MOUSELEAVE:"mouseleave"+Vn},ii=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){p(this.getTipElement()).addClass(zn+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},o.setContent=function(){var t=p(this.getTipElement());this.setElementContent(t.find(ti),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ei),e),t.removeClass(Jn+" "+Zn)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=p(this.getTipElement()),e=t.attr("class").match(Xn);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t {\n called = true\n })\n\n setTimeout(() => {\n if (!called) {\n Util.triggerTransitionEnd(this)\n }\n }, duration)\n\n return this\n}\n\nfunction setTransitionEndSupport() {\n $.fn.emulateTransitionEnd = transitionEndEmulator\n $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst Util = {\n\n TRANSITION_END: 'bsTransitionEnd',\n\n getUID(prefix) {\n do {\n // eslint-disable-next-line no-bitwise\n prefix += ~~(Math.random() * MAX_UID) // \"~~\" acts like a faster Math.floor() here\n } while (document.getElementById(prefix))\n return prefix\n },\n\n getSelectorFromElement(element) {\n let selector = element.getAttribute('data-target')\n\n if (!selector || selector === '#') {\n const hrefAttr = element.getAttribute('href')\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''\n }\n\n try {\n return document.querySelector(selector) ? selector : null\n } catch (err) {\n return null\n }\n },\n\n getTransitionDurationFromElement(element) {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let transitionDuration = $(element).css('transition-duration')\n let transitionDelay = $(element).css('transition-delay')\n\n const floatTransitionDuration = parseFloat(transitionDuration)\n const floatTransitionDelay = parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n },\n\n reflow(element) {\n return element.offsetHeight\n },\n\n triggerTransitionEnd(element) {\n $(element).trigger(TRANSITION_END)\n },\n\n // TODO: Remove in v5\n supportsTransitionEnd() {\n return Boolean(TRANSITION_END)\n },\n\n isElement(obj) {\n return (obj[0] || obj).nodeType\n },\n\n typeCheckConfig(componentName, config, configTypes) {\n for (const property in configTypes) {\n if (Object.prototype.hasOwnProperty.call(configTypes, property)) {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && Util.isElement(value)\n ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new Error(\n `${componentName.toUpperCase()}: ` +\n `Option \"${property}\" provided type \"${valueType}\" ` +\n `but expected type \"${expectedTypes}\".`)\n }\n }\n }\n },\n\n findShadowRoot(element) {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return Util.findShadowRoot(element.parentNode)\n }\n}\n\nsetTransitionEndSupport()\n\nexport default Util\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'alert'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Selector = {\n DISMISS : '[data-dismiss=\"alert\"]'\n}\n\nconst Event = {\n CLOSE : `close${EVENT_KEY}`,\n CLOSED : `closed${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n ALERT : 'alert',\n FADE : 'fade',\n SHOW : 'show'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Alert {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n close(element) {\n let rootElement = this._element\n if (element) {\n rootElement = this._getRootElement(element)\n }\n\n const customEvent = this._triggerCloseEvent(rootElement)\n\n if (customEvent.isDefaultPrevented()) {\n return\n }\n\n this._removeElement(rootElement)\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Private\n\n _getRootElement(element) {\n const selector = Util.getSelectorFromElement(element)\n let parent = false\n\n if (selector) {\n parent = document.querySelector(selector)\n }\n\n if (!parent) {\n parent = $(element).closest(`.${ClassName.ALERT}`)[0]\n }\n\n return parent\n }\n\n _triggerCloseEvent(element) {\n const closeEvent = $.Event(Event.CLOSE)\n\n $(element).trigger(closeEvent)\n return closeEvent\n }\n\n _removeElement(element) {\n $(element).removeClass(ClassName.SHOW)\n\n if (!$(element).hasClass(ClassName.FADE)) {\n this._destroyElement(element)\n return\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(element)\n\n $(element)\n .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))\n .emulateTransitionEnd(transitionDuration)\n }\n\n _destroyElement(element) {\n $(element)\n .detach()\n .trigger(Event.CLOSED)\n .remove()\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $element = $(this)\n let data = $element.data(DATA_KEY)\n\n if (!data) {\n data = new Alert(this)\n $element.data(DATA_KEY, data)\n }\n\n if (config === 'close') {\n data[config](this)\n }\n })\n }\n\n static _handleDismiss(alertInstance) {\n return function (event) {\n if (event) {\n event.preventDefault()\n }\n\n alertInstance.close(this)\n }\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(\n Event.CLICK_DATA_API,\n Selector.DISMISS,\n Alert._handleDismiss(new Alert())\n)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Alert._jQueryInterface\n$.fn[NAME].Constructor = Alert\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Alert._jQueryInterface\n}\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'button'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst ClassName = {\n ACTIVE : 'active',\n BUTTON : 'btn',\n FOCUS : 'focus'\n}\n\nconst Selector = {\n DATA_TOGGLE_CARROT : '[data-toggle^=\"button\"]',\n DATA_TOGGLE : '[data-toggle=\"buttons\"]',\n INPUT : 'input:not([type=\"hidden\"])',\n ACTIVE : '.active',\n BUTTON : '.btn'\n}\n\nconst Event = {\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +\n `blur${EVENT_KEY}${DATA_API_KEY}`\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Button {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n toggle() {\n let triggerChangeEvent = true\n let addAriaPressed = true\n const rootElement = $(this._element).closest(\n Selector.DATA_TOGGLE\n )[0]\n\n if (rootElement) {\n const input = this._element.querySelector(Selector.INPUT)\n\n if (input) {\n if (input.type === 'radio') {\n if (input.checked &&\n this._element.classList.contains(ClassName.ACTIVE)) {\n triggerChangeEvent = false\n } else {\n const activeElement = rootElement.querySelector(Selector.ACTIVE)\n\n if (activeElement) {\n $(activeElement).removeClass(ClassName.ACTIVE)\n }\n }\n }\n\n if (triggerChangeEvent) {\n if (input.hasAttribute('disabled') ||\n rootElement.hasAttribute('disabled') ||\n input.classList.contains('disabled') ||\n rootElement.classList.contains('disabled')) {\n return\n }\n input.checked = !this._element.classList.contains(ClassName.ACTIVE)\n $(input).trigger('change')\n }\n\n input.focus()\n addAriaPressed = false\n }\n }\n\n if (addAriaPressed) {\n this._element.setAttribute('aria-pressed',\n !this._element.classList.contains(ClassName.ACTIVE))\n }\n\n if (triggerChangeEvent) {\n $(this._element).toggleClass(ClassName.ACTIVE)\n }\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n\n if (!data) {\n data = new Button(this)\n $(this).data(DATA_KEY, data)\n }\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n event.preventDefault()\n\n let button = event.target\n\n if (!$(button).hasClass(ClassName.BUTTON)) {\n button = $(button).closest(Selector.BUTTON)\n }\n\n Button._jQueryInterface.call($(button), 'toggle')\n })\n .on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n const button = $(event.target).closest(Selector.BUTTON)[0]\n $(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))\n })\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Button._jQueryInterface\n$.fn[NAME].Constructor = Button\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Button._jQueryInterface\n}\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'carousel'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key\nconst ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n interval : 5000,\n keyboard : true,\n slide : false,\n pause : 'hover',\n wrap : true,\n touch : true\n}\n\nconst DefaultType = {\n interval : '(number|boolean)',\n keyboard : 'boolean',\n slide : '(boolean|string)',\n pause : '(string|boolean)',\n wrap : 'boolean',\n touch : 'boolean'\n}\n\nconst Direction = {\n NEXT : 'next',\n PREV : 'prev',\n LEFT : 'left',\n RIGHT : 'right'\n}\n\nconst Event = {\n SLIDE : `slide${EVENT_KEY}`,\n SLID : `slid${EVENT_KEY}`,\n KEYDOWN : `keydown${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`,\n TOUCHSTART : `touchstart${EVENT_KEY}`,\n TOUCHMOVE : `touchmove${EVENT_KEY}`,\n TOUCHEND : `touchend${EVENT_KEY}`,\n POINTERDOWN : `pointerdown${EVENT_KEY}`,\n POINTERUP : `pointerup${EVENT_KEY}`,\n DRAG_START : `dragstart${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n CAROUSEL : 'carousel',\n ACTIVE : 'active',\n SLIDE : 'slide',\n RIGHT : 'carousel-item-right',\n LEFT : 'carousel-item-left',\n NEXT : 'carousel-item-next',\n PREV : 'carousel-item-prev',\n ITEM : 'carousel-item',\n POINTER_EVENT : 'pointer-event'\n}\n\nconst Selector = {\n ACTIVE : '.active',\n ACTIVE_ITEM : '.active.carousel-item',\n ITEM : '.carousel-item',\n ITEM_IMG : '.carousel-item img',\n NEXT_PREV : '.carousel-item-next, .carousel-item-prev',\n INDICATORS : '.carousel-indicators',\n DATA_SLIDE : '[data-slide], [data-slide-to]',\n DATA_RIDE : '[data-ride=\"carousel\"]'\n}\n\nconst PointerType = {\n TOUCH : 'touch',\n PEN : 'pen'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\nclass Carousel {\n constructor(element, config) {\n this._items = null\n this._interval = null\n this._activeElement = null\n this._isPaused = false\n this._isSliding = false\n this.touchTimeout = null\n this.touchStartX = 0\n this.touchDeltaX = 0\n\n this._config = this._getConfig(config)\n this._element = element\n this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)\n this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n next() {\n if (!this._isSliding) {\n this._slide(Direction.NEXT)\n }\n }\n\n nextWhenVisible() {\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden &&\n ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {\n this.next()\n }\n }\n\n prev() {\n if (!this._isSliding) {\n this._slide(Direction.PREV)\n }\n }\n\n pause(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if (this._element.querySelector(Selector.NEXT_PREV)) {\n Util.triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config.interval && !this._isPaused) {\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n $(this._element).one(Event.SLID, () => this.to(index))\n return\n }\n\n if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const direction = index > activeIndex\n ? Direction.NEXT\n : Direction.PREV\n\n this._slide(direction, this._items[index])\n }\n\n dispose() {\n $(this._element).off(EVENT_KEY)\n $.removeData(this._element, DATA_KEY)\n\n this._items = null\n this._config = null\n this._element = null\n this._interval = null\n this._isPaused = null\n this._isSliding = null\n this._activeElement = null\n this._indicatorsElement = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _handleSwipe() {\n const absDeltax = Math.abs(this.touchDeltaX)\n\n if (absDeltax <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltax / this.touchDeltaX\n\n // swipe left\n if (direction > 0) {\n this.prev()\n }\n\n // swipe right\n if (direction < 0) {\n this.next()\n }\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n $(this._element)\n .on(Event.KEYDOWN, (event) => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n $(this._element)\n .on(Event.MOUSEENTER, (event) => this.pause(event))\n .on(Event.MOUSELEAVE, (event) => this.cycle(event))\n }\n\n if (this._config.touch) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n if (!this._touchSupported) {\n return\n }\n\n const start = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchStartX = event.originalEvent.clientX\n } else if (!this._pointerEvent) {\n this.touchStartX = event.originalEvent.touches[0].clientX\n }\n }\n\n const move = (event) => {\n // ensure swiping with one touch and not pinching\n if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {\n this.touchDeltaX = 0\n } else {\n this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX\n }\n }\n\n const end = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchDeltaX = event.originalEvent.clientX - this.touchStartX\n }\n\n this._handleSwipe()\n if (this._config.pause === 'hover') {\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n }\n\n $(this._element.querySelectorAll(Selector.ITEM_IMG)).on(Event.DRAG_START, (e) => e.preventDefault())\n if (this._pointerEvent) {\n $(this._element).on(Event.POINTERDOWN, (event) => start(event))\n $(this._element).on(Event.POINTERUP, (event) => end(event))\n\n this._element.classList.add(ClassName.POINTER_EVENT)\n } else {\n $(this._element).on(Event.TOUCHSTART, (event) => start(event))\n $(this._element).on(Event.TOUCHMOVE, (event) => move(event))\n $(this._element).on(Event.TOUCHEND, (event) => end(event))\n }\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n switch (event.which) {\n case ARROW_LEFT_KEYCODE:\n event.preventDefault()\n this.prev()\n break\n case ARROW_RIGHT_KEYCODE:\n event.preventDefault()\n this.next()\n break\n default:\n }\n }\n\n _getItemIndex(element) {\n this._items = element && element.parentNode\n ? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))\n : []\n return this._items.indexOf(element)\n }\n\n _getItemByDirection(direction, activeElement) {\n const isNextDirection = direction === Direction.NEXT\n const isPrevDirection = direction === Direction.PREV\n const activeIndex = this._getItemIndex(activeElement)\n const lastItemIndex = this._items.length - 1\n const isGoingToWrap = isPrevDirection && activeIndex === 0 ||\n isNextDirection && activeIndex === lastItemIndex\n\n if (isGoingToWrap && !this._config.wrap) {\n return activeElement\n }\n\n const delta = direction === Direction.PREV ? -1 : 1\n const itemIndex = (activeIndex + delta) % this._items.length\n\n return itemIndex === -1\n ? this._items[this._items.length - 1] : this._items[itemIndex]\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))\n const slideEvent = $.Event(Event.SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n\n $(this._element).trigger(slideEvent)\n\n return slideEvent\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\n const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))\n $(indicators)\n .removeClass(ClassName.ACTIVE)\n\n const nextIndicator = this._indicatorsElement.children[\n this._getItemIndex(element)\n ]\n\n if (nextIndicator) {\n $(nextIndicator).addClass(ClassName.ACTIVE)\n }\n }\n }\n\n _slide(direction, element) {\n const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || activeElement &&\n this._getItemByDirection(direction, activeElement)\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n let directionalClassName\n let orderClassName\n let eventDirectionName\n\n if (direction === Direction.NEXT) {\n directionalClassName = ClassName.LEFT\n orderClassName = ClassName.NEXT\n eventDirectionName = Direction.LEFT\n } else {\n directionalClassName = ClassName.RIGHT\n orderClassName = ClassName.PREV\n eventDirectionName = Direction.RIGHT\n }\n\n if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {\n this._isSliding = false\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.isDefaultPrevented()) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n\n const slidEvent = $.Event(Event.SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n\n if ($(this._element).hasClass(ClassName.SLIDE)) {\n $(nextElement).addClass(orderClassName)\n\n Util.reflow(nextElement)\n\n $(activeElement).addClass(directionalClassName)\n $(nextElement).addClass(directionalClassName)\n\n const nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10)\n if (nextElementInterval) {\n this._config.defaultInterval = this._config.defaultInterval || this._config.interval\n this._config.interval = nextElementInterval\n } else {\n this._config.interval = this._config.defaultInterval || this._config.interval\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(activeElement)\n\n $(activeElement)\n .one(Util.TRANSITION_END, () => {\n $(nextElement)\n .removeClass(`${directionalClassName} ${orderClassName}`)\n .addClass(ClassName.ACTIVE)\n\n $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)\n\n this._isSliding = false\n\n setTimeout(() => $(this._element).trigger(slidEvent), 0)\n })\n .emulateTransitionEnd(transitionDuration)\n } else {\n $(activeElement).removeClass(ClassName.ACTIVE)\n $(nextElement).addClass(ClassName.ACTIVE)\n\n this._isSliding = false\n $(this._element).trigger(slidEvent)\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n let _config = {\n ...Default,\n ...$(this).data()\n }\n\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (!data) {\n data = new Carousel(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n data[action]()\n } else if (_config.interval && _config.ride) {\n data.pause()\n data.cycle()\n }\n })\n }\n\n static _dataApiClickHandler(event) {\n const selector = Util.getSelectorFromElement(this)\n\n if (!selector) {\n return\n }\n\n const target = $(selector)[0]\n\n if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {\n return\n }\n\n const config = {\n ...$(target).data(),\n ...$(this).data()\n }\n const slideIndex = this.getAttribute('data-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel._jQueryInterface.call($(target), config)\n\n if (slideIndex) {\n $(target).data(DATA_KEY).to(slideIndex)\n }\n\n event.preventDefault()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)\n\n$(window).on(Event.LOAD_DATA_API, () => {\n const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))\n for (let i = 0, len = carousels.length; i < len; i++) {\n const $carousel = $(carousels[i])\n Carousel._jQueryInterface.call($carousel, $carousel.data())\n }\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Carousel._jQueryInterface\n$.fn[NAME].Constructor = Carousel\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Carousel._jQueryInterface\n}\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'collapse'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n toggle : true,\n parent : ''\n}\n\nconst DefaultType = {\n toggle : 'boolean',\n parent : '(string|element)'\n}\n\nconst Event = {\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SHOW : 'show',\n COLLAPSE : 'collapse',\n COLLAPSING : 'collapsing',\n COLLAPSED : 'collapsed'\n}\n\nconst Dimension = {\n WIDTH : 'width',\n HEIGHT : 'height'\n}\n\nconst Selector = {\n ACTIVES : '.show, .collapsing',\n DATA_TOGGLE : '[data-toggle=\"collapse\"]'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Collapse {\n constructor(element, config) {\n this._isTransitioning = false\n this._element = element\n this._config = this._getConfig(config)\n this._triggerArray = [].slice.call(document.querySelectorAll(\n `[data-toggle=\"collapse\"][href=\"#${element.id}\"],` +\n `[data-toggle=\"collapse\"][data-target=\"#${element.id}\"]`\n ))\n\n const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n for (let i = 0, len = toggleList.length; i < len; i++) {\n const elem = toggleList[i]\n const selector = Util.getSelectorFromElement(elem)\n const filterElement = [].slice.call(document.querySelectorAll(selector))\n .filter((foundElem) => foundElem === element)\n\n if (selector !== null && filterElement.length > 0) {\n this._selector = selector\n this._triggerArray.push(elem)\n }\n }\n\n this._parent = this._config.parent ? this._getParent() : null\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._element, this._triggerArray)\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle() {\n if ($(this._element).hasClass(ClassName.SHOW)) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning ||\n $(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n let actives\n let activesData\n\n if (this._parent) {\n actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))\n .filter((elem) => {\n if (typeof this._config.parent === 'string') {\n return elem.getAttribute('data-parent') === this._config.parent\n }\n\n return elem.classList.contains(ClassName.COLLAPSE)\n })\n\n if (actives.length === 0) {\n actives = null\n }\n }\n\n if (actives) {\n activesData = $(actives).not(this._selector).data(DATA_KEY)\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = $.Event(Event.SHOW)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n if (actives) {\n Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')\n if (!activesData) {\n $(actives).data(DATA_KEY, null)\n }\n }\n\n const dimension = this._getDimension()\n\n $(this._element)\n .removeClass(ClassName.COLLAPSE)\n .addClass(ClassName.COLLAPSING)\n\n this._element.style[dimension] = 0\n\n if (this._triggerArray.length) {\n $(this._triggerArray)\n .removeClass(ClassName.COLLAPSED)\n .attr('aria-expanded', true)\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .addClass(ClassName.SHOW)\n\n this._element.style[dimension] = ''\n\n this.setTransitioning(false)\n\n $(this._element).trigger(Event.SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning ||\n !$(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n const startEvent = $.Event(Event.HIDE)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n Util.reflow(this._element)\n\n $(this._element)\n .addClass(ClassName.COLLAPSING)\n .removeClass(ClassName.COLLAPSE)\n .removeClass(ClassName.SHOW)\n\n const triggerArrayLength = this._triggerArray.length\n if (triggerArrayLength > 0) {\n for (let i = 0; i < triggerArrayLength; i++) {\n const trigger = this._triggerArray[i]\n const selector = Util.getSelectorFromElement(trigger)\n\n if (selector !== null) {\n const $elem = $([].slice.call(document.querySelectorAll(selector)))\n if (!$elem.hasClass(ClassName.SHOW)) {\n $(trigger).addClass(ClassName.COLLAPSED)\n .attr('aria-expanded', false)\n }\n }\n }\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n this.setTransitioning(false)\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .trigger(Event.HIDDEN)\n }\n\n this._element.style[dimension] = ''\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n }\n\n setTransitioning(isTransitioning) {\n this._isTransitioning = isTransitioning\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._parent = null\n this._element = null\n this._triggerArray = null\n this._isTransitioning = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _getDimension() {\n const hasWidth = $(this._element).hasClass(Dimension.WIDTH)\n return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT\n }\n\n _getParent() {\n let parent\n\n if (Util.isElement(this._config.parent)) {\n parent = this._config.parent\n\n // It's a jQuery object\n if (typeof this._config.parent.jquery !== 'undefined') {\n parent = this._config.parent[0]\n }\n } else {\n parent = document.querySelector(this._config.parent)\n }\n\n const selector =\n `[data-toggle=\"collapse\"][data-parent=\"${this._config.parent}\"]`\n\n const children = [].slice.call(parent.querySelectorAll(selector))\n $(children).each((i, element) => {\n this._addAriaAndCollapsedClass(\n Collapse._getTargetFromElement(element),\n [element]\n )\n })\n\n return parent\n }\n\n _addAriaAndCollapsedClass(element, triggerArray) {\n const isOpen = $(element).hasClass(ClassName.SHOW)\n\n if (triggerArray.length) {\n $(triggerArray)\n .toggleClass(ClassName.COLLAPSED, !isOpen)\n .attr('aria-expanded', isOpen)\n }\n }\n\n // Static\n\n static _getTargetFromElement(element) {\n const selector = Util.getSelectorFromElement(element)\n return selector ? document.querySelector(selector) : null\n }\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $this = $(this)\n let data = $this.data(DATA_KEY)\n const _config = {\n ...Default,\n ...$this.data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data && _config.toggle && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n if (!data) {\n data = new Collapse(this, _config)\n $this.data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.currentTarget.tagName === 'A') {\n event.preventDefault()\n }\n\n const $trigger = $(this)\n const selector = Util.getSelectorFromElement(this)\n const selectors = [].slice.call(document.querySelectorAll(selector))\n\n $(selectors).each(function () {\n const $target = $(this)\n const data = $target.data(DATA_KEY)\n const config = data ? 'toggle' : $trigger.data()\n Collapse._jQueryInterface.call($target, config)\n })\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Collapse._jQueryInterface\n$.fn[NAME].Constructor = Collapse\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Collapse._jQueryInterface\n}\n\nexport default Collapse\n","/**!\n * @fileOverview Kickass library to create and place poppers near their reference elements.\n * @version 1.14.7\n * @license\n * Copyright (c) 2016 Federico Zivolo and contributors\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\nvar isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';\n\nvar longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];\nvar timeoutDuration = 0;\nfor (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {\n if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {\n timeoutDuration = 1;\n break;\n }\n}\n\nfunction microtaskDebounce(fn) {\n var called = false;\n return function () {\n if (called) {\n return;\n }\n called = true;\n window.Promise.resolve().then(function () {\n called = false;\n fn();\n });\n };\n}\n\nfunction taskDebounce(fn) {\n var scheduled = false;\n return function () {\n if (!scheduled) {\n scheduled = true;\n setTimeout(function () {\n scheduled = false;\n fn();\n }, timeoutDuration);\n }\n };\n}\n\nvar supportsMicroTasks = isBrowser && window.Promise;\n\n/**\n* Create a debounced version of a method, that's asynchronously deferred\n* but called in the minimum time possible.\n*\n* @method\n* @memberof Popper.Utils\n* @argument {Function} fn\n* @returns {Function}\n*/\nvar debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;\n\n/**\n * Check if the given variable is a function\n * @method\n * @memberof Popper.Utils\n * @argument {Any} functionToCheck - variable to check\n * @returns {Boolean} answer to: is a function?\n */\nfunction isFunction(functionToCheck) {\n var getType = {};\n return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';\n}\n\n/**\n * Get CSS computed property of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Eement} element\n * @argument {String} property\n */\nfunction getStyleComputedProperty(element, property) {\n if (element.nodeType !== 1) {\n return [];\n }\n // NOTE: 1 DOM access here\n var window = element.ownerDocument.defaultView;\n var css = window.getComputedStyle(element, null);\n return property ? css[property] : css;\n}\n\n/**\n * Returns the parentNode or the host of the element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Element} parent\n */\nfunction getParentNode(element) {\n if (element.nodeName === 'HTML') {\n return element;\n }\n return element.parentNode || element.host;\n}\n\n/**\n * Returns the scrolling parent of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Element} scroll parent\n */\nfunction getScrollParent(element) {\n // Return body, `getScroll` will take care to get the correct `scrollTop` from it\n if (!element) {\n return document.body;\n }\n\n switch (element.nodeName) {\n case 'HTML':\n case 'BODY':\n return element.ownerDocument.body;\n case '#document':\n return element.body;\n }\n\n // Firefox want us to check `-x` and `-y` variations as well\n\n var _getStyleComputedProp = getStyleComputedProperty(element),\n overflow = _getStyleComputedProp.overflow,\n overflowX = _getStyleComputedProp.overflowX,\n overflowY = _getStyleComputedProp.overflowY;\n\n if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) {\n return element;\n }\n\n return getScrollParent(getParentNode(element));\n}\n\nvar isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode);\nvar isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent);\n\n/**\n * Determines if the browser is Internet Explorer\n * @method\n * @memberof Popper.Utils\n * @param {Number} version to check\n * @returns {Boolean} isIE\n */\nfunction isIE(version) {\n if (version === 11) {\n return isIE11;\n }\n if (version === 10) {\n return isIE10;\n }\n return isIE11 || isIE10;\n}\n\n/**\n * Returns the offset parent of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Element} offset parent\n */\nfunction getOffsetParent(element) {\n if (!element) {\n return document.documentElement;\n }\n\n var noOffsetParent = isIE(10) ? document.body : null;\n\n // NOTE: 1 DOM access here\n var offsetParent = element.offsetParent || null;\n // Skip hidden elements which don't have an offsetParent\n while (offsetParent === noOffsetParent && element.nextElementSibling) {\n offsetParent = (element = element.nextElementSibling).offsetParent;\n }\n\n var nodeName = offsetParent && offsetParent.nodeName;\n\n if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {\n return element ? element.ownerDocument.documentElement : document.documentElement;\n }\n\n // .offsetParent will return the closest TH, TD or TABLE in case\n // no offsetParent is present, I hate this job...\n if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {\n return getOffsetParent(offsetParent);\n }\n\n return offsetParent;\n}\n\nfunction isOffsetContainer(element) {\n var nodeName = element.nodeName;\n\n if (nodeName === 'BODY') {\n return false;\n }\n return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element;\n}\n\n/**\n * Finds the root node (document, shadowDOM root) of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} node\n * @returns {Element} root node\n */\nfunction getRoot(node) {\n if (node.parentNode !== null) {\n return getRoot(node.parentNode);\n }\n\n return node;\n}\n\n/**\n * Finds the offset parent common to the two provided nodes\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element1\n * @argument {Element} element2\n * @returns {Element} common offset parent\n */\nfunction findCommonOffsetParent(element1, element2) {\n // This check is needed to avoid errors in case one of the elements isn't defined for any reason\n if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {\n return document.documentElement;\n }\n\n // Here we make sure to give as \"start\" the element that comes first in the DOM\n var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;\n var start = order ? element1 : element2;\n var end = order ? element2 : element1;\n\n // Get common ancestor container\n var range = document.createRange();\n range.setStart(start, 0);\n range.setEnd(end, 0);\n var commonAncestorContainer = range.commonAncestorContainer;\n\n // Both nodes are inside #document\n\n if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {\n if (isOffsetContainer(commonAncestorContainer)) {\n return commonAncestorContainer;\n }\n\n return getOffsetParent(commonAncestorContainer);\n }\n\n // one of the nodes is inside shadowDOM, find which one\n var element1root = getRoot(element1);\n if (element1root.host) {\n return findCommonOffsetParent(element1root.host, element2);\n } else {\n return findCommonOffsetParent(element1, getRoot(element2).host);\n }\n}\n\n/**\n * Gets the scroll value of the given element in the given side (top and left)\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @argument {String} side `top` or `left`\n * @returns {number} amount of scrolled pixels\n */\nfunction getScroll(element) {\n var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';\n\n var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';\n var nodeName = element.nodeName;\n\n if (nodeName === 'BODY' || nodeName === 'HTML') {\n var html = element.ownerDocument.documentElement;\n var scrollingElement = element.ownerDocument.scrollingElement || html;\n return scrollingElement[upperSide];\n }\n\n return element[upperSide];\n}\n\n/*\n * Sum or subtract the element scroll values (left and top) from a given rect object\n * @method\n * @memberof Popper.Utils\n * @param {Object} rect - Rect object you want to change\n * @param {HTMLElement} element - The element from the function reads the scroll values\n * @param {Boolean} subtract - set to true if you want to subtract the scroll values\n * @return {Object} rect - The modifier rect object\n */\nfunction includeScroll(rect, element) {\n var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n var scrollTop = getScroll(element, 'top');\n var scrollLeft = getScroll(element, 'left');\n var modifier = subtract ? -1 : 1;\n rect.top += scrollTop * modifier;\n rect.bottom += scrollTop * modifier;\n rect.left += scrollLeft * modifier;\n rect.right += scrollLeft * modifier;\n return rect;\n}\n\n/*\n * Helper to detect borders of a given element\n * @method\n * @memberof Popper.Utils\n * @param {CSSStyleDeclaration} styles\n * Result of `getStyleComputedProperty` on the given element\n * @param {String} axis - `x` or `y`\n * @return {number} borders - The borders size of the given axis\n */\n\nfunction getBordersSize(styles, axis) {\n var sideA = axis === 'x' ? 'Left' : 'Top';\n var sideB = sideA === 'Left' ? 'Right' : 'Bottom';\n\n return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10);\n}\n\nfunction getSize(axis, body, html, computedStyle) {\n return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0);\n}\n\nfunction getWindowSizes(document) {\n var body = document.body;\n var html = document.documentElement;\n var computedStyle = isIE(10) && getComputedStyle(html);\n\n return {\n height: getSize('Height', body, html, computedStyle),\n width: getSize('Width', body, html, computedStyle)\n };\n}\n\nvar classCallCheck = function (instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n};\n\nvar createClass = function () {\n function defineProperties(target, props) {\n for (var i = 0; i < props.length; i++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if (\"value\" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, descriptor.key, descriptor);\n }\n }\n\n return function (Constructor, protoProps, staticProps) {\n if (protoProps) defineProperties(Constructor.prototype, protoProps);\n if (staticProps) defineProperties(Constructor, staticProps);\n return Constructor;\n };\n}();\n\n\n\n\n\nvar defineProperty = function (obj, key, value) {\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n\n return obj;\n};\n\nvar _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n};\n\n/**\n * Given element offsets, generate an output similar to getBoundingClientRect\n * @method\n * @memberof Popper.Utils\n * @argument {Object} offsets\n * @returns {Object} ClientRect like output\n */\nfunction getClientRect(offsets) {\n return _extends({}, offsets, {\n right: offsets.left + offsets.width,\n bottom: offsets.top + offsets.height\n });\n}\n\n/**\n * Get bounding client rect of given element\n * @method\n * @memberof Popper.Utils\n * @param {HTMLElement} element\n * @return {Object} client rect\n */\nfunction getBoundingClientRect(element) {\n var rect = {};\n\n // IE10 10 FIX: Please, don't ask, the element isn't\n // considered in DOM in some circumstances...\n // This isn't reproducible in IE10 compatibility mode of IE11\n try {\n if (isIE(10)) {\n rect = element.getBoundingClientRect();\n var scrollTop = getScroll(element, 'top');\n var scrollLeft = getScroll(element, 'left');\n rect.top += scrollTop;\n rect.left += scrollLeft;\n rect.bottom += scrollTop;\n rect.right += scrollLeft;\n } else {\n rect = element.getBoundingClientRect();\n }\n } catch (e) {}\n\n var result = {\n left: rect.left,\n top: rect.top,\n width: rect.right - rect.left,\n height: rect.bottom - rect.top\n };\n\n // subtract scrollbar size from sizes\n var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {};\n var width = sizes.width || element.clientWidth || result.right - result.left;\n var height = sizes.height || element.clientHeight || result.bottom - result.top;\n\n var horizScrollbar = element.offsetWidth - width;\n var vertScrollbar = element.offsetHeight - height;\n\n // if an hypothetical scrollbar is detected, we must be sure it's not a `border`\n // we make this check conditional for performance reasons\n if (horizScrollbar || vertScrollbar) {\n var styles = getStyleComputedProperty(element);\n horizScrollbar -= getBordersSize(styles, 'x');\n vertScrollbar -= getBordersSize(styles, 'y');\n\n result.width -= horizScrollbar;\n result.height -= vertScrollbar;\n }\n\n return getClientRect(result);\n}\n\nfunction getOffsetRectRelativeToArbitraryNode(children, parent) {\n var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n var isIE10 = isIE(10);\n var isHTML = parent.nodeName === 'HTML';\n var childrenRect = getBoundingClientRect(children);\n var parentRect = getBoundingClientRect(parent);\n var scrollParent = getScrollParent(children);\n\n var styles = getStyleComputedProperty(parent);\n var borderTopWidth = parseFloat(styles.borderTopWidth, 10);\n var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10);\n\n // In cases where the parent is fixed, we must ignore negative scroll in offset calc\n if (fixedPosition && isHTML) {\n parentRect.top = Math.max(parentRect.top, 0);\n parentRect.left = Math.max(parentRect.left, 0);\n }\n var offsets = getClientRect({\n top: childrenRect.top - parentRect.top - borderTopWidth,\n left: childrenRect.left - parentRect.left - borderLeftWidth,\n width: childrenRect.width,\n height: childrenRect.height\n });\n offsets.marginTop = 0;\n offsets.marginLeft = 0;\n\n // Subtract margins of documentElement in case it's being used as parent\n // we do this only on HTML because it's the only element that behaves\n // differently when margins are applied to it. The margins are included in\n // the box of the documentElement, in the other cases not.\n if (!isIE10 && isHTML) {\n var marginTop = parseFloat(styles.marginTop, 10);\n var marginLeft = parseFloat(styles.marginLeft, 10);\n\n offsets.top -= borderTopWidth - marginTop;\n offsets.bottom -= borderTopWidth - marginTop;\n offsets.left -= borderLeftWidth - marginLeft;\n offsets.right -= borderLeftWidth - marginLeft;\n\n // Attach marginTop and marginLeft because in some circumstances we may need them\n offsets.marginTop = marginTop;\n offsets.marginLeft = marginLeft;\n }\n\n if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {\n offsets = includeScroll(offsets, parent);\n }\n\n return offsets;\n}\n\nfunction getViewportOffsetRectRelativeToArtbitraryNode(element) {\n var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n var html = element.ownerDocument.documentElement;\n var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);\n var width = Math.max(html.clientWidth, window.innerWidth || 0);\n var height = Math.max(html.clientHeight, window.innerHeight || 0);\n\n var scrollTop = !excludeScroll ? getScroll(html) : 0;\n var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0;\n\n var offset = {\n top: scrollTop - relativeOffset.top + relativeOffset.marginTop,\n left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,\n width: width,\n height: height\n };\n\n return getClientRect(offset);\n}\n\n/**\n * Check if the given element is fixed or is inside a fixed parent\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @argument {Element} customContainer\n * @returns {Boolean} answer to \"isFixed?\"\n */\nfunction isFixed(element) {\n var nodeName = element.nodeName;\n if (nodeName === 'BODY' || nodeName === 'HTML') {\n return false;\n }\n if (getStyleComputedProperty(element, 'position') === 'fixed') {\n return true;\n }\n var parentNode = getParentNode(element);\n if (!parentNode) {\n return false;\n }\n return isFixed(parentNode);\n}\n\n/**\n * Finds the first parent of an element that has a transformed property defined\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Element} first transformed parent or documentElement\n */\n\nfunction getFixedPositionOffsetParent(element) {\n // This check is needed to avoid errors in case one of the elements isn't defined for any reason\n if (!element || !element.parentElement || isIE()) {\n return document.documentElement;\n }\n var el = element.parentElement;\n while (el && getStyleComputedProperty(el, 'transform') === 'none') {\n el = el.parentElement;\n }\n return el || document.documentElement;\n}\n\n/**\n * Computed the boundaries limits and return them\n * @method\n * @memberof Popper.Utils\n * @param {HTMLElement} popper\n * @param {HTMLElement} reference\n * @param {number} padding\n * @param {HTMLElement} boundariesElement - Element used to define the boundaries\n * @param {Boolean} fixedPosition - Is in fixed position mode\n * @returns {Object} Coordinates of the boundaries\n */\nfunction getBoundaries(popper, reference, padding, boundariesElement) {\n var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n\n // NOTE: 1 DOM access here\n\n var boundaries = { top: 0, left: 0 };\n var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference);\n\n // Handle viewport case\n if (boundariesElement === 'viewport') {\n boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition);\n } else {\n // Handle other cases based on DOM element used as boundaries\n var boundariesNode = void 0;\n if (boundariesElement === 'scrollParent') {\n boundariesNode = getScrollParent(getParentNode(reference));\n if (boundariesNode.nodeName === 'BODY') {\n boundariesNode = popper.ownerDocument.documentElement;\n }\n } else if (boundariesElement === 'window') {\n boundariesNode = popper.ownerDocument.documentElement;\n } else {\n boundariesNode = boundariesElement;\n }\n\n var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition);\n\n // In case of HTML, we need a different computation\n if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {\n var _getWindowSizes = getWindowSizes(popper.ownerDocument),\n height = _getWindowSizes.height,\n width = _getWindowSizes.width;\n\n boundaries.top += offsets.top - offsets.marginTop;\n boundaries.bottom = height + offsets.top;\n boundaries.left += offsets.left - offsets.marginLeft;\n boundaries.right = width + offsets.left;\n } else {\n // for all the other DOM elements, this one is good\n boundaries = offsets;\n }\n }\n\n // Add paddings\n padding = padding || 0;\n var isPaddingNumber = typeof padding === 'number';\n boundaries.left += isPaddingNumber ? padding : padding.left || 0;\n boundaries.top += isPaddingNumber ? padding : padding.top || 0;\n boundaries.right -= isPaddingNumber ? padding : padding.right || 0;\n boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0;\n\n return boundaries;\n}\n\nfunction getArea(_ref) {\n var width = _ref.width,\n height = _ref.height;\n\n return width * height;\n}\n\n/**\n * Utility used to transform the `auto` placement to the placement with more\n * available space.\n * @method\n * @memberof Popper.Utils\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) {\n var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;\n\n if (placement.indexOf('auto') === -1) {\n return placement;\n }\n\n var boundaries = getBoundaries(popper, reference, padding, boundariesElement);\n\n var rects = {\n top: {\n width: boundaries.width,\n height: refRect.top - boundaries.top\n },\n right: {\n width: boundaries.right - refRect.right,\n height: boundaries.height\n },\n bottom: {\n width: boundaries.width,\n height: boundaries.bottom - refRect.bottom\n },\n left: {\n width: refRect.left - boundaries.left,\n height: boundaries.height\n }\n };\n\n var sortedAreas = Object.keys(rects).map(function (key) {\n return _extends({\n key: key\n }, rects[key], {\n area: getArea(rects[key])\n });\n }).sort(function (a, b) {\n return b.area - a.area;\n });\n\n var filteredAreas = sortedAreas.filter(function (_ref2) {\n var width = _ref2.width,\n height = _ref2.height;\n return width >= popper.clientWidth && height >= popper.clientHeight;\n });\n\n var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;\n\n var variation = placement.split('-')[1];\n\n return computedPlacement + (variation ? '-' + variation : '');\n}\n\n/**\n * Get offsets to the reference element\n * @method\n * @memberof Popper.Utils\n * @param {Object} state\n * @param {Element} popper - the popper element\n * @param {Element} reference - the reference element (the popper will be relative to this)\n * @param {Element} fixedPosition - is in fixed position mode\n * @returns {Object} An object containing the offsets which will be applied to the popper\n */\nfunction getReferenceOffsets(state, popper, reference) {\n var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;\n\n var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference);\n return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition);\n}\n\n/**\n * Get the outer sizes of the given element (offset size + margins)\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Object} object containing width and height properties\n */\nfunction getOuterSizes(element) {\n var window = element.ownerDocument.defaultView;\n var styles = window.getComputedStyle(element);\n var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0);\n var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0);\n var result = {\n width: element.offsetWidth + y,\n height: element.offsetHeight + x\n };\n return result;\n}\n\n/**\n * Get the opposite placement of the given one\n * @method\n * @memberof Popper.Utils\n * @argument {String} placement\n * @returns {String} flipped placement\n */\nfunction getOppositePlacement(placement) {\n var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}\n\n/**\n * Get offsets to the popper\n * @method\n * @memberof Popper.Utils\n * @param {Object} position - CSS position the Popper will get applied\n * @param {HTMLElement} popper - the popper element\n * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)\n * @param {String} placement - one of the valid placement options\n * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper\n */\nfunction getPopperOffsets(popper, referenceOffsets, placement) {\n placement = placement.split('-')[0];\n\n // Get popper node sizes\n var popperRect = getOuterSizes(popper);\n\n // Add position, width and height to our offsets object\n var popperOffsets = {\n width: popperRect.width,\n height: popperRect.height\n };\n\n // depending by the popper placement we have to compute its offsets slightly differently\n var isHoriz = ['right', 'left'].indexOf(placement) !== -1;\n var mainSide = isHoriz ? 'top' : 'left';\n var secondarySide = isHoriz ? 'left' : 'top';\n var measurement = isHoriz ? 'height' : 'width';\n var secondaryMeasurement = !isHoriz ? 'height' : 'width';\n\n popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;\n if (placement === secondarySide) {\n popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];\n } else {\n popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];\n }\n\n return popperOffsets;\n}\n\n/**\n * Mimics the `find` method of Array\n * @method\n * @memberof Popper.Utils\n * @argument {Array} arr\n * @argument prop\n * @argument value\n * @returns index or -1\n */\nfunction find(arr, check) {\n // use native find if supported\n if (Array.prototype.find) {\n return arr.find(check);\n }\n\n // use `filter` to obtain the same behavior of `find`\n return arr.filter(check)[0];\n}\n\n/**\n * Return the index of the matching object\n * @method\n * @memberof Popper.Utils\n * @argument {Array} arr\n * @argument prop\n * @argument value\n * @returns index or -1\n */\nfunction findIndex(arr, prop, value) {\n // use native findIndex if supported\n if (Array.prototype.findIndex) {\n return arr.findIndex(function (cur) {\n return cur[prop] === value;\n });\n }\n\n // use `find` + `indexOf` if `findIndex` isn't supported\n var match = find(arr, function (obj) {\n return obj[prop] === value;\n });\n return arr.indexOf(match);\n}\n\n/**\n * Loop trough the list of modifiers and run them in order,\n * each of them will then edit the data object.\n * @method\n * @memberof Popper.Utils\n * @param {dataObject} data\n * @param {Array} modifiers\n * @param {String} ends - Optional modifier name used as stopper\n * @returns {dataObject}\n */\nfunction runModifiers(modifiers, data, ends) {\n var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));\n\n modifiersToRun.forEach(function (modifier) {\n if (modifier['function']) {\n // eslint-disable-line dot-notation\n console.warn('`modifier.function` is deprecated, use `modifier.fn`!');\n }\n var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation\n if (modifier.enabled && isFunction(fn)) {\n // Add properties to offsets to make them a complete clientRect object\n // we do this before each modifier to make sure the previous one doesn't\n // mess with these values\n data.offsets.popper = getClientRect(data.offsets.popper);\n data.offsets.reference = getClientRect(data.offsets.reference);\n\n data = fn(data, modifier);\n }\n });\n\n return data;\n}\n\n/**\n * Updates the position of the popper, computing the new offsets and applying\n * the new style.
\n * Prefer `scheduleUpdate` over `update` because of performance reasons.\n * @method\n * @memberof Popper\n */\nfunction update() {\n // if popper is destroyed, don't perform any further update\n if (this.state.isDestroyed) {\n return;\n }\n\n var data = {\n instance: this,\n styles: {},\n arrowStyles: {},\n attributes: {},\n flipped: false,\n offsets: {}\n };\n\n // compute reference element offsets\n data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed);\n\n // compute auto placement, store placement inside the data object,\n // modifiers will be able to edit `placement` if needed\n // and refer to originalPlacement to know the original value\n data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding);\n\n // store the computed placement inside `originalPlacement`\n data.originalPlacement = data.placement;\n\n data.positionFixed = this.options.positionFixed;\n\n // compute the popper offsets\n data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement);\n\n data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute';\n\n // run the modifiers\n data = runModifiers(this.modifiers, data);\n\n // the first `update` will call `onCreate` callback\n // the other ones will call `onUpdate` callback\n if (!this.state.isCreated) {\n this.state.isCreated = true;\n this.options.onCreate(data);\n } else {\n this.options.onUpdate(data);\n }\n}\n\n/**\n * Helper used to know if the given modifier is enabled.\n * @method\n * @memberof Popper.Utils\n * @returns {Boolean}\n */\nfunction isModifierEnabled(modifiers, modifierName) {\n return modifiers.some(function (_ref) {\n var name = _ref.name,\n enabled = _ref.enabled;\n return enabled && name === modifierName;\n });\n}\n\n/**\n * Get the prefixed supported property name\n * @method\n * @memberof Popper.Utils\n * @argument {String} property (camelCase)\n * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)\n */\nfunction getSupportedPropertyName(property) {\n var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];\n var upperProp = property.charAt(0).toUpperCase() + property.slice(1);\n\n for (var i = 0; i < prefixes.length; i++) {\n var prefix = prefixes[i];\n var toCheck = prefix ? '' + prefix + upperProp : property;\n if (typeof document.body.style[toCheck] !== 'undefined') {\n return toCheck;\n }\n }\n return null;\n}\n\n/**\n * Destroys the popper.\n * @method\n * @memberof Popper\n */\nfunction destroy() {\n this.state.isDestroyed = true;\n\n // touch DOM only if `applyStyle` modifier is enabled\n if (isModifierEnabled(this.modifiers, 'applyStyle')) {\n this.popper.removeAttribute('x-placement');\n this.popper.style.position = '';\n this.popper.style.top = '';\n this.popper.style.left = '';\n this.popper.style.right = '';\n this.popper.style.bottom = '';\n this.popper.style.willChange = '';\n this.popper.style[getSupportedPropertyName('transform')] = '';\n }\n\n this.disableEventListeners();\n\n // remove the popper if user explicity asked for the deletion on destroy\n // do not use `remove` because IE11 doesn't support it\n if (this.options.removeOnDestroy) {\n this.popper.parentNode.removeChild(this.popper);\n }\n return this;\n}\n\n/**\n * Get the window associated with the element\n * @argument {Element} element\n * @returns {Window}\n */\nfunction getWindow(element) {\n var ownerDocument = element.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView : window;\n}\n\nfunction attachToScrollParents(scrollParent, event, callback, scrollParents) {\n var isBody = scrollParent.nodeName === 'BODY';\n var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;\n target.addEventListener(event, callback, { passive: true });\n\n if (!isBody) {\n attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);\n }\n scrollParents.push(target);\n}\n\n/**\n * Setup needed event listeners used to update the popper position\n * @method\n * @memberof Popper.Utils\n * @private\n */\nfunction setupEventListeners(reference, options, state, updateBound) {\n // Resize event listener on window\n state.updateBound = updateBound;\n getWindow(reference).addEventListener('resize', state.updateBound, { passive: true });\n\n // Scroll event listener on scroll parents\n var scrollElement = getScrollParent(reference);\n attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);\n state.scrollElement = scrollElement;\n state.eventsEnabled = true;\n\n return state;\n}\n\n/**\n * It will add resize/scroll events and start recalculating\n * position of the popper element when they are triggered.\n * @method\n * @memberof Popper\n */\nfunction enableEventListeners() {\n if (!this.state.eventsEnabled) {\n this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);\n }\n}\n\n/**\n * Remove event listeners used to update the popper position\n * @method\n * @memberof Popper.Utils\n * @private\n */\nfunction removeEventListeners(reference, state) {\n // Remove resize event listener on window\n getWindow(reference).removeEventListener('resize', state.updateBound);\n\n // Remove scroll event listener on scroll parents\n state.scrollParents.forEach(function (target) {\n target.removeEventListener('scroll', state.updateBound);\n });\n\n // Reset state\n state.updateBound = null;\n state.scrollParents = [];\n state.scrollElement = null;\n state.eventsEnabled = false;\n return state;\n}\n\n/**\n * It will remove resize/scroll events and won't recalculate popper position\n * when they are triggered. It also won't trigger `onUpdate` callback anymore,\n * unless you call `update` method manually.\n * @method\n * @memberof Popper\n */\nfunction disableEventListeners() {\n if (this.state.eventsEnabled) {\n cancelAnimationFrame(this.scheduleUpdate);\n this.state = removeEventListeners(this.reference, this.state);\n }\n}\n\n/**\n * Tells if a given input is a number\n * @method\n * @memberof Popper.Utils\n * @param {*} input to check\n * @return {Boolean}\n */\nfunction isNumeric(n) {\n return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);\n}\n\n/**\n * Set the style to the given popper\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element - Element to apply the style to\n * @argument {Object} styles\n * Object with a list of properties and values which will be applied to the element\n */\nfunction setStyles(element, styles) {\n Object.keys(styles).forEach(function (prop) {\n var unit = '';\n // add unit if the value is numeric and is one of the following\n if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {\n unit = 'px';\n }\n element.style[prop] = styles[prop] + unit;\n });\n}\n\n/**\n * Set the attributes to the given popper\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element - Element to apply the attributes to\n * @argument {Object} styles\n * Object with a list of properties and values which will be applied to the element\n */\nfunction setAttributes(element, attributes) {\n Object.keys(attributes).forEach(function (prop) {\n var value = attributes[prop];\n if (value !== false) {\n element.setAttribute(prop, attributes[prop]);\n } else {\n element.removeAttribute(prop);\n }\n });\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} data.styles - List of style properties - values to apply to popper element\n * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The same data object\n */\nfunction applyStyle(data) {\n // any property present in `data.styles` will be applied to the popper,\n // in this way we can make the 3rd party modifiers add custom styles to it\n // Be aware, modifiers could override the properties defined in the previous\n // lines of this modifier!\n setStyles(data.instance.popper, data.styles);\n\n // any property present in `data.attributes` will be applied to the popper,\n // they will be set as HTML attributes of the element\n setAttributes(data.instance.popper, data.attributes);\n\n // if arrowElement is defined and arrowStyles has some properties\n if (data.arrowElement && Object.keys(data.arrowStyles).length) {\n setStyles(data.arrowElement, data.arrowStyles);\n }\n\n return data;\n}\n\n/**\n * Set the x-placement attribute before everything else because it could be used\n * to add margins to the popper margins needs to be calculated to get the\n * correct popper offsets.\n * @method\n * @memberof Popper.modifiers\n * @param {HTMLElement} reference - The reference element used to position the popper\n * @param {HTMLElement} popper - The HTML element used as popper\n * @param {Object} options - Popper.js options\n */\nfunction applyStyleOnLoad(reference, popper, options, modifierOptions, state) {\n // compute reference element offsets\n var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed);\n\n // compute auto placement, store placement inside the data object,\n // modifiers will be able to edit `placement` if needed\n // and refer to originalPlacement to know the original value\n var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding);\n\n popper.setAttribute('x-placement', placement);\n\n // Apply `position` to popper before anything else because\n // without the position applied we can't guarantee correct computations\n setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' });\n\n return options;\n}\n\n/**\n * @function\n * @memberof Popper.Utils\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Boolean} shouldRound - If the offsets should be rounded at all\n * @returns {Object} The popper's position offsets rounded\n *\n * The tale of pixel-perfect positioning. It's still not 100% perfect, but as\n * good as it can be within reason.\n * Discussion here: https://github.com/FezVrasta/popper.js/pull/715\n *\n * Low DPI screens cause a popper to be blurry if not using full pixels (Safari\n * as well on High DPI screens).\n *\n * Firefox prefers no rounding for positioning and does not have blurriness on\n * high DPI screens.\n *\n * Only horizontal placement and left/right values need to be considered.\n */\nfunction getRoundedOffsets(data, shouldRound) {\n var _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n var round = Math.round,\n floor = Math.floor;\n\n var noRound = function noRound(v) {\n return v;\n };\n\n var referenceWidth = round(reference.width);\n var popperWidth = round(popper.width);\n\n var isVertical = ['left', 'right'].indexOf(data.placement) !== -1;\n var isVariation = data.placement.indexOf('-') !== -1;\n var sameWidthParity = referenceWidth % 2 === popperWidth % 2;\n var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1;\n\n var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor;\n var verticalToInteger = !shouldRound ? noRound : round;\n\n return {\n left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left),\n top: verticalToInteger(popper.top),\n bottom: verticalToInteger(popper.bottom),\n right: horizontalToInteger(popper.right)\n };\n}\n\nvar isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent);\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction computeStyle(data, options) {\n var x = options.x,\n y = options.y;\n var popper = data.offsets.popper;\n\n // Remove this legacy support in Popper.js v2\n\n var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) {\n return modifier.name === 'applyStyle';\n }).gpuAcceleration;\n if (legacyGpuAccelerationOption !== undefined) {\n console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');\n }\n var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration;\n\n var offsetParent = getOffsetParent(data.instance.popper);\n var offsetParentRect = getBoundingClientRect(offsetParent);\n\n // Styles\n var styles = {\n position: popper.position\n };\n\n var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox);\n\n var sideA = x === 'bottom' ? 'top' : 'bottom';\n var sideB = y === 'right' ? 'left' : 'right';\n\n // if gpuAcceleration is set to `true` and transform is supported,\n // we use `translate3d` to apply the position to the popper we\n // automatically use the supported prefixed version if needed\n var prefixedProperty = getSupportedPropertyName('transform');\n\n // now, let's make a step back and look at this code closely (wtf?)\n // If the content of the popper grows once it's been positioned, it\n // may happen that the popper gets misplaced because of the new content\n // overflowing its reference element\n // To avoid this problem, we provide two options (x and y), which allow\n // the consumer to define the offset origin.\n // If we position a popper on top of a reference element, we can set\n // `x` to `top` to make the popper grow towards its top instead of\n // its bottom.\n var left = void 0,\n top = void 0;\n if (sideA === 'bottom') {\n // when offsetParent is the positioning is relative to the bottom of the screen (excluding the scrollbar)\n // and not the bottom of the html element\n if (offsetParent.nodeName === 'HTML') {\n top = -offsetParent.clientHeight + offsets.bottom;\n } else {\n top = -offsetParentRect.height + offsets.bottom;\n }\n } else {\n top = offsets.top;\n }\n if (sideB === 'right') {\n if (offsetParent.nodeName === 'HTML') {\n left = -offsetParent.clientWidth + offsets.right;\n } else {\n left = -offsetParentRect.width + offsets.right;\n }\n } else {\n left = offsets.left;\n }\n if (gpuAcceleration && prefixedProperty) {\n styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';\n styles[sideA] = 0;\n styles[sideB] = 0;\n styles.willChange = 'transform';\n } else {\n // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties\n var invertTop = sideA === 'bottom' ? -1 : 1;\n var invertLeft = sideB === 'right' ? -1 : 1;\n styles[sideA] = top * invertTop;\n styles[sideB] = left * invertLeft;\n styles.willChange = sideA + ', ' + sideB;\n }\n\n // Attributes\n var attributes = {\n 'x-placement': data.placement\n };\n\n // Update `data` attributes, styles and arrowStyles\n data.attributes = _extends({}, attributes, data.attributes);\n data.styles = _extends({}, styles, data.styles);\n data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles);\n\n return data;\n}\n\n/**\n * Helper used to know if the given modifier depends from another one.
\n * It checks if the needed modifier is listed and enabled.\n * @method\n * @memberof Popper.Utils\n * @param {Array} modifiers - list of modifiers\n * @param {String} requestingName - name of requesting modifier\n * @param {String} requestedName - name of requested modifier\n * @returns {Boolean}\n */\nfunction isModifierRequired(modifiers, requestingName, requestedName) {\n var requesting = find(modifiers, function (_ref) {\n var name = _ref.name;\n return name === requestingName;\n });\n\n var isRequired = !!requesting && modifiers.some(function (modifier) {\n return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;\n });\n\n if (!isRequired) {\n var _requesting = '`' + requestingName + '`';\n var requested = '`' + requestedName + '`';\n console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!');\n }\n return isRequired;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction arrow(data, options) {\n var _data$offsets$arrow;\n\n // arrow depends on keepTogether in order to work\n if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {\n return data;\n }\n\n var arrowElement = options.element;\n\n // if arrowElement is a string, suppose it's a CSS selector\n if (typeof arrowElement === 'string') {\n arrowElement = data.instance.popper.querySelector(arrowElement);\n\n // if arrowElement is not found, don't run the modifier\n if (!arrowElement) {\n return data;\n }\n } else {\n // if the arrowElement isn't a query selector we must check that the\n // provided DOM node is child of its popper node\n if (!data.instance.popper.contains(arrowElement)) {\n console.warn('WARNING: `arrow.element` must be child of its popper element!');\n return data;\n }\n }\n\n var placement = data.placement.split('-')[0];\n var _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var isVertical = ['left', 'right'].indexOf(placement) !== -1;\n\n var len = isVertical ? 'height' : 'width';\n var sideCapitalized = isVertical ? 'Top' : 'Left';\n var side = sideCapitalized.toLowerCase();\n var altSide = isVertical ? 'left' : 'top';\n var opSide = isVertical ? 'bottom' : 'right';\n var arrowElementSize = getOuterSizes(arrowElement)[len];\n\n //\n // extends keepTogether behavior making sure the popper and its\n // reference have enough pixels in conjunction\n //\n\n // top/left side\n if (reference[opSide] - arrowElementSize < popper[side]) {\n data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);\n }\n // bottom/right side\n if (reference[side] + arrowElementSize > popper[opSide]) {\n data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];\n }\n data.offsets.popper = getClientRect(data.offsets.popper);\n\n // compute center of the popper\n var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;\n\n // Compute the sideValue using the updated popper offsets\n // take popper margin in account because we don't have this info available\n var css = getStyleComputedProperty(data.instance.popper);\n var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10);\n var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10);\n var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;\n\n // prevent arrowElement from being placed not contiguously to its popper\n sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);\n\n data.arrowElement = arrowElement;\n data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow);\n\n return data;\n}\n\n/**\n * Get the opposite placement variation of the given one\n * @method\n * @memberof Popper.Utils\n * @argument {String} placement variation\n * @returns {String} flipped placement variation\n */\nfunction getOppositeVariation(variation) {\n if (variation === 'end') {\n return 'start';\n } else if (variation === 'start') {\n return 'end';\n }\n return variation;\n}\n\n/**\n * List of accepted placements to use as values of the `placement` option.
\n * Valid placements are:\n * - `auto`\n * - `top`\n * - `right`\n * - `bottom`\n * - `left`\n *\n * Each placement can have a variation from this list:\n * - `-start`\n * - `-end`\n *\n * Variations are interpreted easily if you think of them as the left to right\n * written languages. Horizontally (`top` and `bottom`), `start` is left and `end`\n * is right.
\n * Vertically (`left` and `right`), `start` is top and `end` is bottom.\n *\n * Some valid examples are:\n * - `top-end` (on top of reference, right aligned)\n * - `right-start` (on right of reference, top aligned)\n * - `bottom` (on bottom, centered)\n * - `auto-end` (on the side with more space available, alignment depends by placement)\n *\n * @static\n * @type {Array}\n * @enum {String}\n * @readonly\n * @method placements\n * @memberof Popper\n */\nvar placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start'];\n\n// Get rid of `auto` `auto-start` and `auto-end`\nvar validPlacements = placements.slice(3);\n\n/**\n * Given an initial placement, returns all the subsequent placements\n * clockwise (or counter-clockwise).\n *\n * @method\n * @memberof Popper.Utils\n * @argument {String} placement - A valid placement (it accepts variations)\n * @argument {Boolean} counter - Set to true to walk the placements counterclockwise\n * @returns {Array} placements including their variations\n */\nfunction clockwise(placement) {\n var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n var index = validPlacements.indexOf(placement);\n var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));\n return counter ? arr.reverse() : arr;\n}\n\nvar BEHAVIORS = {\n FLIP: 'flip',\n CLOCKWISE: 'clockwise',\n COUNTERCLOCKWISE: 'counterclockwise'\n};\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction flip(data, options) {\n // if `inner` modifier is enabled, we can't use the `flip` modifier\n if (isModifierEnabled(data.instance.modifiers, 'inner')) {\n return data;\n }\n\n if (data.flipped && data.placement === data.originalPlacement) {\n // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides\n return data;\n }\n\n var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed);\n\n var placement = data.placement.split('-')[0];\n var placementOpposite = getOppositePlacement(placement);\n var variation = data.placement.split('-')[1] || '';\n\n var flipOrder = [];\n\n switch (options.behavior) {\n case BEHAVIORS.FLIP:\n flipOrder = [placement, placementOpposite];\n break;\n case BEHAVIORS.CLOCKWISE:\n flipOrder = clockwise(placement);\n break;\n case BEHAVIORS.COUNTERCLOCKWISE:\n flipOrder = clockwise(placement, true);\n break;\n default:\n flipOrder = options.behavior;\n }\n\n flipOrder.forEach(function (step, index) {\n if (placement !== step || flipOrder.length === index + 1) {\n return data;\n }\n\n placement = data.placement.split('-')[0];\n placementOpposite = getOppositePlacement(placement);\n\n var popperOffsets = data.offsets.popper;\n var refOffsets = data.offsets.reference;\n\n // using floor because the reference offsets may contain decimals we are not going to consider here\n var floor = Math.floor;\n var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);\n\n var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);\n var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);\n var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);\n var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);\n\n var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;\n\n // flip the variation if required\n var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;\n var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);\n\n if (overlapsRef || overflowsBoundaries || flippedVariation) {\n // this boolean to detect any flip loop\n data.flipped = true;\n\n if (overlapsRef || overflowsBoundaries) {\n placement = flipOrder[index + 1];\n }\n\n if (flippedVariation) {\n variation = getOppositeVariation(variation);\n }\n\n data.placement = placement + (variation ? '-' + variation : '');\n\n // this object contains `position`, we want to preserve it along with\n // any additional property we may add in the future\n data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement));\n\n data = runModifiers(data.instance.modifiers, data, 'flip');\n }\n });\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction keepTogether(data) {\n var _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var placement = data.placement.split('-')[0];\n var floor = Math.floor;\n var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;\n var side = isVertical ? 'right' : 'bottom';\n var opSide = isVertical ? 'left' : 'top';\n var measurement = isVertical ? 'width' : 'height';\n\n if (popper[side] < floor(reference[opSide])) {\n data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];\n }\n if (popper[opSide] > floor(reference[side])) {\n data.offsets.popper[opSide] = floor(reference[side]);\n }\n\n return data;\n}\n\n/**\n * Converts a string containing value + unit into a px value number\n * @function\n * @memberof {modifiers~offset}\n * @private\n * @argument {String} str - Value + unit string\n * @argument {String} measurement - `height` or `width`\n * @argument {Object} popperOffsets\n * @argument {Object} referenceOffsets\n * @returns {Number|String}\n * Value in pixels, or original string if no values were extracted\n */\nfunction toValue(str, measurement, popperOffsets, referenceOffsets) {\n // separate value from unit\n var split = str.match(/((?:\\-|\\+)?\\d*\\.?\\d*)(.*)/);\n var value = +split[1];\n var unit = split[2];\n\n // If it's not a number it's an operator, I guess\n if (!value) {\n return str;\n }\n\n if (unit.indexOf('%') === 0) {\n var element = void 0;\n switch (unit) {\n case '%p':\n element = popperOffsets;\n break;\n case '%':\n case '%r':\n default:\n element = referenceOffsets;\n }\n\n var rect = getClientRect(element);\n return rect[measurement] / 100 * value;\n } else if (unit === 'vh' || unit === 'vw') {\n // if is a vh or vw, we calculate the size based on the viewport\n var size = void 0;\n if (unit === 'vh') {\n size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);\n } else {\n size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);\n }\n return size / 100 * value;\n } else {\n // if is an explicit pixel unit, we get rid of the unit and keep the value\n // if is an implicit unit, it's px, and we return just the value\n return value;\n }\n}\n\n/**\n * Parse an `offset` string to extrapolate `x` and `y` numeric offsets.\n * @function\n * @memberof {modifiers~offset}\n * @private\n * @argument {String} offset\n * @argument {Object} popperOffsets\n * @argument {Object} referenceOffsets\n * @argument {String} basePlacement\n * @returns {Array} a two cells array with x and y offsets in numbers\n */\nfunction parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) {\n var offsets = [0, 0];\n\n // Use height if placement is left or right and index is 0 otherwise use width\n // in this way the first offset will use an axis and the second one\n // will use the other one\n var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1;\n\n // Split the offset string to obtain a list of values and operands\n // The regex addresses values with the plus or minus sign in front (+10, -20, etc)\n var fragments = offset.split(/(\\+|\\-)/).map(function (frag) {\n return frag.trim();\n });\n\n // Detect if the offset string contains a pair of values or a single one\n // they could be separated by comma or space\n var divider = fragments.indexOf(find(fragments, function (frag) {\n return frag.search(/,|\\s/) !== -1;\n }));\n\n if (fragments[divider] && fragments[divider].indexOf(',') === -1) {\n console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');\n }\n\n // If divider is found, we divide the list of values and operands to divide\n // them by ofset X and Y.\n var splitRegex = /\\s*,\\s*|\\s+/;\n var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments];\n\n // Convert the values with units to absolute pixels to allow our computations\n ops = ops.map(function (op, index) {\n // Most of the units rely on the orientation of the popper\n var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width';\n var mergeWithPrevious = false;\n return op\n // This aggregates any `+` or `-` sign that aren't considered operators\n // e.g.: 10 + +5 => [10, +, +5]\n .reduce(function (a, b) {\n if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) {\n a[a.length - 1] = b;\n mergeWithPrevious = true;\n return a;\n } else if (mergeWithPrevious) {\n a[a.length - 1] += b;\n mergeWithPrevious = false;\n return a;\n } else {\n return a.concat(b);\n }\n }, [])\n // Here we convert the string values into number values (in px)\n .map(function (str) {\n return toValue(str, measurement, popperOffsets, referenceOffsets);\n });\n });\n\n // Loop trough the offsets arrays and execute the operations\n ops.forEach(function (op, index) {\n op.forEach(function (frag, index2) {\n if (isNumeric(frag)) {\n offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1);\n }\n });\n });\n return offsets;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @argument {Number|String} options.offset=0\n * The offset value as described in the modifier description\n * @returns {Object} The data object, properly modified\n */\nfunction offset(data, _ref) {\n var offset = _ref.offset;\n var placement = data.placement,\n _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var basePlacement = placement.split('-')[0];\n\n var offsets = void 0;\n if (isNumeric(+offset)) {\n offsets = [+offset, 0];\n } else {\n offsets = parseOffset(offset, popper, reference, basePlacement);\n }\n\n if (basePlacement === 'left') {\n popper.top += offsets[0];\n popper.left -= offsets[1];\n } else if (basePlacement === 'right') {\n popper.top += offsets[0];\n popper.left += offsets[1];\n } else if (basePlacement === 'top') {\n popper.left += offsets[0];\n popper.top -= offsets[1];\n } else if (basePlacement === 'bottom') {\n popper.left += offsets[0];\n popper.top += offsets[1];\n }\n\n data.popper = popper;\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction preventOverflow(data, options) {\n var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);\n\n // If offsetParent is the reference element, we really want to\n // go one step up and use the next offsetParent as reference to\n // avoid to make this modifier completely useless and look like broken\n if (data.instance.reference === boundariesElement) {\n boundariesElement = getOffsetParent(boundariesElement);\n }\n\n // NOTE: DOM access here\n // resets the popper's position so that the document size can be calculated excluding\n // the size of the popper element itself\n var transformProp = getSupportedPropertyName('transform');\n var popperStyles = data.instance.popper.style; // assignment to help minification\n var top = popperStyles.top,\n left = popperStyles.left,\n transform = popperStyles[transformProp];\n\n popperStyles.top = '';\n popperStyles.left = '';\n popperStyles[transformProp] = '';\n\n var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed);\n\n // NOTE: DOM access here\n // restores the original style properties after the offsets have been computed\n popperStyles.top = top;\n popperStyles.left = left;\n popperStyles[transformProp] = transform;\n\n options.boundaries = boundaries;\n\n var order = options.priority;\n var popper = data.offsets.popper;\n\n var check = {\n primary: function primary(placement) {\n var value = popper[placement];\n if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {\n value = Math.max(popper[placement], boundaries[placement]);\n }\n return defineProperty({}, placement, value);\n },\n secondary: function secondary(placement) {\n var mainSide = placement === 'right' ? 'left' : 'top';\n var value = popper[mainSide];\n if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {\n value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));\n }\n return defineProperty({}, mainSide, value);\n }\n };\n\n order.forEach(function (placement) {\n var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';\n popper = _extends({}, popper, check[side](placement));\n });\n\n data.offsets.popper = popper;\n\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction shift(data) {\n var placement = data.placement;\n var basePlacement = placement.split('-')[0];\n var shiftvariation = placement.split('-')[1];\n\n // if shift shiftvariation is specified, run the modifier\n if (shiftvariation) {\n var _data$offsets = data.offsets,\n reference = _data$offsets.reference,\n popper = _data$offsets.popper;\n\n var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;\n var side = isVertical ? 'left' : 'top';\n var measurement = isVertical ? 'width' : 'height';\n\n var shiftOffsets = {\n start: defineProperty({}, side, reference[side]),\n end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])\n };\n\n data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);\n }\n\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction hide(data) {\n if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {\n return data;\n }\n\n var refRect = data.offsets.reference;\n var bound = find(data.instance.modifiers, function (modifier) {\n return modifier.name === 'preventOverflow';\n }).boundaries;\n\n if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {\n // Avoid unnecessary DOM access if visibility hasn't changed\n if (data.hide === true) {\n return data;\n }\n\n data.hide = true;\n data.attributes['x-out-of-boundaries'] = '';\n } else {\n // Avoid unnecessary DOM access if visibility hasn't changed\n if (data.hide === false) {\n return data;\n }\n\n data.hide = false;\n data.attributes['x-out-of-boundaries'] = false;\n }\n\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction inner(data) {\n var placement = data.placement;\n var basePlacement = placement.split('-')[0];\n var _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;\n\n var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;\n\n popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);\n\n data.placement = getOppositePlacement(placement);\n data.offsets.popper = getClientRect(popper);\n\n return data;\n}\n\n/**\n * Modifier function, each modifier can have a function of this type assigned\n * to its `fn` property.
\n * These functions will be called on each update, this means that you must\n * make sure they are performant enough to avoid performance bottlenecks.\n *\n * @function ModifierFn\n * @argument {dataObject} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {dataObject} The data object, properly modified\n */\n\n/**\n * Modifiers are plugins used to alter the behavior of your poppers.
\n * Popper.js uses a set of 9 modifiers to provide all the basic functionalities\n * needed by the library.\n *\n * Usually you don't want to override the `order`, `fn` and `onLoad` props.\n * All the other properties are configurations that could be tweaked.\n * @namespace modifiers\n */\nvar modifiers = {\n /**\n * Modifier used to shift the popper on the start or end of its reference\n * element.
\n * It will read the variation of the `placement` property.
\n * It can be one either `-end` or `-start`.\n * @memberof modifiers\n * @inner\n */\n shift: {\n /** @prop {number} order=100 - Index used to define the order of execution */\n order: 100,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: shift\n },\n\n /**\n * The `offset` modifier can shift your popper on both its axis.\n *\n * It accepts the following units:\n * - `px` or unit-less, interpreted as pixels\n * - `%` or `%r`, percentage relative to the length of the reference element\n * - `%p`, percentage relative to the length of the popper element\n * - `vw`, CSS viewport width unit\n * - `vh`, CSS viewport height unit\n *\n * For length is intended the main axis relative to the placement of the popper.
\n * This means that if the placement is `top` or `bottom`, the length will be the\n * `width`. In case of `left` or `right`, it will be the `height`.\n *\n * You can provide a single value (as `Number` or `String`), or a pair of values\n * as `String` divided by a comma or one (or more) white spaces.
\n * The latter is a deprecated method because it leads to confusion and will be\n * removed in v2.
\n * Additionally, it accepts additions and subtractions between different units.\n * Note that multiplications and divisions aren't supported.\n *\n * Valid examples are:\n * ```\n * 10\n * '10%'\n * '10, 10'\n * '10%, 10'\n * '10 + 10%'\n * '10 - 5vh + 3%'\n * '-10px + 5vh, 5px - 6%'\n * ```\n * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap\n * > with their reference element, unfortunately, you will have to disable the `flip` modifier.\n * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373).\n *\n * @memberof modifiers\n * @inner\n */\n offset: {\n /** @prop {number} order=200 - Index used to define the order of execution */\n order: 200,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: offset,\n /** @prop {Number|String} offset=0\n * The offset value as described in the modifier description\n */\n offset: 0\n },\n\n /**\n * Modifier used to prevent the popper from being positioned outside the boundary.\n *\n * A scenario exists where the reference itself is not within the boundaries.
\n * We can say it has \"escaped the boundaries\" — or just \"escaped\".
\n * In this case we need to decide whether the popper should either:\n *\n * - detach from the reference and remain \"trapped\" in the boundaries, or\n * - if it should ignore the boundary and \"escape with its reference\"\n *\n * When `escapeWithReference` is set to`true` and reference is completely\n * outside its boundaries, the popper will overflow (or completely leave)\n * the boundaries in order to remain attached to the edge of the reference.\n *\n * @memberof modifiers\n * @inner\n */\n preventOverflow: {\n /** @prop {number} order=300 - Index used to define the order of execution */\n order: 300,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: preventOverflow,\n /**\n * @prop {Array} [priority=['left','right','top','bottom']]\n * Popper will try to prevent overflow following these priorities by default,\n * then, it could overflow on the left and on top of the `boundariesElement`\n */\n priority: ['left', 'right', 'top', 'bottom'],\n /**\n * @prop {number} padding=5\n * Amount of pixel used to define a minimum distance between the boundaries\n * and the popper. This makes sure the popper always has a little padding\n * between the edges of its container\n */\n padding: 5,\n /**\n * @prop {String|HTMLElement} boundariesElement='scrollParent'\n * Boundaries used by the modifier. Can be `scrollParent`, `window`,\n * `viewport` or any DOM element.\n */\n boundariesElement: 'scrollParent'\n },\n\n /**\n * Modifier used to make sure the reference and its popper stay near each other\n * without leaving any gap between the two. Especially useful when the arrow is\n * enabled and you want to ensure that it points to its reference element.\n * It cares only about the first axis. You can still have poppers with margin\n * between the popper and its reference element.\n * @memberof modifiers\n * @inner\n */\n keepTogether: {\n /** @prop {number} order=400 - Index used to define the order of execution */\n order: 400,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: keepTogether\n },\n\n /**\n * This modifier is used to move the `arrowElement` of the popper to make\n * sure it is positioned between the reference element and its popper element.\n * It will read the outer size of the `arrowElement` node to detect how many\n * pixels of conjunction are needed.\n *\n * It has no effect if no `arrowElement` is provided.\n * @memberof modifiers\n * @inner\n */\n arrow: {\n /** @prop {number} order=500 - Index used to define the order of execution */\n order: 500,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: arrow,\n /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */\n element: '[x-arrow]'\n },\n\n /**\n * Modifier used to flip the popper's placement when it starts to overlap its\n * reference element.\n *\n * Requires the `preventOverflow` modifier before it in order to work.\n *\n * **NOTE:** this modifier will interrupt the current update cycle and will\n * restart it if it detects the need to flip the placement.\n * @memberof modifiers\n * @inner\n */\n flip: {\n /** @prop {number} order=600 - Index used to define the order of execution */\n order: 600,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: flip,\n /**\n * @prop {String|Array} behavior='flip'\n * The behavior used to change the popper's placement. It can be one of\n * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid\n * placements (with optional variations)\n */\n behavior: 'flip',\n /**\n * @prop {number} padding=5\n * The popper will flip if it hits the edges of the `boundariesElement`\n */\n padding: 5,\n /**\n * @prop {String|HTMLElement} boundariesElement='viewport'\n * The element which will define the boundaries of the popper position.\n * The popper will never be placed outside of the defined boundaries\n * (except if `keepTogether` is enabled)\n */\n boundariesElement: 'viewport'\n },\n\n /**\n * Modifier used to make the popper flow toward the inner of the reference element.\n * By default, when this modifier is disabled, the popper will be placed outside\n * the reference element.\n * @memberof modifiers\n * @inner\n */\n inner: {\n /** @prop {number} order=700 - Index used to define the order of execution */\n order: 700,\n /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */\n enabled: false,\n /** @prop {ModifierFn} */\n fn: inner\n },\n\n /**\n * Modifier used to hide the popper when its reference element is outside of the\n * popper boundaries. It will set a `x-out-of-boundaries` attribute which can\n * be used to hide with a CSS selector the popper when its reference is\n * out of boundaries.\n *\n * Requires the `preventOverflow` modifier before it in order to work.\n * @memberof modifiers\n * @inner\n */\n hide: {\n /** @prop {number} order=800 - Index used to define the order of execution */\n order: 800,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: hide\n },\n\n /**\n * Computes the style that will be applied to the popper element to gets\n * properly positioned.\n *\n * Note that this modifier will not touch the DOM, it just prepares the styles\n * so that `applyStyle` modifier can apply it. This separation is useful\n * in case you need to replace `applyStyle` with a custom implementation.\n *\n * This modifier has `850` as `order` value to maintain backward compatibility\n * with previous versions of Popper.js. Expect the modifiers ordering method\n * to change in future major versions of the library.\n *\n * @memberof modifiers\n * @inner\n */\n computeStyle: {\n /** @prop {number} order=850 - Index used to define the order of execution */\n order: 850,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: computeStyle,\n /**\n * @prop {Boolean} gpuAcceleration=true\n * If true, it uses the CSS 3D transformation to position the popper.\n * Otherwise, it will use the `top` and `left` properties\n */\n gpuAcceleration: true,\n /**\n * @prop {string} [x='bottom']\n * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin.\n * Change this if your popper should grow in a direction different from `bottom`\n */\n x: 'bottom',\n /**\n * @prop {string} [x='left']\n * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin.\n * Change this if your popper should grow in a direction different from `right`\n */\n y: 'right'\n },\n\n /**\n * Applies the computed styles to the popper element.\n *\n * All the DOM manipulations are limited to this modifier. This is useful in case\n * you want to integrate Popper.js inside a framework or view library and you\n * want to delegate all the DOM manipulations to it.\n *\n * Note that if you disable this modifier, you must make sure the popper element\n * has its position set to `absolute` before Popper.js can do its work!\n *\n * Just disable this modifier and define your own to achieve the desired effect.\n *\n * @memberof modifiers\n * @inner\n */\n applyStyle: {\n /** @prop {number} order=900 - Index used to define the order of execution */\n order: 900,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: applyStyle,\n /** @prop {Function} */\n onLoad: applyStyleOnLoad,\n /**\n * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier\n * @prop {Boolean} gpuAcceleration=true\n * If true, it uses the CSS 3D transformation to position the popper.\n * Otherwise, it will use the `top` and `left` properties\n */\n gpuAcceleration: undefined\n }\n};\n\n/**\n * The `dataObject` is an object containing all the information used by Popper.js.\n * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks.\n * @name dataObject\n * @property {Object} data.instance The Popper.js instance\n * @property {String} data.placement Placement applied to popper\n * @property {String} data.originalPlacement Placement originally defined on init\n * @property {Boolean} data.flipped True if popper has been flipped by flip modifier\n * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper\n * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier\n * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`)\n * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`)\n * @property {Object} data.boundaries Offsets of the popper boundaries\n * @property {Object} data.offsets The measurements of popper, reference and arrow elements\n * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values\n * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values\n * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0\n */\n\n/**\n * Default options provided to Popper.js constructor.
\n * These can be overridden using the `options` argument of Popper.js.
\n * To override an option, simply pass an object with the same\n * structure of the `options` object, as the 3rd argument. For example:\n * ```\n * new Popper(ref, pop, {\n * modifiers: {\n * preventOverflow: { enabled: false }\n * }\n * })\n * ```\n * @type {Object}\n * @static\n * @memberof Popper\n */\nvar Defaults = {\n /**\n * Popper's placement.\n * @prop {Popper.placements} placement='bottom'\n */\n placement: 'bottom',\n\n /**\n * Set this to true if you want popper to position it self in 'fixed' mode\n * @prop {Boolean} positionFixed=false\n */\n positionFixed: false,\n\n /**\n * Whether events (resize, scroll) are initially enabled.\n * @prop {Boolean} eventsEnabled=true\n */\n eventsEnabled: true,\n\n /**\n * Set to true if you want to automatically remove the popper when\n * you call the `destroy` method.\n * @prop {Boolean} removeOnDestroy=false\n */\n removeOnDestroy: false,\n\n /**\n * Callback called when the popper is created.
\n * By default, it is set to no-op.
\n * Access Popper.js instance with `data.instance`.\n * @prop {onCreate}\n */\n onCreate: function onCreate() {},\n\n /**\n * Callback called when the popper is updated. This callback is not called\n * on the initialization/creation of the popper, but only on subsequent\n * updates.
\n * By default, it is set to no-op.
\n * Access Popper.js instance with `data.instance`.\n * @prop {onUpdate}\n */\n onUpdate: function onUpdate() {},\n\n /**\n * List of modifiers used to modify the offsets before they are applied to the popper.\n * They provide most of the functionalities of Popper.js.\n * @prop {modifiers}\n */\n modifiers: modifiers\n};\n\n/**\n * @callback onCreate\n * @param {dataObject} data\n */\n\n/**\n * @callback onUpdate\n * @param {dataObject} data\n */\n\n// Utils\n// Methods\nvar Popper = function () {\n /**\n * Creates a new Popper.js instance.\n * @class Popper\n * @param {HTMLElement|referenceObject} reference - The reference element used to position the popper\n * @param {HTMLElement} popper - The HTML element used as the popper\n * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)\n * @return {Object} instance - The generated Popper.js instance\n */\n function Popper(reference, popper) {\n var _this = this;\n\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n classCallCheck(this, Popper);\n\n this.scheduleUpdate = function () {\n return requestAnimationFrame(_this.update);\n };\n\n // make update() debounced, so that it only runs at most once-per-tick\n this.update = debounce(this.update.bind(this));\n\n // with {} we create a new object with the options inside it\n this.options = _extends({}, Popper.Defaults, options);\n\n // init state\n this.state = {\n isDestroyed: false,\n isCreated: false,\n scrollParents: []\n };\n\n // get reference and popper elements (allow jQuery wrappers)\n this.reference = reference && reference.jquery ? reference[0] : reference;\n this.popper = popper && popper.jquery ? popper[0] : popper;\n\n // Deep merge modifiers options\n this.options.modifiers = {};\n Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) {\n _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});\n });\n\n // Refactoring modifiers' list (Object => Array)\n this.modifiers = Object.keys(this.options.modifiers).map(function (name) {\n return _extends({\n name: name\n }, _this.options.modifiers[name]);\n })\n // sort the modifiers by order\n .sort(function (a, b) {\n return a.order - b.order;\n });\n\n // modifiers have the ability to execute arbitrary code when Popper.js get inited\n // such code is executed in the same order of its modifier\n // they could add new properties to their options configuration\n // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!\n this.modifiers.forEach(function (modifierOptions) {\n if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {\n modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);\n }\n });\n\n // fire the first update to position the popper in the right place\n this.update();\n\n var eventsEnabled = this.options.eventsEnabled;\n if (eventsEnabled) {\n // setup event listeners, they will take care of update the position in specific situations\n this.enableEventListeners();\n }\n\n this.state.eventsEnabled = eventsEnabled;\n }\n\n // We can't use class properties because they don't get listed in the\n // class prototype and break stuff like Sinon stubs\n\n\n createClass(Popper, [{\n key: 'update',\n value: function update$$1() {\n return update.call(this);\n }\n }, {\n key: 'destroy',\n value: function destroy$$1() {\n return destroy.call(this);\n }\n }, {\n key: 'enableEventListeners',\n value: function enableEventListeners$$1() {\n return enableEventListeners.call(this);\n }\n }, {\n key: 'disableEventListeners',\n value: function disableEventListeners$$1() {\n return disableEventListeners.call(this);\n }\n\n /**\n * Schedules an update. It will run on the next UI update available.\n * @method scheduleUpdate\n * @memberof Popper\n */\n\n\n /**\n * Collection of utilities useful when writing custom modifiers.\n * Starting from version 1.7, this method is available only if you\n * include `popper-utils.js` before `popper.js`.\n *\n * **DEPRECATION**: This way to access PopperUtils is deprecated\n * and will be removed in v2! Use the PopperUtils module directly instead.\n * Due to the high instability of the methods contained in Utils, we can't\n * guarantee them to follow semver. Use them at your own risk!\n * @static\n * @private\n * @type {Object}\n * @deprecated since version 1.8\n * @member Utils\n * @memberof Popper\n */\n\n }]);\n return Popper;\n}();\n\n/**\n * The `referenceObject` is an object that provides an interface compatible with Popper.js\n * and lets you use it as replacement of a real DOM node.
\n * You can use this method to position a popper relatively to a set of coordinates\n * in case you don't have a DOM node to use as reference.\n *\n * ```\n * new Popper(referenceObject, popperNode);\n * ```\n *\n * NB: This feature isn't supported in Internet Explorer 10.\n * @name referenceObject\n * @property {Function} data.getBoundingClientRect\n * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.\n * @property {number} data.clientWidth\n * An ES6 getter that will return the width of the virtual reference element.\n * @property {number} data.clientHeight\n * An ES6 getter that will return the height of the virtual reference element.\n */\n\n\nPopper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;\nPopper.placements = placements;\nPopper.Defaults = Defaults;\n\nexport default Popper;\n//# sourceMappingURL=popper.js.map\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'dropdown'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\nconst SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key\nconst TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key\nconst ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key\nconst ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key\nconst RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)\nconst REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`,\n KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n DISABLED : 'disabled',\n SHOW : 'show',\n DROPUP : 'dropup',\n DROPRIGHT : 'dropright',\n DROPLEFT : 'dropleft',\n MENURIGHT : 'dropdown-menu-right',\n MENULEFT : 'dropdown-menu-left',\n POSITION_STATIC : 'position-static'\n}\n\nconst Selector = {\n DATA_TOGGLE : '[data-toggle=\"dropdown\"]',\n FORM_CHILD : '.dropdown form',\n MENU : '.dropdown-menu',\n NAVBAR_NAV : '.navbar-nav',\n VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n}\n\nconst AttachmentMap = {\n TOP : 'top-start',\n TOPEND : 'top-end',\n BOTTOM : 'bottom-start',\n BOTTOMEND : 'bottom-end',\n RIGHT : 'right-start',\n RIGHTEND : 'right-end',\n LEFT : 'left-start',\n LEFTEND : 'left-end'\n}\n\nconst Default = {\n offset : 0,\n flip : true,\n boundary : 'scrollParent',\n reference : 'toggle',\n display : 'dynamic'\n}\n\nconst DefaultType = {\n offset : '(number|string|function)',\n flip : 'boolean',\n boundary : '(string|element)',\n reference : '(string|element)',\n display : 'string'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Dropdown {\n constructor(element, config) {\n this._element = element\n this._popper = null\n this._config = this._getConfig(config)\n this._menu = this._getMenuElement()\n this._inNavbar = this._detectNavbar()\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n toggle() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this._element)\n const isActive = $(this._menu).hasClass(ClassName.SHOW)\n\n Dropdown._clearMenus()\n\n if (isActive) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const showEvent = $.Event(Event.SHOW, relatedTarget)\n\n $(parent).trigger(showEvent)\n\n if (showEvent.isDefaultPrevented()) {\n return\n }\n\n // Disable totally Popper.js for Dropdown in Navbar\n if (!this._inNavbar) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper.js (https://popper.js.org/)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = parent\n } else if (Util.isElement(this._config.reference)) {\n referenceElement = this._config.reference\n\n // Check if it's jQuery element\n if (typeof this._config.reference.jquery !== 'undefined') {\n referenceElement = this._config.reference[0]\n }\n }\n\n // If boundary is not `scrollParent`, then set position to `static`\n // to allow the menu to \"escape\" the scroll parent's boundaries\n // https://github.com/twbs/bootstrap/issues/24251\n if (this._config.boundary !== 'scrollParent') {\n $(parent).addClass(ClassName.POSITION_STATIC)\n }\n this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement &&\n $(parent).closest(Selector.NAVBAR_NAV).length === 0) {\n $(document.body).children().on('mouseover', null, $.noop)\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.SHOWN, relatedTarget))\n }\n\n show() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || $(this._menu).hasClass(ClassName.SHOW)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const showEvent = $.Event(Event.SHOW, relatedTarget)\n const parent = Dropdown._getParentFromElement(this._element)\n\n $(parent).trigger(showEvent)\n\n if (showEvent.isDefaultPrevented()) {\n return\n }\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.SHOWN, relatedTarget))\n }\n\n hide() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || !$(this._menu).hasClass(ClassName.SHOW)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const hideEvent = $.Event(Event.HIDE, relatedTarget)\n const parent = Dropdown._getParentFromElement(this._element)\n\n $(parent).trigger(hideEvent)\n\n if (hideEvent.isDefaultPrevented()) {\n return\n }\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.HIDDEN, relatedTarget))\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._element).off(EVENT_KEY)\n this._element = null\n this._menu = null\n if (this._popper !== null) {\n this._popper.destroy()\n this._popper = null\n }\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Private\n\n _addEventListeners() {\n $(this._element).on(Event.CLICK, (event) => {\n event.preventDefault()\n event.stopPropagation()\n this.toggle()\n })\n }\n\n _getConfig(config) {\n config = {\n ...this.constructor.Default,\n ...$(this._element).data(),\n ...config\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n return config\n }\n\n _getMenuElement() {\n if (!this._menu) {\n const parent = Dropdown._getParentFromElement(this._element)\n\n if (parent) {\n this._menu = parent.querySelector(Selector.MENU)\n }\n }\n return this._menu\n }\n\n _getPlacement() {\n const $parentDropdown = $(this._element.parentNode)\n let placement = AttachmentMap.BOTTOM\n\n // Handle dropup\n if ($parentDropdown.hasClass(ClassName.DROPUP)) {\n placement = AttachmentMap.TOP\n if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.TOPEND\n }\n } else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {\n placement = AttachmentMap.RIGHT\n } else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {\n placement = AttachmentMap.LEFT\n } else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.BOTTOMEND\n }\n return placement\n }\n\n _detectNavbar() {\n return $(this._element).closest('.navbar').length > 0\n }\n\n _getOffset() {\n const offset = {}\n\n if (typeof this._config.offset === 'function') {\n offset.fn = (data) => {\n data.offsets = {\n ...data.offsets,\n ...this._config.offset(data.offsets, this._element) || {}\n }\n\n return data\n }\n } else {\n offset.offset = this._config.offset\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const popperConfig = {\n placement: this._getPlacement(),\n modifiers: {\n offset: this._getOffset(),\n flip: {\n enabled: this._config.flip\n },\n preventOverflow: {\n boundariesElement: this._config.boundary\n }\n }\n }\n\n // Disable Popper.js if we have a static display\n if (this._config.display === 'static') {\n popperConfig.modifiers.applyStyle = {\n enabled: false\n }\n }\n\n return popperConfig\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data) {\n data = new Dropdown(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n\n static _clearMenus(event) {\n if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||\n event.type === 'keyup' && event.which !== TAB_KEYCODE)) {\n return\n }\n\n const toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n\n for (let i = 0, len = toggles.length; i < len; i++) {\n const parent = Dropdown._getParentFromElement(toggles[i])\n const context = $(toggles[i]).data(DATA_KEY)\n const relatedTarget = {\n relatedTarget: toggles[i]\n }\n\n if (event && event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n if (!context) {\n continue\n }\n\n const dropdownMenu = context._menu\n if (!$(parent).hasClass(ClassName.SHOW)) {\n continue\n }\n\n if (event && (event.type === 'click' &&\n /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&\n $.contains(parent, event.target)) {\n continue\n }\n\n const hideEvent = $.Event(Event.HIDE, relatedTarget)\n $(parent).trigger(hideEvent)\n if (hideEvent.isDefaultPrevented()) {\n continue\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().off('mouseover', null, $.noop)\n }\n\n toggles[i].setAttribute('aria-expanded', 'false')\n\n $(dropdownMenu).removeClass(ClassName.SHOW)\n $(parent)\n .removeClass(ClassName.SHOW)\n .trigger($.Event(Event.HIDDEN, relatedTarget))\n }\n }\n\n static _getParentFromElement(element) {\n let parent\n const selector = Util.getSelectorFromElement(element)\n\n if (selector) {\n parent = document.querySelector(selector)\n }\n\n return parent || element.parentNode\n }\n\n // eslint-disable-next-line complexity\n static _dataApiKeydownHandler(event) {\n // If not input/textarea:\n // - And not a key in REGEXP_KEYDOWN => not a dropdown command\n // If input/textarea:\n // - If space key => not a dropdown command\n // - If key is other than escape\n // - If key is not up or down => not a dropdown command\n // - If trigger inside the menu => not a dropdown command\n if (/input|textarea/i.test(event.target.tagName)\n ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&\n (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||\n $(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n\n if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this)\n const isActive = $(parent).hasClass(ClassName.SHOW)\n\n if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {\n if (event.which === ESCAPE_KEYCODE) {\n const toggle = parent.querySelector(Selector.DATA_TOGGLE)\n $(toggle).trigger('focus')\n }\n\n $(this).trigger('click')\n return\n }\n\n const items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS))\n\n if (items.length === 0) {\n return\n }\n\n let index = items.indexOf(event.target)\n\n if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up\n index--\n }\n\n if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down\n index++\n }\n\n if (index < 0) {\n index = 0\n }\n\n items[index].focus()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)\n .on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)\n .on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n event.preventDefault()\n event.stopPropagation()\n Dropdown._jQueryInterface.call($(this), 'toggle')\n })\n .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {\n e.stopPropagation()\n })\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Dropdown._jQueryInterface\n$.fn[NAME].Constructor = Dropdown\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Dropdown._jQueryInterface\n}\n\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'modal'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\n\nconst Default = {\n backdrop : true,\n keyboard : true,\n focus : true,\n show : true\n}\n\nconst DefaultType = {\n backdrop : '(boolean|string)',\n keyboard : 'boolean',\n focus : 'boolean',\n show : 'boolean'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n RESIZE : `resize${EVENT_KEY}`,\n CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,\n KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`,\n MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`,\n MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SCROLLABLE : 'modal-dialog-scrollable',\n SCROLLBAR_MEASURER : 'modal-scrollbar-measure',\n BACKDROP : 'modal-backdrop',\n OPEN : 'modal-open',\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n DIALOG : '.modal-dialog',\n MODAL_BODY : '.modal-body',\n DATA_TOGGLE : '[data-toggle=\"modal\"]',\n DATA_DISMISS : '[data-dismiss=\"modal\"]',\n FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',\n STICKY_CONTENT : '.sticky-top'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Modal {\n constructor(element, config) {\n this._config = this._getConfig(config)\n this._element = element\n this._dialog = element.querySelector(Selector.DIALOG)\n this._backdrop = null\n this._isShown = false\n this._isBodyOverflowing = false\n this._ignoreBackdropClick = false\n this._isTransitioning = false\n this._scrollbarWidth = 0\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n this._isTransitioning = true\n }\n\n const showEvent = $.Event(Event.SHOW, {\n relatedTarget\n })\n\n $(this._element).trigger(showEvent)\n\n if (this._isShown || showEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = true\n\n this._checkScrollbar()\n this._setScrollbar()\n\n this._adjustDialog()\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(this._element).on(\n Event.CLICK_DISMISS,\n Selector.DATA_DISMISS,\n (event) => this.hide(event)\n )\n\n $(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => {\n $(this._element).one(Event.MOUSEUP_DISMISS, (event) => {\n if ($(event.target).is(this._element)) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => this._showElement(relatedTarget))\n }\n\n hide(event) {\n if (event) {\n event.preventDefault()\n }\n\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = $.Event(Event.HIDE)\n\n $(this._element).trigger(hideEvent)\n\n if (!this._isShown || hideEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = false\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (transition) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(document).off(Event.FOCUSIN)\n\n $(this._element).removeClass(ClassName.SHOW)\n\n $(this._element).off(Event.CLICK_DISMISS)\n $(this._dialog).off(Event.MOUSEDOWN_DISMISS)\n\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, (event) => this._hideModal(event))\n .emulateTransitionEnd(transitionDuration)\n } else {\n this._hideModal()\n }\n }\n\n dispose() {\n [window, this._element, this._dialog]\n .forEach((htmlElement) => $(htmlElement).off(EVENT_KEY))\n\n /**\n * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`\n * Do not move `document` in `htmlElements` array\n * It will remove `Event.CLICK_DATA_API` event that should remain\n */\n $(document).off(Event.FOCUSIN)\n\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._element = null\n this._dialog = null\n this._backdrop = null\n this._isShown = null\n this._isBodyOverflowing = null\n this._ignoreBackdropClick = null\n this._isTransitioning = null\n this._scrollbarWidth = null\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (!this._element.parentNode ||\n this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\n document.body.appendChild(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n\n if ($(this._dialog).hasClass(ClassName.SCROLLABLE)) {\n this._dialog.querySelector(Selector.MODAL_BODY).scrollTop = 0\n } else {\n this._element.scrollTop = 0\n }\n\n if (transition) {\n Util.reflow(this._element)\n }\n\n $(this._element).addClass(ClassName.SHOW)\n\n if (this._config.focus) {\n this._enforceFocus()\n }\n\n const shownEvent = $.Event(Event.SHOWN, {\n relatedTarget\n })\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._element.focus()\n }\n this._isTransitioning = false\n $(this._element).trigger(shownEvent)\n }\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._dialog)\n\n $(this._dialog)\n .one(Util.TRANSITION_END, transitionComplete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n transitionComplete()\n }\n }\n\n _enforceFocus() {\n $(document)\n .off(Event.FOCUSIN) // Guard against infinite focus loop\n .on(Event.FOCUSIN, (event) => {\n if (document !== event.target &&\n this._element !== event.target &&\n $(this._element).has(event.target).length === 0) {\n this._element.focus()\n }\n })\n }\n\n _setEscapeEvent() {\n if (this._isShown && this._config.keyboard) {\n $(this._element).on(Event.KEYDOWN_DISMISS, (event) => {\n if (event.which === ESCAPE_KEYCODE) {\n event.preventDefault()\n this.hide()\n }\n })\n } else if (!this._isShown) {\n $(this._element).off(Event.KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n $(window).on(Event.RESIZE, (event) => this.handleUpdate(event))\n } else {\n $(window).off(Event.RESIZE)\n }\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._isTransitioning = false\n this._showBackdrop(() => {\n $(document.body).removeClass(ClassName.OPEN)\n this._resetAdjustments()\n this._resetScrollbar()\n $(this._element).trigger(Event.HIDDEN)\n })\n }\n\n _removeBackdrop() {\n if (this._backdrop) {\n $(this._backdrop).remove()\n this._backdrop = null\n }\n }\n\n _showBackdrop(callback) {\n const animate = $(this._element).hasClass(ClassName.FADE)\n ? ClassName.FADE : ''\n\n if (this._isShown && this._config.backdrop) {\n this._backdrop = document.createElement('div')\n this._backdrop.className = ClassName.BACKDROP\n\n if (animate) {\n this._backdrop.classList.add(animate)\n }\n\n $(this._backdrop).appendTo(document.body)\n\n $(this._element).on(Event.CLICK_DISMISS, (event) => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n if (event.target !== event.currentTarget) {\n return\n }\n if (this._config.backdrop === 'static') {\n this._element.focus()\n } else {\n this.hide()\n }\n })\n\n if (animate) {\n Util.reflow(this._backdrop)\n }\n\n $(this._backdrop).addClass(ClassName.SHOW)\n\n if (!callback) {\n return\n }\n\n if (!animate) {\n callback()\n return\n }\n\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callback)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else if (!this._isShown && this._backdrop) {\n $(this._backdrop).removeClass(ClassName.SHOW)\n\n const callbackRemove = () => {\n this._removeBackdrop()\n if (callback) {\n callback()\n }\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callbackRemove)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else {\n callbackRemove()\n }\n } else if (callback) {\n callback()\n }\n }\n\n // ----------------------------------------------------------------------\n // the following methods are used to handle overflowing modals\n // todo (fat): these should probably be refactored out of modal.js\n // ----------------------------------------------------------------------\n\n _adjustDialog() {\n const isModalOverflowing =\n this._element.scrollHeight > document.documentElement.clientHeight\n\n if (!this._isBodyOverflowing && isModalOverflowing) {\n this._element.style.paddingLeft = `${this._scrollbarWidth}px`\n }\n\n if (this._isBodyOverflowing && !isModalOverflowing) {\n this._element.style.paddingRight = `${this._scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n _checkScrollbar() {\n const rect = document.body.getBoundingClientRect()\n this._isBodyOverflowing = rect.left + rect.right < window.innerWidth\n this._scrollbarWidth = this._getScrollbarWidth()\n }\n\n _setScrollbar() {\n if (this._isBodyOverflowing) {\n // Note: DOMNode.style.paddingRight returns the actual value or '' if not set\n // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n const stickyContent = [].slice.call(document.querySelectorAll(Selector.STICKY_CONTENT))\n\n // Adjust fixed content padding\n $(fixedContent).each((index, element) => {\n const actualPadding = element.style.paddingRight\n const calculatedPadding = $(element).css('padding-right')\n $(element)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n })\n\n // Adjust sticky content margin\n $(stickyContent).each((index, element) => {\n const actualMargin = element.style.marginRight\n const calculatedMargin = $(element).css('margin-right')\n $(element)\n .data('margin-right', actualMargin)\n .css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)\n })\n\n // Adjust body padding\n const actualPadding = document.body.style.paddingRight\n const calculatedPadding = $(document.body).css('padding-right')\n $(document.body)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n }\n\n $(document.body).addClass(ClassName.OPEN)\n }\n\n _resetScrollbar() {\n // Restore fixed content padding\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n $(fixedContent).each((index, element) => {\n const padding = $(element).data('padding-right')\n $(element).removeData('padding-right')\n element.style.paddingRight = padding ? padding : ''\n })\n\n // Restore sticky content\n const elements = [].slice.call(document.querySelectorAll(`${Selector.STICKY_CONTENT}`))\n $(elements).each((index, element) => {\n const margin = $(element).data('margin-right')\n if (typeof margin !== 'undefined') {\n $(element).css('margin-right', margin).removeData('margin-right')\n }\n })\n\n // Restore body padding\n const padding = $(document.body).data('padding-right')\n $(document.body).removeData('padding-right')\n document.body.style.paddingRight = padding ? padding : ''\n }\n\n _getScrollbarWidth() { // thx d.walsh\n const scrollDiv = document.createElement('div')\n scrollDiv.className = ClassName.SCROLLBAR_MEASURER\n document.body.appendChild(scrollDiv)\n const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth\n document.body.removeChild(scrollDiv)\n return scrollbarWidth\n }\n\n // Static\n\n static _jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = {\n ...Default,\n ...$(this).data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data) {\n data = new Modal(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config](relatedTarget)\n } else if (_config.show) {\n data.show(relatedTarget)\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n let target\n const selector = Util.getSelectorFromElement(this)\n\n if (selector) {\n target = document.querySelector(selector)\n }\n\n const config = $(target).data(DATA_KEY)\n ? 'toggle' : {\n ...$(target).data(),\n ...$(this).data()\n }\n\n if (this.tagName === 'A' || this.tagName === 'AREA') {\n event.preventDefault()\n }\n\n const $target = $(target).one(Event.SHOW, (showEvent) => {\n if (showEvent.isDefaultPrevented()) {\n // Only register focus restorer if modal will actually get shown\n return\n }\n\n $target.one(Event.HIDDEN, () => {\n if ($(this).is(':visible')) {\n this.focus()\n }\n })\n })\n\n Modal._jQueryInterface.call($(target), config, this)\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Modal._jQueryInterface\n$.fn[NAME].Constructor = Modal\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Modal._jQueryInterface\n}\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): tools/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst uriAttrs = [\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n]\n\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultWhitelist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n\n/**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi\n\n/**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i\n\nfunction allowedAttribute(attr, allowedAttributeList) {\n const attrName = attr.nodeName.toLowerCase()\n\n if (allowedAttributeList.indexOf(attrName) !== -1) {\n if (uriAttrs.indexOf(attrName) !== -1) {\n return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))\n }\n\n return true\n }\n\n const regExp = allowedAttributeList.filter((attrRegex) => attrRegex instanceof RegExp)\n\n // Check if a regular expression validates the attribute.\n for (let i = 0, l = regExp.length; i < l; i++) {\n if (attrName.match(regExp[i])) {\n return true\n }\n }\n\n return false\n}\n\nexport function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {\n if (unsafeHtml.length === 0) {\n return unsafeHtml\n }\n\n if (sanitizeFn && typeof sanitizeFn === 'function') {\n return sanitizeFn(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const whitelistKeys = Object.keys(whiteList)\n const elements = [].slice.call(createdDocument.body.querySelectorAll('*'))\n\n for (let i = 0, len = elements.length; i < len; i++) {\n const el = elements[i]\n const elName = el.nodeName.toLowerCase()\n\n if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {\n el.parentNode.removeChild(el)\n\n continue\n }\n\n const attributeList = [].slice.call(el.attributes)\n const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])\n\n attributeList.forEach((attr) => {\n if (!allowedAttribute(attr, whitelistedAttributes)) {\n el.removeAttribute(attr.nodeName)\n }\n })\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n DefaultWhitelist,\n sanitizeHtml\n} from './tools/sanitizer'\nimport $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'tooltip'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.tooltip'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst CLASS_PREFIX = 'bs-tooltip'\nconst BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\nconst DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']\n\nconst DefaultType = {\n animation : 'boolean',\n template : 'string',\n title : '(string|element|function)',\n trigger : 'string',\n delay : '(number|object)',\n html : 'boolean',\n selector : '(string|boolean)',\n placement : '(string|function)',\n offset : '(number|string|function)',\n container : '(string|element|boolean)',\n fallbackPlacement : '(string|array)',\n boundary : '(string|element)',\n sanitize : 'boolean',\n sanitizeFn : '(null|function)',\n whiteList : 'object'\n}\n\nconst AttachmentMap = {\n AUTO : 'auto',\n TOP : 'top',\n RIGHT : 'right',\n BOTTOM : 'bottom',\n LEFT : 'left'\n}\n\nconst Default = {\n animation : true,\n template : '
' +\n '
' +\n '
',\n trigger : 'hover focus',\n title : '',\n delay : 0,\n html : false,\n selector : false,\n placement : 'top',\n offset : 0,\n container : false,\n fallbackPlacement : 'flip',\n boundary : 'scrollParent',\n sanitize : true,\n sanitizeFn : null,\n whiteList : DefaultWhitelist\n}\n\nconst HoverState = {\n SHOW : 'show',\n OUT : 'out'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n}\n\nconst ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n TOOLTIP : '.tooltip',\n TOOLTIP_INNER : '.tooltip-inner',\n ARROW : '.arrow'\n}\n\nconst Trigger = {\n HOVER : 'hover',\n FOCUS : 'focus',\n CLICK : 'click',\n MANUAL : 'manual'\n}\n\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Tooltip {\n constructor(element, config) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper.js (https://popper.js.org/)')\n }\n\n // private\n this._isEnabled = true\n this._timeout = 0\n this._hoverState = ''\n this._activeTrigger = {}\n this._popper = null\n\n // Protected\n this.element = element\n this.config = this._getConfig(config)\n this.tip = null\n\n this._setListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle(event) {\n if (!this._isEnabled) {\n return\n }\n\n if (event) {\n const dataKey = this.constructor.DATA_KEY\n let context = $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n context._activeTrigger.click = !context._activeTrigger.click\n\n if (context._isWithActiveTrigger()) {\n context._enter(null, context)\n } else {\n context._leave(null, context)\n }\n } else {\n if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {\n this._leave(null, this)\n return\n }\n\n this._enter(null, this)\n }\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n $.removeData(this.element, this.constructor.DATA_KEY)\n\n $(this.element).off(this.constructor.EVENT_KEY)\n $(this.element).closest('.modal').off('hide.bs.modal')\n\n if (this.tip) {\n $(this.tip).remove()\n }\n\n this._isEnabled = null\n this._timeout = null\n this._hoverState = null\n this._activeTrigger = null\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n this._popper = null\n this.element = null\n this.config = null\n this.tip = null\n }\n\n show() {\n if ($(this.element).css('display') === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n const showEvent = $.Event(this.constructor.Event.SHOW)\n if (this.isWithContent() && this._isEnabled) {\n $(this.element).trigger(showEvent)\n\n const shadowRoot = Util.findShadowRoot(this.element)\n const isInTheDom = $.contains(\n shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,\n this.element\n )\n\n if (showEvent.isDefaultPrevented() || !isInTheDom) {\n return\n }\n\n const tip = this.getTipElement()\n const tipId = Util.getUID(this.constructor.NAME)\n\n tip.setAttribute('id', tipId)\n this.element.setAttribute('aria-describedby', tipId)\n\n this.setContent()\n\n if (this.config.animation) {\n $(tip).addClass(ClassName.FADE)\n }\n\n const placement = typeof this.config.placement === 'function'\n ? this.config.placement.call(this, tip, this.element)\n : this.config.placement\n\n const attachment = this._getAttachment(placement)\n this.addAttachmentClass(attachment)\n\n const container = this._getContainer()\n $(tip).data(this.constructor.DATA_KEY, this)\n\n if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {\n $(tip).appendTo(container)\n }\n\n $(this.element).trigger(this.constructor.Event.INSERTED)\n\n this._popper = new Popper(this.element, tip, {\n placement: attachment,\n modifiers: {\n offset: this._getOffset(),\n flip: {\n behavior: this.config.fallbackPlacement\n },\n arrow: {\n element: Selector.ARROW\n },\n preventOverflow: {\n boundariesElement: this.config.boundary\n }\n },\n onCreate: (data) => {\n if (data.originalPlacement !== data.placement) {\n this._handlePopperPlacementChange(data)\n }\n },\n onUpdate: (data) => this._handlePopperPlacementChange(data)\n })\n\n $(tip).addClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().on('mouseover', null, $.noop)\n }\n\n const complete = () => {\n if (this.config.animation) {\n this._fixTransition()\n }\n const prevHoverState = this._hoverState\n this._hoverState = null\n\n $(this.element).trigger(this.constructor.Event.SHOWN)\n\n if (prevHoverState === HoverState.OUT) {\n this._leave(null, this)\n }\n }\n\n if ($(this.tip).hasClass(ClassName.FADE)) {\n const transitionDuration = Util.getTransitionDurationFromElement(this.tip)\n\n $(this.tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n complete()\n }\n }\n }\n\n hide(callback) {\n const tip = this.getTipElement()\n const hideEvent = $.Event(this.constructor.Event.HIDE)\n const complete = () => {\n if (this._hoverState !== HoverState.SHOW && tip.parentNode) {\n tip.parentNode.removeChild(tip)\n }\n\n this._cleanTipClass()\n this.element.removeAttribute('aria-describedby')\n $(this.element).trigger(this.constructor.Event.HIDDEN)\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n if (callback) {\n callback()\n }\n }\n\n $(this.element).trigger(hideEvent)\n\n if (hideEvent.isDefaultPrevented()) {\n return\n }\n\n $(tip).removeClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().off('mouseover', null, $.noop)\n }\n\n this._activeTrigger[Trigger.CLICK] = false\n this._activeTrigger[Trigger.FOCUS] = false\n this._activeTrigger[Trigger.HOVER] = false\n\n if ($(this.tip).hasClass(ClassName.FADE)) {\n const transitionDuration = Util.getTransitionDurationFromElement(tip)\n\n $(tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n complete()\n }\n\n this._hoverState = ''\n }\n\n update() {\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Protected\n\n isWithContent() {\n return Boolean(this.getTitle())\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const tip = this.getTipElement()\n this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle())\n $(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n setElementContent($element, content) {\n if (typeof content === 'object' && (content.nodeType || content.jquery)) {\n // Content is a DOM node or a jQuery\n if (this.config.html) {\n if (!$(content).parent().is($element)) {\n $element.empty().append(content)\n }\n } else {\n $element.text($(content).text())\n }\n\n return\n }\n\n if (this.config.html) {\n if (this.config.sanitize) {\n content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)\n }\n\n $element.html(content)\n } else {\n $element.text(content)\n }\n }\n\n getTitle() {\n let title = this.element.getAttribute('data-original-title')\n\n if (!title) {\n title = typeof this.config.title === 'function'\n ? this.config.title.call(this.element)\n : this.config.title\n }\n\n return title\n }\n\n // Private\n\n _getOffset() {\n const offset = {}\n\n if (typeof this.config.offset === 'function') {\n offset.fn = (data) => {\n data.offsets = {\n ...data.offsets,\n ...this.config.offset(data.offsets, this.element) || {}\n }\n\n return data\n }\n } else {\n offset.offset = this.config.offset\n }\n\n return offset\n }\n\n _getContainer() {\n if (this.config.container === false) {\n return document.body\n }\n\n if (Util.isElement(this.config.container)) {\n return $(this.config.container)\n }\n\n return $(document).find(this.config.container)\n }\n\n _getAttachment(placement) {\n return AttachmentMap[placement.toUpperCase()]\n }\n\n _setListeners() {\n const triggers = this.config.trigger.split(' ')\n\n triggers.forEach((trigger) => {\n if (trigger === 'click') {\n $(this.element).on(\n this.constructor.Event.CLICK,\n this.config.selector,\n (event) => this.toggle(event)\n )\n } else if (trigger !== Trigger.MANUAL) {\n const eventIn = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSEENTER\n : this.constructor.Event.FOCUSIN\n const eventOut = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSELEAVE\n : this.constructor.Event.FOCUSOUT\n\n $(this.element)\n .on(\n eventIn,\n this.config.selector,\n (event) => this._enter(event)\n )\n .on(\n eventOut,\n this.config.selector,\n (event) => this._leave(event)\n )\n }\n })\n\n $(this.element).closest('.modal').on(\n 'hide.bs.modal',\n () => {\n if (this.element) {\n this.hide()\n }\n }\n )\n\n if (this.config.selector) {\n this.config = {\n ...this.config,\n trigger: 'manual',\n selector: ''\n }\n } else {\n this._fixTitle()\n }\n }\n\n _fixTitle() {\n const titleType = typeof this.element.getAttribute('data-original-title')\n\n if (this.element.getAttribute('title') || titleType !== 'string') {\n this.element.setAttribute(\n 'data-original-title',\n this.element.getAttribute('title') || ''\n )\n\n this.element.setAttribute('title', '')\n }\n }\n\n _enter(event, context) {\n const dataKey = this.constructor.DATA_KEY\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER\n ] = true\n }\n\n if ($(context.getTipElement()).hasClass(ClassName.SHOW) || context._hoverState === HoverState.SHOW) {\n context._hoverState = HoverState.SHOW\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.SHOW\n\n if (!context.config.delay || !context.config.delay.show) {\n context.show()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.SHOW) {\n context.show()\n }\n }, context.config.delay.show)\n }\n\n _leave(event, context) {\n const dataKey = this.constructor.DATA_KEY\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER\n ] = false\n }\n\n if (context._isWithActiveTrigger()) {\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.OUT\n\n if (!context.config.delay || !context.config.delay.hide) {\n context.hide()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.OUT) {\n context.hide()\n }\n }, context.config.delay.hide)\n }\n\n _isWithActiveTrigger() {\n for (const trigger in this._activeTrigger) {\n if (this._activeTrigger[trigger]) {\n return true\n }\n }\n\n return false\n }\n\n _getConfig(config) {\n const dataAttributes = $(this.element).data()\n\n Object.keys(dataAttributes)\n .forEach((dataAttr) => {\n if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {\n delete dataAttributes[dataAttr]\n }\n })\n\n config = {\n ...this.constructor.Default,\n ...dataAttributes,\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n if (config.sanitize) {\n config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n if (this.config) {\n for (const key in this.config) {\n if (this.constructor.Default[key] !== this.config[key]) {\n config[key] = this.config[key]\n }\n }\n }\n\n return config\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n _handlePopperPlacementChange(popperData) {\n const popperInstance = popperData.instance\n this.tip = popperInstance.popper\n this._cleanTipClass()\n this.addAttachmentClass(this._getAttachment(popperData.placement))\n }\n\n _fixTransition() {\n const tip = this.getTipElement()\n const initConfigAnimation = this.config.animation\n\n if (tip.getAttribute('x-placement') !== null) {\n return\n }\n\n $(tip).removeClass(ClassName.FADE)\n this.config.animation = false\n this.hide()\n this.show()\n this.config.animation = initConfigAnimation\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' && config\n\n if (!data && /dispose|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Tooltip(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Tooltip._jQueryInterface\n$.fn[NAME].Constructor = Tooltip\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Tooltip._jQueryInterface\n}\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Tooltip from './tooltip'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'popover'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.popover'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst CLASS_PREFIX = 'bs-popover'\nconst BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\n\nconst Default = {\n ...Tooltip.Default,\n placement : 'right',\n trigger : 'click',\n content : '',\n template : '
' +\n '
' +\n '

' +\n '
'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content : '(string|element|function)'\n}\n\nconst ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n TITLE : '.popover-header',\n CONTENT : '.popover-body'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Popover extends Tooltip {\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Overrides\n\n isWithContent() {\n return this.getTitle() || this._getContent()\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const $tip = $(this.getTipElement())\n\n // We use append for html objects to maintain js events\n this.setElementContent($tip.find(Selector.TITLE), this.getTitle())\n let content = this._getContent()\n if (typeof content === 'function') {\n content = content.call(this.element)\n }\n this.setElementContent($tip.find(Selector.CONTENT), content)\n\n $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n // Private\n\n _getContent() {\n return this.element.getAttribute('data-content') ||\n this.config.content\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length > 0) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data && /dispose|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Popover(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Popover._jQueryInterface\n$.fn[NAME].Constructor = Popover\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Popover._jQueryInterface\n}\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'scrollspy'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n offset : 10,\n method : 'auto',\n target : ''\n}\n\nconst DefaultType = {\n offset : 'number',\n method : 'string',\n target : '(string|element)'\n}\n\nconst Event = {\n ACTIVATE : `activate${EVENT_KEY}`,\n SCROLL : `scroll${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n DROPDOWN_ITEM : 'dropdown-item',\n DROPDOWN_MENU : 'dropdown-menu',\n ACTIVE : 'active'\n}\n\nconst Selector = {\n DATA_SPY : '[data-spy=\"scroll\"]',\n ACTIVE : '.active',\n NAV_LIST_GROUP : '.nav, .list-group',\n NAV_LINKS : '.nav-link',\n NAV_ITEMS : '.nav-item',\n LIST_ITEMS : '.list-group-item',\n DROPDOWN : '.dropdown',\n DROPDOWN_ITEMS : '.dropdown-item',\n DROPDOWN_TOGGLE : '.dropdown-toggle'\n}\n\nconst OffsetMethod = {\n OFFSET : 'offset',\n POSITION : 'position'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass ScrollSpy {\n constructor(element, config) {\n this._element = element\n this._scrollElement = element.tagName === 'BODY' ? window : element\n this._config = this._getConfig(config)\n this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +\n `${this._config.target} ${Selector.LIST_ITEMS},` +\n `${this._config.target} ${Selector.DROPDOWN_ITEMS}`\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n $(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window\n ? OffsetMethod.OFFSET : OffsetMethod.POSITION\n\n const offsetMethod = this._config.method === 'auto'\n ? autoMethod : this._config.method\n\n const offsetBase = offsetMethod === OffsetMethod.POSITION\n ? this._getScrollTop() : 0\n\n this._offsets = []\n this._targets = []\n\n this._scrollHeight = this._getScrollHeight()\n\n const targets = [].slice.call(document.querySelectorAll(this._selector))\n\n targets\n .map((element) => {\n let target\n const targetSelector = Util.getSelectorFromElement(element)\n\n if (targetSelector) {\n target = document.querySelector(targetSelector)\n }\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n // TODO (fat): remove sketch reliance on jQuery position/offset\n return [\n $(target)[offsetMethod]().top + offsetBase,\n targetSelector\n ]\n }\n }\n return null\n })\n .filter((item) => item)\n .sort((a, b) => a[0] - b[0])\n .forEach((item) => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._scrollElement).off(EVENT_KEY)\n\n this._element = null\n this._scrollElement = null\n this._config = null\n this._selector = null\n this._offsets = null\n this._targets = null\n this._activeTarget = null\n this._scrollHeight = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (typeof config.target !== 'string') {\n let id = $(config.target).attr('id')\n if (!id) {\n id = Util.getUID(NAME)\n $(config.target).attr('id', id)\n }\n config.target = `#${id}`\n }\n\n Util.typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window\n ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window\n ? window.innerHeight : this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset +\n scrollHeight -\n this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n const offsetLength = this._offsets.length\n for (let i = offsetLength; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' ||\n scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n const queries = this._selector\n .split(',')\n .map((selector) => `${selector}[data-target=\"${target}\"],${selector}[href=\"${target}\"]`)\n\n const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))\n\n if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {\n $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)\n $link.addClass(ClassName.ACTIVE)\n } else {\n // Set triggered link as active\n $link.addClass(ClassName.ACTIVE)\n // Set triggered links parents as active\n // With both
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Ee},je="show",He="out",Re={HIDE:"hide"+De,HIDDEN:"hidden"+De,SHOW:"show"+De,SHOWN:"shown"+De,INSERTED:"inserted"+De,CLICK:"click"+De,FOCUSIN:"focusin"+De,FOCUSOUT:"focusout"+De,MOUSEENTER:"mouseenter"+De,MOUSELEAVE:"mouseleave"+De},xe="fade",Fe="show",Ue=".tooltip-inner",We=".arrow",qe="hover",Me="focus",Ke="click",Qe="manual",Be=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Fe))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(xe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,{placement:a,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:We},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),g(o).addClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===He&&e._leave(null,e)};if(g(this.tip).hasClass(xe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=g.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==je&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),g(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(g(this.element).trigger(i),!i.isDefaultPrevented()){if(g(n).removeClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ke]=!1,this._activeTrigger[Me]=!1,this._activeTrigger[qe]=!1,g(this.tip).hasClass(xe)){var r=_.getTransitionDurationFromElement(n);g(n).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Ae+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ue)),this.getTitle()),g(t).removeClass(xe+" "+Fe)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Se(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Pe[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Qe){var e=t===qe?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===qe?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),g(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Me:qe]=!0),g(e.getTipElement()).hasClass(Fe)||e._hoverState===je?e._hoverState=je:(clearTimeout(e._timeout),e._hoverState=je,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===je&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Me:qe]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=He,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===He&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==Oe.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(be,t,this.constructor.DefaultType),t.sanitize&&(t.template=Se(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ne);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(xe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ie),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ie,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return Le}},{key:"NAME",get:function(){return be}},{key:"DATA_KEY",get:function(){return Ie}},{key:"Event",get:function(){return Re}},{key:"EVENT_KEY",get:function(){return De}},{key:"DefaultType",get:function(){return ke}}]),i}();g.fn[be]=Be._jQueryInterface,g.fn[be].Constructor=Be,g.fn[be].noConflict=function(){return g.fn[be]=we,Be._jQueryInterface};var Ve="popover",Ye="bs.popover",ze="."+Ye,Xe=g.fn[Ve],$e="bs-popover",Ge=new RegExp("(^|\\s)"+$e+"\\S+","g"),Je=l({},Be.Default,{placement:"right",trigger:"click",content:"",template:''}),Ze=l({},Be.DefaultType,{content:"(string|element|function)"}),tn="fade",en="show",nn=".popover-header",on=".popover-body",rn={HIDE:"hide"+ze,HIDDEN:"hidden"+ze,SHOW:"show"+ze,SHOWN:"shown"+ze,INSERTED:"inserted"+ze,CLICK:"click"+ze,FOCUSIN:"focusin"+ze,FOCUSOUT:"focusout"+ze,MOUSEENTER:"mouseenter"+ze,MOUSELEAVE:"mouseleave"+ze},sn=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){g(this.getTipElement()).addClass($e+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},o.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(nn),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(on),e),t.removeClass(tn+" "+en)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ge);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t {\n called = true\n })\n\n setTimeout(() => {\n if (!called) {\n Util.triggerTransitionEnd(this)\n }\n }, duration)\n\n return this\n}\n\nfunction setTransitionEndSupport() {\n $.fn.emulateTransitionEnd = transitionEndEmulator\n $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst Util = {\n\n TRANSITION_END: 'bsTransitionEnd',\n\n getUID(prefix) {\n do {\n // eslint-disable-next-line no-bitwise\n prefix += ~~(Math.random() * MAX_UID) // \"~~\" acts like a faster Math.floor() here\n } while (document.getElementById(prefix))\n return prefix\n },\n\n getSelectorFromElement(element) {\n let selector = element.getAttribute('data-target')\n\n if (!selector || selector === '#') {\n const hrefAttr = element.getAttribute('href')\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''\n }\n\n try {\n return document.querySelector(selector) ? selector : null\n } catch (err) {\n return null\n }\n },\n\n getTransitionDurationFromElement(element) {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let transitionDuration = $(element).css('transition-duration')\n let transitionDelay = $(element).css('transition-delay')\n\n const floatTransitionDuration = parseFloat(transitionDuration)\n const floatTransitionDelay = parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n },\n\n reflow(element) {\n return element.offsetHeight\n },\n\n triggerTransitionEnd(element) {\n $(element).trigger(TRANSITION_END)\n },\n\n // TODO: Remove in v5\n supportsTransitionEnd() {\n return Boolean(TRANSITION_END)\n },\n\n isElement(obj) {\n return (obj[0] || obj).nodeType\n },\n\n typeCheckConfig(componentName, config, configTypes) {\n for (const property in configTypes) {\n if (Object.prototype.hasOwnProperty.call(configTypes, property)) {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && Util.isElement(value)\n ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new Error(\n `${componentName.toUpperCase()}: ` +\n `Option \"${property}\" provided type \"${valueType}\" ` +\n `but expected type \"${expectedTypes}\".`)\n }\n }\n }\n },\n\n findShadowRoot(element) {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return Util.findShadowRoot(element.parentNode)\n }\n}\n\nsetTransitionEndSupport()\n\nexport default Util\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'alert'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Selector = {\n DISMISS : '[data-dismiss=\"alert\"]'\n}\n\nconst Event = {\n CLOSE : `close${EVENT_KEY}`,\n CLOSED : `closed${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n ALERT : 'alert',\n FADE : 'fade',\n SHOW : 'show'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Alert {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n close(element) {\n let rootElement = this._element\n if (element) {\n rootElement = this._getRootElement(element)\n }\n\n const customEvent = this._triggerCloseEvent(rootElement)\n\n if (customEvent.isDefaultPrevented()) {\n return\n }\n\n this._removeElement(rootElement)\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Private\n\n _getRootElement(element) {\n const selector = Util.getSelectorFromElement(element)\n let parent = false\n\n if (selector) {\n parent = document.querySelector(selector)\n }\n\n if (!parent) {\n parent = $(element).closest(`.${ClassName.ALERT}`)[0]\n }\n\n return parent\n }\n\n _triggerCloseEvent(element) {\n const closeEvent = $.Event(Event.CLOSE)\n\n $(element).trigger(closeEvent)\n return closeEvent\n }\n\n _removeElement(element) {\n $(element).removeClass(ClassName.SHOW)\n\n if (!$(element).hasClass(ClassName.FADE)) {\n this._destroyElement(element)\n return\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(element)\n\n $(element)\n .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))\n .emulateTransitionEnd(transitionDuration)\n }\n\n _destroyElement(element) {\n $(element)\n .detach()\n .trigger(Event.CLOSED)\n .remove()\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $element = $(this)\n let data = $element.data(DATA_KEY)\n\n if (!data) {\n data = new Alert(this)\n $element.data(DATA_KEY, data)\n }\n\n if (config === 'close') {\n data[config](this)\n }\n })\n }\n\n static _handleDismiss(alertInstance) {\n return function (event) {\n if (event) {\n event.preventDefault()\n }\n\n alertInstance.close(this)\n }\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(\n Event.CLICK_DATA_API,\n Selector.DISMISS,\n Alert._handleDismiss(new Alert())\n)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Alert._jQueryInterface\n$.fn[NAME].Constructor = Alert\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Alert._jQueryInterface\n}\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'button'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst ClassName = {\n ACTIVE : 'active',\n BUTTON : 'btn',\n FOCUS : 'focus'\n}\n\nconst Selector = {\n DATA_TOGGLE_CARROT : '[data-toggle^=\"button\"]',\n DATA_TOGGLE : '[data-toggle=\"buttons\"]',\n INPUT : 'input:not([type=\"hidden\"])',\n ACTIVE : '.active',\n BUTTON : '.btn'\n}\n\nconst Event = {\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +\n `blur${EVENT_KEY}${DATA_API_KEY}`\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Button {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n toggle() {\n let triggerChangeEvent = true\n let addAriaPressed = true\n const rootElement = $(this._element).closest(\n Selector.DATA_TOGGLE\n )[0]\n\n if (rootElement) {\n const input = this._element.querySelector(Selector.INPUT)\n\n if (input) {\n if (input.type === 'radio') {\n if (input.checked &&\n this._element.classList.contains(ClassName.ACTIVE)) {\n triggerChangeEvent = false\n } else {\n const activeElement = rootElement.querySelector(Selector.ACTIVE)\n\n if (activeElement) {\n $(activeElement).removeClass(ClassName.ACTIVE)\n }\n }\n }\n\n if (triggerChangeEvent) {\n if (input.hasAttribute('disabled') ||\n rootElement.hasAttribute('disabled') ||\n input.classList.contains('disabled') ||\n rootElement.classList.contains('disabled')) {\n return\n }\n input.checked = !this._element.classList.contains(ClassName.ACTIVE)\n $(input).trigger('change')\n }\n\n input.focus()\n addAriaPressed = false\n }\n }\n\n if (addAriaPressed) {\n this._element.setAttribute('aria-pressed',\n !this._element.classList.contains(ClassName.ACTIVE))\n }\n\n if (triggerChangeEvent) {\n $(this._element).toggleClass(ClassName.ACTIVE)\n }\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n\n if (!data) {\n data = new Button(this)\n $(this).data(DATA_KEY, data)\n }\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n event.preventDefault()\n\n let button = event.target\n\n if (!$(button).hasClass(ClassName.BUTTON)) {\n button = $(button).closest(Selector.BUTTON)\n }\n\n Button._jQueryInterface.call($(button), 'toggle')\n })\n .on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n const button = $(event.target).closest(Selector.BUTTON)[0]\n $(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))\n })\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Button._jQueryInterface\n$.fn[NAME].Constructor = Button\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Button._jQueryInterface\n}\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'carousel'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key\nconst ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n interval : 5000,\n keyboard : true,\n slide : false,\n pause : 'hover',\n wrap : true,\n touch : true\n}\n\nconst DefaultType = {\n interval : '(number|boolean)',\n keyboard : 'boolean',\n slide : '(boolean|string)',\n pause : '(string|boolean)',\n wrap : 'boolean',\n touch : 'boolean'\n}\n\nconst Direction = {\n NEXT : 'next',\n PREV : 'prev',\n LEFT : 'left',\n RIGHT : 'right'\n}\n\nconst Event = {\n SLIDE : `slide${EVENT_KEY}`,\n SLID : `slid${EVENT_KEY}`,\n KEYDOWN : `keydown${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`,\n TOUCHSTART : `touchstart${EVENT_KEY}`,\n TOUCHMOVE : `touchmove${EVENT_KEY}`,\n TOUCHEND : `touchend${EVENT_KEY}`,\n POINTERDOWN : `pointerdown${EVENT_KEY}`,\n POINTERUP : `pointerup${EVENT_KEY}`,\n DRAG_START : `dragstart${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n CAROUSEL : 'carousel',\n ACTIVE : 'active',\n SLIDE : 'slide',\n RIGHT : 'carousel-item-right',\n LEFT : 'carousel-item-left',\n NEXT : 'carousel-item-next',\n PREV : 'carousel-item-prev',\n ITEM : 'carousel-item',\n POINTER_EVENT : 'pointer-event'\n}\n\nconst Selector = {\n ACTIVE : '.active',\n ACTIVE_ITEM : '.active.carousel-item',\n ITEM : '.carousel-item',\n ITEM_IMG : '.carousel-item img',\n NEXT_PREV : '.carousel-item-next, .carousel-item-prev',\n INDICATORS : '.carousel-indicators',\n DATA_SLIDE : '[data-slide], [data-slide-to]',\n DATA_RIDE : '[data-ride=\"carousel\"]'\n}\n\nconst PointerType = {\n TOUCH : 'touch',\n PEN : 'pen'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\nclass Carousel {\n constructor(element, config) {\n this._items = null\n this._interval = null\n this._activeElement = null\n this._isPaused = false\n this._isSliding = false\n this.touchTimeout = null\n this.touchStartX = 0\n this.touchDeltaX = 0\n\n this._config = this._getConfig(config)\n this._element = element\n this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)\n this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n next() {\n if (!this._isSliding) {\n this._slide(Direction.NEXT)\n }\n }\n\n nextWhenVisible() {\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden &&\n ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {\n this.next()\n }\n }\n\n prev() {\n if (!this._isSliding) {\n this._slide(Direction.PREV)\n }\n }\n\n pause(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if (this._element.querySelector(Selector.NEXT_PREV)) {\n Util.triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config.interval && !this._isPaused) {\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n $(this._element).one(Event.SLID, () => this.to(index))\n return\n }\n\n if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const direction = index > activeIndex\n ? Direction.NEXT\n : Direction.PREV\n\n this._slide(direction, this._items[index])\n }\n\n dispose() {\n $(this._element).off(EVENT_KEY)\n $.removeData(this._element, DATA_KEY)\n\n this._items = null\n this._config = null\n this._element = null\n this._interval = null\n this._isPaused = null\n this._isSliding = null\n this._activeElement = null\n this._indicatorsElement = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _handleSwipe() {\n const absDeltax = Math.abs(this.touchDeltaX)\n\n if (absDeltax <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltax / this.touchDeltaX\n\n // swipe left\n if (direction > 0) {\n this.prev()\n }\n\n // swipe right\n if (direction < 0) {\n this.next()\n }\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n $(this._element)\n .on(Event.KEYDOWN, (event) => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n $(this._element)\n .on(Event.MOUSEENTER, (event) => this.pause(event))\n .on(Event.MOUSELEAVE, (event) => this.cycle(event))\n }\n\n if (this._config.touch) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n if (!this._touchSupported) {\n return\n }\n\n const start = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchStartX = event.originalEvent.clientX\n } else if (!this._pointerEvent) {\n this.touchStartX = event.originalEvent.touches[0].clientX\n }\n }\n\n const move = (event) => {\n // ensure swiping with one touch and not pinching\n if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {\n this.touchDeltaX = 0\n } else {\n this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX\n }\n }\n\n const end = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchDeltaX = event.originalEvent.clientX - this.touchStartX\n }\n\n this._handleSwipe()\n if (this._config.pause === 'hover') {\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n }\n\n $(this._element.querySelectorAll(Selector.ITEM_IMG)).on(Event.DRAG_START, (e) => e.preventDefault())\n if (this._pointerEvent) {\n $(this._element).on(Event.POINTERDOWN, (event) => start(event))\n $(this._element).on(Event.POINTERUP, (event) => end(event))\n\n this._element.classList.add(ClassName.POINTER_EVENT)\n } else {\n $(this._element).on(Event.TOUCHSTART, (event) => start(event))\n $(this._element).on(Event.TOUCHMOVE, (event) => move(event))\n $(this._element).on(Event.TOUCHEND, (event) => end(event))\n }\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n switch (event.which) {\n case ARROW_LEFT_KEYCODE:\n event.preventDefault()\n this.prev()\n break\n case ARROW_RIGHT_KEYCODE:\n event.preventDefault()\n this.next()\n break\n default:\n }\n }\n\n _getItemIndex(element) {\n this._items = element && element.parentNode\n ? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))\n : []\n return this._items.indexOf(element)\n }\n\n _getItemByDirection(direction, activeElement) {\n const isNextDirection = direction === Direction.NEXT\n const isPrevDirection = direction === Direction.PREV\n const activeIndex = this._getItemIndex(activeElement)\n const lastItemIndex = this._items.length - 1\n const isGoingToWrap = isPrevDirection && activeIndex === 0 ||\n isNextDirection && activeIndex === lastItemIndex\n\n if (isGoingToWrap && !this._config.wrap) {\n return activeElement\n }\n\n const delta = direction === Direction.PREV ? -1 : 1\n const itemIndex = (activeIndex + delta) % this._items.length\n\n return itemIndex === -1\n ? this._items[this._items.length - 1] : this._items[itemIndex]\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))\n const slideEvent = $.Event(Event.SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n\n $(this._element).trigger(slideEvent)\n\n return slideEvent\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\n const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))\n $(indicators)\n .removeClass(ClassName.ACTIVE)\n\n const nextIndicator = this._indicatorsElement.children[\n this._getItemIndex(element)\n ]\n\n if (nextIndicator) {\n $(nextIndicator).addClass(ClassName.ACTIVE)\n }\n }\n }\n\n _slide(direction, element) {\n const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || activeElement &&\n this._getItemByDirection(direction, activeElement)\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n let directionalClassName\n let orderClassName\n let eventDirectionName\n\n if (direction === Direction.NEXT) {\n directionalClassName = ClassName.LEFT\n orderClassName = ClassName.NEXT\n eventDirectionName = Direction.LEFT\n } else {\n directionalClassName = ClassName.RIGHT\n orderClassName = ClassName.PREV\n eventDirectionName = Direction.RIGHT\n }\n\n if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {\n this._isSliding = false\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.isDefaultPrevented()) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n\n const slidEvent = $.Event(Event.SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n\n if ($(this._element).hasClass(ClassName.SLIDE)) {\n $(nextElement).addClass(orderClassName)\n\n Util.reflow(nextElement)\n\n $(activeElement).addClass(directionalClassName)\n $(nextElement).addClass(directionalClassName)\n\n const nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10)\n if (nextElementInterval) {\n this._config.defaultInterval = this._config.defaultInterval || this._config.interval\n this._config.interval = nextElementInterval\n } else {\n this._config.interval = this._config.defaultInterval || this._config.interval\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(activeElement)\n\n $(activeElement)\n .one(Util.TRANSITION_END, () => {\n $(nextElement)\n .removeClass(`${directionalClassName} ${orderClassName}`)\n .addClass(ClassName.ACTIVE)\n\n $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)\n\n this._isSliding = false\n\n setTimeout(() => $(this._element).trigger(slidEvent), 0)\n })\n .emulateTransitionEnd(transitionDuration)\n } else {\n $(activeElement).removeClass(ClassName.ACTIVE)\n $(nextElement).addClass(ClassName.ACTIVE)\n\n this._isSliding = false\n $(this._element).trigger(slidEvent)\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n let _config = {\n ...Default,\n ...$(this).data()\n }\n\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (!data) {\n data = new Carousel(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n data[action]()\n } else if (_config.interval && _config.ride) {\n data.pause()\n data.cycle()\n }\n })\n }\n\n static _dataApiClickHandler(event) {\n const selector = Util.getSelectorFromElement(this)\n\n if (!selector) {\n return\n }\n\n const target = $(selector)[0]\n\n if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {\n return\n }\n\n const config = {\n ...$(target).data(),\n ...$(this).data()\n }\n const slideIndex = this.getAttribute('data-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel._jQueryInterface.call($(target), config)\n\n if (slideIndex) {\n $(target).data(DATA_KEY).to(slideIndex)\n }\n\n event.preventDefault()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)\n\n$(window).on(Event.LOAD_DATA_API, () => {\n const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))\n for (let i = 0, len = carousels.length; i < len; i++) {\n const $carousel = $(carousels[i])\n Carousel._jQueryInterface.call($carousel, $carousel.data())\n }\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Carousel._jQueryInterface\n$.fn[NAME].Constructor = Carousel\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Carousel._jQueryInterface\n}\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'collapse'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n toggle : true,\n parent : ''\n}\n\nconst DefaultType = {\n toggle : 'boolean',\n parent : '(string|element)'\n}\n\nconst Event = {\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SHOW : 'show',\n COLLAPSE : 'collapse',\n COLLAPSING : 'collapsing',\n COLLAPSED : 'collapsed'\n}\n\nconst Dimension = {\n WIDTH : 'width',\n HEIGHT : 'height'\n}\n\nconst Selector = {\n ACTIVES : '.show, .collapsing',\n DATA_TOGGLE : '[data-toggle=\"collapse\"]'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Collapse {\n constructor(element, config) {\n this._isTransitioning = false\n this._element = element\n this._config = this._getConfig(config)\n this._triggerArray = [].slice.call(document.querySelectorAll(\n `[data-toggle=\"collapse\"][href=\"#${element.id}\"],` +\n `[data-toggle=\"collapse\"][data-target=\"#${element.id}\"]`\n ))\n\n const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n for (let i = 0, len = toggleList.length; i < len; i++) {\n const elem = toggleList[i]\n const selector = Util.getSelectorFromElement(elem)\n const filterElement = [].slice.call(document.querySelectorAll(selector))\n .filter((foundElem) => foundElem === element)\n\n if (selector !== null && filterElement.length > 0) {\n this._selector = selector\n this._triggerArray.push(elem)\n }\n }\n\n this._parent = this._config.parent ? this._getParent() : null\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._element, this._triggerArray)\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle() {\n if ($(this._element).hasClass(ClassName.SHOW)) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning ||\n $(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n let actives\n let activesData\n\n if (this._parent) {\n actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))\n .filter((elem) => {\n if (typeof this._config.parent === 'string') {\n return elem.getAttribute('data-parent') === this._config.parent\n }\n\n return elem.classList.contains(ClassName.COLLAPSE)\n })\n\n if (actives.length === 0) {\n actives = null\n }\n }\n\n if (actives) {\n activesData = $(actives).not(this._selector).data(DATA_KEY)\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = $.Event(Event.SHOW)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n if (actives) {\n Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')\n if (!activesData) {\n $(actives).data(DATA_KEY, null)\n }\n }\n\n const dimension = this._getDimension()\n\n $(this._element)\n .removeClass(ClassName.COLLAPSE)\n .addClass(ClassName.COLLAPSING)\n\n this._element.style[dimension] = 0\n\n if (this._triggerArray.length) {\n $(this._triggerArray)\n .removeClass(ClassName.COLLAPSED)\n .attr('aria-expanded', true)\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .addClass(ClassName.SHOW)\n\n this._element.style[dimension] = ''\n\n this.setTransitioning(false)\n\n $(this._element).trigger(Event.SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning ||\n !$(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n const startEvent = $.Event(Event.HIDE)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n Util.reflow(this._element)\n\n $(this._element)\n .addClass(ClassName.COLLAPSING)\n .removeClass(ClassName.COLLAPSE)\n .removeClass(ClassName.SHOW)\n\n const triggerArrayLength = this._triggerArray.length\n if (triggerArrayLength > 0) {\n for (let i = 0; i < triggerArrayLength; i++) {\n const trigger = this._triggerArray[i]\n const selector = Util.getSelectorFromElement(trigger)\n\n if (selector !== null) {\n const $elem = $([].slice.call(document.querySelectorAll(selector)))\n if (!$elem.hasClass(ClassName.SHOW)) {\n $(trigger).addClass(ClassName.COLLAPSED)\n .attr('aria-expanded', false)\n }\n }\n }\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n this.setTransitioning(false)\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .trigger(Event.HIDDEN)\n }\n\n this._element.style[dimension] = ''\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n }\n\n setTransitioning(isTransitioning) {\n this._isTransitioning = isTransitioning\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._parent = null\n this._element = null\n this._triggerArray = null\n this._isTransitioning = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _getDimension() {\n const hasWidth = $(this._element).hasClass(Dimension.WIDTH)\n return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT\n }\n\n _getParent() {\n let parent\n\n if (Util.isElement(this._config.parent)) {\n parent = this._config.parent\n\n // It's a jQuery object\n if (typeof this._config.parent.jquery !== 'undefined') {\n parent = this._config.parent[0]\n }\n } else {\n parent = document.querySelector(this._config.parent)\n }\n\n const selector =\n `[data-toggle=\"collapse\"][data-parent=\"${this._config.parent}\"]`\n\n const children = [].slice.call(parent.querySelectorAll(selector))\n $(children).each((i, element) => {\n this._addAriaAndCollapsedClass(\n Collapse._getTargetFromElement(element),\n [element]\n )\n })\n\n return parent\n }\n\n _addAriaAndCollapsedClass(element, triggerArray) {\n const isOpen = $(element).hasClass(ClassName.SHOW)\n\n if (triggerArray.length) {\n $(triggerArray)\n .toggleClass(ClassName.COLLAPSED, !isOpen)\n .attr('aria-expanded', isOpen)\n }\n }\n\n // Static\n\n static _getTargetFromElement(element) {\n const selector = Util.getSelectorFromElement(element)\n return selector ? document.querySelector(selector) : null\n }\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $this = $(this)\n let data = $this.data(DATA_KEY)\n const _config = {\n ...Default,\n ...$this.data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data && _config.toggle && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n if (!data) {\n data = new Collapse(this, _config)\n $this.data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.currentTarget.tagName === 'A') {\n event.preventDefault()\n }\n\n const $trigger = $(this)\n const selector = Util.getSelectorFromElement(this)\n const selectors = [].slice.call(document.querySelectorAll(selector))\n\n $(selectors).each(function () {\n const $target = $(this)\n const data = $target.data(DATA_KEY)\n const config = data ? 'toggle' : $trigger.data()\n Collapse._jQueryInterface.call($target, config)\n })\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Collapse._jQueryInterface\n$.fn[NAME].Constructor = Collapse\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Collapse._jQueryInterface\n}\n\nexport default Collapse\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'dropdown'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\nconst SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key\nconst TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key\nconst ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key\nconst ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key\nconst RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)\nconst REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`,\n KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n DISABLED : 'disabled',\n SHOW : 'show',\n DROPUP : 'dropup',\n DROPRIGHT : 'dropright',\n DROPLEFT : 'dropleft',\n MENURIGHT : 'dropdown-menu-right',\n MENULEFT : 'dropdown-menu-left',\n POSITION_STATIC : 'position-static'\n}\n\nconst Selector = {\n DATA_TOGGLE : '[data-toggle=\"dropdown\"]',\n FORM_CHILD : '.dropdown form',\n MENU : '.dropdown-menu',\n NAVBAR_NAV : '.navbar-nav',\n VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n}\n\nconst AttachmentMap = {\n TOP : 'top-start',\n TOPEND : 'top-end',\n BOTTOM : 'bottom-start',\n BOTTOMEND : 'bottom-end',\n RIGHT : 'right-start',\n RIGHTEND : 'right-end',\n LEFT : 'left-start',\n LEFTEND : 'left-end'\n}\n\nconst Default = {\n offset : 0,\n flip : true,\n boundary : 'scrollParent',\n reference : 'toggle',\n display : 'dynamic'\n}\n\nconst DefaultType = {\n offset : '(number|string|function)',\n flip : 'boolean',\n boundary : '(string|element)',\n reference : '(string|element)',\n display : 'string'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Dropdown {\n constructor(element, config) {\n this._element = element\n this._popper = null\n this._config = this._getConfig(config)\n this._menu = this._getMenuElement()\n this._inNavbar = this._detectNavbar()\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n toggle() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this._element)\n const isActive = $(this._menu).hasClass(ClassName.SHOW)\n\n Dropdown._clearMenus()\n\n if (isActive) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const showEvent = $.Event(Event.SHOW, relatedTarget)\n\n $(parent).trigger(showEvent)\n\n if (showEvent.isDefaultPrevented()) {\n return\n }\n\n // Disable totally Popper.js for Dropdown in Navbar\n if (!this._inNavbar) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper.js (https://popper.js.org/)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = parent\n } else if (Util.isElement(this._config.reference)) {\n referenceElement = this._config.reference\n\n // Check if it's jQuery element\n if (typeof this._config.reference.jquery !== 'undefined') {\n referenceElement = this._config.reference[0]\n }\n }\n\n // If boundary is not `scrollParent`, then set position to `static`\n // to allow the menu to \"escape\" the scroll parent's boundaries\n // https://github.com/twbs/bootstrap/issues/24251\n if (this._config.boundary !== 'scrollParent') {\n $(parent).addClass(ClassName.POSITION_STATIC)\n }\n this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement &&\n $(parent).closest(Selector.NAVBAR_NAV).length === 0) {\n $(document.body).children().on('mouseover', null, $.noop)\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.SHOWN, relatedTarget))\n }\n\n show() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || $(this._menu).hasClass(ClassName.SHOW)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const showEvent = $.Event(Event.SHOW, relatedTarget)\n const parent = Dropdown._getParentFromElement(this._element)\n\n $(parent).trigger(showEvent)\n\n if (showEvent.isDefaultPrevented()) {\n return\n }\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.SHOWN, relatedTarget))\n }\n\n hide() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || !$(this._menu).hasClass(ClassName.SHOW)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const hideEvent = $.Event(Event.HIDE, relatedTarget)\n const parent = Dropdown._getParentFromElement(this._element)\n\n $(parent).trigger(hideEvent)\n\n if (hideEvent.isDefaultPrevented()) {\n return\n }\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.HIDDEN, relatedTarget))\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._element).off(EVENT_KEY)\n this._element = null\n this._menu = null\n if (this._popper !== null) {\n this._popper.destroy()\n this._popper = null\n }\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Private\n\n _addEventListeners() {\n $(this._element).on(Event.CLICK, (event) => {\n event.preventDefault()\n event.stopPropagation()\n this.toggle()\n })\n }\n\n _getConfig(config) {\n config = {\n ...this.constructor.Default,\n ...$(this._element).data(),\n ...config\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n return config\n }\n\n _getMenuElement() {\n if (!this._menu) {\n const parent = Dropdown._getParentFromElement(this._element)\n\n if (parent) {\n this._menu = parent.querySelector(Selector.MENU)\n }\n }\n return this._menu\n }\n\n _getPlacement() {\n const $parentDropdown = $(this._element.parentNode)\n let placement = AttachmentMap.BOTTOM\n\n // Handle dropup\n if ($parentDropdown.hasClass(ClassName.DROPUP)) {\n placement = AttachmentMap.TOP\n if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.TOPEND\n }\n } else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {\n placement = AttachmentMap.RIGHT\n } else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {\n placement = AttachmentMap.LEFT\n } else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.BOTTOMEND\n }\n return placement\n }\n\n _detectNavbar() {\n return $(this._element).closest('.navbar').length > 0\n }\n\n _getOffset() {\n const offset = {}\n\n if (typeof this._config.offset === 'function') {\n offset.fn = (data) => {\n data.offsets = {\n ...data.offsets,\n ...this._config.offset(data.offsets, this._element) || {}\n }\n\n return data\n }\n } else {\n offset.offset = this._config.offset\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const popperConfig = {\n placement: this._getPlacement(),\n modifiers: {\n offset: this._getOffset(),\n flip: {\n enabled: this._config.flip\n },\n preventOverflow: {\n boundariesElement: this._config.boundary\n }\n }\n }\n\n // Disable Popper.js if we have a static display\n if (this._config.display === 'static') {\n popperConfig.modifiers.applyStyle = {\n enabled: false\n }\n }\n\n return popperConfig\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data) {\n data = new Dropdown(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n\n static _clearMenus(event) {\n if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||\n event.type === 'keyup' && event.which !== TAB_KEYCODE)) {\n return\n }\n\n const toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n\n for (let i = 0, len = toggles.length; i < len; i++) {\n const parent = Dropdown._getParentFromElement(toggles[i])\n const context = $(toggles[i]).data(DATA_KEY)\n const relatedTarget = {\n relatedTarget: toggles[i]\n }\n\n if (event && event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n if (!context) {\n continue\n }\n\n const dropdownMenu = context._menu\n if (!$(parent).hasClass(ClassName.SHOW)) {\n continue\n }\n\n if (event && (event.type === 'click' &&\n /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&\n $.contains(parent, event.target)) {\n continue\n }\n\n const hideEvent = $.Event(Event.HIDE, relatedTarget)\n $(parent).trigger(hideEvent)\n if (hideEvent.isDefaultPrevented()) {\n continue\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().off('mouseover', null, $.noop)\n }\n\n toggles[i].setAttribute('aria-expanded', 'false')\n\n $(dropdownMenu).removeClass(ClassName.SHOW)\n $(parent)\n .removeClass(ClassName.SHOW)\n .trigger($.Event(Event.HIDDEN, relatedTarget))\n }\n }\n\n static _getParentFromElement(element) {\n let parent\n const selector = Util.getSelectorFromElement(element)\n\n if (selector) {\n parent = document.querySelector(selector)\n }\n\n return parent || element.parentNode\n }\n\n // eslint-disable-next-line complexity\n static _dataApiKeydownHandler(event) {\n // If not input/textarea:\n // - And not a key in REGEXP_KEYDOWN => not a dropdown command\n // If input/textarea:\n // - If space key => not a dropdown command\n // - If key is other than escape\n // - If key is not up or down => not a dropdown command\n // - If trigger inside the menu => not a dropdown command\n if (/input|textarea/i.test(event.target.tagName)\n ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&\n (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||\n $(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n\n if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this)\n const isActive = $(parent).hasClass(ClassName.SHOW)\n\n if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {\n if (event.which === ESCAPE_KEYCODE) {\n const toggle = parent.querySelector(Selector.DATA_TOGGLE)\n $(toggle).trigger('focus')\n }\n\n $(this).trigger('click')\n return\n }\n\n const items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS))\n\n if (items.length === 0) {\n return\n }\n\n let index = items.indexOf(event.target)\n\n if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up\n index--\n }\n\n if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down\n index++\n }\n\n if (index < 0) {\n index = 0\n }\n\n items[index].focus()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)\n .on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)\n .on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n event.preventDefault()\n event.stopPropagation()\n Dropdown._jQueryInterface.call($(this), 'toggle')\n })\n .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {\n e.stopPropagation()\n })\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Dropdown._jQueryInterface\n$.fn[NAME].Constructor = Dropdown\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Dropdown._jQueryInterface\n}\n\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'modal'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\n\nconst Default = {\n backdrop : true,\n keyboard : true,\n focus : true,\n show : true\n}\n\nconst DefaultType = {\n backdrop : '(boolean|string)',\n keyboard : 'boolean',\n focus : 'boolean',\n show : 'boolean'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n RESIZE : `resize${EVENT_KEY}`,\n CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,\n KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`,\n MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`,\n MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SCROLLABLE : 'modal-dialog-scrollable',\n SCROLLBAR_MEASURER : 'modal-scrollbar-measure',\n BACKDROP : 'modal-backdrop',\n OPEN : 'modal-open',\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n DIALOG : '.modal-dialog',\n MODAL_BODY : '.modal-body',\n DATA_TOGGLE : '[data-toggle=\"modal\"]',\n DATA_DISMISS : '[data-dismiss=\"modal\"]',\n FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',\n STICKY_CONTENT : '.sticky-top'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Modal {\n constructor(element, config) {\n this._config = this._getConfig(config)\n this._element = element\n this._dialog = element.querySelector(Selector.DIALOG)\n this._backdrop = null\n this._isShown = false\n this._isBodyOverflowing = false\n this._ignoreBackdropClick = false\n this._isTransitioning = false\n this._scrollbarWidth = 0\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n this._isTransitioning = true\n }\n\n const showEvent = $.Event(Event.SHOW, {\n relatedTarget\n })\n\n $(this._element).trigger(showEvent)\n\n if (this._isShown || showEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = true\n\n this._checkScrollbar()\n this._setScrollbar()\n\n this._adjustDialog()\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(this._element).on(\n Event.CLICK_DISMISS,\n Selector.DATA_DISMISS,\n (event) => this.hide(event)\n )\n\n $(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => {\n $(this._element).one(Event.MOUSEUP_DISMISS, (event) => {\n if ($(event.target).is(this._element)) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => this._showElement(relatedTarget))\n }\n\n hide(event) {\n if (event) {\n event.preventDefault()\n }\n\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = $.Event(Event.HIDE)\n\n $(this._element).trigger(hideEvent)\n\n if (!this._isShown || hideEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = false\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (transition) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(document).off(Event.FOCUSIN)\n\n $(this._element).removeClass(ClassName.SHOW)\n\n $(this._element).off(Event.CLICK_DISMISS)\n $(this._dialog).off(Event.MOUSEDOWN_DISMISS)\n\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, (event) => this._hideModal(event))\n .emulateTransitionEnd(transitionDuration)\n } else {\n this._hideModal()\n }\n }\n\n dispose() {\n [window, this._element, this._dialog]\n .forEach((htmlElement) => $(htmlElement).off(EVENT_KEY))\n\n /**\n * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`\n * Do not move `document` in `htmlElements` array\n * It will remove `Event.CLICK_DATA_API` event that should remain\n */\n $(document).off(Event.FOCUSIN)\n\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._element = null\n this._dialog = null\n this._backdrop = null\n this._isShown = null\n this._isBodyOverflowing = null\n this._ignoreBackdropClick = null\n this._isTransitioning = null\n this._scrollbarWidth = null\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (!this._element.parentNode ||\n this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\n document.body.appendChild(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n\n if ($(this._dialog).hasClass(ClassName.SCROLLABLE)) {\n this._dialog.querySelector(Selector.MODAL_BODY).scrollTop = 0\n } else {\n this._element.scrollTop = 0\n }\n\n if (transition) {\n Util.reflow(this._element)\n }\n\n $(this._element).addClass(ClassName.SHOW)\n\n if (this._config.focus) {\n this._enforceFocus()\n }\n\n const shownEvent = $.Event(Event.SHOWN, {\n relatedTarget\n })\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._element.focus()\n }\n this._isTransitioning = false\n $(this._element).trigger(shownEvent)\n }\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._dialog)\n\n $(this._dialog)\n .one(Util.TRANSITION_END, transitionComplete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n transitionComplete()\n }\n }\n\n _enforceFocus() {\n $(document)\n .off(Event.FOCUSIN) // Guard against infinite focus loop\n .on(Event.FOCUSIN, (event) => {\n if (document !== event.target &&\n this._element !== event.target &&\n $(this._element).has(event.target).length === 0) {\n this._element.focus()\n }\n })\n }\n\n _setEscapeEvent() {\n if (this._isShown && this._config.keyboard) {\n $(this._element).on(Event.KEYDOWN_DISMISS, (event) => {\n if (event.which === ESCAPE_KEYCODE) {\n event.preventDefault()\n this.hide()\n }\n })\n } else if (!this._isShown) {\n $(this._element).off(Event.KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n $(window).on(Event.RESIZE, (event) => this.handleUpdate(event))\n } else {\n $(window).off(Event.RESIZE)\n }\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._isTransitioning = false\n this._showBackdrop(() => {\n $(document.body).removeClass(ClassName.OPEN)\n this._resetAdjustments()\n this._resetScrollbar()\n $(this._element).trigger(Event.HIDDEN)\n })\n }\n\n _removeBackdrop() {\n if (this._backdrop) {\n $(this._backdrop).remove()\n this._backdrop = null\n }\n }\n\n _showBackdrop(callback) {\n const animate = $(this._element).hasClass(ClassName.FADE)\n ? ClassName.FADE : ''\n\n if (this._isShown && this._config.backdrop) {\n this._backdrop = document.createElement('div')\n this._backdrop.className = ClassName.BACKDROP\n\n if (animate) {\n this._backdrop.classList.add(animate)\n }\n\n $(this._backdrop).appendTo(document.body)\n\n $(this._element).on(Event.CLICK_DISMISS, (event) => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n if (event.target !== event.currentTarget) {\n return\n }\n if (this._config.backdrop === 'static') {\n this._element.focus()\n } else {\n this.hide()\n }\n })\n\n if (animate) {\n Util.reflow(this._backdrop)\n }\n\n $(this._backdrop).addClass(ClassName.SHOW)\n\n if (!callback) {\n return\n }\n\n if (!animate) {\n callback()\n return\n }\n\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callback)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else if (!this._isShown && this._backdrop) {\n $(this._backdrop).removeClass(ClassName.SHOW)\n\n const callbackRemove = () => {\n this._removeBackdrop()\n if (callback) {\n callback()\n }\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callbackRemove)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else {\n callbackRemove()\n }\n } else if (callback) {\n callback()\n }\n }\n\n // ----------------------------------------------------------------------\n // the following methods are used to handle overflowing modals\n // todo (fat): these should probably be refactored out of modal.js\n // ----------------------------------------------------------------------\n\n _adjustDialog() {\n const isModalOverflowing =\n this._element.scrollHeight > document.documentElement.clientHeight\n\n if (!this._isBodyOverflowing && isModalOverflowing) {\n this._element.style.paddingLeft = `${this._scrollbarWidth}px`\n }\n\n if (this._isBodyOverflowing && !isModalOverflowing) {\n this._element.style.paddingRight = `${this._scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n _checkScrollbar() {\n const rect = document.body.getBoundingClientRect()\n this._isBodyOverflowing = rect.left + rect.right < window.innerWidth\n this._scrollbarWidth = this._getScrollbarWidth()\n }\n\n _setScrollbar() {\n if (this._isBodyOverflowing) {\n // Note: DOMNode.style.paddingRight returns the actual value or '' if not set\n // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n const stickyContent = [].slice.call(document.querySelectorAll(Selector.STICKY_CONTENT))\n\n // Adjust fixed content padding\n $(fixedContent).each((index, element) => {\n const actualPadding = element.style.paddingRight\n const calculatedPadding = $(element).css('padding-right')\n $(element)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n })\n\n // Adjust sticky content margin\n $(stickyContent).each((index, element) => {\n const actualMargin = element.style.marginRight\n const calculatedMargin = $(element).css('margin-right')\n $(element)\n .data('margin-right', actualMargin)\n .css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)\n })\n\n // Adjust body padding\n const actualPadding = document.body.style.paddingRight\n const calculatedPadding = $(document.body).css('padding-right')\n $(document.body)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n }\n\n $(document.body).addClass(ClassName.OPEN)\n }\n\n _resetScrollbar() {\n // Restore fixed content padding\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n $(fixedContent).each((index, element) => {\n const padding = $(element).data('padding-right')\n $(element).removeData('padding-right')\n element.style.paddingRight = padding ? padding : ''\n })\n\n // Restore sticky content\n const elements = [].slice.call(document.querySelectorAll(`${Selector.STICKY_CONTENT}`))\n $(elements).each((index, element) => {\n const margin = $(element).data('margin-right')\n if (typeof margin !== 'undefined') {\n $(element).css('margin-right', margin).removeData('margin-right')\n }\n })\n\n // Restore body padding\n const padding = $(document.body).data('padding-right')\n $(document.body).removeData('padding-right')\n document.body.style.paddingRight = padding ? padding : ''\n }\n\n _getScrollbarWidth() { // thx d.walsh\n const scrollDiv = document.createElement('div')\n scrollDiv.className = ClassName.SCROLLBAR_MEASURER\n document.body.appendChild(scrollDiv)\n const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth\n document.body.removeChild(scrollDiv)\n return scrollbarWidth\n }\n\n // Static\n\n static _jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = {\n ...Default,\n ...$(this).data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data) {\n data = new Modal(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config](relatedTarget)\n } else if (_config.show) {\n data.show(relatedTarget)\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n let target\n const selector = Util.getSelectorFromElement(this)\n\n if (selector) {\n target = document.querySelector(selector)\n }\n\n const config = $(target).data(DATA_KEY)\n ? 'toggle' : {\n ...$(target).data(),\n ...$(this).data()\n }\n\n if (this.tagName === 'A' || this.tagName === 'AREA') {\n event.preventDefault()\n }\n\n const $target = $(target).one(Event.SHOW, (showEvent) => {\n if (showEvent.isDefaultPrevented()) {\n // Only register focus restorer if modal will actually get shown\n return\n }\n\n $target.one(Event.HIDDEN, () => {\n if ($(this).is(':visible')) {\n this.focus()\n }\n })\n })\n\n Modal._jQueryInterface.call($(target), config, this)\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Modal._jQueryInterface\n$.fn[NAME].Constructor = Modal\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Modal._jQueryInterface\n}\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): tools/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst uriAttrs = [\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n]\n\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultWhitelist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n\n/**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi\n\n/**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i\n\nfunction allowedAttribute(attr, allowedAttributeList) {\n const attrName = attr.nodeName.toLowerCase()\n\n if (allowedAttributeList.indexOf(attrName) !== -1) {\n if (uriAttrs.indexOf(attrName) !== -1) {\n return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))\n }\n\n return true\n }\n\n const regExp = allowedAttributeList.filter((attrRegex) => attrRegex instanceof RegExp)\n\n // Check if a regular expression validates the attribute.\n for (let i = 0, l = regExp.length; i < l; i++) {\n if (attrName.match(regExp[i])) {\n return true\n }\n }\n\n return false\n}\n\nexport function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {\n if (unsafeHtml.length === 0) {\n return unsafeHtml\n }\n\n if (sanitizeFn && typeof sanitizeFn === 'function') {\n return sanitizeFn(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const whitelistKeys = Object.keys(whiteList)\n const elements = [].slice.call(createdDocument.body.querySelectorAll('*'))\n\n for (let i = 0, len = elements.length; i < len; i++) {\n const el = elements[i]\n const elName = el.nodeName.toLowerCase()\n\n if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {\n el.parentNode.removeChild(el)\n\n continue\n }\n\n const attributeList = [].slice.call(el.attributes)\n const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])\n\n attributeList.forEach((attr) => {\n if (!allowedAttribute(attr, whitelistedAttributes)) {\n el.removeAttribute(attr.nodeName)\n }\n })\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n DefaultWhitelist,\n sanitizeHtml\n} from './tools/sanitizer'\nimport $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'tooltip'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.tooltip'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst CLASS_PREFIX = 'bs-tooltip'\nconst BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\nconst DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']\n\nconst DefaultType = {\n animation : 'boolean',\n template : 'string',\n title : '(string|element|function)',\n trigger : 'string',\n delay : '(number|object)',\n html : 'boolean',\n selector : '(string|boolean)',\n placement : '(string|function)',\n offset : '(number|string|function)',\n container : '(string|element|boolean)',\n fallbackPlacement : '(string|array)',\n boundary : '(string|element)',\n sanitize : 'boolean',\n sanitizeFn : '(null|function)',\n whiteList : 'object'\n}\n\nconst AttachmentMap = {\n AUTO : 'auto',\n TOP : 'top',\n RIGHT : 'right',\n BOTTOM : 'bottom',\n LEFT : 'left'\n}\n\nconst Default = {\n animation : true,\n template : '
' +\n '
' +\n '
',\n trigger : 'hover focus',\n title : '',\n delay : 0,\n html : false,\n selector : false,\n placement : 'top',\n offset : 0,\n container : false,\n fallbackPlacement : 'flip',\n boundary : 'scrollParent',\n sanitize : true,\n sanitizeFn : null,\n whiteList : DefaultWhitelist\n}\n\nconst HoverState = {\n SHOW : 'show',\n OUT : 'out'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n}\n\nconst ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n TOOLTIP : '.tooltip',\n TOOLTIP_INNER : '.tooltip-inner',\n ARROW : '.arrow'\n}\n\nconst Trigger = {\n HOVER : 'hover',\n FOCUS : 'focus',\n CLICK : 'click',\n MANUAL : 'manual'\n}\n\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Tooltip {\n constructor(element, config) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper.js (https://popper.js.org/)')\n }\n\n // private\n this._isEnabled = true\n this._timeout = 0\n this._hoverState = ''\n this._activeTrigger = {}\n this._popper = null\n\n // Protected\n this.element = element\n this.config = this._getConfig(config)\n this.tip = null\n\n this._setListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle(event) {\n if (!this._isEnabled) {\n return\n }\n\n if (event) {\n const dataKey = this.constructor.DATA_KEY\n let context = $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n context._activeTrigger.click = !context._activeTrigger.click\n\n if (context._isWithActiveTrigger()) {\n context._enter(null, context)\n } else {\n context._leave(null, context)\n }\n } else {\n if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {\n this._leave(null, this)\n return\n }\n\n this._enter(null, this)\n }\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n $.removeData(this.element, this.constructor.DATA_KEY)\n\n $(this.element).off(this.constructor.EVENT_KEY)\n $(this.element).closest('.modal').off('hide.bs.modal')\n\n if (this.tip) {\n $(this.tip).remove()\n }\n\n this._isEnabled = null\n this._timeout = null\n this._hoverState = null\n this._activeTrigger = null\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n this._popper = null\n this.element = null\n this.config = null\n this.tip = null\n }\n\n show() {\n if ($(this.element).css('display') === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n const showEvent = $.Event(this.constructor.Event.SHOW)\n if (this.isWithContent() && this._isEnabled) {\n $(this.element).trigger(showEvent)\n\n const shadowRoot = Util.findShadowRoot(this.element)\n const isInTheDom = $.contains(\n shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,\n this.element\n )\n\n if (showEvent.isDefaultPrevented() || !isInTheDom) {\n return\n }\n\n const tip = this.getTipElement()\n const tipId = Util.getUID(this.constructor.NAME)\n\n tip.setAttribute('id', tipId)\n this.element.setAttribute('aria-describedby', tipId)\n\n this.setContent()\n\n if (this.config.animation) {\n $(tip).addClass(ClassName.FADE)\n }\n\n const placement = typeof this.config.placement === 'function'\n ? this.config.placement.call(this, tip, this.element)\n : this.config.placement\n\n const attachment = this._getAttachment(placement)\n this.addAttachmentClass(attachment)\n\n const container = this._getContainer()\n $(tip).data(this.constructor.DATA_KEY, this)\n\n if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {\n $(tip).appendTo(container)\n }\n\n $(this.element).trigger(this.constructor.Event.INSERTED)\n\n this._popper = new Popper(this.element, tip, {\n placement: attachment,\n modifiers: {\n offset: this._getOffset(),\n flip: {\n behavior: this.config.fallbackPlacement\n },\n arrow: {\n element: Selector.ARROW\n },\n preventOverflow: {\n boundariesElement: this.config.boundary\n }\n },\n onCreate: (data) => {\n if (data.originalPlacement !== data.placement) {\n this._handlePopperPlacementChange(data)\n }\n },\n onUpdate: (data) => this._handlePopperPlacementChange(data)\n })\n\n $(tip).addClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().on('mouseover', null, $.noop)\n }\n\n const complete = () => {\n if (this.config.animation) {\n this._fixTransition()\n }\n const prevHoverState = this._hoverState\n this._hoverState = null\n\n $(this.element).trigger(this.constructor.Event.SHOWN)\n\n if (prevHoverState === HoverState.OUT) {\n this._leave(null, this)\n }\n }\n\n if ($(this.tip).hasClass(ClassName.FADE)) {\n const transitionDuration = Util.getTransitionDurationFromElement(this.tip)\n\n $(this.tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n complete()\n }\n }\n }\n\n hide(callback) {\n const tip = this.getTipElement()\n const hideEvent = $.Event(this.constructor.Event.HIDE)\n const complete = () => {\n if (this._hoverState !== HoverState.SHOW && tip.parentNode) {\n tip.parentNode.removeChild(tip)\n }\n\n this._cleanTipClass()\n this.element.removeAttribute('aria-describedby')\n $(this.element).trigger(this.constructor.Event.HIDDEN)\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n if (callback) {\n callback()\n }\n }\n\n $(this.element).trigger(hideEvent)\n\n if (hideEvent.isDefaultPrevented()) {\n return\n }\n\n $(tip).removeClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().off('mouseover', null, $.noop)\n }\n\n this._activeTrigger[Trigger.CLICK] = false\n this._activeTrigger[Trigger.FOCUS] = false\n this._activeTrigger[Trigger.HOVER] = false\n\n if ($(this.tip).hasClass(ClassName.FADE)) {\n const transitionDuration = Util.getTransitionDurationFromElement(tip)\n\n $(tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n complete()\n }\n\n this._hoverState = ''\n }\n\n update() {\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Protected\n\n isWithContent() {\n return Boolean(this.getTitle())\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const tip = this.getTipElement()\n this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle())\n $(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n setElementContent($element, content) {\n if (typeof content === 'object' && (content.nodeType || content.jquery)) {\n // Content is a DOM node or a jQuery\n if (this.config.html) {\n if (!$(content).parent().is($element)) {\n $element.empty().append(content)\n }\n } else {\n $element.text($(content).text())\n }\n\n return\n }\n\n if (this.config.html) {\n if (this.config.sanitize) {\n content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)\n }\n\n $element.html(content)\n } else {\n $element.text(content)\n }\n }\n\n getTitle() {\n let title = this.element.getAttribute('data-original-title')\n\n if (!title) {\n title = typeof this.config.title === 'function'\n ? this.config.title.call(this.element)\n : this.config.title\n }\n\n return title\n }\n\n // Private\n\n _getOffset() {\n const offset = {}\n\n if (typeof this.config.offset === 'function') {\n offset.fn = (data) => {\n data.offsets = {\n ...data.offsets,\n ...this.config.offset(data.offsets, this.element) || {}\n }\n\n return data\n }\n } else {\n offset.offset = this.config.offset\n }\n\n return offset\n }\n\n _getContainer() {\n if (this.config.container === false) {\n return document.body\n }\n\n if (Util.isElement(this.config.container)) {\n return $(this.config.container)\n }\n\n return $(document).find(this.config.container)\n }\n\n _getAttachment(placement) {\n return AttachmentMap[placement.toUpperCase()]\n }\n\n _setListeners() {\n const triggers = this.config.trigger.split(' ')\n\n triggers.forEach((trigger) => {\n if (trigger === 'click') {\n $(this.element).on(\n this.constructor.Event.CLICK,\n this.config.selector,\n (event) => this.toggle(event)\n )\n } else if (trigger !== Trigger.MANUAL) {\n const eventIn = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSEENTER\n : this.constructor.Event.FOCUSIN\n const eventOut = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSELEAVE\n : this.constructor.Event.FOCUSOUT\n\n $(this.element)\n .on(\n eventIn,\n this.config.selector,\n (event) => this._enter(event)\n )\n .on(\n eventOut,\n this.config.selector,\n (event) => this._leave(event)\n )\n }\n })\n\n $(this.element).closest('.modal').on(\n 'hide.bs.modal',\n () => {\n if (this.element) {\n this.hide()\n }\n }\n )\n\n if (this.config.selector) {\n this.config = {\n ...this.config,\n trigger: 'manual',\n selector: ''\n }\n } else {\n this._fixTitle()\n }\n }\n\n _fixTitle() {\n const titleType = typeof this.element.getAttribute('data-original-title')\n\n if (this.element.getAttribute('title') || titleType !== 'string') {\n this.element.setAttribute(\n 'data-original-title',\n this.element.getAttribute('title') || ''\n )\n\n this.element.setAttribute('title', '')\n }\n }\n\n _enter(event, context) {\n const dataKey = this.constructor.DATA_KEY\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER\n ] = true\n }\n\n if ($(context.getTipElement()).hasClass(ClassName.SHOW) || context._hoverState === HoverState.SHOW) {\n context._hoverState = HoverState.SHOW\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.SHOW\n\n if (!context.config.delay || !context.config.delay.show) {\n context.show()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.SHOW) {\n context.show()\n }\n }, context.config.delay.show)\n }\n\n _leave(event, context) {\n const dataKey = this.constructor.DATA_KEY\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER\n ] = false\n }\n\n if (context._isWithActiveTrigger()) {\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.OUT\n\n if (!context.config.delay || !context.config.delay.hide) {\n context.hide()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.OUT) {\n context.hide()\n }\n }, context.config.delay.hide)\n }\n\n _isWithActiveTrigger() {\n for (const trigger in this._activeTrigger) {\n if (this._activeTrigger[trigger]) {\n return true\n }\n }\n\n return false\n }\n\n _getConfig(config) {\n const dataAttributes = $(this.element).data()\n\n Object.keys(dataAttributes)\n .forEach((dataAttr) => {\n if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {\n delete dataAttributes[dataAttr]\n }\n })\n\n config = {\n ...this.constructor.Default,\n ...dataAttributes,\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n if (config.sanitize) {\n config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n if (this.config) {\n for (const key in this.config) {\n if (this.constructor.Default[key] !== this.config[key]) {\n config[key] = this.config[key]\n }\n }\n }\n\n return config\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n _handlePopperPlacementChange(popperData) {\n const popperInstance = popperData.instance\n this.tip = popperInstance.popper\n this._cleanTipClass()\n this.addAttachmentClass(this._getAttachment(popperData.placement))\n }\n\n _fixTransition() {\n const tip = this.getTipElement()\n const initConfigAnimation = this.config.animation\n\n if (tip.getAttribute('x-placement') !== null) {\n return\n }\n\n $(tip).removeClass(ClassName.FADE)\n this.config.animation = false\n this.hide()\n this.show()\n this.config.animation = initConfigAnimation\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' && config\n\n if (!data && /dispose|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Tooltip(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Tooltip._jQueryInterface\n$.fn[NAME].Constructor = Tooltip\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Tooltip._jQueryInterface\n}\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Tooltip from './tooltip'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'popover'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.popover'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst CLASS_PREFIX = 'bs-popover'\nconst BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\n\nconst Default = {\n ...Tooltip.Default,\n placement : 'right',\n trigger : 'click',\n content : '',\n template : '
' +\n '
' +\n '

' +\n '
'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content : '(string|element|function)'\n}\n\nconst ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n TITLE : '.popover-header',\n CONTENT : '.popover-body'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Popover extends Tooltip {\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Overrides\n\n isWithContent() {\n return this.getTitle() || this._getContent()\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const $tip = $(this.getTipElement())\n\n // We use append for html objects to maintain js events\n this.setElementContent($tip.find(Selector.TITLE), this.getTitle())\n let content = this._getContent()\n if (typeof content === 'function') {\n content = content.call(this.element)\n }\n this.setElementContent($tip.find(Selector.CONTENT), content)\n\n $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n // Private\n\n _getContent() {\n return this.element.getAttribute('data-content') ||\n this.config.content\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length > 0) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data && /dispose|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Popover(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Popover._jQueryInterface\n$.fn[NAME].Constructor = Popover\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Popover._jQueryInterface\n}\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'scrollspy'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n offset : 10,\n method : 'auto',\n target : ''\n}\n\nconst DefaultType = {\n offset : 'number',\n method : 'string',\n target : '(string|element)'\n}\n\nconst Event = {\n ACTIVATE : `activate${EVENT_KEY}`,\n SCROLL : `scroll${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n DROPDOWN_ITEM : 'dropdown-item',\n DROPDOWN_MENU : 'dropdown-menu',\n ACTIVE : 'active'\n}\n\nconst Selector = {\n DATA_SPY : '[data-spy=\"scroll\"]',\n ACTIVE : '.active',\n NAV_LIST_GROUP : '.nav, .list-group',\n NAV_LINKS : '.nav-link',\n NAV_ITEMS : '.nav-item',\n LIST_ITEMS : '.list-group-item',\n DROPDOWN : '.dropdown',\n DROPDOWN_ITEMS : '.dropdown-item',\n DROPDOWN_TOGGLE : '.dropdown-toggle'\n}\n\nconst OffsetMethod = {\n OFFSET : 'offset',\n POSITION : 'position'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass ScrollSpy {\n constructor(element, config) {\n this._element = element\n this._scrollElement = element.tagName === 'BODY' ? window : element\n this._config = this._getConfig(config)\n this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +\n `${this._config.target} ${Selector.LIST_ITEMS},` +\n `${this._config.target} ${Selector.DROPDOWN_ITEMS}`\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n $(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window\n ? OffsetMethod.OFFSET : OffsetMethod.POSITION\n\n const offsetMethod = this._config.method === 'auto'\n ? autoMethod : this._config.method\n\n const offsetBase = offsetMethod === OffsetMethod.POSITION\n ? this._getScrollTop() : 0\n\n this._offsets = []\n this._targets = []\n\n this._scrollHeight = this._getScrollHeight()\n\n const targets = [].slice.call(document.querySelectorAll(this._selector))\n\n targets\n .map((element) => {\n let target\n const targetSelector = Util.getSelectorFromElement(element)\n\n if (targetSelector) {\n target = document.querySelector(targetSelector)\n }\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n // TODO (fat): remove sketch reliance on jQuery position/offset\n return [\n $(target)[offsetMethod]().top + offsetBase,\n targetSelector\n ]\n }\n }\n return null\n })\n .filter((item) => item)\n .sort((a, b) => a[0] - b[0])\n .forEach((item) => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._scrollElement).off(EVENT_KEY)\n\n this._element = null\n this._scrollElement = null\n this._config = null\n this._selector = null\n this._offsets = null\n this._targets = null\n this._activeTarget = null\n this._scrollHeight = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (typeof config.target !== 'string') {\n let id = $(config.target).attr('id')\n if (!id) {\n id = Util.getUID(NAME)\n $(config.target).attr('id', id)\n }\n config.target = `#${id}`\n }\n\n Util.typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window\n ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window\n ? window.innerHeight : this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset +\n scrollHeight -\n this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n const offsetLength = this._offsets.length\n for (let i = offsetLength; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' ||\n scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n const queries = this._selector\n .split(',')\n .map((selector) => `${selector}[data-target=\"${target}\"],${selector}[href=\"${target}\"]`)\n\n const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))\n\n if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {\n $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)\n $link.addClass(ClassName.ACTIVE)\n } else {\n // Set triggered link as active\n $link.addClass(ClassName.ACTIVE)\n // Set triggered links parents as active\n // With both

@@ -303,5 +232,13 @@

+ { + "utility" : "cck", + "url": { + "en": "https://my.ec.europa.eu/cookie-policy_en" + } + } + \ No newline at end of file diff --git a/documentation/administrator-manual.md b/documentation/administrator-manual.md index e3186a9e..6ec88be9 100644 --- a/documentation/administrator-manual.md +++ b/documentation/administrator-manual.md @@ -11,7 +11,6 @@ This is a live document; it is being improved continuously. To have the last ver Please report any feedback on the documentation [in the GitHub issue tracker](https://github.com/ec-jrc/re3gistry/issues). ## Overview - The Re3gistry 2 is a reusable open source solution for managing and sharing "reference codes". This manual will cover the following topics: @@ -112,6 +111,13 @@ The next step allows you to choose between 2 options: * Installation: choose this option if you want to install the Re3gistry software for the first time and to create a registry from scratch * Migration: choose this option if you want to upgrade a preexisting Re3gistry instance. This option will keep your settings and content. Note that this option **will work only on an existing instance running on Re3gistry version 1.3.X**. +![Installation wizard - Installation type](images/install-workflow.png) + +The installation process also gives you the option to choose the application workflow to use. + +* ISO 19135: Recommended if you plan to receive contributions from different organisations and manage them following a formal and standardised governance process. +* Simplified: Recommended if the data management will be handled entirely internally in the same organisation, or within a small group of contributors, not requiring such formal process. + #### Installation option In case the "Installation" option is chosen, the basic configuration for the new registry will be requested. diff --git a/documentation/developer-manual.md b/documentation/developer-manual.md index 5ecd6b95..7cb9b485 100644 --- a/documentation/developer-manual.md +++ b/documentation/developer-manual.md @@ -20,7 +20,7 @@ A summary of some of the technologies used to implement the software is presente * Programming language: Java (1.8) * Middle-ware: EclipseLink (tested with PostgreSQL) -* Authentication: Apache Shiro +* Authentication: Apache Shiro / EU Login * Front-end languages and components: * HTML 5 * jQuery 3.x @@ -150,3 +150,31 @@ Roles ### How to customize GitHub to use your own files Please check the folder "dist\customize-interface\example-profile-developer". Here you will find a script to be run after the customization of your own paths to your local repository of the project and you own customize files: such as footer, header, configurations. Once the script has copied all the files into your own repository you can start using the system already containing your own customize interface. + +### Configuration Apache SHIRO +The Apache SHIRO configuration is defined in `source\Re3gistry2\src\main\resources\shiro.ini` and `sources\Re3gistry2\src\main\webapp\WEB-INF\web.xml`. + +Add to your profile + + SHIRO + +Restart the service and check the authentication menchanism. + +### Configuration EU Login +EU Login is the central login mechanism of the European Commission. You can enable login against that central service in case your intended users have or can acquire a EU Login. + +The EU Login configuration is defined in +`sources\Re3gistry2\src\main\resources\ecas-config.properties` and `sources\Re3gistry2\src\main\webapp\WEB-INF\web.xml`. + +Add to your profile + + ECAS + CHANGE_ME_SERVER_NAME + CHANGE_ME_PORT + CHANGE_ME_CONTEXT_APPLICATION + +For tomcat, add two files to the tomcat lib folder: ecas-tomcat-x.y.z.jar and log4j-x.y.z.jar. + +Verify that the JDK trusts the [ECAS certificates](https://webgate.ec.europa.eu/CITnet/confluence/display/IAM/Downloads-Certificates) else import them on the keystore of the JVM. + +Restart the service and check the authentication menchanism. diff --git a/documentation/images/data_export_registry_manager.PNG b/documentation/images/data_export_registry_manager.PNG new file mode 100644 index 00000000..2e48fa3c Binary files /dev/null and b/documentation/images/data_export_registry_manager.PNG differ diff --git a/documentation/images/install-workflow.png b/documentation/images/install-workflow.png new file mode 100644 index 00000000..f55bacf2 Binary files /dev/null and b/documentation/images/install-workflow.png differ diff --git a/documentation/user-manual.md b/documentation/user-manual.md index f5aace36..ded8b9a3 100644 --- a/documentation/user-manual.md +++ b/documentation/user-manual.md @@ -64,6 +64,10 @@ The common tabs available to any users are: Details for each menu item are provided in the following sections. +## Simplified workflow + +If the simplified workflow has been chosen during the installation, the only role that will be use is the Registry Manager. This workflow will simplify the steps mentioned in the following sections. There will be no need to process the data through all the different roles, all the actions will appear in the Registry manager tab directly. + ## Users, roles and groups The Re3gistry 2 follows the [ISO 19135 Standard "Geographic information — Procedures for item registration"](https://www.iso.org/standard/54721.html). @@ -76,6 +80,7 @@ The possible user roles that the registry 2 manages are: * Control body * Submitting organisations + The system is highly flexible in managing the **roles** since it is not assigning the role directly to the **user**. Instead, the user is assigned to one or more **groups**. Then this group is associated to one or more **registers**, specifying the role for each of the group. This means that a group can have different roles on different registers. Each user in the system can be associated to one or more groups. An example on roles and groups assignment is provided in the following box. @@ -505,7 +510,16 @@ In the group detail page apart from editing, group-related information can be ch ### Data export -This option helps to easily run a full export of the registry data to the Solr index on the published items. +The Data export sub-tab give the registry manager the possibility to run an complete SOLR index and a complete CACHE of the items presented in the system. + +SOLR: This option helps to easily run a full export of the registry data to the Solr index on the published items. + +CACHE: +* Button "Cache all - Master language": This option helps to run a full cache of the master language of the system. +* Button "Cache all": This option helps to run a full cache of the active languages of the system. +* Button "Remove cache": This option helps to remove the index of all the cache items. + +![Registry manager - Data export](images/data_export_registry_manager.PNG) ## User profile diff --git a/release-strategy/README.md b/release-strategy/README.md index df960711..c3768d9b 100644 --- a/release-strategy/README.md +++ b/release-strategy/README.md @@ -21,9 +21,9 @@ The core of the release plan is **the annual major release** of the Re3gistry so In addition to this major release, every year **a number of minor releases** are also published (in January, March and September). -Expected releses +Expected releases -R3gistry software: +Re3gistry software: - Major releases: September 2022, September 2023, September 2024 - Minor releases every three months between major releases. December, March, June. @@ -44,7 +44,7 @@ To inform users in advance about when the solution to each issue will be include Schedule of next Major and Minor releases of the Re3gistry software. Plan for 2022-2024: -- Minor release: June 2022 (v2.3.3 6/2022) +- Minor release: June 2022 (v2.3.2 30/6/2022) - Major release: September 2022 (v2.4.0 09/2022) - Minor release: December 2022 (v2.4.1 12/2022) - Minor release: March 2023 (v2.4.2 3/2023) diff --git a/sources/Re3gistry2-build-helper/nbactions-int-i5-seville.xml b/sources/Re3gistry2-build-helper/nbactions-int-i5-seville.xml deleted file mode 100644 index 862563ff..00000000 --- a/sources/Re3gistry2-build-helper/nbactions-int-i5-seville.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - rebuild - - * - - - clean - install - - - epuread - - - diff --git a/sources/Re3gistry2-build-helper/nbactions.xml b/sources/Re3gistry2-build-helper/nbactions.xml deleted file mode 100644 index b77cdcdd..00000000 --- a/sources/Re3gistry2-build-helper/nbactions.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - CUSTOM-sonar-qube-check - SonarQube check - - clean - verify - org.sonarsource.scanner.maven:sonar-maven-plugin:sonar - - - sonar - - - - CUSTOM-clean-install-francdb - clean install - francdb - - clean - install - - - francdb - - - - CUSTOM-install-francdb - install - francdb - - install - - - francdb - - - - CUSTOM-clean-install-epuread - clean install - epuread - - clean - install - - - epuread - - - - CUSTOM-install-epuread - install - epuread - - install - - - epuread - - - - CUSTOM-clean-install-testbed - clean install - testbed - - clean - install - - - testbed - - - - CUSTOM-clean-install-staging - clean install - staging - - clean - install - - - staging - - - - CUSTOM-clean-install-development - clean install - development - - clean - install - - - development - - - - CUSTOM-clean-install-int-i5-deville - clean install - int-i5-seville - - clean - install - - - int-i5-seville - - - \ No newline at end of file diff --git a/sources/Re3gistry2-build-helper/pom.xml b/sources/Re3gistry2-build-helper/pom.xml index 098fc77e..e31efa30 100644 --- a/sources/Re3gistry2-build-helper/pom.xml +++ b/sources/Re3gistry2-build-helper/pom.xml @@ -17,7 +17,7 @@ none CONFIG false - 9.4.1212 + 42.2.18 @@ -65,9 +65,15 @@ sender@example.eu true + alert-warning + development environment of the Re3gistry 2 software

Link to the Re3gistry - localhost Instance - frontend view]]>
/path/to/cache /path/to/rss + /path/to/log OFF + neutral + ON + /schemas/2.0 @@ -91,9 +97,15 @@ sender@example.eu true + alert-warning + production environment of the Re3gistry 2 software

Link to the Re3gistry - localhost Instance - frontend view]]>
/path/to/cache /path/to/rss - ON + /path/to/log + OFF + neutral + ON + /schemas/2.0 diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/AddRegister.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/AddRegister.java index 63c1c7c4..1de61e98 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/AddRegister.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/AddRegister.java @@ -38,6 +38,7 @@ import eu.europa.ec.re3gistry2.model.RegItemclasstype; import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.base.utility.WebConstants; +import eu.europa.ec.re3gistry2.crudimplementation.RegGroupManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemManager; import eu.europa.ec.re3gistry2.javaapi.handler.RegItemproposedHandler; import eu.europa.ec.re3gistry2.model.RegItem; @@ -81,6 +82,7 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp RegItemclassManager regItemclassManager = new RegItemclassManager(entityManager); RegItemclasstypeManager regItemclasstypeManager = new RegItemclasstypeManager(entityManager); RegItemManager regItemManager = new RegItemManager(entityManager); + RegGroupManager regGroupManager = new RegGroupManager(entityManager); // Getting the user permission mapping from the session HashMap currentUserGroupsMap = (HashMap) request.getSession().getAttribute(BaseConstants.KEY_SESSION_USERPERGROUPSMAP); @@ -108,7 +110,19 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp formLocalId = (formLocalId != null) ? InputSanitizerHelper.sanitizeInput(formLocalId) : null; formRegistryItemclassUuid = (formRegistryItemclassUuid != null) ? InputSanitizerHelper.sanitizeInput(formRegistryItemclassUuid) : null; formRegisterBaseUri = (formRegisterBaseUri != null) ? InputSanitizerHelper.sanitizeInput(formRegisterBaseUri) : null; - + + // Workflow simplified + + boolean workflowSimplified = Configuration.checkWorkflowSimplified(); + if (workflowSimplified) { + RegGroup registryManagerRegGroup = regGroupManager.getByLocalid("registryManager"); + String uuid = registryManagerRegGroup.getUuid(); + formRegisterOwner = uuid; + formRegisterManager = uuid; + formControlBody = uuid; + formSubmittingOrganizations = new String[] {uuid}; + } + // Handling charset for the textual contents byte[] bytes; if (formLabel!=null) { diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/ControlBody.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/ControlBody.java index ab72b98c..681b6c07 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/ControlBody.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/ControlBody.java @@ -35,7 +35,12 @@ import eu.europa.ec.re3gistry2.crudimplementation.RegItemhistoryManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemproposedManager; import eu.europa.ec.re3gistry2.crudimplementation.RegLanguagecodeManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegRelationpredicateManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegRelationproposedManager; import eu.europa.ec.re3gistry2.crudimplementation.RegRoleManager; +import eu.europa.ec.re3gistry2.javaapi.cache.CacheAll; +import eu.europa.ec.re3gistry2.javaapi.cache.EhCache; +import eu.europa.ec.re3gistry2.javaapi.cache.ItemCache; import eu.europa.ec.re3gistry2.javaapi.handler.RegActionHandler; import eu.europa.ec.re3gistry2.model.RegAction; import eu.europa.ec.re3gistry2.model.RegGroup; @@ -44,16 +49,22 @@ import eu.europa.ec.re3gistry2.model.RegItemhistory; import eu.europa.ec.re3gistry2.model.RegItemproposed; import eu.europa.ec.re3gistry2.model.RegLanguagecode; +import eu.europa.ec.re3gistry2.model.RegRelationpredicate; +import eu.europa.ec.re3gistry2.model.RegRelationproposed; import eu.europa.ec.re3gistry2.model.RegRole; import eu.europa.ec.re3gistry2.model.RegUser; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Optional; import javax.persistence.EntityManager; import javax.persistence.NoResultException; +import javax.persistence.Persistence; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; @@ -83,6 +94,8 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp RegItemhistoryManager regItemhistoryManager = new RegItemhistoryManager(entityManager); RegItemManager regItemManager = new RegItemManager(entityManager); RegLanguagecodeManager regLanguagecodeManager = new RegLanguagecodeManager(entityManager); + RegRelationproposedManager regRelationproposedManager = new RegRelationproposedManager(entityManager); + RegRelationpredicateManager relationPredicateManager = new RegRelationpredicateManager(entityManager); //Getting request parameter String regActionUuid = request.getParameter(BaseConstants.KEY_REQUEST_ACTION_UUID); @@ -153,6 +166,56 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp } // This is a view request + + //Cache the parent of the proposed item + ItemCache cache = (ItemCache) request.getAttribute(BaseConstants.ATTRIBUTE_CACHE_KEY); + if (cache == null) { + cache = new EhCache(); + request.setAttribute(BaseConstants.ATTRIBUTE_CACHE_KEY, cache); + } + if (formRegActionUuid != null){ + RegAction regActionForCache; + List regItemProposeds; + try{ + regActionForCache = regActionManager.get(formRegActionUuid); + regItemProposeds = regItemproposedManager.getAll(regActionForCache); + }catch(Exception e){ + regItemProposeds = Collections.emptyList(); + } + + HashSet parentsList = new HashSet (); + HashSet collectionsList = new HashSet (); + for (RegItemproposed regItemProposed : regItemProposeds) { + List collections = regRelationproposedManager.getAllByObject(regItemProposed); + + if(collections != null && collections.size() > 0 ){ + for (RegRelationproposed collection : collections) { + if(collection.getRegRelationpredicate().getLocalid().equals(BaseConstants.KEY_PREDICATE_COLLECTION)){ + String uuid = collection.getRegItemObject().getUuid(); + collectionsList.add(uuid); + } + } + } + if(regItemProposed.getRegAction() != null && regItemProposed.getRegAction().getRegItemRegister() != null){ + if(regItemProposed.getRegAction().getRegItemRegister().getRegItemclass() != null && regItemProposed.getRegAction().getRegItemRegister().getRegItemclass().getUuid() != null){ + String uuid = regItemProposed.getRegAction().getRegItemRegister().getRegItemclass().getUuid(); + parentsList.add(uuid); + } + } + } + EntityManager emCache = PersistenceFactory.getEntityManagerFactory().createEntityManager(); + CacheAll cacheAll = new CacheAll(emCache, cache, null); + for (String uuid : parentsList) { +// EntityManager emCache = Persistence.createEntityManagerFactory(BaseConstants.KEY_PROPERTY_PERSISTENCE_UNIT_NAME).createEntityManager(); + cacheAll.run(uuid); + } + for (String uuid : collectionsList) { +// EntityManager emCache = Persistence.createEntityManagerFactory(BaseConstants.KEY_PROPERTY_PERSISTENCE_UNIT_NAME).createEntityManager(); + cacheAll.run(uuid); + } + } + + // Getting the submitting organization RegRole RegRole regRoleControlBody = regRoleManager.getByLocalId(BaseConstants.KEY_ROLE_CONTROLBODY); diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/Install.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/Install.java index 99d311d6..3ac1b4ca 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/Install.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/Install.java @@ -44,6 +44,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; @WebServlet(WebConstants.PAGE_URINAME_INSTALL) @@ -84,6 +85,9 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp } if (step.equals(BaseConstants.KEY_REQUEST_CLEAN_INSTALLATION_PROFILE)) { + if(session!=null) { + request.getSession().setAttribute(BaseConstants.KEY_REQUEST_WORKFLOW, request.getParameterValues(BaseConstants.KEY_REQUEST_WORKFLOW)[0]); + } try { new RegInstallationStepCleanInstallationProfileHandler(request); } catch (Exception ex) { @@ -110,6 +114,12 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp new RegInstallationStepCleanInstallationProcessHandler(request, entityManagerRe3gistry2); deleteSystemInstallingFile(); createSystemInstalledFile(); + if (session!=null) { + String workflow = (String) session.getAttribute(BaseConstants.KEY_REQUEST_WORKFLOW); + if (StringUtils.equals(workflow, BaseConstants.KEY_REQUEST_WORKFLOW_SIMPLIFIED)) { + createWorkflowFile(); + } + } lastStep = true; if (session != null) { session.setAttribute(BaseConstants.KEY_REQUEST_INSTALLATION_SUCCESS, BaseConstants.KEY_REQUEST_INSTALLATION_SUCCESS); @@ -140,6 +150,12 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp new RegInstallationStepMigrationPorcessHandler(request, entityManagerRe3gistry2); deleteSystemInstallingFile(); createSystemInstalledFile(); + if (session!=null) { + String workflow = (String) session.getAttribute(BaseConstants.KEY_REQUEST_WORKFLOW); + if (StringUtils.equals(workflow, BaseConstants.KEY_REQUEST_WORKFLOW_SIMPLIFIED)) { + createWorkflowFile(); + } + } lastStep = true; if (session != null) { session.setAttribute(BaseConstants.KEY_REQUEST_INSTALLATION_SUCCESS, BaseConstants.KEY_REQUEST_INSTALLATION_SUCCESS); @@ -199,6 +215,25 @@ private void createSystemInstallingFile() throws Exception { throw new Exception(ex.getMessage()); } } + + private void createWorkflowFile() throws Exception { + String systemInstallingPath = Configuration.getPathHelperFiles() + File.separator + BaseConstants.KEY_FILE_NAME_WORKFLOW_SIMPLIFIED; + File workflowFile = new File(systemInstallingPath); + + workflowFile.getParentFile().mkdirs(); + try { + + boolean success = workflowFile.createNewFile(); + + if (!success) { + throw new IOException(); + } + + } catch (IOException ex) { + Configuration.getInstance().getLogger().error("Error while trying to create the system workflow file", ex); + throw new Exception(ex.getMessage()); + } + } private void deleteSystemInstallingFile() throws Exception { String systemInstallingPath = Configuration.getPathHelperFiles() + File.separator + BaseConstants.KEY_FILE_NAME_SYSTEMINSTALLING; diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegisterManager.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegisterManager.java index 847e114a..35e1fed9 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegisterManager.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegisterManager.java @@ -23,9 +23,11 @@ */ package eu.europa.ec.re3gistry2.web.controller; +import eu.europa.ec.re3gistry2.web.utility.UpdateRSS; import eu.europa.ec.re3gistry2.base.utility.Configuration; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; import eu.europa.ec.re3gistry2.base.utility.InputSanitizerHelper; +import eu.europa.ec.re3gistry2.base.utility.MailManager; import eu.europa.ec.re3gistry2.base.utility.PersistenceFactory; import eu.europa.ec.re3gistry2.base.utility.UserHelper; import eu.europa.ec.re3gistry2.base.utility.WebConstants; @@ -36,9 +38,6 @@ import eu.europa.ec.re3gistry2.crudimplementation.RegItemproposedManager; import eu.europa.ec.re3gistry2.crudimplementation.RegLanguagecodeManager; import eu.europa.ec.re3gistry2.crudimplementation.RegRoleManager; -import eu.europa.ec.re3gistry2.javaapi.cache.CacheHelper; -import eu.europa.ec.re3gistry2.javaapi.cache.EhCache; -import eu.europa.ec.re3gistry2.javaapi.cache.ItemCache; import eu.europa.ec.re3gistry2.javaapi.cache.RecacheItems; import eu.europa.ec.re3gistry2.javaapi.handler.RegActionHandler; import eu.europa.ec.re3gistry2.model.RegAction; @@ -50,14 +49,20 @@ import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.model.RegRole; import eu.europa.ec.re3gistry2.model.RegUser; +import eu.europa.ec.re3gistry2.web.utility.SendEmailFromAction; import java.io.IOException; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.ResourceBundle; import java.util.Set; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; +import javax.mail.MessagingException; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.servlet.ServletException; @@ -65,6 +70,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.Logger; @WebServlet(WebConstants.PAGE_URINAME_REGISTERMANAGER) @@ -184,13 +190,15 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp if (formSubmitAction != null && !regItems.isEmpty()) { //update RSS UpdateRSS.updateRSS(regAction, regItems); - + //update CACHE RecacheItems recacheItems = new RecacheItems(entityManager, request, regItems); recacheItems.start(); ResourceBundle systemLocalization = Configuration.getInstance().getLocalization(); String operationResult = systemLocalization.getString(BaseConstants.KEY_OPERATION_CACHE_ISRUNNING); + + SendEmailFromAction.sendEmailToAllUsersOfAnAction(regAction, systemLocalization); // Setting the operation success attribute request.setAttribute(BaseConstants.KEY_REQUEST_RESULT_MESSAGE, operationResult); } diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManager.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManager.java index ce379ddf..899ffa55 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManager.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManager.java @@ -23,6 +23,7 @@ */ package eu.europa.ec.re3gistry2.web.controller; +import eu.europa.ec.re3gistry2.web.utility.UpdateRSS; import eu.europa.ec.re3gistry2.base.utility.Configuration; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; import eu.europa.ec.re3gistry2.base.utility.InputSanitizerHelper; @@ -30,6 +31,7 @@ import eu.europa.ec.re3gistry2.base.utility.UserHelper; import eu.europa.ec.re3gistry2.base.utility.WebConstants; import eu.europa.ec.re3gistry2.crudimplementation.RegActionManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegGroupManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemRegGroupRegRoleMappingManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemhistoryManager; @@ -47,6 +49,7 @@ import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.model.RegRole; import eu.europa.ec.re3gistry2.model.RegUser; +import eu.europa.ec.re3gistry2.web.utility.SendEmailFromAction; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; @@ -61,6 +64,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.logging.log4j.Logger; +import org.apache.commons.lang3.StringUtils; @WebServlet(WebConstants.PAGE_URINAME_REGISTRYMANAGER) public class RegistryManager extends HttpServlet { @@ -81,6 +85,7 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp // Instantiating managers RegRoleManager regRoleManager = new RegRoleManager(entityManager); + RegGroupManager regGroupManager = new RegGroupManager(entityManager); RegItemRegGroupRegRoleMappingManager regItemRegGroupRegRoleMappingManager = new RegItemRegGroupRegRoleMappingManager(entityManager); RegActionManager regActionManager = new RegActionManager(entityManager); RegItemproposedManager regItemproposedManager = new RegItemproposedManager(entityManager); @@ -94,6 +99,10 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp String regActionUuid = request.getParameter(BaseConstants.KEY_REQUEST_ACTION_UUID); String languageUUID = request.getParameter(BaseConstants.KEY_REQUEST_LANGUAGEUUID); + + String formDirectPublish = request.getParameter(BaseConstants.KEY_FORM_FIELD_NAME_DIRECTPUBLISH); + String changelog = request.getParameter(BaseConstants.KEY_FORM_FIELD_NAME_CHANGELOG); + String issueReference = request.getParameter(BaseConstants.KEY_FORM_FIELD_NAME_ISSUEREFERENCE); formRegActionUuid = (formRegActionUuid != null) ? InputSanitizerHelper.sanitizeInput(formRegActionUuid) : null; formSubmitAction = (formSubmitAction != null) ? InputSanitizerHelper.sanitizeInput(formSubmitAction) : null; @@ -124,13 +133,18 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp String[] actionManageSystem = {BaseConstants.KEY_USER_ACTION_MANAGESYSTEM}; boolean permissionManageSystem = UserHelper.checkGenericAction(actionManageSystem, currentUserGroupsMap, regItemRegGroupRegRoleMappingManager); - if (permissionManageSystem) { + if (permissionManageSystem || StringUtils.equals(formDirectPublish,BaseConstants.KEY_BOOLEAN_STRING_TRUE)) { if (formRegActionUuid != null && formRegActionUuid.length() > 0 && formSubmitAction != null && formSubmitAction.length() > 0) { // This is a save request RegActionHandler regActionHandler = new RegActionHandler(); - regActionHandler.registryManagerAction(formRegActionUuid, regUser); + if (StringUtils.equals(formDirectPublish, BaseConstants.KEY_BOOLEAN_STRING_TRUE)) { + regActionHandler.registryManagerActionSimplifiedWorkflow(formRegActionUuid, regUser, changelog, issueReference); + } else { + regActionHandler.registryManagerAction(formRegActionUuid, regUser); + } + regActionUuid = formRegActionUuid; @@ -138,10 +152,14 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp // This is a view request // Getting the submitting organization RegRole + RegRole regRoleRegistryManager = regRoleManager.getByLocalId(BaseConstants.KEY_ROLE_REGISTRYMANAGER); - + RegGroup regGroupRegistry = regGroupManager.getByLocalid(BaseConstants.KEY_ROLE_REGISTRYMANAGER); // Getting the list of mapping that contains the specified role List tmps = regItemRegGroupRegRoleMappingManager.getAll(regRoleRegistryManager); + + tmps = regItemRegGroupRegRoleMappingManager.getAll(regGroupRegistry); + // Getting the action for which the current user is RYM Set regActions = new HashSet<>(); @@ -189,6 +207,9 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp ResourceBundle systemLocalization = Configuration.getInstance().getLocalization(); String operationResult = systemLocalization.getString(BaseConstants.KEY_OPERATION_CACHE_ISRUNNING); + + SendEmailFromAction.sendEmailToAllUsersOfAnAction(regAction, systemLocalization); + // Setting the operation success attribute request.setAttribute(BaseConstants.KEY_REQUEST_RESULT_MESSAGE, operationResult); } diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.java index c62cf7d6..f30f08e4 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/RegistryManagerDataExport.java @@ -167,7 +167,7 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp if (cacheClassUUID!=null) { cacheall.run(cacheClassUUID); } else { - cacheall.run(); + cacheall.run(null); } subject = systemLocalization.getString(BaseConstants.KEY_EMAIL_SUBJECT_CACHE_SUCCESS); body = systemLocalization.getString(BaseConstants.KEY_EMAIL_BODY_CACHE_SUCCESS); diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/CharacterEncodingFilter.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/CharacterEncodingFilter.java similarity index 97% rename from sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/CharacterEncodingFilter.java rename to sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/CharacterEncodingFilter.java index 6c9438aa..652b782d 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/CharacterEncodingFilter.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/CharacterEncodingFilter.java @@ -21,7 +21,7 @@ * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) */ -package eu.europa.ec.re3gistry2.web.controller; +package eu.europa.ec.re3gistry2.web.utility; import java.io.IOException; import javax.servlet.Filter; diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/LoadSalt.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/LoadSalt.java similarity index 97% rename from sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/LoadSalt.java rename to sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/LoadSalt.java index 80e9ac31..483f1ff6 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/LoadSalt.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/LoadSalt.java @@ -21,8 +21,9 @@ * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) */ -package eu.europa.ec.re3gistry2.web.controller; +package eu.europa.ec.re3gistry2.web.utility; +import eu.europa.ec.re3gistry2.web.utility.ValidateSalt; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/SendEmailFromAction.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/SendEmailFromAction.java new file mode 100644 index 00000000..8316b80e --- /dev/null +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/SendEmailFromAction.java @@ -0,0 +1,66 @@ +/* + * Copyright 2007,2016 EUROPEAN UNION + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * Date: 2020/05/11 + * Authors: + * European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu + * + * This work was supported by the Interoperability solutions for public + * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) + * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) + */ +package eu.europa.ec.re3gistry2.web.utility; + +import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.base.utility.MailManager; +import eu.europa.ec.re3gistry2.model.RegAction; +import java.security.InvalidParameterException; +import java.util.LinkedHashSet; +import java.util.ResourceBundle; +import javax.mail.MessagingException; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + +public class SendEmailFromAction { + + public SendEmailFromAction() { + } + + public static void sendEmailToAllUsersOfAnAction(RegAction regAction, ResourceBundle systemLocalization) throws InvalidParameterException, MessagingException, AddressException { + // Send email notification + // email in the action. changes_request, changes_implemented, submitted_by + LinkedHashSet users = new LinkedHashSet<>(); + users.add(new InternetAddress(regAction.getSubmittedBy().getEmail())); + users.add(new InternetAddress(regAction.getApprovedBy().getEmail())); + users.add(new InternetAddress(regAction.getRegUser().getEmail())); + + if (!users.isEmpty()) { + InternetAddress[] recipient = new InternetAddress[users.size()]; + users.toArray(recipient); + + String subject = systemLocalization.getString(BaseConstants.KEY_EMAIL_SUBJECT_ITEM_PUBLISHED); + String body = systemLocalization.getString(BaseConstants.KEY_EMAIL_BODY_ITEM_PUBLISHED); + subject = (subject != null) + ? subject.replace("{label}", regAction.getLabel()) + : ""; + body = (body != null) + ? body.replace("{label}", regAction.getLabel()) + .replace("{changelog}", regAction.getChangelog()) + : ""; + MailManager.sendMail(recipient, subject, body); + } + } + +} diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/UpdateRSS.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/UpdateRSS.java similarity index 98% rename from sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/UpdateRSS.java rename to sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/UpdateRSS.java index 8ae1675b..c3c3c81e 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/UpdateRSS.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/UpdateRSS.java @@ -21,7 +21,7 @@ * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) */ -package eu.europa.ec.re3gistry2.web.controller; +package eu.europa.ec.re3gistry2.web.utility; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; import eu.europa.ec.re3gistry2.base.utility.Configuration; @@ -29,6 +29,7 @@ import eu.europa.ec.re3gistry2.base.utility.WebConstants; import eu.europa.ec.re3gistry2.model.RegAction; import eu.europa.ec.re3gistry2.model.RegItem; +import eu.europa.ec.re3gistry2.web.controller.RegisterManager; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; diff --git a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/ValidateSalt.java b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/ValidateSalt.java similarity index 98% rename from sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/ValidateSalt.java rename to sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/ValidateSalt.java index c0bd327d..e5900bdb 100644 --- a/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/controller/ValidateSalt.java +++ b/sources/Re3gistry2/src/main/java/eu/europa/ec/re3gistry2/web/utility/ValidateSalt.java @@ -21,7 +21,7 @@ * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) */ -package eu.europa.ec.re3gistry2.web.controller; +package eu.europa.ec.re3gistry2.web.utility; import com.google.common.cache.Cache; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; diff --git a/sources/Re3gistry2/src/main/resources/META-INF/persistence.xml.orig b/sources/Re3gistry2/src/main/resources/META-INF/persistence.xml.orig new file mode 100644 index 00000000..44097756 --- /dev/null +++ b/sources/Re3gistry2/src/main/resources/META-INF/persistence.xml.orig @@ -0,0 +1,44 @@ + + + + org.eclipse.persistence.jpa.PersistenceProvider + + eu.europa.ec.re3gistry2.model.RegAction + eu.europa.ec.re3gistry2.model.RegField + eu.europa.ec.re3gistry2.model.RegFieldmapping + eu.europa.ec.re3gistry2.model.RegFieldtype + eu.europa.ec.re3gistry2.model.RegGroup + eu.europa.ec.re3gistry2.model.RegItemRegGroupRegRoleMapping + eu.europa.ec.re3gistry2.model.RegItem + eu.europa.ec.re3gistry2.model.RegItemclass + eu.europa.ec.re3gistry2.model.RegItemclasstype + eu.europa.ec.re3gistry2.model.RegItemhistory + eu.europa.ec.re3gistry2.model.RegItemproposed + eu.europa.ec.re3gistry2.model.RegLanguagecode + eu.europa.ec.re3gistry2.model.RegLocalization + eu.europa.ec.re3gistry2.model.RegLocalizationhistory + eu.europa.ec.re3gistry2.model.RegLocalizationproposed + eu.europa.ec.re3gistry2.model.RegRelation + eu.europa.ec.re3gistry2.model.RegRelationhistory + eu.europa.ec.re3gistry2.model.RegRelationpredicate + eu.europa.ec.re3gistry2.model.RegRelationproposed + eu.europa.ec.re3gistry2.model.RegRole + eu.europa.ec.re3gistry2.model.RegStatus + eu.europa.ec.re3gistry2.model.RegStatusgroup + eu.europa.ec.re3gistry2.model.RegStatuslocalization + eu.europa.ec.re3gistry2.model.RegUser + eu.europa.ec.re3gistry2.model.RegUserRegGroupMapping + eu.europa.ec.re3gistry2.model.RegItemproposedRegGroupRegRoleMapping + eu.europa.ec.re3gistry2.model.RegItemhistoryRegGroupRegRoleMapping + + false + + + + + + + + + + diff --git a/sources/Re3gistry2/src/main/resources/configurations_files/configuration.properties b/sources/Re3gistry2/src/main/resources/configurations_files/configuration.properties index f4bb4862..7e5f2ffd 100644 --- a/sources/Re3gistry2/src/main/resources/configurations_files/configuration.properties +++ b/sources/Re3gistry2/src/main/resources/configurations_files/configuration.properties @@ -33,7 +33,7 @@ application.persistenceunitname=Re3gistry2 # Application version -application.version=Version: 2.0.0 +application.version=Version: 2.4.0-rc.1 # Localization application.language.available=en @@ -45,6 +45,9 @@ application.language.defaultLocale=en # ECAS base URL (authetication method) application.ecas.baseurl = https://webgate.ec.europa.eu +# Cache +application.cache.disk.path=${application.cache.disk.path} + # Login type - ECAS | SHIRO application.login.type=SHIRO @@ -102,7 +105,7 @@ application.solr.fieldtoindex.localid=label,description,definition # * propose to add RegItem Register and RegItem Registry # # - |ManageFieldMapping| with this permission a RegUser can: -# * add field mpping (between field and item) +# * add field mapping (between field and item) # * eidt firld mapping (e.g. change properties such mandatory, multivalued, ecc.) # # - |ManageField| with this permission a RegUser can: diff --git a/sources/Re3gistry2/src/main/resources/configurations_files/configuration.properties.orig b/sources/Re3gistry2/src/main/resources/configurations_files/configuration.properties.orig new file mode 100644 index 00000000..8faa890c --- /dev/null +++ b/sources/Re3gistry2/src/main/resources/configurations_files/configuration.properties.orig @@ -0,0 +1,201 @@ +# +# Copyright 2007,2016 EUROPEAN UNION +# Licensed under the EUPL, Version 1.2 or - as soon they will be approved by +# the European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Licence is distributed on an "AS IS" basis, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the Licence for the specific language governing permissions and +# limitations under the Licence. +# +# Date: 2020/05/11 +# Authors: +# European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu +# +# This work was supported by the Interoperability solutions for public +# administrations, businesses and citizens programme (http://ec.europa.eu/isa2) +# through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) +# for e-Government (ELISE) +# + +##################################### +### Re3gistry2 Configuration file ### +##################################### + +#### Application properties #### + +# Persistence unit (DB connection reference specifyed in the persistence.xml file) +application.persistenceunitname=Re3gistry2 + +# Application version +application.version=Version: 2.0.0 + +# Localization +application.language.available=en +#application.language.available=en-it +application.language.label.en=English (en) +#application.language.label.it=Italiano (it) +application.language.defaultLocale=en + +# ECAS base URL (authetication method) +application.ecas.baseurl = https://webgate.ec.europa.eu + +# Cache +application.cache.disk.path=${application.cache.disk.path} + +# Login type - ECAS | SHIRO +application.login.type=SHIRO + +# Status base URI +#application.status.baseuri=statusbaseuri +application.status.maingroup=1 + +# Input max length in characters (textatea have no limits) +application.input.maxlength=1024 + +# Input sanitizer level (powered by Jsoup) +# Allowed values: simpleText, basic, basicWithImages, relaxed +# Documentation: https://jsoup.org/apidocs/org/jsoup/safety/Whitelist.html +application.input.sanitizer.level=relaxed + +# Option to allow multi registry +# Note that this functionality is not yet fully implemented: leave false# +application.multiregistry.allow=false +application.multiregistry.mainregistryitemclasslocalid= +application.multiregistry.mainregistrylocalid= + +# Option to show/hide system registers +application.systemregisters.show=false + +# Date format +application.dateformat=yyyy-MM-dd + +# RSS release-note +application.releasenote.rss.path=rsspath + +# Optional heading message +application.headingmessage.class= +application.headingmessage.text= + +# Application root taken from pom +application.rooturl=applicationrooturl + +# Solr configurations +application.solr.isactive=true +application.solr.url=solrurl +application.solr.core=re3gistry2 +application.solr.connectiontimeout=10000 +application.solr.sockettimeout=60000 +application.solr.fieldtoindex.localid=label,description,definition + +#### Roles and permissions ##################################################### +# Thios section defines the permissions associated with the roles. The list of +# actions available is shown below: +# +# - |ManageItemProposal| with this permission a RegUser can: +# * propose to add and edit simple RegItem ; +# * propose to edit RegItem Register +# +# - |ManageRegisterRegistry| with this permission a RegUser can: +# * add and edit itemclass +# * propose to add RegItem Register and RegItem Registry +# +# - |ManageFieldMapping| with this permission a RegUser can: +# * add field mapping (between field and item) +# * eidt firld mapping (e.g. change properties such mandatory, multivalued, ecc.) +# +# - |ManageField| with this permission a RegUser can: +# * add and edit fields to the system +# +# - |ManageUser| with this permission a RegUser can: +# * add and edit users +# +# - |ManageGroup| with this permission a RegUser can: +# * add and edit groups +# +# - |MapGroupToItem| with this permission a RegUser can: +# * map groups to items +# +# - |MapUserToGroup| with this permission a RegUser can: +# * map userss to grous +# +# - |SubmitProposal| with this pesmission a RegUser can: +# * submit proposals +# +# - |ApproveProposal| : with this permission a RegUser can: +# * approve proposals +# +# - |PublishProposal| : with this permission a RegUser can: +# * publish proposals +# +# - |ManageSystem| : with this permission a RegUser can: +# * manage system properties +# + +#role.permissions.registryManager=ManageRegisterRegistry,ManageItemProposal,ManageFieldMapping,ManageField,ManageUser,ManageGroup,MapGroupToItem,MapUserToGroup,ManageSystem +#role.permissions.registerManager=ManageItemProposal,ManageFieldMapping,PublishProposal +role.permissions.registryManager=ManageRegisterRegistry,ManageItemProposal,ManageFieldMapping,ManageField,ManageUser,ManageGroup,MapGroupToItem,MapUserToGroup,ManageSystem,SubmitProposal +role.permissions.registerManager=ManageFieldMapping,PublishProposal +role.permissions.registerOwner=ManageUser,ManageGroup,MapUserToGroup +role.permissions.controlBody=ApproveProposal +role.permissions.submittingOrganization=ManageItemProposal,SubmitProposal + +#### End Roles and permissions section ######################################### + +### Mail ### +mail.smtp.host=smtphost +mail.smtp.port=25 +mail.smtp.auth=false +mail.smtp.starttls.enable=true +mail.user=name +mail.password=password +mail.sender=jrc-inspire-support@ec.europa.eu +mail.template=%subject%
%subject%

%mailbody%


This email has been automatically generated, please do not reply to it. For more details please access the management interface.

Best regards,
INSPIRE Re3gistry Team
+ +mail.text.subject.success=Re3gistry - installation success +mail.text.body.success=The Re3gistry software has been properly installed. You can start managing your registry content by signing in. +mail.text.subject.error=Re3gistry - installation error +mail.text.body.error=An error has occurred during the Re3gistry installation, please review your settings. +mail.text.subject.newregistration=Re3gistry - Your account have been successfully added to the system +mail.text.error.newregistration=Dear {name},

Your account has been successfully created and it is now enabled.

You can access the management interface using the following credentials:
Username: {email}
Key: {key}

Please change your key after the first access.
+ +mail.text.subject.bulkimport.success=Re3gistry - bulk import {itemclass} success +mail.text.subject.bulkimport.error=Re3gistry - bulk import {itemclass} error +mail.text.body.bulkimport.success=Dear {name},

The bulk import has been completed with success. +mail.text.body.bulkimport.error=Dear {name},

The bulk import has been completed with some error(s) when importing the file.
{errors} Than retry to load the file. + +### Webapp properties #### +web.application_root_url=applicationrooturl +web.cdn_url=https://inspire.ec.europa.eu/cdn/1.0/ +web.default_locale=en +web.default_title=Re3gistry 2 - Reference codes management system +web.default_description=Re3gistry 2 - Reference codes management system +web.head_prefix=og: http://ogp.me/ns# +web.meta_charset=UTF-8 +web.meta_author=European Commission - Joint Research Centre +web.meta_xuacompatible=IE=edge +web.meta_viewport=width=device-width, initial-scale=1 +web.meta_msthemecolor=#2b5797 +web.meta_themecolor=#003399 +web.search_max_item_per_page=10 +web.search_page_max_adjacent=2 +web.text_big_limit=600 +web.optimize_output=1 +web.link_about=/about +web.link_privacy=https://inspire.ec.europa.eu/privacy-policy/59294 +web.link_contact=mailto:JRC-INSPIRE-SUPPORT@ec.europa.eu +web.link_legal=https://ec.europa.eu/info/legal-notice___lng__ +web.link_termsofuse=http://inspire.ec.europa.eu/terms-use +web.link_cookies=https://ec.europa.eu/cookies/index_en.htm +web.link_eu=https://ec.europa.eu +web.link_inspire=https://inspire.ec.europa.eu +web.link_social_tw=https://twitter.com/INSPIRE_EU +web.link_social_li=https://www.linkedin.com/groups/1066897 +web.link_social_fb=https://www.facebook.com/groups/inspiredirective/ +web.analytics.id= +web.analytics.url= diff --git a/sources/Re3gistry2/src/main/resources/localizations/LocalizationBundle_en.properties b/sources/Re3gistry2/src/main/resources/localizations/LocalizationBundle_en.properties index d9f0f598..2df722c3 100644 --- a/sources/Re3gistry2/src/main/resources/localizations/LocalizationBundle_en.properties +++ b/sources/Re3gistry2/src/main/resources/localizations/LocalizationBundle_en.properties @@ -533,6 +533,13 @@ installation.clean.registry.label.description=The label of the registry will be installation.clean.registry.content.summary=Content summary installation.clean.registry.content.summary.description=This text should describe shortly what would be available under your Re3gistry instance. It will be shown in the registry\u2019s landing page. +installation.workflow.title=Type of workflow +installation.workflow.description=Select the governance model you would like the registry to apply. +installation.workflow.iso19135.title=Following ISO 19135 +installation.workflow.iso19135.description=Recommended if you plan to receive contributions from different organisations and manage them following a formal and standardised governance process. +installation.workflow.simplified.title=Simplified workflow +installation.workflow.simplified.description=Recommended if the data management will be handled entirely internally in the same organisation, or within a small group of contributors, not requiring such formal process. + installation.finalstep.title=Installation progress installation.finalstep.description.1=The Re3gistry is in process of installation. installation.finalstep.description.2=You can close this tab if you wish. @@ -594,7 +601,8 @@ mail.text.subject.cache.success=Re3gistry - complete CACHE success mail.text.body.cache.success=The Re3gistry software has has complete the CACHE all items with success. You can start managing your registry content by signing in. mail.text.subject.cache.error=Re3gistry - complete CACHE error mail.text.body.cache.error=An error has occurred during the Re3gistry CACHE all items, please review your settings. - +mail.text.subject.item.publised={label} had been published +mail.text.body.item.publised= {label} had been published successfully. The changelog associated with this action is: {changelog} mail.text.subject.bulkimport.success=Re3gistry - bulk import {itemclass} success mail.text.subject.bulkimport.error=Re3gistry - bulk import {itemclass} error diff --git a/sources/Re3gistry2/src/main/webapp/WEB-INF/shiro.ini.orig b/sources/Re3gistry2/src/main/webapp/WEB-INF/shiro.ini.orig new file mode 100644 index 00000000..61e872a6 --- /dev/null +++ b/sources/Re3gistry2/src/main/webapp/WEB-INF/shiro.ini.orig @@ -0,0 +1,81 @@ +# +# Copyright 2007,2016 EUROPEAN UNION +# Licensed under the EUPL, Version 1.2 or - as soon they will be approved by +# the European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Licence is distributed on an "AS IS" basis, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the Licence for the specific language governing permissions and +# limitations under the Licence. +# +# Date: 2020/05/11 +# Authors: +# European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu +# +# This work was supported by the Interoperability solutions for public +# administrations, businesses and citizens programme (http://ec.europa.eu/isa2) +# through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) +# for e-Government (ELISE) +# + +[main] +authc.loginUrl = /login +authc.successUrl = /index +logout.redirectUrl = /login + +# ------------------------ +# Database + +# Own Realm +jdbcRealm = eu.europa.ec.re3gistry2.base.utility.RegistryRealm + +# Sha256 +sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher +# base64 encoding, not hex in this example: +sha256Matcher.storedCredentialsHexEncoded = false +sha256Matcher.hashIterations = 1024 + +jdbcRealm.credentialsMatcher = $sha256Matcher + +# User Query +# default is "select password from users where username = ?" +jdbcRealm.authenticationQuery = SELECT shiropassword, shirosalt FROM RegUser WHERE email = ? + +# permissions +# jdbcRealm.permissionsLookupEnabled = true +# jdbcRealm.userRolesQuery = select roleName from UserRole where email = ? +# jdbcRealm.permissionsQuery = select permission from RolesPermission where roleName = ? + +# Connection +ds = org.postgresql.jdbc2.optional.SimpleDataSource +ds.serverName = dbhost +ds.databaseName = dbname +ds.user = dbuser +ds.password = dbpassword +jdbcRealm.dataSource=$ds + +authc.usernameParam = email +authc.passwordParam = password +authc.failureKeyAttribute = shiroLoginFailure + +# Use Built-in Chache Manager +builtInCacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager +securityManager.cacheManager = $builtInCacheManager + +securityManager.realms = $jdbcRealm + +[urls] +/res/** = anon +/install/** = anon +/installRunning/** = anon +/resetPassword/** = anon +/about/** = anon +/help/** = anon +/login = authc +/logout = logout +/** = authc diff --git a/sources/Re3gistry2/src/main/webapp/WEB-INF/web.xml b/sources/Re3gistry2/src/main/webapp/WEB-INF/web.xml index 3550f214..659885d9 100644 --- a/sources/Re3gistry2/src/main/webapp/WEB-INF/web.xml +++ b/sources/Re3gistry2/src/main/webapp/WEB-INF/web.xml @@ -35,18 +35,17 @@ - + loadSalt - eu.europa.ec.re3gistry2.web.controller.LoadSalt + eu.europa.ec.re3gistry2.web.utility.LoadSalt loadSalt * - validateSalt - eu.europa.ec.re3gistry2.web.controller.ValidateSalt + eu.europa.ec.re3gistry2.web.utility.ValidateSalt validateSalt diff --git a/sources/Re3gistry2/src/main/webapp/jsp/addRegister.jsp b/sources/Re3gistry2/src/main/webapp/jsp/addRegister.jsp index 9588eb57..5125dffc 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/addRegister.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/addRegister.jsp @@ -62,7 +62,7 @@ <% // Setting system localization ResourceBundle localization = (ResourceBundle) request.getAttribute(BaseConstants.KEY_REQUEST_LOCALIZATION); - + boolean checWorkflowSimplifiedAdd = configuration.checkWorkflowSimplified(); // Initializing managers RegFieldManager regFieldManager = new RegFieldManager(entityManager); RegLocalizationManager regLocalizationManager = new RegLocalizationManager(entityManager); @@ -191,7 +191,7 @@

- + <% if (checWorkflowSimplifiedAdd==false) { %> <%-- Showing the register ownerr field and selector --%>
<% @@ -292,6 +292,7 @@
+ <% } %> <%-- If the user has the rights to insert a new RegItemclass, showing diff --git a/sources/Re3gistry2/src/main/webapp/jsp/field.jsp b/sources/Re3gistry2/src/main/webapp/jsp/field.jsp index 7b6748f1..6f08dc68 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/field.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/field.jsp @@ -108,9 +108,9 @@ <% } %> - <% + <% String buttonCacheDisabled = (CacheHelper.checkCacheCompleteRunning()) ? " disabled" : ""; - %> + %>

${regItemclass.localid}

@@ -128,62 +128,60 @@ <%if (childItemClassSize != 0) {%> -
+ -
- - ${localization.getString("label.cache.class")} - + -
+ <%} else {%> -
+ -
+ -
- - ${localization.getString("label.cache.class")} - + -
+ <%}%> - - -
+ -
- ${localization.getString("label.remove")} + -
- - ${localization.getString("label.cache.class")} - + @@ -368,18 +366,18 @@ columnDefs: [{orderable: true, className: 'reorder', targets: 0, type: 'num-fmt' }], <% } %> "columns": [null, { "width": "16%" }, null, null, null, null, null, null, null], - 'drawCallback': function () { + 'drawCallback': function () { <%-- Init the update on checkbox (if the user has the right) --%> <% if (permissionManageFieldMapping) {%> - $(".cbUpdate").on('change', function(){ - var itemclassUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_ITEMCLASSUUID%>"); - var fieldUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_FIELDUUID%>"); - var checkboxType = $(this).data("type"); - var checked = $(this).is(":checked"); - $.get(".<%=WebConstants.PAGE_URINAME_FIELD%>?<%=BaseConstants.KEY_REQUEST_FIELDUUID%>=" + fieldUuid + "&<%=BaseConstants.KEY_REQUEST_ITEMCLASSUUID%>=" + itemclassUuid + "&<%=BaseConstants.KEY_REQUEST_CHECKBOXTYPE%>=" + checkboxType + "&<%=BaseConstants.KEY_REQUEST_CHECKED%>=" + checked, function(data) {}); - }); + $(".cbUpdate").on('change', function(){ + var itemclassUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_ITEMCLASSUUID%>"); + var fieldUuid = $(this).parent().parent().data("<%=WebConstants.DATA_PARAMETER_FIELDUUID%>"); + var checkboxType = $(this).data("type"); + var checked = $(this).is(":checked"); + $.get(".<%=WebConstants.PAGE_URINAME_FIELD%>?<%=BaseConstants.KEY_REQUEST_FIELDUUID%>=" + fieldUuid + "&<%=BaseConstants.KEY_REQUEST_ITEMCLASSUUID%>=" + itemclassUuid + "&<%=BaseConstants.KEY_REQUEST_CHECKBOXTYPE%>=" + checkboxType + "&<%=BaseConstants.KEY_REQUEST_CHECKED%>=" + checked, function(data) {}); + }); <% } %> - } + } }); <%-- If the user has the rights, activating the update of the list order on drag & drop --%> @@ -458,12 +456,12 @@ <%@include file="includes/footer.inc.jsp" %> @@ -479,31 +477,31 @@ <% }%> diff --git a/sources/Re3gistry2/src/main/webapp/jsp/includes/menu.inc.jsp b/sources/Re3gistry2/src/main/webapp/jsp/includes/menu.inc.jsp index ce3861dd..23e6488d 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/includes/menu.inc.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/includes/menu.inc.jsp @@ -51,6 +51,7 @@ <% // Getting the configuration boolean checInstallation = configuration.checkInstallation(); + boolean checWorkflowSimplified = configuration.checkWorkflowSimplified(); // Instantiating managers RegUserRegGroupMappingManager regUserRegGroupMappingManager = new RegUserRegGroupMappingManager(entityManager); @@ -87,6 +88,7 @@ ${checInstallation} <% if (checInstallation) { + %> @@ -102,17 +104,17 @@ <% } %> - <% if (partOfSubmittingOrganizations || partOfRegistryManager) { %> + <% if ((partOfSubmittingOrganizations || partOfRegistryManager) && (checWorkflowSimplified==false)) { %> <% } %> - <% if (partOfControlBody) { %> + <% if (partOfControlBody && (checWorkflowSimplified==false)) { %> <% } %> - <% if (partOfRegisterManager) { %> + <% if (partOfRegisterManager && (checWorkflowSimplified==false)) { %> diff --git a/sources/Re3gistry2/src/main/webapp/jsp/install/step-3.jsp b/sources/Re3gistry2/src/main/webapp/jsp/install/step-3.jsp index 287e0e99..b73cf5ca 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/install/step-3.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/install/step-3.jsp @@ -108,6 +108,8 @@

${localization.getString("installation.profile.choose.description")}

+
+
+ +

${localization.getString("installation.workflow.description")}

+ +
+ +

${localization.getString("installation.workflow.iso19135.description")}

+
+
+ +

${localization.getString("installation.workflow.simplified.description")}

+
diff --git a/sources/Re3gistry2/src/main/webapp/jsp/registryManager.jsp b/sources/Re3gistry2/src/main/webapp/jsp/registryManager.jsp index e3be8bfa..007b987d 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/registryManager.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/registryManager.jsp @@ -91,7 +91,11 @@
@@ -191,7 +195,7 @@ <%=tmp.getRegUser().getName() + " (" + tmp.getRegUser().getSsoreference() + ")"%> - <% + <% // Checking the status of the action boolean showActionPublish = false; @@ -200,6 +204,37 @@ showActionPublish = true; } %> + + <% + // Checking the status of the action + boolean showSubmit = false; + boolean showChangesRequest = false; + + // Check if the user is the owner of the action + if (Configuration.checkWorkflowSimplified()== false) { + + } + if (tmp.getRegUser().getSsoreference().equals(regUser.getSsoreference())) { + if (tmp.getRegStatus().getLocalid().equals(BaseConstants.KEY_STATUS_LOCALID_DRAFT)) { + showSubmit = true; + } + if (tmp.getRegStatus().getLocalid().equals(BaseConstants.KEY_STATUS_LOCALID_DRAFT) && (tmp.getChangeRequest() != null && tmp.getChangeRequest().length() > 0)) { + showChangesRequest = true; + } + } + + %> + <%if (showActionPublish==false) {%> + <%if (showSubmit && !(regAction == null && showChangesRequest)) {%> + ="<%=tmp.getUuid()%>"> ${localization.getString("label.submit")}
+ ${localization.getString("label.discardaction")} + <%}%> + + <%if (showChangesRequest && regAction == null) {%> + ="<%=tmp.getUuid()%>" href="?<%=WebConstants.DATA_PARAMETER_ACTIONUUID%>=<%=tmp.getUuid()%>"> ${localization.getString("submittingorganisations.label.readchangerequest")}
+ <%}%> + <%}%> + <%-- Action buttons --%> <%if (showActionPublish) {%> <% if (!CacheHelper.checkCacheCompleteRunning()) {%> @@ -502,7 +537,57 @@ %>
- + <%@include file="includes/footer.inc.jsp" %> <%@include file="includes/pageend.inc.jsp" %> @@ -526,6 +611,41 @@ "order": [[0, "desc"]] }); }); + + + + + <%-- Init the confirm message --%> + $('[data-toggle=confirmation]').confirmation({ + rootSelector: '[data-toggle=confirmation]' + }); + + $('.btn-submit-action').on('click', function () { + var actionUuid = $(this).data('<%=WebConstants.DATA_PARAMETER_ACTIONUUID%>'); + $.get(".<%=WebConstants.PAGE_URINAME_ACTIONDETAIL%>?<%=BaseConstants.KEY_REQUEST_ACTION_UUID%>=" + actionUuid, function (data) { + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_CHANGELOG%>').val(data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGELOG%>); + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_ISSUEREFERENCE%>').val(data.<%=BaseConstants.KEY_JSON_FIELDS_ISSUETRACKERLINK%>); + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_REACTIONLABEL%>').val(data.<%=BaseConstants.KEY_JSON_FIELDS_LABEL%>); + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_DIRECTPUBLISH%>').val(true); + $('#actionIdDisplay').text(actionUuid); + $('#submit_action #<%=BaseConstants.KEY_FORM_FIELD_NAME_ACTIONUUID%>').val(actionUuid); + if (data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGEREQUEST%> != '' && data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGEREQUEST%> != 'undefined' && data.<%=BaseConstants.KEY_JSON_FIELDS_CHANGEREQUEST%> != null) { + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_REACTIONLABEL%>').attr('disabled', 'disabled'); + } + $('#submit_action').modal(); + }); + }); + + $('#<%=BaseConstants.KEY_FORM_FIELD_NAME_CHANGELOG%>').bind('input propertychange', function () { + + if (this.value.length) { + $('#submitButton').removeClass("d-none"); + } else { + $('#submitButton').addClass("d-none"); + } + }); + + diff --git a/sources/Re3gistry2/src/main/webapp/jsp/registryManagerDataExport.jsp b/sources/Re3gistry2/src/main/webapp/jsp/registryManagerDataExport.jsp index 6a5bf31a..66832437 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/registryManagerDataExport.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/registryManagerDataExport.jsp @@ -90,7 +90,11 @@
diff --git a/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsers.jsp b/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsers.jsp index 1ff648e8..23285635 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsers.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsers.jsp @@ -88,7 +88,11 @@
diff --git a/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsersAdd.jsp b/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsersAdd.jsp index fc2b9966..52b36550 100644 --- a/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsersAdd.jsp +++ b/sources/Re3gistry2/src/main/webapp/jsp/registryManagerUsersAdd.jsp @@ -84,7 +84,11 @@
@@ -196,10 +200,23 @@ List regGroups = (List) request.getAttribute(BaseConstants.KEY_REQUEST_REGGROUPS); %> <% + if (Configuration.checkWorkflowSimplified()) { + for (RegGroup tmp : regGroups) { + if (tmp.getLocalid().equals(BaseConstants.KEY_FIELD_MANDATORY_REGISTRYMANAGER)) { + %> + + + <%=tmp.getName()%> + + <% + } + } + } else { for (RegGroup tmp : regGroups) { boolean found = false; String foundUuid = null; // Check if the user is in the current group + %> @@ -208,6 +225,7 @@ <% } +} %> diff --git a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/BaseConstants.java b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/BaseConstants.java index 1fc937f7..d49f17e2 100644 --- a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/BaseConstants.java +++ b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/BaseConstants.java @@ -57,6 +57,8 @@ public class BaseConstants { // Installation file flag public static final String KEY_FILE_NAME_SYSTEMINSTALLED = "system.installed"; public static final String KEY_FILE_NAME_SYSTEMINSTALLING = "system.installing"; + + public static final String KEY_FILE_NAME_WORKFLOW_SIMPLIFIED = "workflow.simplified"; public static final String KEY_FILE_NAME_SOLR_COMPLETE_INDEXING_RUNNING = "solr_complete_indexing.running"; public static final String KEY_FILE_NAME_CACHEALL_COMPLETE_RUNNING = "cacheall_complete.running"; @@ -82,7 +84,7 @@ public class BaseConstants { public static final String KEY_PROPERTY_LOGIN_TYPE = "application.login.type"; public static final String KEY_PROPERTY_LOGIN_TYPE_SHIRO = "SHIRO"; public static final String KEY_PROPERTY_LOGIN_TYPE_ECAS = "ECAS"; - + public static final String KEY_PROPERTY_INTERFACE_TYPE = "application.selected.interface"; public static final String KEY_PROPERTY_INTERFACE_NEUTRAL_TYPE = "neutral"; public static final String KEY_PROPERTY_INTERFACE_ECL_TYPE = "ecl"; @@ -108,11 +110,15 @@ public class BaseConstants { public static final String KEY_RELEASENOTE_RSS_APPLICATION = "application.releasenote.rss.path"; public static final String KEY_TEXT_BANNER_APPLICATION = "application.banner"; public static final String KEY_PRIVACY_APPLICATION = "application.privacy"; + public static final String KEY_APPLICATION_LEGACY_FLAG = "application.legacy.flag"; + public static final String KEY_APPLICATION_LEGACY_FLAG_ON = "ON"; + public static final String KEY_APPLICATION_LEGACY_FLAG_OFF = "OFF"; + public static final String KEY_APPLICATION_SCHEMA_PATH = "application.schemas.path"; public static final String KEY_SELECTED_INTERFACE = "application.selected.interface"; public static final String KEY_SELECTED_INTERFACE_ECL = "ecl"; public static final String KEY_SELECTED_INTERFACE_NEUTRAL = "neutral"; - + /* Mail Subjects and body */ public static final String KEY_EMAIL_SUBJECT_SUCCESS = "mail.text.subject.success"; public static final String KEY_EMAIL_BODY_SUCCESS = "mail.text.body.success"; @@ -132,6 +138,8 @@ public class BaseConstants { public static final String KEY_EMAIL_BODY_CACHE_SUCCESS = "mail.text.body.cache.success"; public static final String KEY_EMAIL_SUBJECT_CACHE_ERROR = "mail.text.subject.cache.error"; public static final String KEY_EMAIL_BODY_CACHE_ERROR = "mail.text.body.cache.error"; + public static final String KEY_EMAIL_SUBJECT_ITEM_PUBLISHED = "mail.text.subject.item.publised"; + public static final String KEY_EMAIL_BODY_ITEM_PUBLISHED = "mail.text.body.item.publised"; /** * mail subject and body bulk import @@ -373,6 +381,9 @@ public class BaseConstants { public static final String KEY_REQUEST_MIGRATION_PROCESS = "migration-process"; public static final String KEY_REQUEST_MIGRATION_SUMMARY = "migration-summary"; public static final String KEY_REQUEST_PROFILE = "profile"; + public static final String KEY_REQUEST_WORKFLOW = "workflow"; + public static final String KEY_REQUEST_WORKFLOW_SIMPLIFIED = "workflow-simplified"; + public static final String KEY_REQUEST_WORKFLOW_ISO = "workflow-iso"; public static final String KEY_REQUEST_USER_OLD_PASSWORD = "oldPassword"; public static final String KEY_REQUEST_USER_NEW_PASSWORD = "newPassword"; @@ -418,6 +429,7 @@ public class BaseConstants { public static final String KEY_FORM_FIELD_NAME_USERUUID = "userUuid"; public static final String KEY_FORM_FIELD_NAME_GROUPUUID = "groupUuid"; public static final String KEY_FORM_FIELD_NAME_SUBMITACTION = "submitAction"; + public static final String KEY_FORM_FIELD_NAME_DIRECTPUBLISH = "directPublish"; public static final String KEY_FORM_FIELD_NAME_UPDATELABELACTION = "updateLabelAction"; public static final String KEY_FORM_FIELD_NAME_APPROVE_TYPE = "approveType"; public static final String KEY_FORM_FIELD_NAME_ITEMCLASSUUID = "itemclassUuid"; @@ -1289,6 +1301,15 @@ public String getKEY_REQUEST_MIGRATION_SUMMARY() { public String getKEY_REQUEST_PROFILE() { return KEY_REQUEST_PROFILE; } + public String getKEY_REQUEST_WORKFLOW() { + return KEY_REQUEST_WORKFLOW; + } + public String getKEY_REQUEST_WORKFLOW_SIMPLIFIED() { + return KEY_REQUEST_WORKFLOW_SIMPLIFIED; + } + public String getKEY_REQUEST_WORKFLOW_ISO() { + return KEY_REQUEST_WORKFLOW_ISO; + } public String getKEY_REQUEST_USER_OLD_PASSWORD() { return KEY_REQUEST_USER_OLD_PASSWORD; diff --git a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/Configuration.java b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/Configuration.java index b8061664..6964e547 100644 --- a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/Configuration.java +++ b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/Configuration.java @@ -127,6 +127,23 @@ public static boolean checkInstallation() { } return installed; } + + public static boolean checkWorkflowSimplified() { + boolean workflowSimplified = false; + try { + File f = new File(Configuration.getPathHelperFiles() + File.separator + BaseConstants.KEY_FILE_NAME_WORKFLOW_SIMPLIFIED); + logger.trace("Checking for existence of file " + f.getAbsolutePath()); + if (f.exists() && !f.isDirectory()) { + workflowSimplified = true; + logger.trace("The system has simplified workflow"); + } else { + logger.trace("The system has not simplified workflow"); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + return workflowSimplified; + } public static boolean checkInstallRunning() { boolean installing = false; diff --git a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/InputSanitizerHelper.java b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/InputSanitizerHelper.java index b718272c..22cce1bc 100644 --- a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/InputSanitizerHelper.java +++ b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/InputSanitizerHelper.java @@ -32,7 +32,7 @@ public class InputSanitizerHelper { private InputSanitizerHelper(){ } - public static String sanitizeInput(String input){ + public static String sanitizeInput(String input){ Configuration configuration = Configuration.getInstance(); diff --git a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemHelper.java b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemHelper.java index 896bb4e0..563961d8 100644 --- a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemHelper.java +++ b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemHelper.java @@ -36,6 +36,7 @@ import eu.europa.ec.re3gistry2.model.RegLocalization; import eu.europa.ec.re3gistry2.model.RegRelation; import eu.europa.ec.re3gistry2.model.RegRelationpredicate; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -250,7 +251,7 @@ public static String getURI(RegItem regItem) throws Exception { throw new RuntimeException("Invalid type"); } } - + private static List getCollectionChain(RegItem regItem, EntityManager entityManager) throws Exception { RegRelationpredicateManager regRelationpredicateManager = new RegRelationpredicateManager(entityManager); RegRelationpredicate hasCollection = regRelationpredicateManager.get(BaseConstants.KEY_PREDICATE_COLLECTION); @@ -266,7 +267,7 @@ private static List getCollectionChain(RegItem regItem, EntityManager e return collectionChain; } - protected static RegItem getRelatedItemBySubject(RegItem regItem, RegRelationpredicate predicate, EntityManager entityManager) throws Exception { + public static RegItem getRelatedItemBySubject(RegItem regItem, RegRelationpredicate predicate, EntityManager entityManager) throws Exception { List list = getRelatedItemsBySubject(regItem, predicate, entityManager); if (list != null && !list.isEmpty()) { return list.stream().findAny().orElse(null); @@ -276,16 +277,34 @@ protected static RegItem getRelatedItemBySubject(RegItem regItem, RegRelationpre // .findAny() // .orElse(null); } - - private static List getRelatedItemsBySubject(RegItem regItem, RegRelationpredicate predicate, EntityManager entityManager) throws Exception { + + public static List getRelatedItemsBySubject(RegItem regItem, RegRelationpredicate predicate, EntityManager entityManager) throws Exception { RegRelationManager regRelationManager = new RegRelationManager(entityManager); + List regItemsBySubject = regRelationManager.getAllByRegItemSubjectAndPredicate(regItem, predicate); if (regRelationManager != null && regItem != null && predicate != null - && regRelationManager.getAllByRegItemSubjectAndPredicate(regItem, predicate) != null) { - return regRelationManager.getAllByRegItemSubjectAndPredicate(regItem, predicate).stream() + && regItemsBySubject != null) { + return regItemsBySubject.stream() .map(rel -> rel.getRegItemObject()) .collect(Collectors.toList()); } else { return null; } } + + public static List getRelatedItemsByObject(RegItem regItem, RegRelationpredicate predicate, EntityManager entityManager) throws Exception { + RegRelationManager regRelationManager = new RegRelationManager(entityManager); + List relations = regRelationManager.getAllByRegItemObjectAndPredicate(regItem, predicate); + + if (regRelationManager != null && regItem != null && predicate != null + && relations != null) { + List subjects = new ArrayList<>(); + relations.forEach((relation) -> { + subjects.add(relation.getRegItemSubject()); + }); + return subjects; + } else { + return null; + } + } + } diff --git a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemproposedHelper.java b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemproposedHelper.java index f4940b81..588fef46 100644 --- a/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemproposedHelper.java +++ b/sources/Re3gistry2Base/src/main/java/eu/europa/ec/re3gistry2/base/utility/ItemproposedHelper.java @@ -55,36 +55,79 @@ public static String getProposedURI(RegItemproposed regItemproposed, RegItem reg // Creating the full URI String uri = regItemproposed.getLocalid(); - // URI for external items - if (regItemproposed.getExternal()) { + if (regItemproposed.getExternal() != null) { + if (regItemproposed.getExternal()) { return uri; } + }else{ + + } + // URI for external items +// if (regItemproposed.getExternal()) { +// return uri; +// } List regRelationproposeds = regRelationproposedManager.getAll(regItemproposed, regRelationpredicateCollection); - List regRelations = null; +// List regRelations = null; +// +// while (true) { +// +// if (regRelationproposeds == null || regRelationproposeds.isEmpty() || (regRelations != null && regRelations.isEmpty())) { +// break; +// } +// // Setting the localid of the collection +// if (regRelations == null) { +// uri = regRelationproposeds.get(0).getRegItemObject().getLocalid() + "/" + uri; +// regRelations = regRelationManager.getAll(regRelationproposeds.get(0).getRegItemObject(), regRelationpredicateCollection); +// } else { +// uri = regRelations.get(0).getRegItemObject().getLocalid() + "/" + uri; +// regRelations = regRelationManager.getAll(regRelations.get(0).getRegItemObject(), regRelationpredicateCollection); +// } +// +// // Checking for other collections at an upper level. +// // For proposed items only one collecion is in RegRelationproposed (the one related to the current oproposed items) +// // The eventual other upper cololectionare in the RegRelation +// } + + while (true) { - if (regRelationproposeds == null || regRelationproposeds.isEmpty() || (regRelations != null && regRelations.isEmpty())) { + if (regRelationproposeds == null || regRelationproposeds.isEmpty()) { break; } // Setting the localid of the collection - if (regRelations == null) { - uri = regRelationproposeds.get(0).getRegItemObject().getLocalid() + "/" + uri; - regRelations = regRelationManager.getAll(regRelationproposeds.get(0).getRegItemObject(), regRelationpredicateCollection); - } else { - uri = regRelations.get(0).getRegItemObject().getLocalid() + "/" + uri; - regRelations = regRelationManager.getAll(regRelations.get(0).getRegItemObject(), regRelationpredicateCollection); - } + uri = regRelationproposeds.get(0).getRegItemproposedObject().getLocalid() + "/" + uri; // Checking for other collections at an upper level. - // For proposed items only one collecion is in RegRelationproposed (the one related to the current oproposed items) - // The eventual other upper cololectionare in the RegRelation + regRelationproposeds = regRelationproposedManager.getAll(regRelationproposeds.get(0).getRegItemproposedObject(), regRelationpredicateCollection); + } + switch (regItemproposed.getRegItemclass().getRegItemclasstype().getLocalid()) { case BaseConstants.KEY_ITEMCLASS_TYPE_ITEM: + + + +// NOT VALID : TEST APPROACH +// if(regItemRegister.getRegItemclass().getBaseuri() == null){ +// uri = regItemRegister.getRegItemclass().getRegItemclassParent().getBaseuri() + "/" + regItemRegister.getRegItemclass().getRegItemclassParent().getLocalid() + "/" + uri; +// }else{ +// uri = regItemRegister.getRegItemclass().getBaseuri() + "/" + regItemRegister.getLocalid() + "/" + uri; +// } +// uri = regItemRegister.getRegItemclass().getRegItemclassParent().getBaseuri(); + + +// if(regItemRegister.getRegItemclass().getBaseuri() == null){ +// uri = regItemRegister.getRegItemclass().getRegItemclassParent().getBaseuri() + regItemRegister.getRegItemclass().getRegItemclassParent().getLocalid(); +// }else{ +// uri = regItemRegister.getRegItemclass().getBaseuri() + "/" + regItemRegister.getLocalid() + "/" + uri; +// } + +// uri = regItemproposed.getRegItemclass().getBaseuri() + "/" + uri; uri = regItemRegister.getRegItemclass().getBaseuri() + "/" + regItemRegister.getLocalid() + "/" + uri; + break; case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: uri = regItemproposed.getRegItemclass().getBaseuri() + "/" + uri; diff --git a/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegItemManager.java b/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegItemManager.java index 310161b0..1ec262c3 100644 --- a/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegItemManager.java +++ b/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegItemManager.java @@ -39,6 +39,7 @@ public interface IRegItemManager{ public RegItem getByLocalidAndRegItemClass(String localid, RegItemclass regItemclass) throws Exception; public List getAll(RegItemclasstype regItemcalsstype) throws Exception; public List getAll(RegItemclass regItemcalss) throws Exception; + public List getAllInternalItems(RegItemclass regItemcalss) throws Exception; public List getAllValid(RegItemclass regItemcalss) throws Exception; public List getAll(List regItemcalsses, int start, int maxResults) throws Exception; public int countAll(List regItemcalsses) throws Exception; @@ -50,6 +51,7 @@ public interface IRegItemManager{ public int countAll(List regItemcalsses, boolean systemItems) throws Exception; public int countAll(RegRelationpredicate regRelationpredicate, RegItem regItemObject, boolean systemItems) throws Exception; public List getAllActive(RegItemclasstype regItemcalsstype) throws Exception; + public List getAllActive() throws Exception; public List getChildItemsList(RegItem regItem) throws Exception; public List getAllSubjectsByRegItemObjectAndPredicateAndSubjectNotPredicate(RegItem regItem, RegRelationpredicate regRelationPredicate, RegRelationpredicate subjectNotHavingPredicate) throws Exception; public List getAllItemByRegItemObjectAndPredicateAndSubjectNotPredicate(RegItem regItem, RegStatus regStatus, RegRelationpredicate regRelationPredicate, RegRelationpredicate subjectNotHavingPredicate) throws Exception; diff --git a/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegLocalizationManager.java b/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegLocalizationManager.java index 7bac023f..38d0cde5 100644 --- a/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegLocalizationManager.java +++ b/sources/Re3gistry2CRUDinterface/src/main/java/eu/europa/ec/re3gistry2/crudinterface/IRegLocalizationManager.java @@ -42,6 +42,7 @@ public interface IRegLocalizationManager{ public List getAll(RegItem regItem) throws Exception; public List getAll(RegItem regItem, RegLanguagecode regLanguagecode) throws Exception; + public List getAllHrefNotNull(RegField regField, RegItemclass regItemclass) throws Exception; public List getAll(RegLanguagecode regLanguagecode, List regItems) throws Exception; public List getAll(RegItemclass regItemclass) throws Exception; public List getAll(RegField regField) throws Exception; diff --git a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemManager.java b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemManager.java index 02b32324..9bacd230 100644 --- a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemManager.java +++ b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemManager.java @@ -202,6 +202,21 @@ public RegItem getByLocalidAndRegItemClass(String localid, RegItemclass regItemc return (RegItem) q.getSingleResult(); } + + public RegItem getByLocalidAndRegItemClassAndRegStatus(String localid, RegItemclass regItemclass, RegStatus regStatus) throws Exception { + //Checking parameters + if (localid == null || regItemclass == null) { + throw new Exception(MessageFormat.format(ErrorConstants.ERROR_MANAGER_PATTERN_NULL, "uuid")); + } + + //Preparing query + Query q = this.em.createQuery(SQLConstants.SQL_GET_REGITEM_BY_LOCALID_REGITEMCLASS_REGSTATUS); + q.setParameter(SQLConstants.SQL_PARAMETERS_LOCALID, localid); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGITEMCLASS, regItemclass); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGSTATUS, regStatus); + + return (RegItem) q.getSingleResult(); + } /** * Returns all the RegItems by RegItemType @@ -234,6 +249,21 @@ public List getAllActive(RegItemclasstype regItemcalsstype) throws Exce q.setParameter("regItemclasstype", regItemcalsstype); return (List) q.getResultList(); } + + /** + * Returns all the RegItems by RegItemType + * + * @param regItemcalsstype + * @return all the RegItems + * @throws Exception + */ + @Override + public List getAllActive() throws Exception { + + //Preparing query + Query q = this.em.createQuery(SQLConstants.SQL_GET_REGITEM_ACTIVE); + return (List) q.getResultList(); + } /** * Returns all the RegItems by RegItemclass @@ -249,6 +279,20 @@ public List getAll(RegItemclass regItemcalss) throws Exception { q.setParameter(SQLConstants.SQL_PARAMETERS_REGITEMCLASS, regItemcalss); return (List) q.getResultList(); } + /** + * Returns all the RegItems by RegItemclass + * + * @param regItemcalss + * @return all the RegItems + * @throws Exception + */ + @Override + public List getAllInternalItems(RegItemclass regItemcalss) throws Exception { + //Preparing query + Query q = this.em.createQuery(SQLConstants.SQL_GET_REGITEM_BY_REGITEMCLASS_INTERNAL); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGITEMCLASS, regItemcalss); + return (List) q.getResultList(); + } /** * Returns all the RegItems by RegAction diff --git a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemhistoryManager.java b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemhistoryManager.java index e12238a0..6b4a5d08 100644 --- a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemhistoryManager.java +++ b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemhistoryManager.java @@ -30,6 +30,7 @@ import eu.europa.ec.re3gistry2.model.RegItem; import eu.europa.ec.re3gistry2.model.RegItemclass; import eu.europa.ec.re3gistry2.model.RegItemhistory; +import eu.europa.ec.re3gistry2.model.RegItemproposed; import eu.europa.ec.re3gistry2.model.RegRelationpredicate; import eu.europa.ec.re3gistry2.model.RegStatus; import java.text.MessageFormat; @@ -219,6 +220,19 @@ public List getByRegItemReference(RegItem regItemReference) thro return (List) q.getResultList(); } + public List getByRegItemReferenceProposed(RegItemproposed regItemReference) throws Exception { + //Checking parameters + if (regItemReference == null) { + throw new Exception(MessageFormat.format(ErrorConstants.ERROR_MANAGER_PATTERN_NULL, "uuid")); + } + + //Preparing query + Query q = this.em.createQuery(SQLConstants.SQL_GET_REGITEMHISTORY_BY_REITEMREFERENCE); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGITEMREFERENCE, regItemReference.getRegItemReference()); + + return (List) q.getResultList(); + } + /** * Find the RegItemhistory specified by parameter. Returns RegItemhistory if * the operation succeed. diff --git a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemproposedManager.java b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemproposedManager.java index a426c2dd..8b41e9a7 100644 --- a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemproposedManager.java +++ b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegItemproposedManager.java @@ -184,6 +184,22 @@ public RegItemproposed getByLocalidAndRegItemClass(String localid, RegItemclass return (RegItemproposed) q.getSingleResult(); } + + public RegItemproposed getByLocalidAndRegItemClassAndRegStatus(String localid, RegItemclass regItemclass, RegStatus regStatus) throws Exception { + //Checking parameters + if (localid == null || regItemclass == null) { + throw new Exception(MessageFormat.format(ErrorConstants.ERROR_MANAGER_PATTERN_NULL, "uuid")); + } + + //Preparing query + Query q = this.em.createQuery(SQLConstants.SQL_GET_REGITEMPROPOSED_BY_LOCALID_REGITEMCLASS_REGSTATUS); + //Query q = this.em.createQuery(SQLConstants.SQL_GET_REGITEMPROPOSED_BY_LOCALID_REGITEMCLASS); + q.setParameter(SQLConstants.SQL_PARAMETERS_LOCALID, localid); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGITEMCLASS, regItemclass); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGSTATUS, regStatus); + + return (RegItemproposed) q.getSingleResult(); + } /** * Returns all the RegItemproposeds by RegItemType @@ -476,7 +492,7 @@ public List getAll(RegAction regAction) throws Exception { return (List) q.getResultList(); } - public List getAllItemByRegItemProposedObjectAndPredicateAndSubjectNotPredicate(RegItem regItem, RegStatus regStatus, RegRelationpredicate regRelationPredicate, RegRelationpredicate subjectNotHavingPredicate) throws Exception { + public List getAllItemByRegItemProposedObjectAndPredicateAndSubjectNotPredicate(RegItemproposed regItemproposed, RegStatus regStatus, RegRelationpredicate regRelationPredicate, RegRelationpredicate subjectNotHavingPredicate) throws Exception { //Preparing query Query q = null; // q = this.em.createQuery(SQLConstants.SQL_GET_REG_ITEM_BY_SUBJECT_PREDICATE_AND_FILTER_PREDICATE); @@ -486,10 +502,10 @@ public List getAllItemByRegItemProposedObjectAndPredicateAndSubjectNotPr // q.setParameter(SQLConstants.SQL_PARAMETERS_NOT_PREDICATE, subjectNotHavingPredicate); try { String query = "select r0.reg_item_subject from (select * from reg_relation r JOIN reg_item ri on ri.uuid = r.reg_item_subject WHERE ri.reg_status = ':regStatus' AND r.reg_item_object = ':regitem' and r.reg_relationpredicate = ':predicate') as r0 where r0.reg_item_subject not in (select r1.reg_item_subject from reg_relation r1 where r1.reg_relationpredicate = ':notpredicate')"; - query = query.replace(":" + SQLConstants.SQL_PARAMETERS_REGSTATUS, regStatus.getUuid()).replace(":" + SQLConstants.SQL_PARAMETERS_REGITEM, regItem.getUuid()).replace(":" + SQLConstants.SQL_PARAMETERS_PREDICATE, regRelationPredicate.getUuid()).replace(":" + SQLConstants.SQL_PARAMETERS_NOT_PREDICATE, subjectNotHavingPredicate.getUuid()); + query = query.replace(":" + SQLConstants.SQL_PARAMETERS_REGSTATUS, regStatus.getUuid()).replace(":" + SQLConstants.SQL_PARAMETERS_REGITEM, regItemproposed.getUuid()).replace(":" + SQLConstants.SQL_PARAMETERS_PREDICATE, regRelationPredicate.getUuid()).replace(":" + SQLConstants.SQL_PARAMETERS_NOT_PREDICATE, subjectNotHavingPredicate.getUuid()); q = this.em.createNativeQuery(query); } catch (Exception ex) { - System.err.println("RegItemproposedManager.getAllItemByRegItemProposedObjectAndPredicateAndSubjectNotPredicate(), regItem: " + regItem.getLocalid()); + System.err.println("RegItemproposedManager.getAllItemByRegItemProposedObjectAndPredicateAndSubjectNotPredicate(), regItem: " + regItemproposed.getLocalid()); return null; } return (List) q.getResultList(); diff --git a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegLocalizationManager.java b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegLocalizationManager.java index 1649efe8..467e5f1a 100644 --- a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegLocalizationManager.java +++ b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/RegLocalizationManager.java @@ -189,10 +189,10 @@ public List getAll(RegItem regItem, RegLanguagecode regLanguage q.setParameter(SQLConstants.SQL_PARAMETERS_REGLANGUAGECODE, regLanguagecode); return (List) q.getResultList(); } - + /** - * Returns all the RegLocalization for the specified RegItems - * in the specific language + * Returns all the RegLocalization for the specified RegItems in the + * specific language * * @param regItem The regItem to find the localization fields * @param regLanguagecode @@ -236,7 +236,7 @@ public List getAll(RegItemclass regItemclass) throws Exception //Preparing query Query q = this.em.createQuery(SQLConstants.SQL_GET_LOCALIZATION_FIELDS_BY_ITEMCLASS); - q.setParameter("regitemclass", regItemclass); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGITEMCLASS, regItemclass); return (List) q.getResultList(); } @@ -313,7 +313,7 @@ public List getAll(RegField regField, RegItem regItem, RegLangu q.setParameter(SQLConstants.SQL_PARAMETERS_REGLANGUAGECODE, regLanguagecode); return (List) q.getResultList(); } - + /** * Returns all the RegLocalization for the specified RegField, RegItem and * RegAction @@ -343,4 +343,14 @@ public List getAllFieldsByValue(String value) throws Exception return (List) q.getResultList(); } -} + @Override + public List getAllHrefNotNull(RegField regField, RegItemclass regItemclass) throws Exception { + + //Preparing query + Query q = this.em.createQuery(SQLConstants.SQL_GET_LOCALIZATION_FIELDS_BY_REGFIELD_ITEMCLASS_HREFNORNULL); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGFIELD, regField); + q.setParameter(SQLConstants.SQL_PARAMETERS_REGITEMCLASS, regItemclass); + return (List) q.getResultList(); + } + +} \ No newline at end of file diff --git a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/constants/SQLConstants.java b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/constants/SQLConstants.java index 7838057d..6b591d3b 100644 --- a/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/constants/SQLConstants.java +++ b/sources/Re3gistry2CRUDrdb/src/main/java/eu/europa/ec/re3gistry2/crudimplementation/constants/SQLConstants.java @@ -59,9 +59,12 @@ private SQLConstants() { // RegItem public static final String SQL_GET_REGITEM_BY_LOCALID = "SELECT r FROM RegItem r WHERE r.localid = :localid"; public static final String SQL_GET_REGITEM_BY_LOCALID_REGITEMCLASS = "SELECT r FROM RegItem r WHERE r.localid = :localid AND r.regItemclass = :regItemclass"; + public static final String SQL_GET_REGITEM_BY_LOCALID_REGITEMCLASS_REGSTATUS = "SELECT r FROM RegItem r WHERE r.localid = :localid AND r.regItemclass = :regItemclass AND r.regStatus = :regStatus"; public static final String SQL_GET_REGITEM_BY_REGITEMCLASSTYPE = "SELECT r FROM RegItem r JOIN r.regItemclass i WHERE i.regItemclasstype = :regItemclasstype"; public static final String SQL_GET_REGITEM_BY_REGITEMCLASSTYPE_ACTIVE = "SELECT r FROM RegItem r JOIN r.regItemclass i WHERE i.regItemclasstype = :regItemclasstype AND i.active = TRUE"; + public static final String SQL_GET_REGITEM_ACTIVE = "SELECT r FROM RegItem r JOIN r.regItemclass i WHERE i.active = TRUE"; public static final String SQL_GET_REGITEM_BY_REGITEMCLASS = "SELECT r FROM RegItem r WHERE r.regItemclass = :regItemclass"; + public static final String SQL_GET_REGITEM_BY_REGITEMCLASS_INTERNAL = "SELECT r FROM RegItem r WHERE r.regItemclass = :regItemclass AND r.external = FALSE"; public static final String SQL_GET_REGITEM_BY_REGITEMCLASS_AND_STATUS = "SELECT r FROM RegItem r WHERE r.regItemclass = :regItemclass AND r.regStatus = :regStatus"; public static final String SQL_GET_REGITEM_BY_REGITEMCLASSES = "SELECT r FROM RegItem r WHERE r.regItemclass IN :regItemclasses ORDER BY r.localid"; @@ -83,6 +86,7 @@ private SQLConstants() { // RegItemproposed public static final String SQL_GET_REGITEMPROPOSED_BY_LOCALID_REGITEMCLASS = "SELECT r FROM RegItemproposed r WHERE r.localid = :localid AND r.regItemclass = :regItemclass"; + public static final String SQL_GET_REGITEMPROPOSED_BY_LOCALID_REGITEMCLASS_REGSTATUS = "SELECT r FROM RegItemproposed r WHERE r.localid = :localid AND r.regItemclass = :regItemclass AND r.regStatus = :regStatus"; public static final String SQL_GET_REGITEMPROPOSED_BY_REGITEMCLASSTYPE = "SELECT r FROM RegItemproposed r JOIN r.regItemclass i WHERE i.regItemclasstype = :regItemclasstype"; public static final String SQL_GET_REGITEMPROPOSED_BY_REGITEMCLASS = "SELECT r FROM RegItemproposed r WHERE r.regItemclass = :regItemclass"; public static final String SQL_GET_REGITEMPROPOSED_BY_REGITEMCLASSES = "SELECT r FROM RegItemproposed r WHERE r.regItemclass IN :regItemclasses ORDER BY r.localid"; @@ -126,7 +130,10 @@ private SQLConstants() { public static final String SQL_GET_LOCALIZATION_FIELDS_BY_ITEM = "SELECT r FROM RegLocalization r WHERE r.regItem = :regitem AND r.regField IS NOT NULL"; public static final String SQL_GET_LOCALIZATION_FIELDS_BY_ITEM_AND_LANGUAGE = "SELECT r FROM RegLocalization r WHERE r.regItem = :regitem AND r.regLanguagecode = :regLanguagecode AND r.regField IS NOT NULL"; public static final String SQL_GET_LOCALIZATION_FIELDS_BY_LANGUAGE_AND_ITEMS = "SELECT r FROM RegLocalization r WHERE r.regLanguagecode = :regLanguagecode AND r.regField IS NOT NULL AND r.regItem IN :regitemList"; - public static final String SQL_GET_LOCALIZATION_FIELDS_BY_ITEMCLASS = "SELECT r FROM RegLocalization r WHERE r.regItemclass = :regitemclass"; + public static final String SQL_GET_LOCALIZATION_FIELDS_BY_ITEMCLASS = "SELECT r FROM RegLocalization r WHERE r.regItemclass = :regItemclass"; + + public static final String SQL_GET_LOCALIZATION_FIELDS_BY_REGFIELD_ITEMCLASS_HREFNORNULL = "SELECT * FROM RegLocalization r INNER JOIN RegItem ri ON ri.uuid = r.regItem AND ri.regItemclass = :regItemclass WHERE r.regField = :regfield AND r.href IS NOT NULL"; + public static final String SQL_GET_LOCALIZATION_BY_FIELD = "SELECT r FROM RegLocalization r WHERE r.regField = :regfield AND r.regItem IS NULL"; public static final String SQL_GET_LOCALIZATION_BY_FIELD_LANGUAGECODE = "SELECT r FROM RegLocalization r WHERE r.regField = :regfield AND r.regLanguagecode = :regLanguagecode AND r.regItem IS NULL"; public static final String SQL_GET_LOCALIZATION_BY_FIELD_ITEM = "SELECT r FROM RegLocalization r WHERE r.regField = :regfield AND r.regItem = :regitem"; @@ -158,7 +165,7 @@ private SQLConstants() { public static final String SQL_GET_LOCALIZATIONHISTORY_BY_FIELD_ITEM_ACTION = "SELECT r FROM RegLocalizationhistory r WHERE r.regField = :regfield AND r.regItemhistory = :regitem AND r.regAction = :regaction"; public static final String SQL_GET_LOCALIZATION_FIELDS_BY_ITEMHISTORY = "SELECT r FROM RegLocalizationhistory r WHERE r.regItemhistory = :regItemhistory AND r.regField IS NOT NULL"; -// RegRelation + // RegRelation public static final String SQL_GET_RELATION_BY_SUBJECT_ITEM = "SELECT r FROM RegRelation r WHERE r.regItemSubject = :regitem"; public static final String SQL_GET_RELATION_BY_OBJECT_ITEM = "SELECT r FROM RegRelation r WHERE r.regItemObject = :regitem"; public static final String SQL_GET_RELATION_COLLECTION_REFERENCE = "SELECT r FROM RegRelation r WHERE r.regItemSubject = :regitem AND r.regRelationpredicate = :predicate"; @@ -167,7 +174,7 @@ private SQLConstants() { public static final String SQL_GET_RELATION_SUBJECTS_PREDICATE = "SELECT r FROM RegRelation r WHERE r.regItemSubject IN :regitemList AND r.regRelationpredicate = :predicate"; public static final String SQL_GET_SUBJECT_BY_OBJECT_PREDICATE_AND_SUBJECT_FILTER = "SELECT r FROM RegRelation r WHERE r.regItemObject = :regitem AND r.regRelationpredicate = :predicate AND r.regItemSubject NOT IN (SELECT r1.regItemSubject FROM RegRelation r1 WHERE r1.regRelationpredicate = :notpredicate)"; - // RegRelation + // RegRelationProposed public static final String SQL_GET_RELATIONPROPOSED_BY_SUBJECT_ITEM = "SELECT r FROM RegRelationproposed r WHERE r.regItemproposedSubject = :regitem"; public static final String SQL_GET_RELATIONPROPOSED_BY_OBJECT_ITEM = "SELECT r FROM RegRelationproposed r WHERE r.regItemproposedObject = :regitem"; public static final String SQL_GET_RELATIONPROPOSED_OBJECT_PREDICATE = "SELECT r FROM RegRelationproposed r WHERE r.regItemObject = :regitem AND r.regRelationpredicate = :predicate"; @@ -223,5 +230,5 @@ private SQLConstants() { // RegStatus public static final String SQL_GET_REGSTATUS_BY_REGSTATUSGROUP = "SELECT r FROM RegStatus r WHERE r.regStatusgroup = :regStatusgroup"; public static final String SQL_GET_REGSTATUSPUBIC_BY_REGSTATUSGROUP = "SELECT r FROM RegStatus r WHERE r.regStatusgroup = :regStatusgroup AND r.ispublic = TRUE"; - + } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/CacheAll.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/CacheAll.java index 369b9e3e..32cbe104 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/CacheAll.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/CacheAll.java @@ -10,7 +10,6 @@ import eu.europa.ec.re3gistry2.crudimplementation.RegItemclassManager; import eu.europa.ec.re3gistry2.crudimplementation.RegLanguagecodeManager; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; -import eu.europa.ec.re3gistry2.javaapi.cache.supplier.ItemHistorySupplier; import eu.europa.ec.re3gistry2.javaapi.cache.supplier.ItemSupplier; import eu.europa.ec.re3gistry2.model.RegItem; import eu.europa.ec.re3gistry2.model.RegItemclass; @@ -19,7 +18,6 @@ import java.util.Date; import java.util.List; import java.util.Optional; -import java.util.logging.Level; import javax.persistence.EntityManager; import org.apache.logging.log4j.Logger; @@ -35,11 +33,7 @@ public CacheAll(EntityManager em, ItemCache cache, RegLanguagecode regMasterLang this.regMasterLanguagecode = regMasterLanguagecode; } - public void run() throws Exception { - this.run(null); - } - - public void run(String classUUID) throws Exception { + public void run(String itemclassID) throws Exception { // Init logger Logger logger = Configuration.getInstance().getLogger(); @@ -64,46 +58,45 @@ public void run(String classUUID) throws Exception { availableLanguages = regLanguagecodeManager.getAllActive(); } - availableLanguages.forEach((RegLanguagecode languageCode) -> { - - try { - logger.trace("---[ STARTING CACHE ALL]--- @ " + new Date()); - System.out.println("---[ STARTING CACHE ALL]--- @ " + new Date()); - - // Getting all ItemClasses - List itemclassList = regItemclassManager.getAlltemclassOrderAscByDataprocedureorder(); - if (classUUID!=null) { - for (RegItemclass regItemclass : itemclassList) { - if (regItemclass.getUuid().equals(classUUID)) { - itemclassList.clear(); - itemclassList.add(regItemclass); -// .getParent() - break; - } - } - } - - //cache.getByUuid(language, uuid) + try { + logger.trace("---[ STARTING CACHE ALL]--- @ " + new Date()); + System.out.println("---[ STARTING CACHE ALL]--- @ " + new Date()); - CacheHelper.createCacheCompleteRunningFile(); + // Getting all ItemClasses + List itemclassList = new ArrayList<>(); - // Iterating on each ItemClasses - itemclassList.forEach((regItemclass) -> { - try { - logger.trace("CACHE ALL - regItemClass: " + regItemclass.getLocalid() + " - @ " + new Date()); - System.out.println("CACHE ALL - regItemClass: " + regItemclass.getLocalid() + " - @ " + new Date()); + if (itemclassID != null) { + RegItemclass regItemclassToCache = regItemclassManager.get(itemclassID); + itemclassList.add(regItemclassToCache); + } else { + itemclassList.addAll(regItemclassManager.getAlltemclassOrderAscByDataprocedureorder()); + } - // Getting all the RegItems by ItemClass - List regitemList = regItemManager.getAll(regItemclass); + CacheHelper.createCacheCompleteRunningFile(); - // Iterating on the RegItems - for (RegItem regItem : regitemList) { + // Iterating on each ItemClasses + itemclassList.forEach((regItemclass) -> { + try { + logger.trace("CACHE ALL - regItemClass: " + regItemclass.getLocalid() + " - @ " + new Date()); + System.out.println("CACHE ALL - regItemClass: " + regItemclass.getLocalid() + " - @ " + new Date()); - try { - logger.trace("CACHE ALL - regItemClass: " + regItemclass.getLocalid() + " - regItem: " + regItem.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); - System.out.println("CACHE ALL - regItemClass: " + regItemclass.getLocalid() + " - regItem: " + regItem.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); + // Getting all the RegItems by ItemClass + List regitemList = regItemManager.getAllInternalItems(regItemclass); + // Iterating on the RegItems + regitemList.forEach((regItem) -> { + availableLanguages.forEach((RegLanguagecode languageCode) -> { + try { + logger.trace("CACHE ALL - regItem: " + regItemclass.getLocalid() + " - regItem: " + regItem.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); + System.out.println("CACHE ALL - regItem: " + regItemclass.getLocalid() + " - regItem: " + regItem.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); + if (!em.isOpen()) { + em = em.getEntityManagerFactory().createEntityManager(); + } + if (!em.getTransaction().isActive()) { + em.getTransaction().begin(); + } ItemSupplier itemSupplier = new ItemSupplier(em, masterLanguage, languageCode); +// cache.remove(languageCode.getIso6391code(), regItemclass.getUuid()); Optional optItem = getItemByUuid(regItem, languageCode.getIso6391code(), itemSupplier); } catch (javax.persistence.PersistenceException | org.eclipse.persistence.exceptions.DatabaseException | org.postgresql.util.PSQLException e) { if (!em.isOpen()) { @@ -115,51 +108,40 @@ public void run(String classUUID) throws Exception { logger.trace("DB Connection problem, trying to reconnect - " + e.getMessage()); System.out.println("DB Connection problem, trying to reconnect - " + e.getMessage()); + } catch (Exception ex) { + logger.trace("Error in creating the Item - " + regItem.getLocalid() + " with reg_itemclass: " + regItem.getRegItemclass().getLocalid() + ", error: " + ex.getMessage()); } - } - - // Getting all RegItemHistory by ItemClass - //List regitemHistoryList = regItemhistoryManager.getByRegItemClass(regItemclass); - // Iterating on RegItemHistory - //for (RegItemhistory regItemhistory : regitemHistoryList) { - //logger.trace("CACHE ALL - regItemhistory: " + regItemhistory.getLocalid() + " - @ " + new Date()); - //System.out.println("CACHE ALL - regItemhistory: " + regItemhistory.getLocalid() + " - @ " + new Date()); - // Iterating on all the available languages - //List localizationHistoryListForRegItem = regLocalizationhistoryManager.getAll(regItemhistory); - //for (RegLocalizationhistory regLocalizationhistory : localizationHistoryListForRegItem) { - //RegLanguagecode languageCode = regLocalizationhistory.getRegLanguagecode(); - //logger.trace("CACHE ALL - regItemhistory: " + regItemhistory.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); - //System.out.println("CACHE ALL - regItemhistory: " + regItemhistory.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); - //ItemHistorySupplier itemHistorySupplier = new ItemHistorySupplier(em, masterLanguage, languageCode); - //Optional optItem = getItemHistoryByUuid(regItemhistory.getUuid(), languageCode.getIso6391code(), itemHistorySupplier); - //} - //} - } catch (javax.persistence.PersistenceException | org.eclipse.persistence.exceptions.DatabaseException | org.postgresql.util.PSQLException e) { - if (!em.isOpen()) { - em = em.getEntityManagerFactory().createEntityManager(); - } - if (!em.getTransaction().isActive()) { - em.getTransaction().begin(); - } - - logger.trace("DB Connection problem, trying to reconnect - " + e.getMessage()); - System.out.println("DB Connection problem, trying to reconnect - " + e.getMessage()); - } catch (Exception ex) { - java.util.logging.Logger.getLogger(CacheAll.class.getName()).log(Level.SEVERE, null, ex); + }); + }); + + } catch (javax.persistence.PersistenceException | org.eclipse.persistence.exceptions.DatabaseException | org.postgresql.util.PSQLException e) { + if (!em.isOpen()) { + em = em.getEntityManagerFactory().createEntityManager(); } - }); - - CacheHelper.deleteCacheCompleteRunningFile(); - logger.trace("END CACHE ALL @ " + new Date()); - System.out.println("END CACHE ALL @ " + new Date()); - } catch (Exception ex) { - logger.trace("Unexpected exception occured", ex); - System.out.println("Unexpected exception occured" + ex.getMessage()); - } - }); + if (!em.getTransaction().isActive()) { + em.getTransaction().begin(); + } + + logger.trace("DB Connection problem, trying to reconnect - " + e.getMessage()); + System.out.println("DB Connection problem, trying to reconnect - " + e.getMessage()); + } catch (Exception ex) { + CacheHelper.deleteCacheCompleteRunningFile(); + logger.trace("Unexpected exception occured", ex); + System.out.println("Unexpected exception occured" + ex.getMessage()); + } + }); + + CacheHelper.deleteCacheCompleteRunningFile(); + logger.trace("END CACHE ALL @ " + new Date()); + System.out.println("END CACHE ALL @ " + new Date()); + } catch (Exception ex) { + CacheHelper.deleteCacheCompleteRunningFile(); + logger.trace("Unexpected exception occured", ex); + System.out.println("Unexpected exception occured" + ex.getMessage()); + } } catch (Exception ex) { - logger.trace("Unexpected exception occured", ex); CacheHelper.deleteCacheCompleteRunningFile(); + logger.trace("Unexpected exception occured", ex); throw new Exception("Unexpected exception occured. " + ex.getMessage()); } finally { if (em != null) { @@ -168,20 +150,6 @@ public void run(String classUUID) throws Exception { } } - private Optional getItemByUuid(String uuid, String language, ItemSupplier itemSupplier) throws Exception { - Item cached = cache.getByUuid(language, uuid); - if (cached != null) { - return Optional.of(cached); - } - Item item = itemSupplier.getItemByUuid(uuid); - if (item == null) { - return Optional.empty(); - } - - cache.add(language, item); - return Optional.of(item); - } - private Optional getItemByUuid(RegItem regItem, String language, ItemSupplier itemSupplier) throws Exception { Item cached = cache.getByUuid(language, regItem.getUuid()); if (cached != null) { @@ -192,21 +160,7 @@ private Optional getItemByUuid(RegItem regItem, String language, ItemSuppl return Optional.empty(); } - cache.add(language, item); - return Optional.of(item); - } - - private Optional getItemHistoryByUuid(String uuid, String language, ItemHistorySupplier itemHistorySupplier) throws Exception { - Item cached = cache.getByUuid(language, uuid); - if (cached != null) { - return Optional.of(cached); - } - Item item = itemHistorySupplier.getItemHistoryByUuid(uuid); - if (item == null) { - return Optional.empty(); - } - - cache.add(language, item); + cache.add(language, item, null); return Optional.of(item); } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/EhCache.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/EhCache.java index 2aaeb51f..d8d76cf0 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/EhCache.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/EhCache.java @@ -53,10 +53,14 @@ public Set getLanguages() { return Collections.unmodifiableSet(languages); } + +// public Item getByUuid(String language, String uuid) { +// return getByUuid(language, uuid); +// } @Override public Item getByUuid(String language, String uuid) { synchronized (sync) { - // Get configuration properties + // Get configuration properties final Properties properties = Configuration.getInstance().getProperties(); String cachePath = properties.getProperty(BaseConstants.KEY_DISK_CACHE_PATH, null); String cacheMaximumDiskSpace = properties.getProperty(BaseConstants.KEY_DISK_CACHE_MAXIMUM_SPACE, "5"); @@ -83,11 +87,30 @@ public Item getByUuid(String language, String uuid) { } } } - + @Override + public Item getByUrl(String language, String url) { + return getByUrl(language, url, null, null); + } + + public Item getByUrl(String language, String url, Integer version) { + return getByUrl(language, url, version, null); + } + + public Item getByUrl(String language, String url, Integer version, String itemStatus) { + if (itemStatus == null + || itemStatus.equalsIgnoreCase("valid") + || itemStatus.equalsIgnoreCase("invalid") + || itemStatus.equalsIgnoreCase("superseded") + || itemStatus.equalsIgnoreCase("retired")) { + + } else { + return null; + } + synchronized (sync) { - // Get configuration properties + // Get configuration properties final Properties properties = Configuration.getInstance().getProperties(); String cachePath = properties.getProperty(BaseConstants.KEY_DISK_CACHE_PATH, null); String cacheMaximumDiskSpace = properties.getProperty(BaseConstants.KEY_DISK_CACHE_MAXIMUM_SPACE, "5"); @@ -116,7 +139,20 @@ public Item getByUrl(String language, String url) { } @Override - public void add(String language, Item item) { + public void add(String language, Item item, Integer version) { + add(language, item, version, null); + } + + public void add(String language, Item item, Integer version, String itemStatus) { + if (itemStatus == null + || itemStatus.equalsIgnoreCase("valid") + || itemStatus.equalsIgnoreCase("invalid") + || itemStatus.equalsIgnoreCase("superseded") + || itemStatus.equalsIgnoreCase("retired")) { + + } else { + return; + } synchronized (sync) { // Get configuration properties final Properties properties = Configuration.getInstance().getProperties(); @@ -139,7 +175,7 @@ public void add(String language, Item item) { ).build(true); persistentCacheManager.getCache(BaseConstants.KEY_CACHE_NAME_UUID, String.class, Item.class).put(getCacheKey(language, item.getUuid()), item); - persistentCacheManager.getCache(BaseConstants.KEY_CACHE_NAME_URL, String.class, Item.class).put(getCacheKey(language, item.getUri()), item); + persistentCacheManager.getCache(BaseConstants.KEY_CACHE_NAME_URL, String.class, Item.class).put(version != null ? getCacheKey(language, item.getVersion().getUri()) : getCacheKey(language, item.getUri()), item); persistentCacheManager.close(); languages.add(language); @@ -150,11 +186,11 @@ public void add(String language, Item item) { @Override public void remove(String language, String uuid) { synchronized (sync) { - // Get configuration properties + // Get configuration properties final Properties properties = Configuration.getInstance().getProperties(); String cachePath = properties.getProperty(BaseConstants.KEY_DISK_CACHE_PATH); String cacheMaximumDiskSpace = properties.getProperty(BaseConstants.KEY_DISK_CACHE_MAXIMUM_SPACE, "5"); - + int cacheMaximumGig; try { cacheMaximumGig = Integer.parseInt(cacheMaximumDiskSpace); @@ -191,7 +227,7 @@ public void removeAll() { final Properties properties = Configuration.getInstance().getProperties(); String cachePath = properties.getProperty(BaseConstants.KEY_DISK_CACHE_PATH); String cacheMaximumDiskSpace = properties.getProperty(BaseConstants.KEY_DISK_CACHE_MAXIMUM_SPACE, "5"); - + int cacheMaximumGig; try { cacheMaximumGig = Integer.parseInt(cacheMaximumDiskSpace); diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/ItemCache.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/ItemCache.java index 84b6244d..f7bcb3f5 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/ItemCache.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/ItemCache.java @@ -36,8 +36,10 @@ public interface ItemCache { public Item getByUuid(String language, String uuid); public Item getByUrl(String language, String url); + public Item getByUrl(String language, String url, Integer version, String itemStatus); + public Item getByUrl(String language, String url, Integer version); - public void add(String language, Item item); + public void add(String language, Item item, Integer version); public void remove(String language, String uuid); public void removeAll(); diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/RecacheItems.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/RecacheItems.java index 94b2b427..9e25fc81 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/RecacheItems.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/RecacheItems.java @@ -19,8 +19,6 @@ import java.util.Date; import java.util.List; import java.util.Optional; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingDeque; import javax.persistence.EntityManager; import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.Logger; @@ -57,7 +55,7 @@ public void run() { try { if (!CacheHelper.checkCacheCompleteRunning()) { cacheItems(regItems); - } + } } catch (Exception e) { CacheHelper.deleteCacheCompleteRunningFile(); this.logger.error("Unexpected exception occured", e); @@ -83,9 +81,9 @@ private void cacheItems(List regItems) throws Exception { List itemsToRecache = new ArrayList<>(); // Iterating on the RegItems - for (RegItem regItem : regItems) { - try { - for (RegLanguagecode languageCode : availableLanguages) { + regItems.forEach((regItem) -> { + availableLanguages.forEach((RegLanguagecode languageCode) -> { + try { this.logger.info("RECACHE - regItems: " + regItem.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); System.out.println("RECACHE - regItems: " + regItem.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); @@ -126,23 +124,27 @@ private void cacheItems(List regItems) throws Exception { default: throw new RuntimeException("Unexpected type"); } + } catch (javax.persistence.PersistenceException | org.eclipse.persistence.exceptions.DatabaseException | org.postgresql.util.PSQLException e) { + if (!em.isOpen()) { + em = em.getEntityManagerFactory().createEntityManager(); + } + if (!em.getTransaction().isActive()) { + em.getTransaction().begin(); + } + CacheHelper.deleteCacheCompleteRunningFile(); + this.logger.info("DB Connection problem, trying to reconnect - " + e.getMessage()); + System.out.println("DB Connection problem, trying to reconnect - " + e.getMessage()); + } catch (Exception ex) { + CacheHelper.deleteCacheCompleteRunningFile(); + this.logger.info("Error: " + ex.getMessage()); + System.out.println("Error: " + ex.getMessage()); } - } catch (javax.persistence.PersistenceException | org.eclipse.persistence.exceptions.DatabaseException | org.postgresql.util.PSQLException e) { - if (!em.isOpen()) { - em = em.getEntityManagerFactory().createEntityManager(); - } - if (!em.getTransaction().isActive()) { - em.getTransaction().begin(); - } - CacheHelper.deleteCacheCompleteRunningFile(); - this.logger.info("DB Connection problem, trying to reconnect - " + e.getMessage()); - System.out.println("DB Connection problem, trying to reconnect - " + e.getMessage()); - } - } + }); + }); // Iterating on the relation RegItems //if the item has a collection, parent, successor, predecessor update it - for (RegItem regItem : itemsToRecache) { + itemsToRecache.forEach((regItem) -> { try { for (RegLanguagecode languageCode : availableLanguages) { this.logger.info("RECACHE - regItems: " + regItem.getLocalid() + ", language: " + languageCode.getIso6391code() + " - @ " + new Date()); @@ -161,8 +163,12 @@ private void cacheItems(List regItems) throws Exception { CacheHelper.deleteCacheCompleteRunningFile(); this.logger.info("DB Connection problem, trying to reconnect - " + e.getMessage()); System.out.println("DB Connection problem, trying to reconnect - " + e.getMessage()); + } catch (Exception ex) { + CacheHelper.deleteCacheCompleteRunningFile(); + this.logger.info("Error: " + ex.getMessage()); + System.out.println("Error: " + ex.getMessage()); } - } + }); CacheHelper.deleteCacheCompleteRunningFile(); this.logger.info("END RECACHE @ " + new Date()); @@ -180,7 +186,7 @@ private Optional updateCacheForItemByUuid(RegItem regItem, String language return Optional.empty(); } - cache.add(language, item); + cache.add(language, item, null); return Optional.of(item); } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/BasicContainedItem.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/BasicContainedItem.java index 477a8d7b..4bd8af8b 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/BasicContainedItem.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/BasicContainedItem.java @@ -27,6 +27,8 @@ package eu.europa.ec.re3gistry2.javaapi.cache.model; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; /** * ContainedItem information @@ -34,6 +36,9 @@ public class BasicContainedItem implements Serializable{ private String uri; + private String label; + private List values = new ArrayList (); + private BasicContainedItem topConceptOf; public String getUri() { return uri; @@ -42,5 +47,33 @@ public String getUri() { public void setUri(String uri) { this.uri = uri; } + + public String getLabel(){ + return label; + } + + public void setLabel(String label){ + this.label=label; + } + + public List getValues(){ + return values; + } + + public void setValues(List values){ + this.values=values; + } + + public void addValue(LocalizedPropertyValue value){ + this.values.add(value); + } + + public BasicContainedItem getTopConceptOf(){ + return topConceptOf; + } + + public void setTopConceptOf(BasicContainedItem topConceptOf){ + this.topConceptOf=topConceptOf; + } } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/BasicItemClass.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/BasicItemClass.java new file mode 100644 index 00000000..adbb42a9 --- /dev/null +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/BasicItemClass.java @@ -0,0 +1,55 @@ +/* + * /* + * * Copyright 2007,2016 EUROPEAN UNION + * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * * the European Commission - subsequent versions of the EUPL (the "Licence"); + * * You may not use this work except in compliance with the Licence. + * * You may obtain a copy of the Licence at: + * * + * * https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the Licence is distributed on an "AS IS" basis, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the Licence for the specific language governing permissions and + * * limitations under the Licence. + * * + * * Date: 2020/05/11 + * * Authors: + * * European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu + * * National Land Survey of Finland, SDI Services - inspire@nls.fi + * * + * * This work was supported by the Interoperability solutions for public + * * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) + * * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) + * * for e-Government (ELISE) + */ +package eu.europa.ec.re3gistry2.javaapi.cache.model; + +import java.io.Serializable; + +public class BasicItemClass implements Serializable { + + private final String id; + private final String parentid; + private final String parentItemClassType; + + public BasicItemClass(String id, String parentid, String parentItemClassType) { + this.id = id; + this.parentid = parentid; + this.parentItemClassType = parentItemClassType; + } + + public String getId() { + return id; + } + + public String getParentid() { + return parentid; + } + + public String getParentItemClassType() { + return parentItemClassType; + } + +} diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ContainedItem.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ContainedItem.java index eac27cd5..57b7387e 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ContainedItem.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ContainedItem.java @@ -31,7 +31,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; -import java.util.Date; /** * ContainedItem information @@ -51,7 +50,7 @@ public class ContainedItem extends BasicContainedItem implements Serializable { private boolean external; private String insertdate; private String editdate; - private ItemClass itemclass; + private BasicItemClass itemclass; private ItemRef registry; private ItemRef register; @@ -165,11 +164,11 @@ public void setEditdate(String editdate) { this.editdate = editdate; } - public ItemClass getItemclass() { + public BasicItemClass getItemclass() { return itemclass; } - public void setItemclass(ItemClass itemclass) { + public void setItemclass(BasicItemClass itemclass) { this.itemclass = itemclass; } @@ -229,7 +228,7 @@ public List getContainedItemsBeeingParentItemClass() { public void setContainedItemsBeeingParentItemClass(List containedItemsBeeingParentItemClass) { this.containedItemsBeeingParentItemClass = containedItemsBeeingParentItemClass; } - + public List getContainedItems() { return containedItems; } @@ -277,7 +276,5 @@ public boolean isLatest() { public void setLatest(boolean latest) { this.latest = latest; } - - } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/Item.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/Item.java index 8b7378ed..96d4a888 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/Item.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/Item.java @@ -29,6 +29,7 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import eu.europa.ec.re3gistry2.model.RegLanguagecode; import java.io.Serializable; /** @@ -55,6 +56,7 @@ public class Item extends ContainedItem implements Serializable { private ItemRef registry; private ItemRef register; private List containedItems; + private List activeLanguages; public ItemRef getRegistry() { return registry; @@ -79,5 +81,13 @@ public List getContainedItems() { public void setContainedItems(List containedItems) { this.containedItems = containedItems; } + + public List getActiveLanguages() { + return activeLanguages; + } + + public void setActiveLanguages(List activeLanguages) { + this.activeLanguages = activeLanguages; + } } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ItemClass.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ItemClass.java index ec57f7fe..fd1ae9b9 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ItemClass.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/model/ItemClass.java @@ -26,18 +26,111 @@ */ package eu.europa.ec.re3gistry2.javaapi.cache.model; -import java.io.Serializable; +import java.util.HashMap; -public class ItemClass implements Serializable{ +public class ItemClass { - private final String id; + private String id; + private String type; + private String baseuri; + private String parentid; + private String parentItemClassType; + private String childItemClassType; + private String childItemClassLocalId; + private String startElement; + private String registerLocalId; + private HashMap fields; + private HashMap containedItemsFields; - public ItemClass(String id) { - this.id = id; + public ItemClass() { } public String getId() { return id; } + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getBaseuri() { + return baseuri; + } + + public void setBaseuri(String baseuri) { + this.baseuri = baseuri; + } + + public String getParentid() { + return parentid; + } + + public void setParentid(String parentid) { + this.parentid = parentid; + } + + public String getParentItemClassType() { + return parentItemClassType; + } + + public void setParentItemClassType(String parentItemClassType) { + this.parentItemClassType = parentItemClassType; + } + + public HashMap getFields() { + return fields; + } + + public void setFields(HashMap fields) { + this.fields = fields; + } + + public HashMap getContainedItemsFields() { + return containedItemsFields; + } + + public void setContainedItemsFields(HashMap containedItemsFields) { + this.containedItemsFields = containedItemsFields; + } + + public String getStartElement() { + return startElement; + } + + public void setStartElement(String startElement) { + this.startElement = startElement; + } + + public void setChildItemClassType(String childItemClassType) { + this.childItemClassType = childItemClassType; + } + + public String getChildItemClassType() { + return childItemClassType; + } + + public String getRegisterLocalId() { + return registerLocalId; + } + + public void setRegisterLocalId(String registerLocalId) { + this.registerLocalId = registerLocalId; + } + + public String getChildItemClassLocalId() { + return childItemClassLocalId; + } + + public void setChildItemClassLocalId(String childItemClassLocalId) { + this.childItemClassLocalId = childItemClassLocalId; + } + } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemHistorySupplier.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemHistorySupplier.java index 87a5c9e7..e34509ba 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemHistorySupplier.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemHistorySupplier.java @@ -67,7 +67,7 @@ import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; -import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; +import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemRef; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedPropertyValue; @@ -99,6 +99,7 @@ public class ItemHistorySupplier { private final RegStatuslocalizationManager regStatusLocalizationManager; private final ItemSupplier itemSupplier; + private final EntityManager entityManager; private final RegLanguagecode masterLanguage; private final RegLanguagecode languageCode; @@ -118,23 +119,24 @@ public class ItemHistorySupplier { private boolean topConceptItem = false; private final RegItemclassManager regItemClassManager; - public ItemHistorySupplier(EntityManager em, + public ItemHistorySupplier(EntityManager entityManager, RegLanguagecode masterLanguage, RegLanguagecode languageCode) throws Exception { - this.regItemClassManager = new RegItemclassManager(em); - this.regItemManager = new RegItemManager(em); - this.regItemhistoryManager = new RegItemhistoryManager(em); - RegRelationpredicateManager relationPredicateManager = new RegRelationpredicateManager(em); - this.regRelationhistoryManager = new RegRelationhistoryManager(em); - this.regFieldManager = new RegFieldManager(em); - this.regLocalizationhistoryManager = new RegLocalizationhistoryManager(em); - this.regLocalizationManager = new RegLocalizationManager(em); - this.regFieldmappingManager = new RegFieldmappingManager(em); - this.regStatusManager = new RegStatusManager(em); - this.regStatusLocalizationManager = new RegStatuslocalizationManager(em); - this.itemSupplier = new ItemSupplier(em, masterLanguage, languageCode); - + this.regItemClassManager = new RegItemclassManager(entityManager); + this.regItemManager = new RegItemManager(entityManager); + this.regItemhistoryManager = new RegItemhistoryManager(entityManager); + RegRelationpredicateManager relationPredicateManager = new RegRelationpredicateManager(entityManager); + this.regRelationhistoryManager = new RegRelationhistoryManager(entityManager); + this.regFieldManager = new RegFieldManager(entityManager); + this.regLocalizationhistoryManager = new RegLocalizationhistoryManager(entityManager); + this.regLocalizationManager = new RegLocalizationManager(entityManager); + this.regFieldmappingManager = new RegFieldmappingManager(entityManager); + this.regStatusManager = new RegStatusManager(entityManager); + this.regStatusLocalizationManager = new RegStatuslocalizationManager(entityManager); + this.itemSupplier = new ItemSupplier(entityManager, masterLanguage, languageCode); + + this.entityManager = entityManager; this.masterLanguage = masterLanguage; this.languageCode = languageCode; @@ -154,6 +156,26 @@ public Item getItemHistoryByUuid(String uuid) throws Exception { } public Item getItemHistoryByUri(String uri, Integer version) throws Exception { + try { + int i = uri.lastIndexOf('/'); + if (i < 0) { + throw new NoResultException(); + } + String localidWithVersion = uri.substring(i + 1); + int uriCollection = uri.substring(0, i).lastIndexOf('/'); + String regItemClassLocalId = uri.replace(localidWithVersion, "").substring(uriCollection + 1).replace("/", ""); + RegItemclass regItemRegItemClass = regItemClassManager.getByLocalid(regItemClassLocalId); + RegItemclass parentRegItemRegItemClass = regItemClassManager.getChildItemclass(regItemRegItemClass).get(0); + String localid = localidWithVersion.replace(":" + localidWithVersion.substring(localidWithVersion.lastIndexOf(":") + 1), ""); + int minVersion = regItemhistoryManager.getMinVersionByLocalidAndRegItemClass(localid, parentRegItemRegItemClass).getVersionnumber(); + if (minVersion == 0) { + int fixedVersion = version -1; + uri = uri.replace(":"+version, ":"+fixedVersion); + version = version - 1; + } + } catch (Exception ex) { + version = version; + } RegItemhistory item = getRegItemByUri(uri, version); if (item == null) { return null; @@ -170,23 +192,28 @@ private RegItemhistory getRegItemByUri(String uri, int version) throws Exception int uriCollection = uri.substring(0, i).lastIndexOf('/'); String regItemClassLocalId = uri.replace(localidWithVersion, "").substring(uriCollection + 1).replace("/", ""); String localid = localidWithVersion.replace(":" + version, ""); - RegItemclass regItemRegItemClass = regItemClassManager.getByLocalid(regItemClassLocalId); + RegItemclass parentRegItemClass = regItemClassManager.getByLocalid(regItemClassLocalId); try { - RegItemclass parentRegItemRegItemClass = regItemClassManager.getChildItemclass(regItemRegItemClass).get(0); + RegItemclass childRegItemClass = regItemClassManager.getChildItemclass(parentRegItemClass).get(0); RegItemhistory regItem; try { - regItem = regItemhistoryManager.getByLocalidVersionnumberAndRegItemClass(localid, version, parentRegItemRegItemClass); - if (regItem != null && uri.equals(getURI(regItem) + ":" + regItem.getVersionnumber())) { - return regItem; + + RegItemhistory minVersion = regItemhistoryManager.getMinVersionByLocalidAndRegItemClass(localid, childRegItemClass); + if (minVersion.getVersionnumber() == 0) { + regItem = regItemhistoryManager.getByLocalidVersionnumberAndRegItemClass(localid, version - 1, childRegItemClass); + } else { + regItem = regItemhistoryManager.getByLocalidVersionnumberAndRegItemClass(localid, version, childRegItemClass); } + + return regItem; } catch (Exception ex) { - List regItemList = regItemhistoryManager.getByLocalidAndRegItemClass(localid, parentRegItemRegItemClass); + List regItemList = regItemhistoryManager.getByLocalidAndRegItemClass(localid, childRegItemClass); if (regItemList.size() + 1 == version) { return null; } } } catch (Exception e) { - RegItemhistory regItem = regItemhistoryManager.getByLocalidVersionnumberAndRegItemClass(localid, version, regItemRegItemClass); + RegItemhistory regItem = regItemhistoryManager.getByLocalidVersionnumberAndRegItemClass(localid, version, parentRegItemClass); if (regItem != null && uri.equals(getURI(regItem) + ":" + regItem.getVersionnumber())) { return regItem; } @@ -194,7 +221,34 @@ private RegItemhistory getRegItemByUri(String uri, int version) throws Exception return null; } - private Integer getVersionFromUri(String uri) { + public int sizeItemInHistory(String uri) throws Exception { + int i = uri.lastIndexOf('/'); + if (i < 0) { + //if no column than is asking for the latest version + return 0; + } + + String localidWithVersion = uri.substring(i + 1); + int uriCollection = uri.substring(0, i).lastIndexOf('/'); + String regItemClassLocalId = uri.replace(localidWithVersion, "").substring(uriCollection + 1).replace("/", ""); + i = uri.indexOf(':', i + 1); + Integer version; + try { + version = Integer.parseInt(uri.substring(i + 1)); + + String localid = localidWithVersion.replace(":" + version, ""); + RegItemclass parentRegItemClass = regItemClassManager.getByLocalid(regItemClassLocalId); + RegItemclass childRegItemClass = regItemClassManager.getChildItemclass(parentRegItemClass).get(0); + List regItemHistoryVersion = regItemhistoryManager.getByLocalidAndRegItemClass(localid, childRegItemClass); + + return regItemHistoryVersion.size(); + + } catch (NumberFormatException ignore) { + } + return 0; + } + + public Integer getVersionFromUri(String uri) { Integer version = null; // Check if the part after the last slash contains a colon int i = uri.lastIndexOf('/'); @@ -242,7 +296,13 @@ private ContainedItem setMainPropertiesForRegItem(RegItemhistory regItemhistory, setVersionAndHistory(regItemhistory, item, version); item.setType(regItemhistory.getRegItemclass().getRegItemclasstype().getLocalid()); item.setLanguage(languageCode.getIso6391code()); - item.setItemclass(new ItemClass(regItemhistory.getRegItemclass().getLocalid())); + + RegItemclass itemclassparent = regItemhistory.getRegItemclass().getRegItemclassParent(); + if (itemclassparent != null) { + item.setItemclass(new BasicItemClass(regItemhistory.getRegItemclass().getLocalid(), itemclassparent.getLocalid(), itemclassparent.getRegItemclasstype().getLocalid())); + } else { + item.setItemclass(new BasicItemClass(regItemhistory.getRegItemclass().getLocalid(), null, null)); + } item.setProperties(getLocalizedProperties(regItemhistory, fieldMapping -> !fieldMapping.getHidden())); switch (regItemhistory.getRegItemclass().getRegItemclasstype().getLocalid()) { @@ -360,7 +420,7 @@ private List getCollectionChain(RegItemhistory regItemhistory) throws E LinkedList collectionChain = new LinkedList<>(); while (collection != null) { collectionChain.addFirst(collection); - collection = itemSupplier.getRelatedItemBySubject(collection, hasCollection); + collection = ItemHelper.getRelatedItemBySubject(collection, hasCollection, entityManager); } return collectionChain; } @@ -659,6 +719,8 @@ private void setVersionAndHistory(RegItemhistory regItemhistory, ContainedItem i List itemHistory = regItemhistoryManager.getByRegItemReference(regItemManager.getByLocalidAndRegItemClass(regItemhistory.getLocalid(), regItemhistory.getRegItemclass())); + int minVersion = regItemhistoryManager.getMinVersionByLocalidAndRegItemClass(regItemhistory.getLocalid(), regItemhistory.getRegItemclass()).getVersionnumber(); + if (version == null) { // Requested current version int maxVersionNumber = itemHistory.stream() @@ -683,10 +745,20 @@ private void setVersionAndHistory(RegItemhistory regItemhistory, ContainedItem i throw new NoVersionException(); } item.setVersion(new VersionInformation(version, uri + ":" + version)); - item.setVersionHistory(itemHistory.stream() - .filter(ih -> ih.getVersionnumber() != version) - .map(ih -> new VersionInformation(ih.getVersionnumber(), uri + ":" + ih.getVersionnumber())) - .collect(Collectors.toList())); + + if (minVersion == 0) { + item.setVersionHistory(itemHistory.stream() + .filter(ih -> ih.getVersionnumber() != version) + .map(ih -> new VersionInformation(ih.getVersionnumber() + 1, uri + ":" + (ih.getVersionnumber() + 1))) + .collect(Collectors.toList())); + + } else { + item.setVersionHistory(itemHistory.stream() + .filter(ih -> ih.getVersionnumber() != version) + .map(ih -> new VersionInformation(ih.getVersionnumber(), uri + ":" + ih.getVersionnumber())) + .collect(Collectors.toList())); + } + // This does add a :version suffix to the max version link even if it's not necessary // but it still works and reduces the complexity of this code } @@ -760,7 +832,7 @@ private void setContainedItemsFromRegItem(RegItemhistory regItemhistory, Contain try { containedItemsList = new ArrayList<>(); // if (complexItem) { -// containedItemsList = getRelatedItemsByObject(regItem, hasCollection); +// containedItemsList = getRelatedItemsByObject(regItemhistory, hasCollection); // } else { List collectionNoParentList = getAllColectionsNoParentOfItem(regItemhistory); for (String uuid : collectionNoParentList) { @@ -821,7 +893,7 @@ private void setInSchemeAndTopConceptOfFromRegItem(RegItemhistory regItemhistory case TYPE_ITEM: RegItem relatedCollection = getRelatedItemByRegitemHistorySubject(regItemhistory, hasCollection); if (relatedCollection != null) { - RegItem parentCollection = itemSupplier.getRelatedItemBySubject(relatedCollection, hasParent); + RegItem parentCollection = ItemHelper.getRelatedItemBySubject(relatedCollection, hasParent, entityManager); if (parentCollection != null) { relatedCollection = parentCollection; } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemSupplier.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemSupplier.java index d8c383fb..3640ed31 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemSupplier.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemSupplier.java @@ -44,10 +44,12 @@ import eu.europa.ec.re3gistry2.base.utility.ItemproposedHelper; import eu.europa.ec.re3gistry2.crudimplementation.RegFieldManager; import eu.europa.ec.re3gistry2.crudimplementation.RegFieldmappingManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegGroupManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemclassManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemhistoryManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemproposedManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegLanguagecodeManager; import eu.europa.ec.re3gistry2.crudimplementation.RegLocalizationManager; import eu.europa.ec.re3gistry2.crudimplementation.RegLocalizationproposedManager; import eu.europa.ec.re3gistry2.crudimplementation.RegRelationManager; @@ -68,14 +70,15 @@ import eu.europa.ec.re3gistry2.model.RegStatusgroup; import eu.europa.ec.re3gistry2.model.RegStatuslocalization; import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicContainedItem; +import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; -import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemRef; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedPropertyValue; import eu.europa.ec.re3gistry2.javaapi.cache.model.VersionInformation; import eu.europa.ec.re3gistry2.javaapi.cache.util.StatusLocalization; +import eu.europa.ec.re3gistry2.model.RegGroup; import eu.europa.ec.re3gistry2.model.RegItemproposed; import eu.europa.ec.re3gistry2.model.RegLocalizationproposed; import java.text.DateFormat; @@ -106,9 +109,12 @@ public class ItemSupplier { private final RegStatuslocalizationManager regStatusLocalizationManager; private final RegItemproposedManager regItemproposedManager; private final RegRelationproposedManager regRelationproposedManager; + private final RegGroupManager regGroupManager; private final RegLocalizationproposedManager reglocalizationproposedManager; private final ItemproposedSupplier itemproposedSupplier; + private final RegLanguagecodeManager regLanguagecodeManager; + private final EntityManager entityManager; private final RegLanguagecode masterLanguage; private final RegLanguagecode languageCode; @@ -127,25 +133,28 @@ public class ItemSupplier { private Map> itemclassToFieldmapping; private boolean topConceptItem = false; - public ItemSupplier(EntityManager em, + public ItemSupplier(EntityManager entityManager, RegLanguagecode masterLanguage, RegLanguagecode languageCode) throws Exception { - this.regItemClassManager = new RegItemclassManager(em); - this.regItemManager = new RegItemManager(em); - this.regItemHistoryManager = new RegItemhistoryManager(em); - RegRelationpredicateManager relationPredicateManager = new RegRelationpredicateManager(em); - this.regRelationManager = new RegRelationManager(em); - this.regFieldManager = new RegFieldManager(em); - this.reglocalizationManager = new RegLocalizationManager(em); - this.regFieldmappingManager = new RegFieldmappingManager(em); - this.regStatusManager = new RegStatusManager(em); - this.regStatusLocalizationManager = new RegStatuslocalizationManager(em); - this.regItemproposedManager = new RegItemproposedManager(em); - this.regRelationproposedManager = new RegRelationproposedManager(em); - this.itemproposedSupplier = new ItemproposedSupplier(em, masterLanguage, languageCode); - this.reglocalizationproposedManager = new RegLocalizationproposedManager(em); - + this.regItemClassManager = new RegItemclassManager(entityManager); + this.regItemManager = new RegItemManager(entityManager); + this.regItemHistoryManager = new RegItemhistoryManager(entityManager); + RegRelationpredicateManager relationPredicateManager = new RegRelationpredicateManager(entityManager); + this.regRelationManager = new RegRelationManager(entityManager); + this.regFieldManager = new RegFieldManager(entityManager); + this.reglocalizationManager = new RegLocalizationManager(entityManager); + this.regFieldmappingManager = new RegFieldmappingManager(entityManager); + this.regStatusManager = new RegStatusManager(entityManager); + this.regStatusLocalizationManager = new RegStatuslocalizationManager(entityManager); + this.regItemproposedManager = new RegItemproposedManager(entityManager); + this.regRelationproposedManager = new RegRelationproposedManager(entityManager); + this.itemproposedSupplier = new ItemproposedSupplier(entityManager, masterLanguage, languageCode); + this.reglocalizationproposedManager = new RegLocalizationproposedManager(entityManager); + this.regLanguagecodeManager = new RegLanguagecodeManager(entityManager); + this.regGroupManager = new RegGroupManager(entityManager); + + this.entityManager = entityManager; this.masterLanguage = masterLanguage; this.languageCode = languageCode; @@ -176,7 +185,59 @@ public Item getItemByUri(String uri) throws Exception { return toItem(item); } + public Item getItemByUriAndStatus(String uri, String status) throws Exception { + RegItem item = getRegItemByUriAndStatus(uri, status); + if (item == null) { + return null; + } + return toItem(item); + } + private RegItem getRegItemByUri(String uri) throws Exception { + int i = uri.lastIndexOf('/'); + if (i < 0) { + throw new NoResultException(); + } + String localid = uri.substring(i + 1); + try { + int uriCollection = uri.substring(0, i).lastIndexOf('/'); + String regItemClassLocalId = uri.substring(uriCollection + 1).replace("/" + localid, ""); + RegItem regItem; + try { + RegItemclass parentClass = regItemClassManager.getByLocalid(regItemClassLocalId); + RegItemclass regItemRegItemClass = regItemClassManager.getChildItemclass(parentClass).get(0); + + regItem = regItemManager.getByLocalidAndRegItemClass(localid, regItemRegItemClass); + + if (uri.replace("https://", "").replace("http://", "").equals(ItemHelper.getURI(regItem).replace("https://", "").replace("http://", ""))) { + return regItem; + } + } catch (Exception e) { + int uriItemClassCollection = uri.substring(0, uriCollection).lastIndexOf('/'); + String regItemClassCollectionLocalId = uri.substring(uriItemClassCollection + 1, uriCollection).replace("/" + regItemClassLocalId, "").replace("/" + localid, ""); + RegItemclass parentClass = regItemClassManager.getByLocalid(regItemClassCollectionLocalId); + RegItemclass regItemRegItemClass = regItemClassManager.getChildItemclass(parentClass).get(0); + + RegItemclass regItemRegItemClassChild = regItemClassManager.getChildItemclass(regItemRegItemClass).get(0); + regItem = regItemManager.getByLocalidAndRegItemClass(localid, regItemRegItemClassChild); + + return regItem; + } + +// if (uri.equals(ItemHelper.getURI(regItem))) { +// return regItem; +// } + } catch (Exception e) { + for (RegItem item : regItemManager.getByLocalid(localid)) { + if (uri.replace("https://", "").replace("http://", "").equals(ItemHelper.getURI(item).replace("https://", "").replace("http://", ""))) { + return item; + } + } + } + return null; + } + + private RegItem getRegItemByUriAndStatus(String uri, String itemStatus) throws Exception { int i = uri.lastIndexOf('/'); if (i < 0) { throw new NoResultException(); @@ -188,7 +249,14 @@ private RegItem getRegItemByUri(String uri) throws Exception { RegItemclass parentClass = regItemClassManager.get(regItemClassLocalId); RegItemclass regItemRegItemClass = regItemClassManager.getChildItemclass(parentClass).get(0); - RegItem regItem = regItemManager.getByLocalidAndRegItemClass(localid, regItemRegItemClass); + RegStatus status = regStatusManager.findByLocalid(itemStatus); + RegItem regItem; + if (status.getIspublic()) { + regItem = regItemManager.getByLocalidAndRegItemClassAndRegStatus(localid, regItemRegItemClass, status); + } else { + return null; + } + if (uri.equals(ItemHelper.getURI(regItem))) { return regItem; } @@ -212,15 +280,33 @@ private Item toItem(RegItem regItem) throws Exception { setRegistryAndRegisterItemRef(regItem, item); setIsDefinedByFromRegItem(regItem, item); + + //refactoring create once the lists and than add them to the list that needs them + //improve cache + if (regItem.getRegItemclass().getRegItemclasstype().getLocalid().equals(TYPE_ITEM)) { + List collection = ItemHelper.getRelatedItemsByObject(regItem, hasCollection, entityManager); + List collectionNoParentList = getAllColectionsNoParentOfItem(regItem); + List parent = ItemHelper.getRelatedItemsByObject(regItem, hasParent, entityManager); + } + + //all the items of the codelist, so all items with hasCollection regItem setContainedItemsFromRegItem(regItem, item); setContainedItemsFromRegItemClassWithParent(regItem, item); + //all the item of codelist without a parent setTopConceptsFromRegItem(regItem, item); + + //get che codelist setInSchemeAndTopConceptOfFromRegItem(regItem, item); + + //all direct suns, so all that have the item as parent hasParent setNarrowerFromRegItem(regItem, item); + + //direct Parent hasParent setBroaderFromRegItem(regItem, item); +// setActiveLangList(item); return item; } @@ -242,9 +328,49 @@ protected ContainedItem toContainedItem(RegItem regItem) throws Exception { return containedItem; } + protected ContainedItem toContainedItemWithoutTopConcept(RegItem regItem) throws Exception { + ContainedItem containedItem = new ContainedItem(); + + setMainPropertiesForRegItem(regItem, containedItem); + setRegistryAndRegisterItemRef(regItem, containedItem); +// setIsDefinedByFromRegItem(regItem, containedItem); +// +// setTopConceptsFromRegItem(regItem, containedItem); +// setInSchemeAndTopConceptOfFromRegItem(regItem, containedItem); +// if (topConceptItem) { +// setNarrowerFromRegItem(regItem, containedItem); +// setBroaderFromRegItem(regItem, containedItem); +// setContainedItemsFromRegItem(regItem, containedItem); +// } + + return containedItem; + } + protected BasicContainedItem toBasicContainedItem(RegItem regItem) throws Exception { - BasicContainedItem citem = new ContainedItem(); + BasicContainedItem citem = new BasicContainedItem(); + String label = getLabelfromItem(regItem); + LocalizedPropertyValue myvalues = new LocalizedPropertyValue(label, ""); + citem.addValue(myvalues); citem.setUri(ItemHelper.getURI(regItem)); + + RegItem topConcept = ItemHelper.getRelatedItemBySubject(regItem, hasCollection, entityManager); + if (topConcept != null) { + citem.setTopConceptOf(toBasicContainedItem(topConcept)); + boolean moreParents = true; + while (moreParents) { + RegItem item = ItemHelper.getRelatedItemBySubject(topConcept, hasCollection, entityManager); + if (item != null) { + if (regItem.equals(item)) { + return null; + } + BasicContainedItem subTopConcept = citem.getTopConceptOf(); + subTopConcept.setTopConceptOf(toBasicContainedItem(item)); + } else { + moreParents = false; + } + } + + } return citem; } @@ -253,13 +379,12 @@ protected ContainedItem toBasicContainedItemDefinedBy(RegItem regItem) throws Ex setMainPropertiesForRegItem(regItem, containedItem); return containedItem; } - + protected ContainedItem toBasicContainedItemproposedDefinedBy(RegItemproposed regItem) throws Exception { ContainedItem containedItem = new ContainedItem(); containedItem = this.itemproposedSupplier.toContainedItemWithoutItemsProposed(regItem); return containedItem; } - protected ContainedItem toContainedItemWithoutItems(RegItem regItem) throws Exception { ContainedItem containedItem = new ContainedItem(); @@ -273,11 +398,11 @@ protected ContainedItem toContainedItemWithoutItems(RegItem regItem) throws Exce return containedItem; } - + protected ContainedItem toContainedItemWithoutItemsproposed(RegItemproposed regItem) throws Exception { ContainedItem containedItem = new ContainedItem(); containedItem = this.itemproposedSupplier.toContainedItemWithoutItemsProposed(regItem); - + return containedItem; } @@ -286,6 +411,9 @@ private ContainedItem setMainPropertiesForRegItem(RegItem regItem, ContainedItem item.setUri(ItemHelper.getURI(regItem)); item.setLocalid(regItem.getLocalid()); item.setLatest(true); + if (regItem.getExternal()){ + item.setExternal(true); + } DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z"); item.setInsertdate(df.format(regItem.getInsertdate())); @@ -296,7 +424,14 @@ private ContainedItem setMainPropertiesForRegItem(RegItem regItem, ContainedItem setVersionAndHistory(regItem, item); item.setType(regItem.getRegItemclass().getRegItemclasstype().getLocalid()); item.setLanguage(languageCode.getIso6391code()); - item.setItemclass(new ItemClass(regItem.getRegItemclass().getLocalid())); + RegItemclass itemclassParent = regItem.getRegItemclass().getRegItemclassParent(); + if (itemclassParent != null) { + item.setItemclass(new BasicItemClass(regItem.getRegItemclass().getLocalid(), + itemclassParent.getLocalid(), + itemclassParent.getRegItemclasstype().getLocalid())); + } else { + item.setItemclass(new BasicItemClass(regItem.getRegItemclass().getLocalid(), null, null)); + } item.setProperties(getLocalizedProperties(regItem, fieldMapping -> !fieldMapping.getHidden())); switch (regItem.getRegItemclass().getRegItemclasstype().getLocalid()) { @@ -313,11 +448,11 @@ private ContainedItem setMainPropertiesForRegItem(RegItem regItem, ContainedItem throw new RuntimeException("Unexpected type"); } if (topConceptItem) { - List hasCollectionList = getRelatedItemsBySubject(regItem, hasCollection); + List hasCollectionList = ItemHelper.getRelatedItemsBySubject(regItem, hasCollection, entityManager); if (hasCollectionList != null && !hasCollectionList.isEmpty()) { item.setHasCollection(true); } - List isParentList = getRelatedItemsBySubject(regItem, hasParent); + List isParentList = ItemHelper.getRelatedItemsBySubject(regItem, hasParent, entityManager); if (isParentList != null && !isParentList.isEmpty()) { item.setIsParent(true); } @@ -327,23 +462,39 @@ private ContainedItem setMainPropertiesForRegItem(RegItem regItem, ContainedItem } return item; } - + private void setVersionAndHistory(RegItem regItem, ContainedItem item) throws Exception { String uri = item.getUri(); List itemHistory = regItemHistoryManager.getByRegItemReference(regItem); - - // Requested current version - int maxVersionNumber = itemHistory.stream() - .mapToInt(ih -> ih.getVersionnumber()) - .max() - .orElse(1); // Default to 1 if for whatever reason we can not find max version - int thisversion = itemHistory.size() + 1; - item.setVersion(new VersionInformation(thisversion, uri + ":" + thisversion)); - item.setVersionHistory(itemHistory.stream() - .filter(ih -> ih.getVersionnumber() != maxVersionNumber + 1) - .map(ih -> new VersionInformation(ih.getVersionnumber(), uri + ":" + ih.getVersionnumber())) - .collect(Collectors.toList())); + if (itemHistory != null && !itemHistory.isEmpty()) { + + // Requested current version + int maxVersionNumber = itemHistory.stream() + .mapToInt(ih -> ih.getVersionnumber()) + .max() + .orElse(1); // Default to 1 if for whatever reason we can not find max version + int minVersionNumber = itemHistory.stream() + .mapToInt(ih -> ih.getVersionnumber()) + .min() + .orElse(1); // Default to 1 if for whatever reason we can not find max version + int thisversion = itemHistory.size() + 1; + item.setVersion(new VersionInformation(thisversion, uri + ":" + thisversion)); + + if (minVersionNumber == 0) { + item.setVersionHistory(itemHistory.stream() + .filter(ih -> ih.getVersionnumber() != maxVersionNumber + 1) + .map(ih -> new VersionInformation(ih.getVersionnumber(), uri + ":" + (ih.getVersionnumber() + 1))) + .collect(Collectors.toList())); + } else if (minVersionNumber == 1) { + item.setVersionHistory(itemHistory.stream() + .filter(ih -> ih.getVersionnumber() != maxVersionNumber + 1) + .map(ih -> new VersionInformation(ih.getVersionnumber(), uri + ":" + ih.getVersionnumber())) + .collect(Collectors.toList())); + } + } else { + item.setVersion(new VersionInformation(1, uri + ":" + 1)); + } } private void setRegistryAndRegisterItemRef(RegItem regItem, ContainedItem item) throws Exception { @@ -356,13 +507,13 @@ private void setRegistryAndRegisterItemRef(RegItem regItem, ContainedItem item) break; case TYPE_REGISTER: - registryRef = toItemRef(getRelatedItemBySubject(regItem, hasRegistry)); + registryRef = toItemRef(ItemHelper.getRelatedItemBySubject(regItem, hasRegistry, entityManager)); // Making an assumption that register can not be parent or collection to any item break; case TYPE_ITEM: - registryRef = toItemRef(getRelatedItemBySubject(regItem, hasRegistry)); - registerRef = toItemRef(getRelatedItemBySubject(regItem, hasRegister)); + registryRef = toItemRef(ItemHelper.getRelatedItemBySubject(regItem, hasRegistry, entityManager)); + registerRef = toItemRef(ItemHelper.getRelatedItemBySubject(regItem, hasRegister, entityManager)); break; default: @@ -375,7 +526,7 @@ private void setRegistryAndRegisterItemRef(RegItem regItem, ContainedItem item) item.setRegister(registerRef); } } - + private void setContainedItemsFromRegItem(RegItem regItem, ContainedItem item) throws Exception { List containedItems = new ArrayList<>(); List containedItemsList = null; @@ -399,50 +550,48 @@ private void setContainedItemsFromRegItem(RegItem regItem, ContainedItem item) t case TYPE_REGISTER: // ADD REGITEMs - containedItemsList = getDirectlyContainedItemsOfRegister(regItem); + containedItemsList = getDirectlyContainedItemsOfRegister(regItem); if (containedItemsList != null && !containedItemsList.isEmpty()) { for (RegItem containedItem : containedItemsList) { if (!containedItem.getRegItemclass().getSystemitem()) { containedItems.add(toContainedItemWithoutItems(containedItem)); } - } + } } - + // ADD REGITEMPROPOSEDs containedItemsproposedList = getDirectlyContainedItemsproposedOfRegister(regItem); - + if (containedItemsproposedList != null && !containedItemsproposedList.isEmpty()) { for (RegItemproposed containedItemproposed : containedItemsproposedList) { if (!containedItemproposed.getRegItemclass().getSystemitem()) { containedItems.add(toContainedItemWithoutItemsproposed(containedItemproposed)); } - } + } } - + //toContainedItemWithoutItemsproposed(containedItemsProposedList); // ADD TO THE CONTAINED ITEMS if (!containedItems.isEmpty()) { - item.setContainedItems(containedItems); - } + item.setContainedItems(containedItems); + } break; case TYPE_ITEM: List topConcepts = new ArrayList<>(); try { - containedItemsList = new ArrayList<>(); -// if (complexItem) { -// containedItemsList = getRelatedItemsByObject(regItem, hasCollection); -// } else { - List collectionNoParentList = getAllColectionsNoParentOfItem(regItem); - for (String uuid : collectionNoParentList) { - containedItemsList.add(regItemManager.get(uuid)); - } + //take all the items af the collection + containedItemsList = ItemHelper.getRelatedItemsByObject(regItem, hasCollection, entityManager); + if (containedItemsList == null || containedItemsList.isEmpty()) { - containedItemsList = getRelatedItemsByObject(regItem, hasCollection); + List collectionNoParentList = getAllColectionsNoParentOfItem(regItem); + for (String uuid : collectionNoParentList) { + containedItemsList.add(regItemManager.get(uuid)); + } } if (containedItemsList == null || containedItemsList.isEmpty()) { - containedItemsList = getRelatedItemsByObject(regItem, hasParent); + containedItemsList = ItemHelper.getRelatedItemsByObject(regItem, hasParent, entityManager); } // } for (RegItem childItem : containedItemsList) { @@ -451,7 +600,7 @@ private void setContainedItemsFromRegItem(RegItem regItem, ContainedItem item) t } } } catch (Exception exception) { - for (RegItem childItem : getRelatedItemsByObject(regItem, hasParent)) { + for (RegItem childItem : ItemHelper.getRelatedItemsByObject(regItem, hasParent, entityManager)) { if (!childItem.getRegItemclass().getSystemitem()) { topConcepts.add(toBasicContainedItem(childItem)); } @@ -543,19 +692,19 @@ private void setInSchemeAndTopConceptOfFromRegItem(RegItem regItem, ContainedIte break; case TYPE_REGISTER: - RegItem relatedRegistry = getRelatedItemBySubject(regItem, hasRegistry); + RegItem relatedRegistry = ItemHelper.getRelatedItemBySubject(regItem, hasRegistry, entityManager); basicContainedItem = toBasicContainedItem(relatedRegistry); break; case TYPE_ITEM: - RegItem relatedCollection = getRelatedItemBySubject(regItem, hasCollection); + RegItem relatedCollection = ItemHelper.getRelatedItemBySubject(regItem, hasCollection, entityManager); if (relatedCollection != null) { - RegItem parentCollection = getRelatedItemBySubject(relatedCollection, hasParent); + RegItem parentCollection = ItemHelper.getRelatedItemBySubject(relatedCollection, hasParent, entityManager); if (parentCollection != null) { relatedCollection = parentCollection; } } if (relatedCollection == null) { - relatedCollection = getRelatedItemBySubject(regItem, hasRegister); + relatedCollection = ItemHelper.getRelatedItemBySubject(regItem, hasRegister, entityManager); } basicContainedItem = toBasicContainedItem(relatedCollection); break; @@ -580,12 +729,12 @@ private void setBroaderFromRegItem(RegItem regItem, ContainedItem containedItem) break; case TYPE_ITEM: - broaderList = getRelatedItemsBySubject(regItem, hasParent); + broaderList = ItemHelper.getRelatedItemsBySubject(regItem, hasParent, entityManager); if ((broaderList == null || broaderList.isEmpty())) { - broaderList = getRelatedItemsBySubject(regItem, hasCollection); + broaderList = ItemHelper.getRelatedItemsBySubject(regItem, hasCollection, entityManager); } if ((broaderList == null || broaderList.isEmpty())) { - broaderList = getRelatedItemsBySubject(regItem, hasRegister); + broaderList = ItemHelper.getRelatedItemsBySubject(regItem, hasRegister, entityManager); } break; @@ -617,9 +766,9 @@ private void setNarrowerFromRegItem(RegItem regItem, ContainedItem containedItem // narrowerList = getDirectlyContainedItemsOfRegister(regItem); break; case TYPE_ITEM: - narrowerList = getRelatedItemsByObject(regItem, hasParent); + narrowerList = ItemHelper.getRelatedItemsByObject(regItem, hasParent, entityManager); if (narrowerList == null || narrowerList.isEmpty()) { - narrowerList = getRelatedItemsByObject(regItem, hasCollection); + narrowerList = ItemHelper.getRelatedItemsByObject(regItem, hasCollection, entityManager); } break; default: @@ -662,10 +811,10 @@ private void setTopConceptsFromRegItem(RegItem regItem, ContainedItem containedI childItemList.add(regItemManager.get(uuid)); } if (childItemList == null || childItemList.isEmpty()) { - childItemList = getRelatedItemsByObject(regItem, hasCollection); + childItemList = ItemHelper.getRelatedItemsByObject(regItem, hasCollection, entityManager); } if (childItemList == null || childItemList.isEmpty()) { - childItemList = getRelatedItemsByObject(regItem, hasParent); + childItemList = ItemHelper.getRelatedItemsByObject(regItem, hasParent, entityManager); } break; @@ -683,7 +832,7 @@ private void setTopConceptsFromRegItem(RegItem regItem, ContainedItem containedI containedItem.setTopConcepts(topConcepts); } } - + private void setIsDefinedByFromRegItem(RegItem regItem, ContainedItem containedItem) throws Exception { List isDefinedBy = new ArrayList<>(); @@ -695,7 +844,7 @@ private void setIsDefinedByFromRegItem(RegItem regItem, ContainedItem containedI break; case TYPE_ITEM: - for (RegItem relatedItem : getRelatedItemsBySubject(regItem, hasReference)) { + for (RegItem relatedItem : ItemHelper.getRelatedItemsBySubject(regItem, hasReference, entityManager)) { if (!relatedItem.getRegItemclass().getSystemitem()) { isDefinedBy.add(toBasicContainedItemDefinedBy(relatedItem)); } @@ -709,7 +858,7 @@ private void setIsDefinedByFromRegItem(RegItem regItem, ContainedItem containedI containedItem.setIsDefinedBy(isDefinedBy); } } - + protected ItemRef toItemRef(RegItem regItem) throws Exception { String uri = ItemHelper.getURI(regItem); @@ -735,6 +884,7 @@ protected ItemRef toItemRef(RegItem regItem) throws Exception { LocalizedProperty property = new LocalizedProperty(lang, id, istitle, id, values, order, tablevisible); return new ItemRef(uri, Arrays.asList(property)); } + protected ItemRef toItemproposedRef(RegItemproposed regItem) throws Exception { String uri = ItemproposedHelper.getProposedURI(regItem); @@ -807,7 +957,7 @@ private List getLocalizedProperties(RegItem regItem, Predicat .filter(Objects::nonNull) .collect(Collectors.toList()); } - + private StatusLocalization getLocalizedStatus(RegStatus regStatus) throws Exception { if (statusToLocalization == null) { statusToLocalization = new HashMap<>(); @@ -837,10 +987,11 @@ private StatusLocalization getLocalizedStatus(RegStatus regStatus) throws Except } return statusLocalization; } + private LocalizedProperty getLocalizedPropertyProposed(RegFieldmapping fieldmapping, - RegItem regItem, - Map> localizationsByField, - Map> localizationsByFieldML) throws Exception { + RegItem regItem, + Map> localizationsByField, + Map> localizationsByFieldML) throws Exception { RegField field = fieldmapping.getRegField(); String id = field.getLocalid(); String lang = languageCode.getIso6391code(); @@ -862,7 +1013,7 @@ private LocalizedProperty getLocalizedPropertyProposed(RegFieldmapping fieldmapp case BaseConstants.KEY_FIELDTYPE_REGISTER_UUID: return null; case BaseConstants.KEY_FIELDTYPE_COLLECTION_UUID: - RegItem collection = getRelatedItemBySubject(regItem, hasCollection); + RegItem collection = ItemHelper.getRelatedItemBySubject(regItem, hasCollection, entityManager); LocalizedProperty linksToRelatedItemsCollection = getLinksToRelationItems(field, label, order, tablevisible, collection); @@ -875,7 +1026,7 @@ private LocalizedProperty getLocalizedPropertyProposed(RegFieldmapping fieldmapp return linksToRelatedItemsCollection; } case BaseConstants.KEY_FIELDTYPE_PARENT_UUID: - RegItem parent = getRelatedItemBySubject(regItem, hasParent); + RegItem parent = ItemHelper.getRelatedItemBySubject(regItem, hasParent, entityManager); LocalizedProperty linksToRelatedItemsParent = getLinksToRelationItems(field, label, order, tablevisible, parent); // Handling the cases in which the values are null and the flag to display null values is true/false @@ -887,7 +1038,7 @@ private LocalizedProperty getLocalizedPropertyProposed(RegFieldmapping fieldmapp return linksToRelatedItemsParent; } case BaseConstants.KEY_FIELDTYPE_SUCCESSOR_UUID: - List successorList = getRelatedItemsBySubject(regItem, hasSuccessor); + List successorList = ItemHelper.getRelatedItemsBySubject(regItem, hasSuccessor, entityManager); List successorValues = new ArrayList<>(); for (RegItem successor : successorList) { @@ -906,7 +1057,7 @@ private LocalizedProperty getLocalizedPropertyProposed(RegFieldmapping fieldmapp return new LocalizedProperty(lang, id, istitle, label, successorValues, order, tablevisible); } case BaseConstants.KEY_FIELDTYPE_PREDECESSOR_UUID: - List predecessorList = getRelatedItemsBySubject(regItem, hasPredecessor); + List predecessorList = ItemHelper.getRelatedItemsBySubject(regItem, hasPredecessor, entityManager); List predecessorValues = new ArrayList<>(); for (RegItem predecessor : predecessorList) { @@ -963,33 +1114,55 @@ private LocalizedProperty getLocalizedPropertyProposed(RegFieldmapping fieldmapp default: String key = field.getUuid(); + RegGroup rolLabel = null; List localizations = localizationsByField.get(key); if (localizations == null || localizations.isEmpty()) { - if (localizationsByFieldML == null) { - break; - //return null; + + if (localizationsByFieldML != null) { + lang = masterLanguage.getIso6391code(); + localizations = localizationsByFieldML.get(key); + if (localizations != null) { + values = localizations.stream() + .map(l -> new LocalizedPropertyValue(l.getValue(), l.getHref())) + .collect(Collectors.toList()); + break; + } } - // fallback to master language localization for this field for this reg item - lang = masterLanguage.getIso6391code(); - localizations = localizationsByFieldML.get(key); - if (localizations == null) { - break; - //return null; + // fallback to master language localization for this field for this reg item. Whan do i need this? +// RegLocalization localization = this.reglocalizationManager.get(field, masterLanguage); +// if (localization != null) { +// localizations = new ArrayList<>(); +// localizations.add(localization); +// } + try { + rolLabel = this.regGroupManager.getByLocalid(regFieldManager.get(key).getLocalid()); + } catch (Exception e) { + } + +// if (localization == null) { +// break; +// } + } + if (localizations != null && !localizations.isEmpty()) { + values = localizations.stream() + .map(l -> new LocalizedPropertyValue(l.getValue(), l.getHref())) + .collect(Collectors.toList()); + } + if (rolLabel != null) { + if (!values.isEmpty()) { + values.clear(); } + values = Collections.singletonList(new LocalizedPropertyValue(rolLabel.getName(), null)); } - values = localizations.stream() - .map(l -> new LocalizedPropertyValue(l.getValue(), l.getHref())) - .collect(Collectors.toList()); break; } - if (values.isEmpty() && !allowEmptyFields.equals(BaseConstants.KEY_BOOLEAN_STRING_TRUE)) { - // Don't add properties that have no zero value/href pairs - return null; - } +// if (values.isEmpty() && !allowEmptyFields.equals(BaseConstants.KEY_BOOLEAN_STRING_TRUE)) { +// // Don't add properties that have no zero value/href pairs +// return null; +// } return new LocalizedProperty(lang, id, istitle, label, values, order, tablevisible); } - private LocalizedProperty getLinksToRelatedItems(RegField field, String label, int order, boolean tablevisible, RegItem regItem, @@ -1036,7 +1209,7 @@ private LocalizedProperty getLinksToRelatedItems(RegField field, return new LocalizedProperty(lang, id, istitle, label, values, order, tablevisible); } - + private LocalizedProperty getLinksToRelationItems(RegField field, String label, int order, boolean tablevisible, RegItem collection) throws Exception { String id = field.getLocalid(); @@ -1058,7 +1231,7 @@ private LocalizedProperty getLinksToRelationItems(RegField field, return new LocalizedProperty(lang, id, istitle, label, values, order, tablevisible); } - + private String getLabelForItem(RegItem regItem) throws Exception { List localizations = reglocalizationManager.getAll(getLabelField(), regItem, languageCode); if (localizations == null || localizations.isEmpty()) { @@ -1070,7 +1243,7 @@ private String getLabelForItem(RegItem regItem) throws Exception { .map(l -> l.getValue()) .orElse(null); } - + private String getLabelForField(RegField field) throws Exception { if (fieldToLabel == null) { fieldToLabel = new HashMap<>(); @@ -1100,7 +1273,7 @@ private List getRegisters(RegItem registry) throws Exception { }); return registrysRegisters; } - + protected List getAllRegisters() throws Exception { return getItemsOfType(TYPE_REGISTER); } @@ -1120,25 +1293,25 @@ private List getItemsOfType(String classtype) throws Exception { private List getDirectlyContainedItemsOfRegister(RegItem register) throws Exception { return regItemManager.getAllSubjectsByRegItemObjectAndPredicateAndSubjectNotPredicate(register, hasRegister, hasCollection); } - + private List getDirectlyContainedItemsproposedOfRegister(RegItem register) throws Exception { RegItemclass parentClass = regItemClassManager.getByLocalid(register.getLocalid()); - List regItemRegItemClass = regItemClassManager.getChildItemclass(parentClass); + List regItemRegItemClass = regItemClassManager.getChildItemclass(parentClass); - List regItemproposedList = new ArrayList (); + List regItemproposedList = new ArrayList<>(); - for(int i=0; i proposedItemsfromList = regItemproposedManager.getAll(regItemRegItemClass.get(i)); + List proposedItemsfromList = regItemproposedManager.getAll(regItemRegItemClass.get(i)); + + for (int y = 0; y < proposedItemsfromList.size(); y++) { + RegItemproposed proposedItem = proposedItemsfromList.get(y); + boolean isPublic = proposedItem.getRegStatus().getIspublic(); - for(int y=0; y getAllColectionsNoParentOfItem(RegItem item) throws Excepti return regItemManager.getAllItemByRegItemObjectAndPredicateAndSubjectNotPredicate(item, regStatusManager.get("1"), hasCollection, hasParent); } - protected RegItem getRelatedItemBySubject(RegItem regItem, RegRelationpredicate predicate) throws Exception { - List list = getRelatedItemsBySubject(regItem, predicate); - if (list != null && !list.isEmpty()) { - return list.stream().findAny().orElse(null); - } - return null; -// return getRelatedItemsBySubject(regItem, predicate).stream() -// .findAny() -// .orElse(null); - } - - private List getRelatedItemsBySubject(RegItem regItem, RegRelationpredicate predicate) throws Exception { + private String getLabelfromItem(RegItem regitem) throws Exception { + RegField field = regFieldManager.getByLocalid("label"); + RegLocalization loc = (reglocalizationManager.getAll(field, regitem)).get(0); - if (regRelationManager != null && regItem != null && predicate != null - && regRelationManager.getAllByRegItemSubjectAndPredicate(regItem, predicate) != null) { - return regRelationManager.getAllByRegItemSubjectAndPredicate(regItem, predicate).stream() - .map(rel -> rel.getRegItemObject()) - .collect(Collectors.toList()); - } else { - return null; - } + return loc.getValue(); } - private List getRelatedItemsByObject(RegItem regItem, RegRelationpredicate predicate) throws Exception { - - if (regRelationManager != null && regItem != null && predicate != null - && regRelationManager.getAllByRegItemObjectAndPredicate(regItem, predicate) != null) { - List relations = regRelationManager.getAllByRegItemObjectAndPredicate(regItem, predicate); - List subjects = new ArrayList<>(); - relations.forEach((relation) -> { - subjects.add(relation.getRegItemSubject()); - }); - return subjects; - } else { - return null; - } + private void setActiveLangList(Item item) throws Exception { + List activeLanguages = regLanguagecodeManager.getAllActive(); + item.setActiveLanguages(activeLanguages); } } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemproposedSupplier.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemproposedSupplier.java index f8d75afe..c42b444b 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemproposedSupplier.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/ItemproposedSupplier.java @@ -42,9 +42,9 @@ import eu.europa.ec.re3gistry2.base.utility.Configuration; import eu.europa.ec.re3gistry2.base.utility.ItemHelper; import eu.europa.ec.re3gistry2.base.utility.ItemproposedHelper; -import eu.europa.ec.re3gistry2.base.utility.PersistenceFactory; import eu.europa.ec.re3gistry2.crudimplementation.RegFieldManager; import eu.europa.ec.re3gistry2.crudimplementation.RegFieldmappingManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegGroupManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemproposedManager; import eu.europa.ec.re3gistry2.crudimplementation.RegItemclassManager; @@ -60,15 +60,13 @@ import eu.europa.ec.re3gistry2.model.RegFieldmapping; import eu.europa.ec.re3gistry2.model.RegItem; import eu.europa.ec.re3gistry2.model.RegItemclass; -import eu.europa.ec.re3gistry2.model.RegItemhistory; import eu.europa.ec.re3gistry2.model.RegLanguagecode; -import eu.europa.ec.re3gistry2.model.RegLocalization; -import eu.europa.ec.re3gistry2.model.RegRelation; import eu.europa.ec.re3gistry2.model.RegRelationpredicate; import eu.europa.ec.re3gistry2.model.RegStatus; import eu.europa.ec.re3gistry2.model.RegStatusgroup; import eu.europa.ec.re3gistry2.model.RegStatuslocalization; import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicContainedItem; +import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; @@ -77,13 +75,16 @@ import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedPropertyValue; import eu.europa.ec.re3gistry2.javaapi.cache.model.VersionInformation; import eu.europa.ec.re3gistry2.javaapi.cache.util.StatusLocalization; +import eu.europa.ec.re3gistry2.model.RegGroup; +import eu.europa.ec.re3gistry2.model.RegItemhistory; import eu.europa.ec.re3gistry2.model.RegItemproposed; +import eu.europa.ec.re3gistry2.model.RegLocalization; import eu.europa.ec.re3gistry2.model.RegLocalizationproposed; import eu.europa.ec.re3gistry2.model.RegRelationproposed; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Vector; import javax.persistence.EntityManager; -import org.apache.commons.collections.CollectionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -110,6 +111,7 @@ public class ItemproposedSupplier { private final RegItemproposedManager regItemproposedManager; private final RegRelationproposedManager regRelationproposedManager; private RegLocalizationproposedManager regLocalizationproposedManager; + private final RegGroupManager regGroupManager; private final RegLanguagecode masterLanguage; private final RegLanguagecode languageCode; @@ -144,6 +146,7 @@ public ItemproposedSupplier(EntityManager em, this.regItemproposedManager = new RegItemproposedManager(em); this.regRelationproposedManager = new RegRelationproposedManager(em); this.regLocalizationproposedManager = new RegLocalizationproposedManager(em); + this.regGroupManager = new RegGroupManager(em); this.masterLanguage = masterLanguage; this.languageCode = languageCode; @@ -164,7 +167,7 @@ public Item getItemProposedByUuid(String uuid) throws Exception { //RegItem finalItem = new RegItem(itemProposed.getUuid(), itemProposed.getLocalid(), itemProposed.getInsertdate()); return toItemProposed(itemProposed); } - + public Item getItemProposedByUri(String uri) throws Exception { RegItemproposed item = getRegItemProposedByUri(uri); if (item == null) { @@ -173,6 +176,14 @@ public Item getItemProposedByUri(String uri) throws Exception { return toItemProposed(item); } + public Item getItemProposedByUriAndStatus(String uri, String itemStatus) throws Exception { + RegItemproposed item = getRegItemProposedByUriAndStatus(uri, itemStatus); + if (item == null) { + return null; + } + return toItemProposed(item); + } + private RegItemproposed getRegItemProposedByUri(String uri) throws Exception { int i = uri.lastIndexOf('/'); if (i < 0) { @@ -184,8 +195,34 @@ private RegItemproposed getRegItemProposedByUri(String uri) throws Exception { String regItemClassLocalId = uri.substring(uriCollection + 1).replace("/" + localid, ""); RegItemclass parentClass = regItemClassManager.getByLocalid(regItemClassLocalId); RegItemclass regItemRegItemClass = regItemClassManager.getChildItemclass(parentClass).get(0); + RegItemproposed regItemproposed = regItemproposedManager.getByLocalidAndRegItemClass(localid, regItemRegItemClass); - RegItemproposed regItemproposed = regItemproposedManager.getByLocalidAndRegItemClass(localid, regItemRegItemClass); + return regItemproposed; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private RegItemproposed getRegItemProposedByUriAndStatus(String uri, String itemStatus) throws Exception { + int i = uri.lastIndexOf('/'); + if (i < 0) { + throw new NoResultException(); + } + String localid = uri.substring(i + 1); + try { + int uriCollection = uri.substring(0, i).lastIndexOf('/'); + String regItemClassLocalId = uri.substring(uriCollection + 1).replace("/" + localid, ""); + RegItemclass parentClass = regItemClassManager.getByLocalid(regItemClassLocalId); + RegItemclass regItemRegItemClass = regItemClassManager.getChildItemclass(parentClass).get(0); + + RegStatus status = regStatusManager.findByLocalid(itemStatus); + RegItemproposed regItemproposed; + if(status.getIspublic()){ + regItemproposed = regItemproposedManager.getByLocalidAndRegItemClassAndRegStatus(localid, regItemRegItemClass, status); + }else{ + return null; + } return regItemproposed; } catch (Exception e) { @@ -193,6 +230,7 @@ private RegItemproposed getRegItemProposedByUri(String uri) throws Exception { return null; } } + private Item toItemProposed(RegItemproposed regItemproposed) throws Exception { boolean isPublic = regItemproposed.getRegStatus().getIspublic(); @@ -204,7 +242,7 @@ private Item toItemProposed(RegItemproposed regItemproposed) throws Exception { setMainPropertiesForRegItemProposed(regItemproposed, item); setRegistryAndRegisterItemRefProposed(regItemproposed, item); -// setIsDefinedByFromRegItemProposed(regIP, item); +// setIsDefinedByFromRegItemProposed(regItemproposed, item); // setContainedItemsFromRegItemProposed(regIP, item); // setContainedItemsFromRegItemProposedClassWithParent(regIP, item); // setTopConceptsFromRegItemProposed(regItemproposed, item); @@ -256,7 +294,7 @@ protected ContainedItem toContainedItemWithoutItemsProposed(RegItemproposed regI ContainedItem containedItem = new ContainedItem(); setMainPropertiesForRegItemProposed(regItemproposed, containedItem); -// setRegistryAndRegisterItemRefProposed(regItemproposed, containedItem); + setRegistryAndRegisterItemRefProposed(regItemproposed, containedItem); // setIsDefinedByFromRegItemProposed(regItemproposed, containedItem); // setTopConceptsFromRegItemProposed(regItemproposed, containedItem); // setInSchemeAndTopConceptOfFromRegItemProposed(regItemproposed, containedItem); @@ -267,20 +305,23 @@ protected ContainedItem toContainedItemWithoutItemsProposed(RegItemproposed regI private void setRegistryAndRegisterItemRefProposed(RegItemproposed regItemproposed, ContainedItem item) throws Exception { ItemRef registryRef = null; ItemRef registerRef = null; - + switch (item.getType()) { case TYPE_REGISTRY: registerRef = null; break; case TYPE_REGISTER: - registryRef = toItemRefProposed(getRelatedItemProposedBySubject(regItemproposed, hasRegistry)); + registryRef = toItemRef(getRelatedItemProposedBySubject2(regItemproposed, hasRegistry)); // Making an assumption that register can not be parent or collection to any item break; case TYPE_ITEM: - registryRef = toItemRefProposed(getRelatedItemProposedBySubject(regItemproposed, hasRegistry)); - registerRef = toItemRefProposed(getRelatedItemProposedBySubject(regItemproposed, hasRegister)); +// registryRef = toItemRefProposed(getRelatedItemProposedBySubject(regItemproposed, hasRegistry)); +// registerRef = toItemRefProposed(getRelatedItemProposedBySubject(regItemproposed, hasRegister)); + + registryRef = toItemRef(getRelatedItemProposedBySubject2(regItemproposed, hasRegistry)); + registerRef = toItemRef(getRelatedItemProposedBySubject2(regItemproposed, hasRegister)); break; default: @@ -799,23 +840,44 @@ private LocalizedProperty getLocalizedPropertyItemProposed(RegFieldmapping field default: String key = field.getUuid(); + RegGroup rolLabel = null; List localizations = localizationsByField.get(key); if (localizations == null || localizations.isEmpty()) { - if (localizationsByFieldML == null) { - break; - //return null; + if (localizationsByFieldML != null) { + lang = masterLanguage.getIso6391code(); + localizations = localizationsByFieldML.get(key); + if (localizations != null) { + values = localizations.stream() + .map(l -> new LocalizedPropertyValue(l.getValue(), l.getHref())) + .collect(Collectors.toList()); + break; + } } // fallback to master language localization for this field for this reg item - lang = masterLanguage.getIso6391code(); - localizations = localizationsByFieldML.get(key); - if (localizations == null) { + RegLocalizationproposed localization = this.regLocalizationproposedManager.get(field, masterLanguage); + if (localization != null) { + localizations = new ArrayList<>(); + localizations.add(localization); + } + + if (localization == null) { break; - //return null; } + + // fix search in role + // rols are not in reglocalization + rolLabel = this.regGroupManager.getByLocalid(label); } + values = localizations.stream() .map(l -> new LocalizedPropertyValue(l.getValue(), l.getHref())) .collect(Collectors.toList()); + if (rolLabel != null) { + } + if (rolLabel != null) { + values.clear(); + values.add(new LocalizedPropertyValue(rolLabel.getName(), null)); + } break; } @@ -870,8 +932,8 @@ private List getDirectlyContainedItemsProposedOfRegister(RegIte } private List getAllColectionsNoParentOfItemProposed(RegItemproposed item) throws Exception { - RegItem item2 = null; - return regItemproposedManager.getAllItemByRegItemProposedObjectAndPredicateAndSubjectNotPredicate(item2, regStatusManager.get("1"), hasCollection, hasParent); + + return regItemproposedManager.getAllItemByRegItemProposedObjectAndPredicateAndSubjectNotPredicate(item, regStatusManager.get("1"), hasCollection, hasParent); } protected RegItemproposed getRelatedItemProposedBySubject(RegItemproposed regItemproposed, RegRelationpredicate predicate) throws Exception { @@ -983,7 +1045,12 @@ private ContainedItem setMainPropertiesForRegItemProposed(RegItemproposed regIte item.setType(regItemproposed.getRegItemclass().getRegItemclasstype().getLocalid()); item.setLanguage(languageCode.getIso6391code()); - item.setItemclass(new ItemClass(regItemproposed.getRegItemclass().getLocalid())); + RegItemclass itemclassParent = regItemproposed.getRegItemclass().getRegItemclassParent(); + if (itemclassParent != null) { + item.setItemclass(new BasicItemClass(regItemproposed.getRegItemclass().getLocalid(), itemclassParent.getLocalid(), itemclassParent.getRegItemclasstype().getLocalid())); + } else { + item.setItemclass(new BasicItemClass(regItemproposed.getRegItemclass().getLocalid(), null, null)); + } item.setProperties(getLocalizedPropertiesItemproposed(regItemproposed, fieldMapping -> !fieldMapping.getHidden())); // ERROR QUERY BD switch (regItemproposed.getRegItemclass().getRegItemclasstype().getLocalid()) { @@ -1008,9 +1075,12 @@ private ContainedItem setMainPropertiesForRegItemProposed(RegItemproposed regIte if (isParentList != null && !isParentList.isEmpty()) { item.setIsParent(true); } - if (regItemproposed.getExternal()) { + if(regItemproposed.getExternal() != null){ + if (regItemproposed.getExternal()) { item.setExternal(true); + } } + } return item; @@ -1481,14 +1551,87 @@ private LocalizedProperty getLinksToRelatedItemsProposed(RegField field, } private void setVersionAndHistoryItemproposed(RegItemproposed regItemproposed, ContainedItem item) throws Exception { - String uri = item.getUri(); - - // Requested current version - int maxVersionNumber = 0; - int thisversion = 0; - item.setVersion(new VersionInformation(thisversion, uri + ":" + thisversion)); - ArrayList versionHistory = new ArrayList(); - item.setVersionHistory(versionHistory); + + RegItem regItemReference = regItemproposed.getRegItemReference(); + String uri = item.getUri(); + List itemHistory = regItemHistoryManager.getByRegItemReferenceProposed(regItemproposed); + + item.setVersion(new VersionInformation(0, null)); +// item.setVersion(new VersionInformation(0, uri + ":" + 0)); + if (regItemReference != null) { +// int thisversion = itemHistory.size() + 2; +// item.setVersion(new VersionInformation(thisversion, uri + ":" + thisversion)); + item.setVersion(new VersionInformation(0, null)); + String localId = regItemproposed.getRegItemReference().getLocalid(); + + + List itemValid = regItemManager.getByLocalid(localId); + + // Requested current version + int maxVersionNumber = itemHistory.stream() + .mapToInt(ih -> ih.getVersionnumber()) + .max() + .orElse(1); // Default to 1 if for whatever reason we can not find max version + + item.setVersionHistory(itemHistory.stream() + .filter(ih -> ih.getVersionnumber() != maxVersionNumber + 1) + .map(ih -> new VersionInformation(ih.getVersionnumber()+1, uri + ":" + (ih.getVersionnumber()+1))) // needed for showing the correct version + .collect(Collectors.toList())); + + List history = item.getVersionHistory(); + int validVersion = itemHistory.size() + 1; + VersionInformation validItem = new VersionInformation(validVersion, uri + ":" + validVersion); + history.add(validItem); + }else{ + item.setVersionHistory(new Vector()); + } } + + protected ItemRef toItemRef(RegItem regItem) throws Exception { + String uri = ItemHelper.getURI(regItem); + + RegField labelField = getLabelField(); + RegFieldmapping labelFieldmapping = regFieldmappingManager.getByFieldAndItemClass(labelField, regItem.getRegItemclass()); + + String lang = languageCode.getIso6391code(); + String id = labelField.getLocalid(); + boolean istitle = labelField.getIstitle(); + int order = labelFieldmapping.getListorder(); + boolean tablevisible = labelFieldmapping.getTablevisible(); + + List localizations = reglocalizationManager.getAll(labelField, regItem, languageCode); + if (localizations == null || localizations.isEmpty()) { + // fallback to master language localization for this field for this reg item + lang = masterLanguage.getIso6391code(); + localizations = reglocalizationManager.getAll(labelField, regItem, masterLanguage); + } + List values = localizations.stream() + .map(l -> new LocalizedPropertyValue(l.getValue(), l.getHref())) + .collect(Collectors.toList()); + + LocalizedProperty property = new LocalizedProperty(lang, id, istitle, id, values, order, tablevisible); + return new ItemRef(uri, Arrays.asList(property)); + } + + private RegItem getRelatedItemProposedBySubject2(RegItemproposed regItemproposed, RegRelationpredicate predicate) throws Exception { + List list = getRelatedItemsBySubject(regItemproposed, predicate); + if (list != null && !list.isEmpty()) { + return list.stream().findAny().orElse(null); + } + return null; + } + + private List getRelatedItemsBySubject(RegItemproposed regItemproposed, RegRelationpredicate predicate) throws Exception { + + if (regRelationproposedManager != null && regItemproposed != null && predicate != null + && regRelationproposedManager.getAll(regItemproposed, predicate) != null) { + return regRelationproposedManager.getAll(regItemproposed, predicate).stream() + .map(rel -> rel.getRegItemObject()) + .collect(Collectors.toList()); + } else { + return null; + } + } + } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/StatusSupplier.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/StatusSupplier.java new file mode 100644 index 00000000..deae4ea5 --- /dev/null +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/StatusSupplier.java @@ -0,0 +1,318 @@ +/* + * /* + * * Copyright 2007,2016 EUROPEAN UNION + * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * * the European Commission - subsequent versions of the EUPL (the "Licence"); + * * You may not use this work except in compliance with the Licence. + * * You may obtain a copy of the Licence at: + * * + * * https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the Licence is distributed on an "AS IS" basis, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the Licence for the specific language governing permissions and + * * limitations under the Licence. + * * + * * Date: 2020/05/11 + * * Authors: + * * European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu + * * National Land Survey of Finland, SDI Services - inspire@nls.fi + * * + * * This work was supported by the Interoperability solutions for public + * * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) + * * through Action 2016.10: European Location Interoperability Solutions + * * for e-Government (ELISE) + */ +package eu.europa.ec.re3gistry2.javaapi.cache.supplier; + +import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.base.utility.Configuration; +import eu.europa.ec.re3gistry2.crudimplementation.RegFieldManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegItemManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegItemclassManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegLocalizationManager; +import javax.persistence.NoResultException; + +import eu.europa.ec.re3gistry2.crudimplementation.RegStatusManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegStatusgroupManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegStatuslocalizationManager; +import eu.europa.ec.re3gistry2.model.RegField; +import eu.europa.ec.re3gistry2.model.RegItem; +import eu.europa.ec.re3gistry2.model.RegLanguagecode; +import eu.europa.ec.re3gistry2.model.RegLocalization; +import eu.europa.ec.re3gistry2.model.RegStatus; +import eu.europa.ec.re3gistry2.model.RegStatusgroup; +import eu.europa.ec.re3gistry2.model.RegStatuslocalization; +import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; +import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemRef; +import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedPropertyValue; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.EntityManager; + +/** + * Fetch RegItems from DB and convert them to Item objects + */ +public class StatusSupplier { + + private final RegStatusManager regStatusManager; + private final RegItemManager regItemManager; + private final RegLocalizationManager regLocalizationManager; + private final RegFieldManager regFieldManager; + private final RegItemclassManager regItemcassManager; + private final RegStatusgroupManager regStatusgroupManager; + private final RegStatuslocalizationManager regStatusLocalizationManager; + + private final RegLanguagecode masterLanguage; + private final RegLanguagecode languageCode; + + public StatusSupplier(EntityManager em, + RegLanguagecode masterLanguage, + RegLanguagecode languageCode) throws Exception { + + this.regStatusManager = new RegStatusManager(em); + this.regItemManager = new RegItemManager(em); + this.regItemcassManager = new RegItemclassManager(em); + this.regFieldManager = new RegFieldManager(em); + this.regLocalizationManager = new RegLocalizationManager(em); + this.regStatusgroupManager = new RegStatusgroupManager(em); + this.regStatusLocalizationManager = new RegStatuslocalizationManager(em); + + this.masterLanguage = masterLanguage; + this.languageCode = languageCode; + } + + public Item getItemByUuid(String uuid) throws Exception { + // Not possible to request specific version with uuid + + try { + return toItem(regStatusManager.get(uuid)); + } catch (Exception ex) { + return toItem(regStatusgroupManager.get(uuid)); + } + } + + public Item getItemByUri(String uri) throws Exception { + int i = uri.lastIndexOf('/'); + if (i < 0) { + throw new NoResultException(); + } + String localid = uri.substring(i + 1); + + try { + RegStatus regStatus = regStatusManager.findByLocalid(localid); + if (regStatus == null) { + return null; + } + return toItem(regStatus); + } catch (Exception ex) { + RegStatusgroup statusgroup = regStatusgroupManager.findByLocalid(localid); + if (statusgroup == null) { + return null; + } + return toItem(statusgroup); + } + } + + private Item toItem(RegStatus regStatus) throws Exception { + Item statusItem = new Item(); + + setMainPropertiesForRegStatus(regStatus, statusItem); + + return statusItem; + } + + private Item toItem(RegStatusgroup regStatusgroup) throws Exception { + Item statusItem = new Item(); + + setMainPropertiesForRegStatusGroup(regStatusgroup, statusItem); + setContainedItems(regStatusgroup, statusItem); + + return statusItem; + } + + private ContainedItem setMainPropertiesForRegStatus(RegStatus regStatus, Item item) throws Exception { + item.setUuid(regStatus.getUuid()); + final RegStatusgroup statusgroup = regStatusgroupManager.get(regStatus.getRegStatusgroup().getUuid()); + + String baseuri = statusgroup.getBaseuri(); + item.setUri(baseuri + "/" + statusgroup.getLocalid() + "/" + regStatus.getLocalid()); + final String localid = regStatus.getLocalid(); + item.setLocalid(localid); + + setStatusRegistry(baseuri, item); + setStatusRegister(statusgroup, item); + + RegStatuslocalization regStatusLocalization; + String language; + try { + regStatusLocalization = regStatusLocalizationManager.get(regStatus, languageCode); + language = languageCode.getIso6391code(); + } catch (Exception ex) { + regStatusLocalization = regStatusLocalizationManager.get(regStatus, masterLanguage); + language = masterLanguage.getIso6391code(); + } + setProperties(regStatusLocalization, language, localid, item); + + String dateformat = Configuration.getInstance().getProperties().getProperty(BaseConstants.KEY_PROPERTY_DATEFORMAT, BaseConstants.KEY_STANDARD_DATEFORMAT); + SimpleDateFormat sdf = new SimpleDateFormat(dateformat); + if (item.getInsertdate() != null) { + item.setInsertdate(sdf.format(item.getInsertdate())); + } + if (item.getEditdate() != null) { + item.setEditdate(sdf.format(item.getEditdate())); + } + item.setLanguage(languageCode.getIso6391code()); + + return item; + } + + private void setStatusRegistry(String baseuri, Item item) throws Exception { + int i = baseuri.lastIndexOf('/'); + if (i < 0) { + throw new NoResultException(); + } + String registrylocalid = baseuri.substring(i + 1); + List regLocalizations; + String groupLanguage; + + RegField labelField = regFieldManager.getByLocalid("label"); + RegItem registryItem = regItemManager.getByLocalidAndRegItemClass(registrylocalid, regItemcassManager.getByLocalid(registrylocalid)); + try { + regLocalizations = regLocalizationManager.getAll(labelField, registryItem, languageCode); + groupLanguage = languageCode.getIso6391code(); + } catch (Exception ex) { + regLocalizations = regLocalizationManager.getAll(labelField, registryItem, masterLanguage); + groupLanguage = masterLanguage.getIso6391code(); + } + if (regLocalizations != null && !regLocalizations.isEmpty()) { + String label = regLocalizations.get(0).getValue(); + + List propertiesStatusRegistry = new ArrayList<>(); + List valuesLabel = new ArrayList<>(); + LocalizedPropertyValue localizedPropertyValueLabel = new LocalizedPropertyValue(label, baseuri); + valuesLabel.add(localizedPropertyValueLabel); + LocalizedProperty labelLocalizedProperty = new LocalizedProperty(groupLanguage, "Label", true, label, valuesLabel, 0, true); + propertiesStatusRegistry.add(labelLocalizedProperty); + + ItemRef statusRegistry = new ItemRef(baseuri, propertiesStatusRegistry); + item.setRegistry(statusRegistry); + } + } + + private void setStatusRegister(final RegStatusgroup statusgroup, Item item) throws Exception { + RegStatuslocalization regStatusGroupLocalization; + String groupLanguage; + try { + regStatusGroupLocalization = regStatusLocalizationManager.get(statusgroup, languageCode); + groupLanguage = languageCode.getIso6391code(); + } catch (Exception ex) { + regStatusGroupLocalization = regStatusLocalizationManager.get(statusgroup, masterLanguage); + groupLanguage = masterLanguage.getIso6391code(); + } + List propertiesStatusRegister = new ArrayList<>(); + List valuesLabel = new ArrayList<>(); + LocalizedPropertyValue localizedPropertyValueLabel = new LocalizedPropertyValue(regStatusGroupLocalization.getLabel(), statusgroup.getBaseuri() + "/" + statusgroup.getLocalid()); + valuesLabel.add(localizedPropertyValueLabel); + LocalizedProperty labelLocalizedProperty = new LocalizedProperty(groupLanguage, "Label", true, regStatusGroupLocalization.getLabel(), valuesLabel, 0, true); + propertiesStatusRegister.add(labelLocalizedProperty); + + ItemRef statusRegister = new ItemRef(statusgroup.getBaseuri() + "/" + statusgroup.getLocalid(), propertiesStatusRegister); + item.setRegister(statusRegister); + } + + private ContainedItem setMainPropertiesForRegStatusGroup(RegStatusgroup regStatusgroup, Item item) throws Exception { + item.setUuid(regStatusgroup.getUuid()); + + String baseuri = regStatusgroup.getBaseuri(); + final String localid = regStatusgroup.getLocalid(); + item.setUri(baseuri + "/" + localid); + item.setLocalid(localid); + + setStatusRegistry(baseuri, item); + + RegStatuslocalization regStatusLocalization; + String language; + try { + regStatusLocalization = regStatusLocalizationManager.get(regStatusgroup, languageCode); + language = languageCode.getIso6391code(); + } catch (Exception ex) { + regStatusLocalization = regStatusLocalizationManager.get(regStatusgroup, masterLanguage); + language = masterLanguage.getIso6391code(); + } + + setProperties(regStatusLocalization, language, localid, item); + + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z"); + item.setInsertdate(df.format(regStatusgroup.getInsertdate())); + if (regStatusgroup.getEditdate() != null) { + item.setEditdate(df.format(regStatusgroup.getEditdate())); + } + item.setLanguage(languageCode.getIso6391code()); + + return item; + } + + private void setProperties(RegStatuslocalization regStatusLocalization, String language, final String localid, Item item) { + List properties = new ArrayList<>(); + + List valuesLabel = new ArrayList<>(); + LocalizedPropertyValue localizedPropertyValueLabel = new LocalizedPropertyValue(regStatusLocalization.getLabel(), null); + valuesLabel.add(localizedPropertyValueLabel); + LocalizedProperty labelLocalizedProperty = new LocalizedProperty(language, "Label", true, "Label", valuesLabel, 0, true); + properties.add(labelLocalizedProperty); + + List valuesDefinition = new ArrayList<>(); + LocalizedPropertyValue localizedPropertyValueDefinition = new LocalizedPropertyValue(regStatusLocalization.getDescription(), null); + valuesDefinition.add(localizedPropertyValueDefinition); + LocalizedProperty definitionLocalizedProperty = new LocalizedProperty(language, "Definition", false, "Definition", valuesDefinition, 1, true); + properties.add(definitionLocalizedProperty); + + item.setProperties(properties); + } + + private void setContainedItems(RegStatusgroup regStatusgroup, Item statusItem) throws Exception { + List regContainedItems = regStatusManager.getAllPublic(regStatusgroup); + List containedItems = new ArrayList<>(); + for (RegStatus regStatus : regContainedItems) { + containedItems.add(toItem(regStatus)); + } + statusItem.setContainedItems(containedItems); + } + +// private LocalizedProperty getLocalizedProperty(RegFieldmapping fieldmapping, +// RegItem regItem, +// Map> localizationsByField, +// Map> localizationsByFieldML) throws Exception { +// RegField field = fieldmapping.getRegField(); +// String id = field.getLocalid(); +// String lang = languageCode.getIso6391code(); +// boolean istitle = field.getIstitle(); +// String label = getLabelForField(field); +// int order = fieldmapping.getListorder(); +// +// List values = Collections.emptyList(); +// +// //Get Property for allowing to return null value fields +// String allowEmptyFields = Configuration.getInstance().getProperties().getProperty(BaseConstants.KEY_ALLOW_NULL_FIELDS, BaseConstants.KEY_BOOLEAN_STRING_FALSE); +// String dateformat = Configuration.getInstance().getProperties().getProperty(BaseConstants.KEY_PROPERTY_DATEFORMAT, BaseConstants.KEY_STANDARD_DATEFORMAT); +// SimpleDateFormat sdf = new SimpleDateFormat(dateformat); +// +// String key = field.getUuid(); +// List localizations = localizationsByField.get(key); +// values = localizations.stream() +// .map(l -> new LocalizedPropertyValue(l.getValue(), l.getHref())) +// .collect(Collectors.toList()); +// +// if (values.isEmpty() && !allowEmptyFields.equals(BaseConstants.KEY_BOOLEAN_STRING_TRUE)) { +// // Don't add properties that have no zero value/href pairs +// return null; +// } +// return new LocalizedProperty(lang, id, istitle, label, values, order); +// } +} diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/XSDSchemaSupplier.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/XSDSchemaSupplier.java new file mode 100644 index 00000000..5ae3a1c8 --- /dev/null +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/cache/supplier/XSDSchemaSupplier.java @@ -0,0 +1,131 @@ +/* + * /* + * * Copyright 2007,2016 EUROPEAN UNION + * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * * the European Commission - subsequent versions of the EUPL (the "Licence"); + * * You may not use this work except in compliance with the Licence. + * * You may obtain a copy of the Licence at: + * * + * * https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the Licence is distributed on an "AS IS" basis, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the Licence for the specific language governing permissions and + * * limitations under the Licence. + * * + * * Date: 2020/05/11 + * * Authors: + * * European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu + * * National Land Survey of Finland, SDI Services - inspire@nls.fi + * * + * * This work was supported by the Interoperability solutions for public + * * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) + * * through Action 2016.10: European Location Interoperability Solutions + * * for e-Government (ELISE) + */ +package eu.europa.ec.re3gistry2.javaapi.cache.supplier; + +import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.crudimplementation.RegFieldmappingManager; +import eu.europa.ec.re3gistry2.crudimplementation.RegItemclassManager; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; +import eu.europa.ec.re3gistry2.model.RegFieldmapping; +import eu.europa.ec.re3gistry2.model.RegItemclass; +import java.util.HashMap; +import java.util.List; +import javax.persistence.EntityManager; + +/** + * Fetch RegItems from DB and convert them to Item objects + */ +public class XSDSchemaSupplier { + + private final EntityManager em; + private final RegItemclassManager regItemcassManager; + private final RegFieldmappingManager regFieldmappingManager; + + public XSDSchemaSupplier(EntityManager em, + RegItemclass regItemclass) throws Exception { + + this.em = em; + this.regItemcassManager = new RegItemclassManager(em); + this.regFieldmappingManager = new RegFieldmappingManager(em); + } + + public ItemClass getItemClass(RegItemclass regItemclass) throws Exception { + ItemClass itemClass = new ItemClass(); + + itemClass.setId(regItemclass.getLocalid()); + itemClass.setType(regItemclass.getRegItemclasstype().getLocalid()); + + String baseuri; + if (regItemclass.getBaseuri() != null) { + baseuri = regItemclass.getBaseuri(); + } else if (regItemclass.getRegItemclassParent().getBaseuri() != null) { + baseuri = regItemclass.getRegItemclassParent().getBaseuri(); + } else { + baseuri = regItemclass.getRegItemclassParent().getRegItemclassParent().getBaseuri(); + } + itemClass.setBaseuri(baseuri); + + if (regItemclass.getRegItemclassParent() != null) { + itemClass.setParentid(regItemclass.getRegItemclassParent().getLocalid()); + itemClass.setParentItemClassType(regItemclass.getRegItemclassParent().getRegItemclasstype().getLocalid()); + } + + String startElement; + switch (regItemclass.getRegItemclasstype().getLocalid()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + startElement = "registry"; + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + startElement = "register"; + break; + default: + if (regItemclass.getRegItemclassParent().getRegItemclasstype().getLocalid().equals("register")) { + startElement = regItemclass.getRegItemclassParent().getLocalid(); + itemClass.setRegisterLocalId(regItemclass.getRegItemclassParent().getLocalid()); + } else { + startElement = "value"; + RegItemclass grandparent = regItemclass.getRegItemclassParent().getRegItemclassParent(); + if (grandparent != null && grandparent.getRegItemclasstype().getLocalid().equals("register")) { + itemClass.setRegisterLocalId(grandparent.getLocalid()); + } + } + break; + } + itemClass.setStartElement(startElement); + + HashMap fields = getFieldmappings(regItemclass); + itemClass.setFields(fields); + + List childItemClass = regItemcassManager.getChildItemclass(regItemclass); + HashMap childfields = new HashMap<>(); + + for (RegItemclass childItemClas : childItemClass) { + childfields.putAll(getFieldmappings(childItemClas)); + } + + itemClass.setContainedItemsFields(childfields); + + if (!childItemClass.isEmpty() && childItemClass.size() >= 1) { + itemClass.setChildItemClassType(childItemClass.get(0).getRegItemclasstype().getLocalid()); + itemClass.setChildItemClassLocalId(childItemClass.get(0).getLocalid()); + } + + return itemClass; + } + + private HashMap getFieldmappings(RegItemclass regItemclass) throws Exception { + HashMap hash = new HashMap<>(); + List fieldMappingsRegItemclass = regFieldmappingManager.getAll(regItemclass); + for (RegFieldmapping fieldMappingsRegItemclas : fieldMappingsRegItemclass) { + String fieldType = fieldMappingsRegItemclas.getRegField().getRegFieldtype().getLocalid(); + String fieldLocalId = fieldMappingsRegItemclas.getRegField().getLocalid(); + hash.put(fieldLocalId, fieldType); + } + return hash; + } + +} diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegActionHandler.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegActionHandler.java index 9d55475f..50fa41f7 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegActionHandler.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegActionHandler.java @@ -367,6 +367,14 @@ public void registerManagerAction(String actionUuid, RegUser regUser) throws Exc } public void registryManagerAction(String actionUuid, RegUser regUser) throws Exception { + this.registryManagerAction(actionUuid, regUser, false, null, null); + } + + public void registryManagerActionSimplifiedWorkflow(String actionUuid, RegUser regUser, String changelog, String issueReference) throws Exception { + this.registryManagerAction(actionUuid, regUser, true, changelog, issueReference); + } + + private void registryManagerAction(String actionUuid, RegUser regUser, boolean simplifiedWorkflow, String changelog, String issueReference) throws Exception { // initializing managers RegActionManager regActionManager = new RegActionManager(entityManager); @@ -396,6 +404,15 @@ public void registryManagerAction(String actionUuid, RegUser regUser) throws Exc // Setting the submitted status to the RegAction regAction.setRegStatus(regStatusUpdatePublished); + + if (simplifiedWorkflow) { + if (changelog!=null) { + regAction.setChangelog(changelog); + } + if (issueReference!=null) { + regAction.setIssueTrackerLink(issueReference); + } + } entityManager.getTransaction().commit(); @@ -417,6 +434,7 @@ public void registryManagerAction(String actionUuid, RegUser regUser) throws Exc if (regItemproposed.getRegStatus().getLocalid().equals(BaseConstants.KEY_STATUS_LOCALID_ACCEPTED) || (regItemproposed.getRegStatus().getLocalid().equals(BaseConstants.KEY_STATUS_LOCALID_DRAFT) && (regItemproposed.getRegItemclass().getRegItemclasstype().getLocalid().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY) + || simplifiedWorkflow || regItemproposed.getRegItemclass().getRegItemclasstype().getLocalid().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER)))) { regItemproposed.setRegStatus(regStatusUpdateValid); } diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegInstallationStepCleanInstallationProcessHandler.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegInstallationStepCleanInstallationProcessHandler.java index 0344e5ba..4d00468c 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegInstallationStepCleanInstallationProcessHandler.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegInstallationStepCleanInstallationProcessHandler.java @@ -82,7 +82,7 @@ private void createRegistryFromSession() throws Exception { String[] activeLanguageCodes = (String[]) session.getAttribute(BaseConstants.KEY_REQUEST_ACTIVE_LANGUAGECODES); String masterLanguageCode = (String) session.getAttribute(BaseConstants.KEY_REQUEST_MASTERLANGUAGE); - + regInstallationHandler.saveActiveLanguages(activeLanguageCodes, commit); RegLanguagecode masterLanguage = regInstallationHandler.saveMasterLanguage(masterLanguageCode, commit); diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemhistoryHandler.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemhistoryHandler.java index e3f28a36..978cac05 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemhistoryHandler.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemhistoryHandler.java @@ -131,11 +131,26 @@ public String copyRegItemToRegItemhistory(RegItem regItem) throws Exception { // Getting the current latest version of thr RegItemhistory if available int versionNumber = 0; try { - RegItemhistory regItemHistory = regItemhistoryManager.getMaxVersionByLocalidAndRegItemClass(regItem.getLocalid(), regItem.getRegItemclass()); - if (regItemHistory.getVersionnumber() >= 0) { - versionNumber = regItemHistory.getVersionnumber() + 1; + RegItemhistory maxVersionRegItemhistory = regItemhistoryManager.getMaxVersionByLocalidAndRegItemClass(regItem.getLocalid(), regItem.getRegItemclass()); + int maxVersion = maxVersionRegItemhistory.getVersionnumber(); + + if (maxVersion > 0) { + + int minVersion = regItemhistoryManager.getMinVersionByLocalidAndRegItemClass(maxVersionRegItemhistory.getLocalid(), maxVersionRegItemhistory.getRegItemclass()).getVersionnumber(); + if (minVersion == 0) { + List regItemHistoryList = regItemhistoryManager.getByLocalidAndRegItemClass(maxVersionRegItemhistory.getLocalid(), maxVersionRegItemhistory.getRegItemclass()); + for (RegItemhistory regItemhistoryItem : regItemHistoryList) { + int itemHistoryVersionupdated = regItemhistoryItem.getVersionnumber() + 1; + maxVersionRegItemhistory.setVersionnumber(itemHistoryVersionupdated); + regItemhistoryManager.update(maxVersionRegItemhistory); + } + } + + versionNumber = maxVersion + 1; } else { - versionNumber = 0; + //history starts with 1 +//it was 0 before the release 2.3.2: versionNumber = 0; + versionNumber = 1; } } catch (NoResultException e) { } @@ -263,7 +278,6 @@ public String copyRegItemToRegItemhistory(RegItem regItem) throws Exception { // Removing the RegItem // Done after the removal of the proposed items int the RegActionHandler - entityManager.getTransaction().commit(); } /* ## End Synchronized ## */ diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemproposedHandler.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemproposedHandler.java index 417d1c9d..b978cc1c 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemproposedHandler.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/handler/RegItemproposedHandler.java @@ -1895,7 +1895,11 @@ private void mapGroup(RegItemproposed registerRegItemproposed, String groupUuid, RegGroupManager regGroupManager = new RegGroupManager(entityManager); RegGroup regGroup; try { - regGroup = regGroupManager.get(groupUuid); +// if (groupUuid == null) { +// regGroup = regGroupManager.getByLocalid("registryManager"); +// } else { + regGroup = regGroupManager.get(groupUuid); +// } if (!entityManager.getTransaction().isActive()) { entityManager.getTransaction().begin(); diff --git a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/solr/SolrHandler.java b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/solr/SolrHandler.java index 2961747c..1eeffc92 100644 --- a/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/solr/SolrHandler.java +++ b/sources/Re3gistry2JavaAPI/src/main/java/eu/europa/ec/re3gistry2/javaapi/solr/SolrHandler.java @@ -87,7 +87,7 @@ public static void indexComplete() throws Exception { RegItemManager regItemManager = new RegItemManager(em); // Getting all RegItems - List regItems = regItemManager.getAll(); + List regItems = regItemManager.getAllActive(); // Processing the RegItems SolrInputDocument document; @@ -95,6 +95,10 @@ public static void indexComplete() throws Exception { // Locking other Solr complete index requests createSolrCompleteIndexinglRunningFile(); + String deleteQuery = "*:*"; + solrClient.deleteByQuery(deleteQuery); + solrClient.commit(); + for (RegItem regItem : regItems) { document = indexItem(regItem, em); @@ -263,6 +267,7 @@ public static SolrDocumentList performSearch(String term, RegItem regItem, RegLa // Getting & setting the collection (if available) String collectionlocalid = ""; String collectionItemclasslocalid = ""; + boolean isCollection = true; RegRelationpredicate regRelationpredicateCollection = regRelationpredicateManager.get(BaseConstants.KEY_PREDICATE_COLLECTION); try { List regRelationCollections = regRelationManager.getAll(regItem, regRelationpredicateCollection); @@ -272,6 +277,7 @@ public static SolrDocumentList performSearch(String term, RegItem regItem, RegLa collectionItemclasslocalid = regItemCollection.getRegItemclass().getLocalid(); } catch (Exception e) { // The item doesn't have a collection + isCollection = false; } // Getting regItem's child itemclass @@ -293,20 +299,22 @@ public static SolrDocumentList performSearch(String term, RegItem regItem, RegLa filterQueries.add("itemclass_localid:" + childItemclasses.get(0).getLocalid()); } - if (regItem.getRegItemclass().getRegItemclasstype().getLocalid().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER) - || regItem.getRegItemclass().getRegItemclasstype().getLocalid().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY)) { - filterQueries.add("-collection_localid:*"); + if (isCollection==true) { + if (regItem.getRegItemclass().getRegItemclasstype().getLocalid().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER) + || regItem.getRegItemclass().getRegItemclasstype().getLocalid().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY)) { + filterQueries.add("-collection_localid:*"); - if (childItemclasses != null && childItemclasses.size() > 1) { - filterQueries.add("itemclass_type:register"); - filterQueries.add("itemclass_localid:*"); - } + if (childItemclasses != null && childItemclasses.size() > 1) { + filterQueries.add("itemclass_type:register"); + filterQueries.add("itemclass_localid:*"); + } - } else { - if (childItemclasses != null && childItemclasses.size() == 1) { - filterQueries.add("collection_localid:" + regItem.getLocalid()); } else { - filterQueries.add("-collection_localid:*"); + if (childItemclasses != null && childItemclasses.size() == 1) { + filterQueries.add("collection_localid:" + regItem.getLocalid()); + } else { + filterQueries.add("-collection_localid:*"); + } } } diff --git a/sources/Re3gistry2Model/pom.xml b/sources/Re3gistry2Model/pom.xml index 6c0e4a57..533559f4 100644 --- a/sources/Re3gistry2Model/pom.xml +++ b/sources/Re3gistry2Model/pom.xml @@ -26,7 +26,7 @@ javax.validation validation-api - 1.1.0.Final + 2.0.1.Final \ No newline at end of file diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.java index 83412a2d..0f7b4391 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheAllRunnable.java @@ -7,11 +7,6 @@ import eu.europa.ec.re3gistry2.javaapi.cache.CacheAll; import eu.europa.ec.re3gistry2.javaapi.cache.ItemCache; -import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; -import eu.europa.ec.re3gistry2.javaapi.cache.supplier.ItemHistorySupplier; -import eu.europa.ec.re3gistry2.javaapi.cache.supplier.ItemSupplier; -import eu.europa.ec.re3gistry2.model.RegItem; -import java.util.Optional; import javax.persistence.EntityManager; import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.Logger; @@ -35,7 +30,7 @@ public void run() { try { CacheAll cacheAll = new CacheAll(em, cache, null); - cacheAll.run(); + cacheAll.run(null); } catch (Exception e) { this.logger.error("Unexpected exception occured", e); } finally { @@ -45,46 +40,4 @@ public void run() { } } - private Optional getItemByUuid(String uuid, String language, ItemSupplier itemSupplier) throws Exception { - Item cached = cache.getByUuid(language, uuid); - if (cached != null) { - return Optional.of(cached); - } - Item item = itemSupplier.getItemByUuid(uuid); - if (item == null) { - return Optional.empty(); - } - - cache.add(language, item); - return Optional.of(item); - } - - private Optional getItemByUuid(RegItem regItem, String language, ItemSupplier itemSupplier) throws Exception { - Item cached = cache.getByUuid(language, regItem.getUuid()); - if (cached != null) { - return Optional.of(cached); - } - Item item = itemSupplier.getItem(regItem); - if (item == null) { - return Optional.empty(); - } - - cache.add(language, item); - return Optional.of(item); - } - - private Optional getItemHistoryByUuid(String uuid, String language, ItemHistorySupplier itemHistorySupplier) throws Exception { - Item cached = cache.getByUuid(language, uuid); - if (cached != null) { - return Optional.of(cached); - } - Item item = itemHistorySupplier.getItemHistoryByUuid(uuid); - if (item == null) { - return Optional.empty(); - } - - cache.add(language, item); - return Optional.of(item); - } - } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheServlet.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheServlet.java index 784c3f0c..a5e7361b 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheServlet.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/CacheServlet.java @@ -26,6 +26,8 @@ */ package eu.europa.ec.re3gistry2.restapi; +import eu.europa.ec.re3gistry2.restapi.util.ApiResponse; +import eu.europa.ec.re3gistry2.restapi.util.ApiError; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; import java.io.IOException; @@ -69,6 +71,7 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) return; } String uuid = RequestUtil.getParamTrimmed(req, "uuid", null); + Integer version = 0; boolean flushAll = uuid == null || uuid.isEmpty(); if (flushAll) { cache.removeAll(); diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ItemsServlet.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ItemsServlet.java index 745efe03..9f2f3e3f 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ItemsServlet.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ItemsServlet.java @@ -26,6 +26,9 @@ */ package eu.europa.ec.re3gistry2.restapi; +import eu.europa.ec.re3gistry2.restapi.util.ApiError; +import eu.europa.ec.re3gistry2.javaapi.cache.supplier.XSDSchemaSupplier; +import eu.europa.ec.re3gistry2.javaapi.cache.supplier.StatusSupplier; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -42,8 +45,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; -import eu.europa.ec.re3gistry2.base.utility.ItemproposedHelper; import eu.europa.ec.re3gistry2.base.utility.PersistenceFactory; +import eu.europa.ec.re3gistry2.crudimplementation.RegItemclassManager; import eu.europa.ec.re3gistry2.crudimplementation.RegLanguagecodeManager; import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.ItemCache; @@ -58,10 +61,18 @@ import eu.europa.ec.re3gistry2.restapi.format.RORFormatter; import eu.europa.ec.re3gistry2.restapi.format.XMLFormatter; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.supplier.ItemproposedSupplier; import eu.europa.ec.re3gistry2.javaapi.cache.util.NoVersionException; +import eu.europa.ec.re3gistry2.model.RegItemclass; +import eu.europa.ec.re3gistry2.restapi.format.ATOMFormatter; +import eu.europa.ec.re3gistry2.restapi.format.XSDFormatter; import eu.europa.ec.re3gistry2.restapi.util.RequestUtil; import eu.europa.ec.re3gistry2.restapi.util.ResponseUtil; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; public class ItemsServlet extends HttpServlet { @@ -86,6 +97,8 @@ public void init(ServletConfig config) throws ServletException { addFormatter(new RDFFormatter()); addFormatter(new CSVFormatter(emf)); addFormatter(new RORFormatter()); + addFormatter(new XSDFormatter()); + addFormatter(new ATOMFormatter()); } catch (Exception e) { LOG.error("Unexpected exception occured: cannot load the configuration system", e); } @@ -104,19 +117,10 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) String uuid = RequestUtil.getParamTrimmed(req, "uuid", null); String uri = RequestUtil.getParamTrimmed(req, "uri", null); String format = RequestUtil.getParamTrimmed(req, "format", null); + String status = RequestUtil.getParamTrimmed(req, "status", null); + String itemclassparam = RequestUtil.getParamTrimmed(req, "itemclass", null); uri = removeTrailingSlashes(uri); -// if (uri != null) { -// int slash = uri.lastIndexOf('/'); -// String localid = uri.substring(slash + 1); -// int count = countOccurance(uri, localid); -// -// if (count == 2) { -// int start = uri.lastIndexOf(localid); -// uri = uri.substring(0, start - 1); -// } -// } - Predicate typeFilter = getTypeFilter(path); Formatter formatter = formatters.get(format); @@ -124,83 +128,125 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) ResponseUtil.err(resp, ApiError.NOT_FOUND); return; } - if (formatter == null) { - ResponseUtil.err(resp, ApiError.FORMAT_NOT_SUPPORTED); - return; - } - if (uuid == null && uri == null) { - ResponseUtil.err(resp, ApiError.UUID_URI_REQUIRED); - return; - } EntityManager em = null; try { em = emf.createEntityManager(); - RegLanguagecodeManager languageManager = new RegLanguagecodeManager(em); - RegLanguagecode masterLanguage = languageManager.getMasterLanguage(); - RegLanguagecode languageCode = getLanguageCode(languageManager, lang, masterLanguage); - if (languageCode == null) { - ResponseUtil.err(resp, ApiError.LANGUAGE_NOT_SUPPORTED); - return; - } + if (lang != null && lang.equalsIgnoreCase("active")) { + String type = "application/json"; + RegLanguagecodeManager regLanguagecodeManager = new RegLanguagecodeManager(em); + List activeLanguages = null; + try { + activeLanguages = regLanguagecodeManager.getAllActive(); + } catch (Exception ex) { + java.util.logging.Logger.getLogger(ItemsServlet.class.getName()).log(Level.SEVERE, null, ex); + } - ItemproposedSupplier itemproposedSupplier = new ItemproposedSupplier(em, masterLanguage, languageCode); - ItemSupplier itemSupplier = new ItemSupplier(em, masterLanguage, languageCode); - - ItemHistorySupplier itemHistorySupplier = new ItemHistorySupplier(em, masterLanguage, languageCode); + //TOITEM + List> toActiveLanguages = new ArrayList(); + for (int i = 0; i < activeLanguages.size(); i++) { + HashMap language = new HashMap<>(); + language.put("uuid", activeLanguages.get(i).getUuid()); + language.put("label", activeLanguages.get(i).getLabel()); + language.put("iso6391code", activeLanguages.get(i).getIso6391code()); + language.put("iso6392code", activeLanguages.get(i).getIso6392code()); + language.put("masterlanguage", activeLanguages.get(i).getMasterlanguage().toString()); + language.put("active", activeLanguages.get(i).getActive().toString()); + toActiveLanguages.add(language); + } - Optional optItem; - - if (uuid != null) { + byte[] body = JSONInternalFormatter.OM.writeValueAsBytes(toActiveLanguages); + resp.setContentType(type); + resp.setContentLength(body.length); + try (OutputStream out = resp.getOutputStream()) { + out.write(body); + } + return; + } else if (itemclassparam != null) { + RegItemclassManager regItemclassManager = new RegItemclassManager(em); try { - optItem = getItemByUuid(uuid, lang, itemSupplier); + RegItemclass regItemclass = regItemclassManager.getByLocalid(itemclassparam.replace("_register", "")); + XSDSchemaSupplier xsdSchemaSupplier = new XSDSchemaSupplier(em, regItemclass); + + ItemClass itemClass = xsdSchemaSupplier.getItemClass(regItemclass); + + if (itemClass == null) { + ResponseUtil.err(resp, ApiError.NOT_FOUND); + } else { + ResponseUtil.ok(resp, itemClass, formatter); + } } catch (Exception ex) { - try{ - optItem = getItemProposedByUuid(uuid, lang, itemproposedSupplier); - }catch(Exception e){ - optItem = getItemHistoryByUuid(uuid, lang, itemHistorySupplier); + + List itemclassList = regItemclassManager.getAll(false); + String list = null; + for (RegItemclass regItemclass : itemclassList) { + list = list + ", " + regItemclass.getLocalid(); } + ApiError.ITEMCLASS_REQUIRED.getError().getDescription().concat(list); + ResponseUtil.err(resp, ApiError.ITEMCLASS_REQUIRED); } - } else{ - Integer version = getVersionFromUri(uri); - //version is null if the uri doesnt contain any version information, so is a RegItem - if (version != null) { - if (version == 0) { - optItem = getItemByUri(uri.replace(":" + version, ""), lang, itemSupplier); - } else { - optItem = getItemHistoryByUri(uri, version, lang, itemHistorySupplier); - if (!optItem.isPresent()) { - optItem = getItemByUri(uri.replace(":" + version, ""), lang, itemSupplier); - } - } - } else { - if (uri.endsWith(":0")) { - optItem = getItemByUri(uri.replace("0:", ""), lang, itemSupplier); + } else { + if (uuid == null && uri == null) { + ResponseUtil.err(resp, ApiError.UUID_URI_REQUIRED); + return; + } + + if (formatter == null) { + ResponseUtil.err(resp, ApiError.FORMAT_NOT_SUPPORTED); + return; + } + + RegLanguagecodeManager languageManager = new RegLanguagecodeManager(em); + RegLanguagecode masterLanguage = languageManager.getMasterLanguage(); + RegLanguagecode languageCode = getLanguageCode(languageManager, lang, masterLanguage); + if (languageCode == null) { + ResponseUtil.err(resp, ApiError.LANGUAGE_NOT_SUPPORTED); + return; + } + + ItemSupplier itemSupplier = new ItemSupplier(em, + masterLanguage, languageCode); + ItemHistorySupplier itemHistorySupplier = new ItemHistorySupplier(em, + masterLanguage, languageCode); + ItemproposedSupplier itemproposedSupplier = new ItemproposedSupplier(em, masterLanguage, languageCode); + + Optional optItem = Optional.empty(); + + Integer version; + try{ + version = getVersionFromUri(uri); + }catch(Exception uriError){ + version = 0; + } + + if(uuid != null && status != null ){ + throw new Exception(); + }else if (uuid != null){ + optItem = searchForItemByUuid(uuid, lang, itemSupplier, itemproposedSupplier, itemHistorySupplier); + }else if (uri != null && status != null){ + optItem = searchForItemByURIStatus(uri, lang, version, status, itemSupplier, itemproposedSupplier, itemHistorySupplier); + } else if (uri != null){ + optItem = searchForItemByURI(uri, version, lang, itemSupplier, itemproposedSupplier, itemHistorySupplier); + } + + //try to see if is a status request + if (!optItem.isPresent() && status == null) { + StatusSupplier statusSupplier = new StatusSupplier(em, masterLanguage, languageCode); + if (uuid != null) { + optItem = getItemStatusByUuid(uuid, lang, statusSupplier); } else { - optItem = getItemByUri(uri, lang, itemSupplier); - if (!optItem.isPresent()) { - optItem = getItemProposedByUri(uri, lang, itemproposedSupplier); - } + optItem = getItemStatusByUri(uri, lang, statusSupplier); } } - } - //try to see if is a status request - if (!optItem.isPresent()) { - StatusSupplier statusSupplier = new StatusSupplier(em, masterLanguage, languageCode); - if (uuid != null) { - optItem = getItemStatusByUuid(uuid, lang, statusSupplier); + + Item item = optItem.filter(typeFilter).orElse(null); + if (item == null) { + ResponseUtil.err(resp, ApiError.NOT_FOUND); } else { - optItem = getItemStatusByUri(uri, lang, statusSupplier); + ResponseUtil.ok(resp, item, languageCode, formatter); } } - - Item item = optItem.filter(typeFilter).orElse(null); // OPT ITEM RESULT - if (item == null) { - ResponseUtil.err(resp, ApiError.NOT_FOUND); - } else { - ResponseUtil.ok(resp, item, languageCode, formatter); - } } catch (NoResultException e) { try { ResponseUtil.err(resp, ApiError.NOT_FOUND); @@ -252,11 +298,12 @@ private Integer getVersionFromUri(String uri) { int i = uri.lastIndexOf('/'); i = uri.indexOf(':', i + 1); if (i < 0) { - version = null; + version = 0; } else { try { version = Integer.parseInt(uri.substring(i + 1)); - } catch (Exception ignore) { + return version; + } catch (NumberFormatException ignore) { return null; } } @@ -313,20 +360,34 @@ private Optional getItemByUuid(String uuid, String language, ItemSupplier return Optional.empty(); } - cache.add(language, item); + cache.add(language, item, null); return Optional.of(item); } - - private Optional getItemProposedByUuid(String uuid, String language, ItemproposedSupplier itemproposedSupplier) throws Exception { + private Optional getItemByUri(String uri, String language, ItemSupplier itemSupplier) throws Exception { + Item cached = cache.getByUrl(language, uri); + if (cached != null) { + return Optional.of(cached); + } + + Item item = itemSupplier.getItemByUri(uri); + if (item == null) { + return Optional.empty(); + } + + cache.add(language, item, null); + return Optional.of(item); + } + + private Optional getItemProposedByUuid(String uuid, String language, ItemproposedSupplier itemproposedSupplier) throws Exception { Item item = itemproposedSupplier.getItemProposedByUuid(uuid); if (item == null) { return Optional.empty(); } return Optional.of(item); } - - private Optional getItemProposedByUri(String uri, String language, ItemproposedSupplier itemproposedSupplier) throws Exception { + + private Optional getItemProposedByUri(String uri, ItemproposedSupplier itemproposedSupplier) throws Exception { Item item = itemproposedSupplier.getItemProposedByUri(uri); if (item == null) { return Optional.empty(); @@ -334,21 +395,26 @@ private Optional getItemProposedByUri(String uri, String language, Itempro return Optional.of(item); } - private Optional getItemByUri(String uri, String language, ItemSupplier itemSupplier) throws Exception { - Item cached = cache.getByUrl(language, uri); + private Optional getItemProposedByUriAndStatus(String uri, String itemStatus, ItemproposedSupplier itemproposedSupplier) throws Exception { + Item item = itemproposedSupplier.getItemProposedByUriAndStatus(uri, itemStatus); + if (item == null) { + return Optional.empty(); + } + return Optional.of(item); + } + + private Optional getItemByUriAndStatus(String uri, String language, ItemSupplier itemSupplier, String itemStatus) throws Exception { + Item cached = cache.getByUrl(language, uri, null, itemStatus); if (cached != null) { return Optional.of(cached); } - Item item = itemSupplier.getItemByUri(uri); + Item item = itemSupplier.getItemByUriAndStatus(uri, itemStatus); if (item == null) { return Optional.empty(); } - cache.add(language, item); - - Item it = cache.getByUrl(language, uri); - + cache.add(language, item, null); return Optional.of(item); } @@ -362,11 +428,11 @@ private Optional getItemHistoryByUuid(String uuid, String language, ItemHi return Optional.empty(); } - cache.add(language, item); + cache.add(language, item, item.getVersion().getNumber()); return Optional.of(item); } - private Optional getItemHistoryByUri(String uri, Integer version, String language, ItemHistorySupplier itemHistorySupplier) throws Exception { + private Optional getItemHistoryByUri(String uri, String language, ItemHistorySupplier itemHistorySupplier, Integer version) throws Exception { Item cached = cache.getByUrl(language, uri); if (cached != null) { return Optional.of(cached); @@ -377,7 +443,7 @@ private Optional getItemHistoryByUri(String uri, Integer version, String l return Optional.empty(); } - cache.add(language, item); + cache.add(language, item, version); return Optional.of(item); } @@ -391,7 +457,7 @@ private Optional getItemStatusByUuid(String uuid, String language, StatusS return Optional.empty(); } - cache.add(language, item); + cache.add(language, item, null); return Optional.of(item); } @@ -406,7 +472,127 @@ private Optional getItemStatusByUri(String uri, String language, StatusSup return Optional.empty(); } - cache.add(language, item); - return Optional.of(item); + cache.add(language, item, null); + return Optional.of(item); + } + + private Optional searchForItemByUuid(String uuid, String lang, ItemSupplier itemSupplier, ItemproposedSupplier itemproposedSupplier, ItemHistorySupplier itemHistorySupplier) { + Optional optItem; + try { + optItem = getItemByUuid(uuid, lang, itemSupplier); + } catch (Exception ex) { + try { + optItem = getItemProposedByUuid(uuid, lang, itemproposedSupplier); + } catch (Exception e) { + try { + optItem = getItemHistoryByUuid(uuid, lang, itemHistorySupplier); + } catch (Exception ex1) { + optItem = Optional.empty(); + } + } + } + return optItem; + } + + private Optional searchForItemByURI(String uri, Integer version, String lang, ItemSupplier itemSupplier, ItemproposedSupplier itemproposedSupplier, ItemHistorySupplier itemHistorySupplier) { + + Optional optItem; + optItem = Optional.empty(); + + if (version == 0) { + + try { + optItem = getItemByUri(uri.replace(":" + version, ""), lang, itemSupplier); + } catch (Exception ex) { + optItem = Optional.empty(); + } + + if (!optItem.isPresent()) { + try { + optItem = getItemProposedByUri(uri.replace(":" + version, ""), itemproposedSupplier); + } catch (Exception ex) { + optItem = Optional.empty(); + } + } + + } else { + int sizeHistory; + sizeHistory = 0; + try { + sizeHistory = itemHistorySupplier.sizeItemInHistory(uri); + } catch (Exception ex) { + sizeHistory = 0; + } + if (sizeHistory != 0 || sizeHistory + 1 != version) { + try { + optItem = getItemHistoryByUri(uri, lang, itemHistorySupplier, version); + } catch (Exception ex) { + optItem = Optional.empty(); + } + } + } + + return optItem; + } + + private Optional searchForItemByURIStatus(String uri, String lang, Integer version, String status, ItemSupplier itemSupplier, ItemproposedSupplier itemproposedSupplier, ItemHistorySupplier itemHistorySupplier) { + + Optional optItem; + + if (version == 0) { + + if (status.equalsIgnoreCase("valid") + || status.equalsIgnoreCase("invalid") + || status.equalsIgnoreCase("superseded") + || status.equalsIgnoreCase("retired")) { + try { + optItem = getItemByUriAndStatus(uri.replace(":" + version, ""), lang, itemSupplier, status); + } catch (Exception ex) { + optItem = Optional.empty(); + } + }else{ + try { + optItem = getItemProposedByUriAndStatus(uri.replace(":" + version, ""), status, itemproposedSupplier); + } catch (Exception ex) { + optItem = Optional.empty(); + } + } + + } else { + int sizeHistory; + try { + sizeHistory = itemHistorySupplier.sizeItemInHistory(uri); + } catch (Exception ex) { + sizeHistory = 0; + } + + if (sizeHistory == 0 || sizeHistory + 1 == version) { + + if (status.equalsIgnoreCase("valid") + || status.equalsIgnoreCase("invalid") + || status.equalsIgnoreCase("superseded") + || status.equalsIgnoreCase("retired")) { + try { + optItem = getItemByUriAndStatus(uri.replace(":" + version, ""), lang, itemSupplier, status); + } catch (Exception ex) { + optItem = Optional.empty(); + } + }else{ + try { + optItem = getItemProposedByUriAndStatus(uri.replace(":" + version, ""), status, itemproposedSupplier); + } catch (Exception e) { + optItem = Optional.empty(); + } + } + } else { + try { + optItem = getItemHistoryByUri(uri, lang, itemHistorySupplier, version); + } catch (Exception ex) { + optItem = Optional.empty(); + } + } + } + + return optItem; } } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/StatusSupplier.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/StatusSupplier.java index d52826c3..87efa225 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/StatusSupplier.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/StatusSupplier.java @@ -37,6 +37,7 @@ import eu.europa.ec.re3gistry2.crudimplementation.RegStatusManager; import eu.europa.ec.re3gistry2.crudimplementation.RegStatusgroupManager; import eu.europa.ec.re3gistry2.crudimplementation.RegStatuslocalizationManager; +import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicContainedItem; import eu.europa.ec.re3gistry2.model.RegField; import eu.europa.ec.re3gistry2.model.RegItem; import eu.europa.ec.re3gistry2.model.RegLanguagecode; diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/cache/CaffeineCache.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/cache/CaffeineCache.java deleted file mode 100644 index c7c74795..00000000 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/cache/CaffeineCache.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * /* - * * Copyright 2007,2016 EUROPEAN UNION - * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * * the European Commission - subsequent versions of the EUPL (the "Licence"); - * * You may not use this work except in compliance with the Licence. - * * You may obtain a copy of the Licence at: - * * - * * https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the Licence is distributed on an "AS IS" basis, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the Licence for the specific language governing permissions and - * * limitations under the Licence. - * * - * * Date: 2020/05/11 - * * Authors: - * * European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu - * * National Land Survey of Finland, SDI Services - inspire@nls.fi - * * - * * This work was supported by the Interoperability solutions for public - * * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) - * * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) - * * for e-Government (ELISE) - */ -package eu.europa.ec.re3gistry2.restapi.cache; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import eu.europa.ec.re3gistry2.javaapi.cache.ItemCache; -import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; - -public class CaffeineCache implements ItemCache { - - private final Set languages; - private final Cache itemsByUuid; - private final Cache itemsByURL; - - public CaffeineCache() { - this.languages = new HashSet<>(); - this.itemsByUuid = Caffeine.newBuilder() - .maximumSize(10000) - .build(); - this.itemsByURL = Caffeine.newBuilder() - .maximumSize(10000) - .build(); - } - - @Override - public Set getLanguages() { - return Collections.unmodifiableSet(languages); - } - - @Override - public Item getByUuid(String language, String uuid) { - return itemsByUuid.getIfPresent(getCacheKey(language, uuid)); - } - - @Override - public Item getByUrl(String language, String url) { - return itemsByURL.getIfPresent(getCacheKey(language, url)); - } - - @Override - public void add(String language, Item item) { - languages.add(language); - itemsByUuid.put(getCacheKey(language, item.getUuid()), item); - itemsByURL.put(getCacheKey(language, item.getUri()), item); - } - - @Override - public void remove(String language, String uuid) { - Item item = itemsByUuid.getIfPresent(getCacheKey(language, uuid)); - if (item != null) { - itemsByUuid.invalidate(getCacheKey(language, uuid)); - itemsByURL.invalidate(getCacheKey(language, item.getUri())); - } - } - - private String getCacheKey(String language, String uuidOrUrl) { - return language + "_" + uuidOrUrl; - } - - @Override - public void removeAll() { - languages.clear(); - itemsByUuid.invalidateAll(); - itemsByURL.invalidateAll(); - } - -} diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/ATOMFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/ATOMFormatter.java new file mode 100644 index 00000000..acb2b86e --- /dev/null +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/ATOMFormatter.java @@ -0,0 +1,410 @@ +/* + * /* + * * Copyright 2007,2016 EUROPEAN UNION + * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * * the European Commission - subsequent versions of the EUPL (the "Licence"); + * * You may not use this work except in compliance with the Licence. + * * You may obtain a copy of the Licence at: + * * + * * https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the Licence is distributed on an "AS IS" basis, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the Licence for the specific language governing permissions and + * * limitations under the Licence. + * * + * * Date: 2020/05/11 + * * Authors: + * * European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu + * * National Land Survey of Finland, SDI Services - inspire@nls.fi + * * + * * This work was supported by the Interoperability solutions for public + * * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) + * * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) + * * for e-Government (ELISE) + */ +package eu.europa.ec.re3gistry2.restapi.format; + +import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.base.utility.Configuration; +import eu.europa.ec.re3gistry2.model.RegLanguagecode; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; +import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; +import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; +import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedPropertyValue; +import eu.europa.ec.re3gistry2.javaapi.cache.model.VersionInformation; +import eu.europa.ec.re3gistry2.restapi.util.IndentingXMLStreamWriter; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Properties; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +public class ATOMFormatter implements Formatter { + + private static final String XSISCHEMALOCATION = "http://www.w3.org/2001/XMLSchema-instance"; + private static final String XMLNS = "http://www.w3.org/2005/Atom"; + private RegLanguagecode languageFile; + + @Override + public String getFormatName() { + return "atom"; + } + + @Override + public String getContentType() { + return "text/xml"; + } + + @Override + public void write(Item item, RegLanguagecode lang, OutputStream out) throws Exception { + this.languageFile = lang; + + switch (item.getType()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + writeRegistry(out, item); + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + writeRegister(out, item); + break; + default: + writeItem(out, item); + break; + } + } + + private XMLStreamWriter getATOMWriter(OutputStream out, Item item) throws XMLStreamException { + XMLStreamWriter xml = XMLFactory.XOF.createXMLStreamWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); + xml = new IndentingXMLStreamWriter(xml, XMLFactory.NEW_LINE, XMLFactory.INDENT); + + xml.writeStartDocument(); + + xml.writeStartElement("feed"); + xml.writeDefaultNamespace(XMLNS); + + xml.writeAttribute("xml", XSISCHEMALOCATION, "lang", languageFile.getIso6391code()); + + return xml; + } + + private void writeRegistry(OutputStream out, Item item) throws XMLStreamException { + XMLStreamWriter xml = getATOMWriter(out, item); + + writeId(xml, item); + writeAuthor(xml, item); + writeDate(xml, item); + writeFields(xml, item, true); + + if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { + for (ContainedItem ci : item.getContainedItems()) { + writeRegisterShortVersion(xml, ci); + } + } + + xml.writeEndDocument(); + xml.close(); + } + + private void writeRegisterShortVersion(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + xml.writeStartElement("entry"); + + writeId(xml, item); + writeDate(xml, item); + writeFields(xml, item, false); + writeRegistryAndRegister(xml, item); + + xml.writeEndElement(); + } + + private void writeRegister(OutputStream out, Item item) throws XMLStreamException { + XMLStreamWriter xml = getATOMWriter(out, item); + + writeId(xml, item); + writeAuthor(xml, item); + writeDate(xml, item); + writeFields(xml, item, true); + writeRegistryAndRegister(xml, item); + + if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { + for (ContainedItem ci : item.getContainedItems()) { + writeItemShortVersion(xml, ci); + } + } + + xml.writeEndDocument(); + xml.close(); + } + + private void writeItemShortVersion(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + xml.writeStartElement("entry"); + + writeId(xml, item); + writeDate(xml, item); + writeFields(xml, item, false); + writeItemclass(xml, item); + writeRegistryAndRegister(xml, item); + + xml.writeEndElement(); + } + + private void writeItem(OutputStream out, Item item) throws XMLStreamException { + XMLStreamWriter xml = getATOMWriter(out, item); + + writeId(xml, item); + writeAuthor(xml, item); + writeDate(xml, item); + writeVersions(xml, item); + + writeFields(xml, item, true); + writeItemclass(xml, item); + writeRegistryAndRegister(xml, item); + + if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { + + for (ContainedItem ci : item.getContainedItems()) { + + if (item.getItemclass().getParentItemClassType().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER) + && ci.getItemclass().getId().equals(item.getItemclass().getId())) { + writeItemShortVersion(xml, ci); + } else { + writeItemShortVersion(xml, ci); + } + if (ci.isHasCollection()) { + if (ci.getContainedItems() != null && !ci.getContainedItems().isEmpty()) { + for (ContainedItem c : ci.getContainedItems()) { + writeItemShortVersion(xml, c); + } + } else if (ci.getContainedItemsBeeingParentItemClass() != null && !ci.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem c : ci.getContainedItemsBeeingParentItemClass()) { + writeItemShortVersion(xml, c); + } + } + } + } + } else if (item.getContainedItemsBeeingParentItemClass() != null && !item.getContainedItemsBeeingParentItemClass().isEmpty()) { + + for (ContainedItem ci : item.getContainedItemsBeeingParentItemClass()) { + if (item.getItemclass().getParentItemClassType().equals(BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER) + && ci.getItemclass().getId().equals(item.getItemclass().getId())) { + writeItemShortVersion(xml, ci); + } else { + writeItemShortVersion(xml, ci); + } + + if (ci.isHasCollection()) { + if (ci.getContainedItemsBeeingParentItemClass() != null && !ci.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem c : ci.getContainedItemsBeeingParentItemClass()) { + writeItemShortVersion(xml, c); + } + } else if (ci.getContainedItems() != null && !ci.getContainedItems().isEmpty()) { + for (ContainedItem c : ci.getContainedItems()) { + writeItemShortVersion(xml, c); + } + } + } + } + } + + xml.writeEndDocument(); + + xml.close(); + } + + private void writeVersions(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + List versionHistory = item.getVersionHistory(); + VersionInformation version = item.getVersion(); + + xml.writeStartElement("link"); + xml.writeAttribute("href", item.getUri()); + xml.writeAttribute("rel", "self"); + xml.writeEndElement(); + + xml.writeStartElement("link"); + if (version!=null && version.getUri()!=null) { + xml.writeAttribute("href", version.getUri()); + } + xml.writeAttribute("rel", "self"); + xml.writeEndElement(); + + xml.writeStartElement("link"); + xml.writeAttribute("href", item.getUri()); + xml.writeAttribute("rel", "latest-version"); + xml.writeEndElement(); + + if (versionHistory!=null && !versionHistory.isEmpty()) { + for (VersionInformation versionInformation : versionHistory) { + xml.writeStartElement("link"); + xml.writeAttribute("href", versionInformation.getUri()); + xml.writeAttribute("rel", "version-history"); + xml.writeEndElement(); + } + } + } + + private void writeId(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + writeEmptyElement(xml, "id", item.getUri()); + + xml.writeStartElement("link"); + xml.writeAttribute("href", item.getUri()); + xml.writeAttribute("rel", "self"); + xml.writeEndElement(); + } + + private void writeAuthor(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + xml.writeStartElement("author"); + + // Get configuration properties + final Properties configurationProperties = Configuration.getInstance().getProperties(); + String legacyFlag = configurationProperties.getProperty(BaseConstants.KEY_APPLICATION_LEGACY_FLAG); + if (!legacyFlag.equals(BaseConstants.KEY_APPLICATION_LEGACY_FLAG_ON)) { + writeEmptyElement(xml, "name", "INSPIRE Registry team"); + writeEmptyElement(xml, "email", "JRC-INSPIRE-SUPPORT@ec.europa.eu"); + } else { + writeEmptyElement(xml, "name", "Registry team"); + } + + switch (item.getType()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + writeEmptyElement(xml, "uri", item.getUri()); + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + writeEmptyElement(xml, "uri", item.getRegistry().getUri()); + break; + default: + writeEmptyElement(xml, "uri", item.getRegistry().getUri()); + break; + } + xml.writeEndElement(); + } + + private void writeDate(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + writeEmptyElement(xml, "created", item.getInsertdate()); + + if (item.getEditdate() != null) { + writeEmptyElement(xml, "updated", item.getEditdate()); + } + } + + private void writeFields(XMLStreamWriter xml, ContainedItem item, boolean mainElement) throws XMLStreamException { + // Get configuration properties + final Properties configurationProperties = Configuration.getInstance().getProperties(); + String legacyFlag = configurationProperties.getProperty(BaseConstants.KEY_APPLICATION_LEGACY_FLAG); + + List localizedProperties = item.getProperties(); + for (LocalizedProperty localizedProperty : localizedProperties) { + + String lang = localizedProperty.getLang(); + if (!localizedProperty.getValues().isEmpty()) { + String value = localizedProperty.getValues().get(0).getValue(); + String href = localizedProperty.getValues().get(0).getHref(); + + String fieldName = localizedProperty.getLabel().replace("-item", "").toLowerCase(); + String fieldLocalId = localizedProperty.getId().replace("-item", "").toLowerCase(); + + if (fieldName != null && "successor".equals(fieldLocalId)) { + writeComplexListElement(xml, localizedProperty, lang, "successors", "next"); + } else if (fieldName != null && "predecessor".equals(fieldLocalId)) { + writeComplexListElement(xml, localizedProperty, lang, "predecessors", "previous"); + } else if (fieldName != null && "parent".equals(fieldLocalId)) { + writeComplexListElement(xml, localizedProperty, lang, "parents", "up"); + } else if (fieldName != null && ("definition".equals(fieldLocalId) || "contentsummary".equals(fieldLocalId))) { + if (mainElement) { + writeEmptyElement(xml, "subtitle", value); + } else { + writeEmptyElement(xml, "summary", value); + } + } else if (fieldName != null && ("description".equals(fieldLocalId) || "contentsummary".equals(fieldLocalId))) { + writeEmptyElement(xml, "content", value); + } else if (fieldName != null && "collection".equals(fieldLocalId)) { + xml.writeStartElement("link"); + xml.writeAttribute("href", href); + xml.writeAttribute("rel", "up"); + xml.writeEndElement(); + } else if (fieldName != null && "label".equals(fieldLocalId)) { + writeEmptyElement(xml, fieldLocalId, value); + } else if (href != null && !href.isEmpty()) { + xml.writeStartElement("link"); + xml.writeAttribute("href", href); + xml.writeAttribute("rel", "related"); + xml.writeEndElement(); + } else { + } + } + } + } + + private void writeComplexListElement(XMLStreamWriter xml, LocalizedProperty localizedProperty, String lang, String mainElement, String secondElement) throws XMLStreamException { + xml.writeStartElement(mainElement); + + List valuesListsuccessors = localizedProperty.getValues(); + if (valuesListsuccessors != null && !valuesListsuccessors.isEmpty()) { + for (LocalizedPropertyValue localizedPropertyValue : valuesListsuccessors) { + String valueSuccessor = localizedPropertyValue.getValue(); + String hrefSuccessor = localizedPropertyValue.getHref(); + + xml.writeStartElement("link"); + xml.writeAttribute("href", hrefSuccessor); + xml.writeAttribute("rel", secondElement); + xml.writeEndElement(); + } + } + xml.writeEndElement(); + } + + private void writeItemclass(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + switch (item.getType()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + break; + default: + String itemClassName = item.getItemclass().getId(); + xml.writeStartElement("term"); + xml.writeAttribute("uriname", itemClassName); + xml.writeEndElement(); + + break; + } + } + + private void writeRegistryAndRegister(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + switch (item.getType()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + xml.writeStartElement("link"); + xml.writeAttribute("href", item.getRegistry().getUri()); + xml.writeAttribute("rel", "up"); + xml.writeEndElement(); + break; + default: + xml.writeStartElement("link"); + xml.writeAttribute("href", item.getRegistry().getUri()); + xml.writeAttribute("rel", "up"); + xml.writeEndElement(); + + xml.writeStartElement("link"); + xml.writeAttribute("href", item.getRegister().getUri()); + xml.writeAttribute("rel", "up"); + xml.writeEndElement(); + + break; + } + } + + private void writeEmptyElement(XMLStreamWriter xml, String localName, String value) throws XMLStreamException { + xml.writeStartElement(localName); + xml.writeCharacters(value); + xml.writeEndElement(); + } + + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.java index 549b51c2..b8215ff3 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/CSVFormatter.java @@ -32,6 +32,7 @@ import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -162,4 +163,9 @@ private String getFieldValue(ContainedItem item, String propertyName) throws XML } } + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/Formatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/Formatter.java index d846b4b6..735e2918 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/Formatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/Formatter.java @@ -30,11 +30,13 @@ import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; public interface Formatter { public String getFormatName(); public String getContentType(); public void write(Item item, RegLanguagecode lang, OutputStream out) throws Exception; + public void write(ItemClass itemClass, OutputStream out) throws Exception; } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.java index c9cb44a1..03ff58cd 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/ISO19135Formatter.java @@ -39,6 +39,7 @@ import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; import eu.europa.ec.re3gistry2.restapi.util.IndentingXMLStreamWriter; @@ -372,5 +373,10 @@ private void writeXLink(XMLStreamWriter xml, String element, String value) throw xml.writeEndElement(); } + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.java index 3d5eaeb0..71c7f2c0 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONFormatter.java @@ -33,10 +33,12 @@ import java.io.OutputStream; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.base.utility.Configuration; import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemRef; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; import eu.europa.ec.re3gistry2.javaapi.cache.model.VersionInformation; @@ -45,7 +47,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Optional; -import org.json.JSONArray; +import java.util.Properties; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; public class JSONFormatter implements Formatter { @@ -94,13 +97,14 @@ private void writeRegistry(OutputStream out, Item item) throws Exception { JSONArray containedArray = new JSONArray(); for (ContainedItem ci : item.getContainedItems()) { JSONObject containedItemsJSON = createOrderedJSONObject(); - containedArray.put(writeRegisterShortVersion(containedItemsJSON, ci)); + containedArray.add(writeRegisterShortVersion(containedItemsJSON, ci)); } regItemJsonObject.put("registers", containedArray); } JSONObject employeeObject = createOrderedJSONObject(); - employeeObject.put(item.getItemclass().getId().toLowerCase(), regItemJsonObject); +// employeeObject.put(item.getItemclass().getId().toLowerCase(), regItemJsonObject); + employeeObject.put("registry", regItemJsonObject); osw.write(employeeObject.toJSONString()); osw.flush(); @@ -130,33 +134,32 @@ private void writeRegister(OutputStream out, Item item) throws Exception { writeIsDefinedBy(regItemJsonObject, item); writeRegistryAndRegister(regItemJsonObject, item); + JSONArray containedJSONArray = new JSONArray(); if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { JSONObject containedItemsJSON = createOrderedJSONObject(); - int index = 0; for (ContainedItem ci : item.getContainedItems()) { JSONObject itemJSON = createOrderedJSONObject(); JSONObject valueJSON = createOrderedJSONObject(); - itemJSON.put("value", writeItemShortVersion(valueJSON, ci)); - containedItemsJSON.put(String.valueOf(index), itemJSON); - index++; + itemJSON.put("value", writeItemShortVersion(valueJSON, ci, ci)); + containedJSONArray.add(itemJSON); if (ci.isHasCollection()) { if (ci.getContainedItems() != null && !ci.getContainedItems().isEmpty()) { for (ContainedItem c : ci.getContainedItems()) { JSONObject containedJSON = createOrderedJSONObject(); JSONObject containedvalueJSON = createOrderedJSONObject(); - containedJSON.put(ci.getItemclass().getId().toLowerCase(), writeItemShortVersion(containedvalueJSON, c)); - containedItemsJSON.put(String.valueOf(index), containedJSON); - index++; + containedJSON.put(ci.getItemclass().getId().toLowerCase(), writeItemShortVersion(containedvalueJSON, c, ci)); + containedJSONArray.add(containedJSON); } } } } - regItemJsonObject.put("containeditems", containedItemsJSON); + regItemJsonObject.put("containeditems", containedJSONArray); } JSONObject regiItemJSONObject = createOrderedJSONObject(); - regiItemJSONObject.put(item.getItemclass().getId().toLowerCase(), regItemJsonObject); +// regiItemJSONObject.put(item.getItemclass().getId().toLowerCase(), regItemJsonObject); + regiItemJSONObject.put("register", regItemJsonObject); osw.write(regiItemJSONObject.toJSONString()); osw.flush(); @@ -167,6 +170,14 @@ private void writeItem(OutputStream out, Item item) throws Exception { OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8"); JSONObject regItemJsonObject = createOrderedJSONObject(); + try { + Field changeMap = regItemJsonObject.getClass().getDeclaredField("map"); + changeMap.setAccessible(true); + changeMap.set(regItemJsonObject, new LinkedHashMap<>()); + changeMap.setAccessible(false); + } catch (IllegalAccessException | NoSuchFieldException e) { + } + writeVersions(regItemJsonObject, item); writeLanguage(regItemJsonObject); writeDate(regItemJsonObject, item); @@ -175,24 +186,80 @@ private void writeItem(OutputStream out, Item item) throws Exception { writeIsDefinedBy(regItemJsonObject, item); writeRegistryAndRegister(regItemJsonObject, item); + JSONArray containedItemsJSONArray = new JSONArray(); if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { - JSONObject containedItemsJSON = createOrderedJSONObject(); for (ContainedItem ci : item.getContainedItems()) { - regItemJsonObject.put("value", writeItemShortVersion(containedItemsJSON, ci)); + JSONObject containedItemsJSON = createOrderedJSONObject(); + JSONObject valuecontainedItemsJSON = createOrderedJSONObject(); + if (item.getItemclass().getParentItemClassType().equals("register") + && ci.getItemclass().getId().equals(item.getItemclass().getId())) { + containedItemsJSON.put(item.getItemclass().getParentid(), writeItemShortVersion(valuecontainedItemsJSON, ci, item)); + } else { + containedItemsJSON.put("value", writeItemShortVersion(valuecontainedItemsJSON, ci, item)); + } + containedItemsJSONArray.add(containedItemsJSON); + + if (ci.isHasCollection()) { + if (ci.getContainedItems() != null && !ci.getContainedItems().isEmpty()) { + for (ContainedItem c : ci.getContainedItems()) { + JSONObject containedCollectionItemsJSON = createOrderedJSONObject(); + JSONObject valuecontainedCollectionItemsJSON = createOrderedJSONObject(); + containedCollectionItemsJSON.put("value", writeItemShortVersion(valuecontainedCollectionItemsJSON, c, ci)); + containedItemsJSONArray.add(containedCollectionItemsJSON); + } + } else if (ci.getContainedItemsBeeingParentItemClass() != null && !ci.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem c : ci.getContainedItems()) { + JSONObject containedCollectionItemsJSON = createOrderedJSONObject(); + JSONObject valuecontainedCollectionItemsJSON = createOrderedJSONObject(); + containedCollectionItemsJSON.put("value", writeItemShortVersion(valuecontainedCollectionItemsJSON, c, ci)); + containedItemsJSONArray.add(containedCollectionItemsJSON); + } + } + } + } + } else if (item.getContainedItemsBeeingParentItemClass() != null && !item.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem ci : item.getContainedItemsBeeingParentItemClass()) { + JSONObject containedItemsJSON = createOrderedJSONObject(); + JSONObject valuecontainedItemsJSON = createOrderedJSONObject(); + if (item.getItemclass().getParentItemClassType().equals("register") + && ci.getItemclass().getId().equals(item.getItemclass().getId())) { + containedItemsJSON.put(item.getItemclass().getParentid(), writeItemShortVersion(valuecontainedItemsJSON, ci, item)); + } else { + containedItemsJSON.put("value", writeItemShortVersion(valuecontainedItemsJSON, ci, item)); + } + containedItemsJSONArray.add(containedItemsJSON); if (ci.isHasCollection()) { if (ci.getContainedItems() != null && !ci.getContainedItems().isEmpty()) { for (ContainedItem c : ci.getContainedItems()) { - regItemJsonObject.put("value", writeItemShortVersion(containedItemsJSON, c)); + JSONObject containedCollectionItemsJSON = createOrderedJSONObject(); + JSONObject valuecontainedCollectionItemsJSON = createOrderedJSONObject(); + containedCollectionItemsJSON.put("value", writeItemShortVersion(valuecontainedCollectionItemsJSON, c, ci)); + containedItemsJSONArray.add(containedCollectionItemsJSON); + } + } else if (ci.getContainedItemsBeeingParentItemClass() != null && !ci.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem c : ci.getContainedItems()) { + JSONObject containedCollectionItemsJSON = createOrderedJSONObject(); + JSONObject valuecontainedCollectionItemsJSON = createOrderedJSONObject(); + containedCollectionItemsJSON.put("value", writeItemShortVersion(valuecontainedCollectionItemsJSON, c, ci)); + containedItemsJSONArray.add(containedCollectionItemsJSON); } } } } - regItemJsonObject.put("containeditems", containedItemsJSON); + } + if (containedItemsJSONArray != null && !containedItemsJSONArray.isEmpty()) { + regItemJsonObject.put("containeditems", containedItemsJSONArray); } JSONObject regiItemJSONObject = createOrderedJSONObject(); - regiItemJSONObject.put(item.getItemclass().getId().toLowerCase(), regItemJsonObject); + +// regiItemJSONObject.put(item.getItemclass().getId().toLowerCase(), regItemJsonObject); + if (item.getItemclass().getParentItemClassType().equals("register")) { + regiItemJSONObject.put(item.getItemclass().getParentid(), regItemJsonObject); + } else { + regiItemJSONObject.put("value", regItemJsonObject); + } ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); @@ -203,7 +270,7 @@ private void writeItem(OutputStream out, Item item) throws Exception { osw.close(); } - private JSONObject writeItemShortVersion(JSONObject regItemJsonObject, ContainedItem item) { + private JSONObject writeItemShortVersion(JSONObject regItemJsonObject, ContainedItem item, ContainedItem collectionItem) { writeVersions(regItemJsonObject, item); writeLanguage(regItemJsonObject); writeDate(regItemJsonObject, item); @@ -234,19 +301,22 @@ private void writeVersions(JSONObject regItemJsonObject, ContainedItem item) { regItemJsonObject.put("thisversion", version.getUri()); // regItemJsonObject.put("thisversion", version.getUri() + ":" + version.getNumber()); regItemJsonObject.put("latestversion", item.getUri()); - if (!versionHistory.isEmpty()) { - JSONArray previousversionsArray = new JSONArray(); + if (versionHistory != null && !versionHistory.isEmpty()) { + JSONArray historyversionArray = new JSONArray(); for (VersionInformation versionInformation : versionHistory) { JSONObject versionJson = createOrderedJSONObject(); versionJson.put("version", versionInformation.getUri()); -// versionJson.put("version", versionInformation.getUri() + ":" + versionInformation.getNumber()); - previousversionsArray.put(versionJson); + historyversionArray.add(versionJson); } - regItemJsonObject.put("previousversions", previousversionsArray); + regItemJsonObject.put("historyversion", historyversionArray); } } private void writeFields(JSONObject regItemJsonObject, ContainedItem item) { + // Get configuration properties + final Properties configurationProperties = Configuration.getInstance().getProperties(); + String legacyFlag = configurationProperties.getProperty(BaseConstants.KEY_APPLICATION_LEGACY_FLAG); + List localizedProperties = item.getProperties(); localizedProperties.forEach((localizedProperty) -> { String lang = localizedProperty.getLang(); @@ -254,63 +324,119 @@ private void writeFields(JSONObject regItemJsonObject, ContainedItem item) { String value = localizedProperty.getValues().get(0).getValue(); String href = localizedProperty.getValues().get(0).getHref(); - String fieldName = localizedProperty.getLabel().replace("-item", ""); - String fieldLocalId = localizedProperty.getId(); - - if (fieldName != null && "contactpoint".equals(fieldLocalId.toLowerCase())) { - JSONObject json = createOrderedJSONObject(); - json.put("label", value); - json.put("email", href); - regItemJsonObject.put(fieldName, json); - } else if (fieldName != null && "license".equals(fieldLocalId.toLowerCase())) { - JSONObject json = createOrderedJSONObject(); - json.put("label", value); - json.put("uri", href); - regItemJsonObject.put(fieldName, json); - } else if (fieldName != null && "governance-level".equals(fieldLocalId.toLowerCase())) { - JSONObject labelJson = createOrderedJSONObject(); - labelJson.put("lang", item.getLanguage()); - labelJson.put("text", value); - - JSONObject json = createOrderedJSONObject(); - json.put("label", labelJson); - json.put("uri", href); - - regItemJsonObject.put(fieldName, json); - } else if (fieldName != null && "status".equals(fieldLocalId.toLowerCase())) { - String itemClassName = item.getItemclass().getId(); - JSONObject labelJson = createOrderedJSONObject(); - labelJson.put("lang", item.getLanguage()); - labelJson.put("text", value); - - JSONObject json = createOrderedJSONObject(); - json.put("label", labelJson); - json.put("id", href); - - regItemJsonObject.put(fieldName, json); - } else if (fieldName != null && ("annex".equals(fieldLocalId.toLowerCase()) || "themenumber".equals(fieldLocalId.toLowerCase()))) { - regItemJsonObject.put(fieldLocalId, value); - } else if (!href.isEmpty() && !value.isEmpty()) { - JSONObject labelJson = createOrderedJSONObject(); - labelJson.put("lang", item.getLanguage()); - labelJson.put("text", value); - - JSONObject json = createOrderedJSONObject(); - json.put("label", labelJson); - json.put("uri", href); - - regItemJsonObject.put(fieldName, json); + String fieldName = localizedProperty.getLabel().replace("-item", "").toLowerCase(); + String fieldLocalId = localizedProperty.getId().replace("-item", "").toLowerCase(); + + if (fieldName != null && "successor".equals(fieldLocalId.toLowerCase())) { + JSONArray successorsArray = writeComplexListElement(value, href, lang, fieldLocalId); + regItemJsonObject.put("successors", successorsArray); + } else if (fieldName != null && "predecessor".equals(fieldLocalId.toLowerCase())) { + JSONArray predecessorsArray = writeComplexListElement(value, href, lang, fieldLocalId); + regItemJsonObject.put("predecessors", predecessorsArray); + } else if (fieldName != null && "parent".equals(fieldLocalId.toLowerCase())) { + JSONArray successorsArray = writeComplexListElement(value, href, lang, fieldLocalId); + regItemJsonObject.put("parents", successorsArray); + } else if (!legacyFlag.equals(BaseConstants.KEY_APPLICATION_LEGACY_FLAG_ON)) { + if (!href.isEmpty() && !value.isEmpty()) { + JSONObject labelJson = createOrderedJSONObject(); + labelJson.put("lang", item.getLanguage()); + labelJson.put("text", value); + + JSONObject json = createOrderedJSONObject(); + json.put("label", labelJson); + json.put("id", href); + + regItemJsonObject.put(fieldName, json); + } else { + JSONObject labelJson = createOrderedJSONObject(); + labelJson.put("lang", lang); + labelJson.put("text", value); + + regItemJsonObject.put(fieldLocalId, labelJson); + } } else { - JSONObject labelJson = createOrderedJSONObject(); - labelJson.put("lang", lang); - labelJson.put("text", value); + if (fieldName != null && "contactpoint".equals(fieldLocalId.toLowerCase())) { + JSONObject json = createOrderedJSONObject(); + json.put("label", value); + json.put("email", href); + regItemJsonObject.put(fieldName, json); + } else if (fieldName != null && "license".equals(fieldLocalId.toLowerCase())) { + JSONObject json = createOrderedJSONObject(); + json.put("label", value); + json.put("uri", href); + regItemJsonObject.put(fieldName, json); + } else if (fieldName != null && "governance-level".equals(fieldLocalId.toLowerCase())) { + JSONObject labelJson = createOrderedJSONObject(); + labelJson.put("lang", item.getLanguage()); + labelJson.put("text", value); + + JSONObject json = createOrderedJSONObject(); + json.put("label", labelJson); + json.put("id", href); - regItemJsonObject.put(fieldLocalId, labelJson); + regItemJsonObject.put(fieldName, json); + } else if (fieldName != null && "collection".equals(fieldLocalId.toLowerCase())) { + JSONObject json = createOrderedJSONObject(); + json.put("label", value); + json.put("id", href); + String registerUri = item.getRegister().getUri(); + int index = registerUri.lastIndexOf("/"); + String registerid = registerUri.substring(index + 1); + regItemJsonObject.put(registerid, json); +// regItemJsonObject.put(item.getItemclass().getParentid(), json); + } else if (fieldName != null && "status".equals(fieldLocalId.toLowerCase())) { + String itemClassName = item.getItemclass().getId(); + JSONObject labelJson = createOrderedJSONObject(); + labelJson.put("lang", item.getLanguage()); + labelJson.put("text", value); + + JSONObject json = createOrderedJSONObject(); + json.put("label", labelJson); + json.put("id", href); + + regItemJsonObject.put(fieldName, json); + } else if (fieldName != null && ("annex".equals(fieldLocalId.toLowerCase()) || "themenumber".equals(fieldLocalId.toLowerCase()))) { + regItemJsonObject.put(fieldLocalId, value); + } else if (!href.isEmpty() && !value.isEmpty()) { + JSONObject labelJson = createOrderedJSONObject(); + labelJson.put("lang", item.getLanguage()); + labelJson.put("text", value); + + JSONObject json = createOrderedJSONObject(); + json.put("label", labelJson); + json.put("id", href); + + regItemJsonObject.put(fieldName, json); + } else { + JSONObject labelJson = createOrderedJSONObject(); + labelJson.put("lang", lang); + labelJson.put("text", value); + + regItemJsonObject.put(fieldLocalId, labelJson); + } } } }); } + private JSONArray writeComplexListElement(String value, String href, String lang, String name) { + JSONObject labelJson = createOrderedJSONObject(); + labelJson.put("lang", lang); + labelJson.put("text", value); + + JSONObject successor = createOrderedJSONObject(); + successor.put("label", labelJson); + successor.put("id", href); + + JSONObject json = createOrderedJSONObject(); + json.put(name, successor); + + JSONArray successorsArray = new JSONArray(); + successorsArray.add(json); + + return successorsArray; + } + private void writeItemclass(JSONObject regItemJsonObject, ContainedItem item) { switch (item.getType()) { case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: @@ -331,7 +457,7 @@ private JSONObject getItemClassJsonFromItem(ContainedItem item) { labelJson.put("text", itemClassName); JSONObject json = createOrderedJSONObject(); json.put("label", labelJson); - json.put("uriname", itemClassName); + json.put("id", itemClassName); return json; } @@ -372,16 +498,16 @@ private void writeRegistryAndRegister(JSONObject regItemJsonObject, ContainedIte regItemJsonObject.put("registry", jsonRegistry); break; default: - JSONObject jsonDefaultRegistry = createOrderedJSONObject(); - jsonDefaultRegistry.put("label", writeTitle(item.getRegistry())); - jsonDefaultRegistry.put("id", item.getRegistry().getUri()); - - regItemJsonObject.put("registry", jsonDefaultRegistry); JSONObject jsonDefaultRegister = createOrderedJSONObject(); jsonDefaultRegister.put("label", writeTitle(item.getRegister())); jsonDefaultRegister.put("id", item.getRegister().getUri()); + JSONObject jsonDefaultRegistry = createOrderedJSONObject(); + jsonDefaultRegistry.put("label", writeTitle(item.getRegistry())); + jsonDefaultRegistry.put("id", item.getRegistry().getUri()); + jsonDefaultRegister.put("registry", jsonDefaultRegistry); + regItemJsonObject.put("register", jsonDefaultRegister); break; } @@ -416,4 +542,9 @@ public JSONObject createOrderedJSONObject() { return jsonObject; } + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.java index bf5111e3..a8a2d745 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/JSONInternalFormatter.java @@ -34,6 +34,7 @@ import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; public class JSONInternalFormatter implements Formatter { @@ -59,4 +60,9 @@ public void write(Item item, RegLanguagecode language, OutputStream out) throws OM.writeValue(out, item); } + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.java index 392807c7..5e7f4a64 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RDFFormatter.java @@ -42,6 +42,7 @@ import eu.europa.ec.re3gistry2.javaapi.cache.model.BasicContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; import eu.europa.ec.re3gistry2.javaapi.cache.model.VersionInformation; import eu.europa.ec.re3gistry2.restapi.util.AvailableFormatsUtil; @@ -307,6 +308,10 @@ private void writeItem(XMLStreamWriter xml, ContainedItem item) throws XMLStream for (ContainedItem ci : item.getContainedItems()) { writeItemShortVersion(xml, ci); } + } else if (item.getContainedItemsBeeingParentItemClass() != null && !item.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem ci : item.getContainedItemsBeeingParentItemClass()) { + writeItemShortVersion(xml, ci); + } } } @@ -600,10 +605,14 @@ private void writeAuthorityFrequency(XMLStreamWriter xml, ContainedItem item) th private void writeVersion(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { List versionHistory = item.getVersionHistory(); VersionInformation version = item.getVersion(); - writeEmptyElement(xml, OWL, "sameAs", RDF, "resource", item.getUri() + ":" + (versionHistory.size() + 1)); - writeEmptyElement(xml, ADMS, "last", RDF, "resource", item.getUri()); - if (versionHistory.size() != 0) { - writeEmptyElement(xml, ADMS, "prev", RDF, "resource", item.getUri() + ":" + versionHistory.size()); + if (version!=null && version.getUri()!=null) { + writeEmptyElement(xml, OWL, "sameAs", RDF, "resource", version.getUri()); + writeEmptyElement(xml, ADMS, "last", RDF, "resource", item.getUri()); + } + if (versionHistory != null && !versionHistory.isEmpty()) { + for (VersionInformation versionInformation : versionHistory) { + writeEmptyElement(xml, ADMS, "prev", RDF, "resource", versionInformation.getUri()); + } } } @@ -619,4 +628,9 @@ private void writeRegisterElement(XMLStreamWriter xml, ContainedItem item) throw xml.writeEndElement(); } + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.java index f42b2373..001f0df2 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/RORFormatter.java @@ -38,11 +38,14 @@ import javax.xml.stream.XMLStreamWriter; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.base.utility.Configuration; import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; import eu.europa.ec.re3gistry2.restapi.util.IndentingXMLStreamWriter; +import java.util.Properties; public class RORFormatter implements Formatter { @@ -138,7 +141,6 @@ private void writeRegistry(XMLStreamWriter xml, ContainedItem item) throws XMLSt writeRoles(xml, item); writeAuthorityFrequency(xml, item); - xml.writeEndElement(); // write registers if (item.getContainedItemsBeeingParentItemClass() != null && !item.getContainedItemsBeeingParentItemClass().isEmpty()) { @@ -146,6 +148,7 @@ private void writeRegistry(XMLStreamWriter xml, ContainedItem item) throws XMLSt writeItemShortVersion(xml, ci); } } + xml.writeEndElement(); } private void writeRegister(XMLStreamWriter xml, Item item) throws XMLStreamException { @@ -163,25 +166,24 @@ private void writeRegister(XMLStreamWriter xml, Item item) throws XMLStreamExcep writeRoles(xml, item); writeAuthorityFrequency(xml, item); - xml.writeEndElement(); - if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { for (ContainedItem ci : item.getContainedItems()) { writeItemShortVersion(xml, ci); } } + xml.writeEndElement(); } private void writeItemShortVersion(XMLStreamWriter xml, ContainedItem containedItem) throws XMLStreamException { - xml.writeStartElement(DCAT, "dataset"); + xml.writeStartElement(DCT, "dataset"); xml.writeStartElement(RDF, "Description"); xml.writeAttribute(RDF, "about", containedItem.getUri()); xml.writeStartElement(DCAT, "distribution"); xml.writeAttribute(RDF, "parseType", "Resource"); - writeEmptyElement(xml, RDF, "format", RDF, "resource", "http://publications.europa.eu/resource/authority/file-type/RDF_XML"); - writeEmptyElement(xml, RDF, "downloadURL", RDF, "resource", containedItem.getUri() + "/" + containedItem.getLocalid() + ".ror"); + writeEmptyElement(xml, DCT, "format", RDF, "resource", "http://publications.europa.eu/resource/authority/file-type/RDF_XML"); + writeEmptyElement(xml, DCAT, "downloadURL", RDF, "resource", containedItem.getUri() + "/" + containedItem.getLocalid() + ".ror"); xml.writeEndElement(); xml.writeEndElement(); @@ -204,13 +206,12 @@ private void writeItem(XMLStreamWriter xml, ContainedItem item) throws XMLStream writeAuthorityFrequency(xml, item); writeRoles(xml, item); - xml.writeEndElement(); - if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { for (ContainedItem ci : item.getContainedItems()) { writeItemShortVersion(xml, ci); } } + xml.writeEndElement(); } private void writeRDFType(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { @@ -297,8 +298,12 @@ private void writeDescription(XMLStreamWriter xml, ContainedItem item) throws XM } private void writeRoles(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { - xml.writeStartElement(DCAT, "publisher"); - writeEmptyElement(xml, FOAF, "Agent", RDF, "about", "http://publications.europa.eu/resource/authority/corporate-body/JRC"); + xml.writeStartElement(DCT, "publisher"); + +// writeEmptyElement(xml, FOAF, "Agent", RDF, "about", "http://publications.europa.eu/resource/authority/corporate-body/JRC"); + xml.writeStartElement(FOAF, "Agent"); + xml.writeAttribute(RDF, "about", "http://publications.europa.eu/resource/authority/corporate-body/JRC"); + for (String roleProperty : ROLE_PROPERTY_TO_ELEMENT.keySet()) { Optional p = item.getProperty(roleProperty); if (!p.isPresent()) { @@ -316,6 +321,12 @@ private void writeRoles(XMLStreamWriter xml, ContainedItem item) throws XMLStrea writeSimpleElement(xml, FOAF, "name", NS_XML, "lang", lang, value); } } + final Properties configurationProperties = Configuration.getInstance().getProperties(); + String legacyFlag = configurationProperties.getProperty(BaseConstants.KEY_APPLICATION_LEGACY_FLAG); + if (!legacyFlag.equals(BaseConstants.KEY_APPLICATION_LEGACY_FLAG_ON)) { + writeEmptyElement(xml, FOAF, "mbox", RDF, "resource", "mailto:jrc-inspire-support@ec.europa.eu"); + } + xml.writeEndElement(); xml.writeEndElement(); } @@ -347,7 +358,7 @@ private void writeEmptyElement(XMLStreamWriter xml, String ns, String localName, } private void writeAuthorityFrequency(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { - writeEmptyElement(xml, DCT, "accrualPeriodicity", RDF, "resource", "http://publications.europa.eu/resource/authority/frequency/UNKNOWN"); + writeEmptyElement(xml, DCT, "accrualPeriodicity", RDF, "resource", "http://publications.europa.eu/resource/authority/frequency/MONTHLY"); } private void writeRegistryElement(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { @@ -362,4 +373,9 @@ private void writeRegisterElement(XMLStreamWriter xml, ContainedItem item) throw xml.writeEndElement(); } + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.java index c37a2409..39635cd1 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/XMLFormatter.java @@ -27,11 +27,14 @@ package eu.europa.ec.re3gistry2.restapi.format; import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.base.utility.Configuration; import eu.europa.ec.re3gistry2.model.RegLanguagecode; import eu.europa.ec.re3gistry2.javaapi.cache.model.ContainedItem; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemRef; import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedProperty; +import eu.europa.ec.re3gistry2.javaapi.cache.model.LocalizedPropertyValue; import eu.europa.ec.re3gistry2.javaapi.cache.model.VersionInformation; import eu.europa.ec.re3gistry2.restapi.util.IndentingXMLStreamWriter; import java.io.OutputStream; @@ -39,6 +42,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; +import java.util.Properties; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; @@ -79,26 +83,49 @@ private XMLStreamWriter getXMLWriter(OutputStream out, Item item) throws XMLStre XMLStreamWriter xml = XMLFactory.XOF.createXMLStreamWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); xml = new IndentingXMLStreamWriter(xml, XMLFactory.NEW_LINE, XMLFactory.INDENT); - String itemclassLocalId; - if (item.isHasCollection()) { - itemclassLocalId = "value"; - } else { - itemclassLocalId = item.getItemclass().getId().toLowerCase(); - } - xml.writeStartDocument(); - xml.writeStartElement(itemclassLocalId); - xml.writeDefaultNamespace(item.getUri()); + + // Get configuration properties + final Properties configurationProperties = Configuration.getInstance().getProperties(); + String schemaPath = configurationProperties.getProperty(BaseConstants.KEY_APPLICATION_SCHEMA_PATH) + "/"; + + switch (item.getType()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + xml.writeStartElement("registry"); + xml.writeDefaultNamespace(item.getUri()); + + String schemaLocationRegistry = item.getUri() + " " + item.getUri() + schemaPath + "registry.xsd"; + xml.writeAttribute("xsi", XSISCHEMALOCATION, "schemaLocation", schemaLocationRegistry); + + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + xml.writeStartElement("register"); + xml.writeDefaultNamespace(item.getUri() + "_register"); + + String schemaLocationRegister = item.getUri() + "_register " + item.getRegistry().getUri() + schemaPath + item.getItemclass().getId() + "_register.xsd"; + xml.writeAttribute("xsi", XSISCHEMALOCATION, "schemaLocation", schemaLocationRegister); + + break; + default: + String itemclassLocalId = item.getItemclass().getId(); + String startElement; + + if (item.getItemclass().getParentItemClassType().equals("register")) { + startElement = item.getItemclass().getParentid(); + } else { + startElement = "value"; + } + xml.writeStartElement(startElement); + xml.writeDefaultNamespace(item.getRegister().getUri() + "_register/" + itemclassLocalId); + + String schemaLocationItem = item.getRegister().getUri() + "_register/" + itemclassLocalId + " " + item.getRegistry().getUri() + schemaPath + itemclassLocalId + ".xsd"; + xml.writeAttribute("xsi", XSISCHEMALOCATION, "schemaLocation", schemaLocationItem); + + break; + } xml.writeAttribute("xmlns", XSISCHEMALOCATION, "xsi", XSISCHEMALOCATION); -// xml.writeAttribute(item.getItemclass().getId(), "xmlns", item.getUri()); -// xml.writeAttribute("xmlns", item.getUri()); -// xml.writeAttribute(NS_XML, "xmlns", item.getUri()); - String schemaLocation = "http://inspire.ec.europa.eu/registry http://inspire.ec.europa.eu/draft-schemas/registry/2.0/"+item.getType()+".xsd"; - xml.writeAttribute("xsi", XSISCHEMALOCATION, "schemaLocation", schemaLocation); -// xml.writeAttribute(item.getItemclass().getId(), "id", item.getUri()); xml.writeAttribute("id", item.getUri()); -// xml.writeAttribute(NS_XML, "id", item.getUri()); return xml; } @@ -108,7 +135,7 @@ private void writeRegistry(OutputStream out, Item item) throws XMLStreamExceptio writeLanguage(xml); writeDate(xml, item); - writeVersions(xml, item); +// writeVersions(xml, item); writeFields(xml, item); if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { @@ -126,8 +153,9 @@ private void writeRegistry(OutputStream out, Item item) throws XMLStreamExceptio private void writeRegisterShortVersion(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { xml.writeStartElement("register"); xml.writeAttribute("id", item.getUri()); -// xml.writeAttribute(NS_XML, "id", item.getUri()); + writeLanguage(xml); + writeDate(xml, item); writeFields(xml, item); writeRegistryAndRegister(xml, item); @@ -139,14 +167,13 @@ private void writeRegister(OutputStream out, Item item) throws XMLStreamExceptio writeLanguage(xml); writeDate(xml, item); - writeVersions(xml, item); writeFields(xml, item); writeRegistryAndRegister(xml, item); if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { xml.writeStartElement("containeditems"); for (ContainedItem ci : item.getContainedItems()) { - writeItemShortVersion(xml, ci, ci.getItemclass().getId()); + writeItemShortVersion(xml, ci, ci.getItemclass().getId(), ci); } xml.writeEndElement(); } @@ -155,16 +182,17 @@ private void writeRegister(OutputStream out, Item item) throws XMLStreamExceptio xml.close(); } - private void writeItemShortVersion(XMLStreamWriter xml, ContainedItem item, String mainElementName) throws XMLStreamException { + private void writeItemShortVersion(XMLStreamWriter xml, ContainedItem item, String mainElementName, ContainedItem collectionItem) throws XMLStreamException { xml.writeStartElement(mainElementName.toLowerCase()); xml.writeAttribute("id", item.getUri()); // xml.writeAttribute(NS_XML, "id", item.getUri()); writeLanguage(xml); + writeDate(xml, item); writeVersions(xml, item); writeFields(xml, item); writeItemclass(xml, item); - writeIsDefinedBy(xml, item); +// writeIsDefinedBy(xml, item); writeRegistryAndRegister(xml, item); xml.writeEndElement(); @@ -172,24 +200,58 @@ private void writeItemShortVersion(XMLStreamWriter xml, ContainedItem item, Stri private void writeItem(OutputStream out, Item item) throws XMLStreamException { XMLStreamWriter xml = getXMLWriter(out, item); - writeLanguage(xml); writeDate(xml, item); writeVersions(xml, item); + writeFields(xml, item); writeItemclass(xml, item); - writeIsDefinedBy(xml, item); +// writeIsDefinedBy(xml, item); writeRegistryAndRegister(xml, item); if (item.getContainedItems() != null && !item.getContainedItems().isEmpty()) { + xml.writeStartElement("containeditems"); for (ContainedItem ci : item.getContainedItems()) { - writeItemShortVersion(xml, ci, "value"); + if (item.getItemclass().getParentItemClassType().equals("register") + && ci.getItemclass().getId().equals(item.getItemclass().getId())) { + writeItemShortVersion(xml, ci, item.getItemclass().getParentid(), item); + } else { + writeItemShortVersion(xml, ci, "value", item); + } if (ci.isHasCollection()) { if (ci.getContainedItems() != null && !ci.getContainedItems().isEmpty()) { for (ContainedItem c : ci.getContainedItems()) { - writeItemShortVersion(xml, c, "value"); + writeItemShortVersion(xml, c, "value", item); + } + } else if (ci.getContainedItemsBeeingParentItemClass() != null && !ci.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem c : ci.getContainedItemsBeeingParentItemClass()) { + writeItemShortVersion(xml, c, "value", item); + } + } + } + } + xml.writeEndElement(); + } else if (item.getContainedItemsBeeingParentItemClass() != null && !item.getContainedItemsBeeingParentItemClass().isEmpty()) { + + xml.writeStartElement("containeditems"); + for (ContainedItem ci : item.getContainedItemsBeeingParentItemClass()) { + if (item.getItemclass().getParentItemClassType().equals("register") + && ci.getItemclass().getId().equals(item.getItemclass().getId())) { + writeItemShortVersion(xml, ci, item.getItemclass().getParentid(), item); + } else { + writeItemShortVersion(xml, ci, "value", item); + } + + if (ci.isHasCollection()) { + if (ci.getContainedItemsBeeingParentItemClass() != null && !ci.getContainedItemsBeeingParentItemClass().isEmpty()) { + for (ContainedItem c : ci.getContainedItemsBeeingParentItemClass()) { + writeItemShortVersion(xml, c, "value", item); + } + } else if (ci.getContainedItems() != null && !ci.getContainedItems().isEmpty()) { + for (ContainedItem c : ci.getContainedItems()) { + writeItemShortVersion(xml, c, "value", item); } } } @@ -198,19 +260,24 @@ private void writeItem(OutputStream out, Item item) throws XMLStreamException { } xml.writeEndDocument(); + xml.close(); } private void writeVersions(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { List versionHistory = item.getVersionHistory(); VersionInformation version = item.getVersion(); - - writeEmptyElement(xml, "thisversion", version.getUri()); - writeEmptyElement(xml, "latestversion", item.getUri()); - if (!versionHistory.isEmpty()) { + if(version!=null && version.getUri()!=null) { + writeEmptyElement(xml, "thisversion", version.getUri()); + } + if(item!=null && item.getUri()!=null) { + writeEmptyElement(xml, "latestversion", item.getUri()); + } + if (versionHistory != null && !versionHistory.isEmpty()) { xml.writeStartElement("previousversions"); for (VersionInformation versionInformation : versionHistory) { - writeEmptyElement(xml, "version", versionInformation.getUri() + ":" + versionInformation.getNumber()); + writeEmptyElement(xml, "version", versionInformation.getUri()); +// writeEmptyElement(xml, "version", versionInformation.getUri() + ":" + versionInformation.getNumber()); } xml.writeEndElement(); } @@ -229,6 +296,10 @@ private void writeDate(XMLStreamWriter xml, ContainedItem item) throws XMLStream } private void writeFields(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { + // Get configuration properties + final Properties configurationProperties = Configuration.getInstance().getProperties(); + String legacyFlag = configurationProperties.getProperty(BaseConstants.KEY_APPLICATION_LEGACY_FLAG); + List localizedProperties = item.getProperties(); for (LocalizedProperty localizedProperty : localizedProperties) { @@ -237,56 +308,88 @@ private void writeFields(XMLStreamWriter xml, ContainedItem item) throws XMLStre String value = localizedProperty.getValues().get(0).getValue(); String href = localizedProperty.getValues().get(0).getHref(); - String fieldName = localizedProperty.getLabel().replace("-item", ""); - String fieldLocalId = localizedProperty.getId(); -// String localNameLowerCase = fieldName.toLowerCase(); - - if (fieldName != null && "contactpoint".equals(fieldLocalId.toLowerCase())) { - xml.writeStartElement("contactPoint"); - writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); - writeEmptyElement(xml, "email", href); - xml.writeEndElement(); - } else if (fieldName != null && "license".equals(fieldLocalId.toLowerCase())) { - xml.writeStartElement("license"); - writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); - writeEmptyElement(xml, "uri", href); - xml.writeEndElement(); - } else if (fieldName != null && "governance-level".equals(fieldLocalId.toLowerCase())) { - xml.writeStartElement("governance-level"); - writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); - writeEmptyElement(xml, "uri", href); - xml.writeEndElement(); - } else if (fieldName != null && "collection".equals(fieldLocalId.toLowerCase())) { - xml.writeStartElement("collection"); - writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); - writeEmptyElement(xml, "uri", href); - xml.writeEndElement(); - } else if (fieldName != null && "successor".equals(fieldLocalId.toLowerCase())) { - xml.writeStartElement("successor"); - writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); - writeEmptyElement(xml, "uri", href); - xml.writeEndElement(); - } else if (fieldName != null && "predecessor".equals(fieldLocalId.toLowerCase())) { - xml.writeStartElement("predecessor"); - writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); - writeEmptyElement(xml, "uri", href); - xml.writeEndElement(); - } else if (fieldName != null && "status".equals(fieldLocalId.toLowerCase())) { - xml.writeStartElement(fieldLocalId); - xml.writeAttribute("id", href); -// xml.writeAttribute(NS_XML, "id", href); - writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); - xml.writeEndElement(); - } else if (fieldName != null && ("annex".equals(fieldLocalId.toLowerCase()) || "themenumber".equals(fieldLocalId.toLowerCase()))) { - writeEmptyElement(xml, fieldLocalId, value); + String fieldName = localizedProperty.getLabel().replace("-item", "").toLowerCase(); + String fieldLocalId = localizedProperty.getId().replace("-item", "").toLowerCase(); + + if (fieldName != null && "successor".equals(fieldLocalId)) { + writeComplexListElement(xml, localizedProperty, lang, "successors", fieldLocalId); + } else if (fieldName != null && "predecessor".equals(fieldLocalId)) { + writeComplexListElement(xml, localizedProperty, lang, "predecessors", fieldLocalId); + } else if (fieldName != null && "parent".equals(fieldLocalId)) { + writeComplexListElement(xml, localizedProperty, lang, "parents", fieldLocalId); + } else if (!legacyFlag.equals(BaseConstants.KEY_APPLICATION_LEGACY_FLAG_ON)) { + if (href != null && !href.isEmpty()) { + xml.writeStartElement(fieldLocalId); + xml.writeAttribute("id", href); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); + xml.writeEndElement(); + } else { + writeSimpleElementWithAttribute(xml, fieldLocalId, NS_XML, "lang", lang, value); + } } else { - writeSimpleElementWithAttribute(xml, fieldLocalId, NS_XML, "lang", lang, value); + if (fieldName != null && "contactpoint".equals(fieldLocalId)) { + xml.writeStartElement("contactpoint"); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); + writeEmptyElement(xml, "email", href); + xml.writeEndElement(); + } else if (fieldName != null && "license".equals(fieldLocalId)) { + xml.writeStartElement("license"); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); + writeEmptyElement(xml, "uri", href); + xml.writeEndElement(); + } else if (fieldName != null && "governance-level".equals(fieldLocalId)) { + xml.writeStartElement("governance-level"); + xml.writeAttribute("id", href); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); + xml.writeEndElement(); + } else if (fieldName != null && "collection".equals(fieldLocalId)) { + String registerUri = item.getRegister().getUri(); + int index = registerUri.lastIndexOf("/"); + String registerid = registerUri.substring(index + 1); + xml.writeStartElement(registerid); +// xml.writeStartElement(item.getItemclass().getParentid()); + xml.writeAttribute("id", href); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); + xml.writeEndElement(); + } else if (fieldName != null && "status".equals(fieldLocalId)) { + xml.writeStartElement(fieldLocalId); + xml.writeAttribute("id", href); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); + xml.writeEndElement(); + } else if (fieldName != null && ("annex".equals(fieldLocalId) || "themenumber".equals(fieldLocalId))) { + writeEmptyElement(xml, fieldLocalId, value); + } else if (href != null && !href.isEmpty()) { + xml.writeStartElement(fieldLocalId); + xml.writeAttribute("id", href); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, value); + xml.writeEndElement(); + } else { + writeSimpleElementWithAttribute(xml, fieldLocalId, NS_XML, "lang", lang, value); + } } } } } + private void writeComplexListElement(XMLStreamWriter xml, LocalizedProperty localizedProperty, String lang, String mainElement, String secondElement) throws XMLStreamException { + xml.writeStartElement(mainElement); + + List valuesListsuccessors = localizedProperty.getValues(); + if (valuesListsuccessors != null && !valuesListsuccessors.isEmpty()) { + for (LocalizedPropertyValue localizedPropertyValue : valuesListsuccessors) { + String valueSuccessor = localizedPropertyValue.getValue(); + String hrefSuccessor = localizedPropertyValue.getHref(); + + xml.writeStartElement(secondElement); + xml.writeAttribute("id", hrefSuccessor); + writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", lang, valueSuccessor); + xml.writeEndElement(); + } + } + xml.writeEndElement(); + } + private void writeIsDefinedBy(XMLStreamWriter xml, ContainedItem item) throws XMLStreamException { switch (item.getType()) { case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: @@ -298,8 +401,8 @@ private void writeIsDefinedBy(XMLStreamWriter xml, ContainedItem item) throws XM if (referenceList != null) { for (ContainedItem containedItem : referenceList) { xml.writeStartElement(containedItem.getItemclass().getId().toLowerCase().replace("-item", "")); + xml.writeAttribute("id", containedItem.getUri()); writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", containedItem.getLanguage(), containedItem.getLocalid()); - writeEmptyElement(xml, "uri", containedItem.getUri()); xml.writeEndElement(); } } @@ -332,19 +435,16 @@ private void writeRegistryAndRegister(XMLStreamWriter xml, ContainedItem item) t case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: xml.writeStartElement("registry"); xml.writeAttribute("id", item.getRegistry().getUri()); -// xml.writeAttribute(NS_XML, "id", item.getRegistry().getUri()); writeSimpleElementWithAttribute(xml, "label", NS_XML, "lang", item.getLanguage(), item.getRegistry().getUri()); xml.writeEndElement(); break; default: - xml.writeStartElement("registry"); - xml.writeAttribute("id", item.getRegistry().getUri()); -// xml.writeAttribute(NS_XML, "id", item.getRegistry().getUri()); - writeTitle(xml, item.getRegistry()); - xml.writeStartElement("register"); xml.writeAttribute("id", item.getRegister().getUri()); -// xml.writeAttribute(NS_XML, "id", item.getRegister().getUri()); + writeTitle(xml, item.getRegister()); + + xml.writeStartElement("registry"); + xml.writeAttribute("id", item.getRegistry().getUri()); writeTitle(xml, item.getRegistry()); xml.writeEndElement(); @@ -373,10 +473,16 @@ private void writeEmptyElement(XMLStreamWriter xml, String localName, String val private void writeSimpleElementWithAttribute(XMLStreamWriter xml, String localName, String nsAttr, String attrKey, String attrValue, String value) throws XMLStreamException { xml.writeStartElement(localName); - xml.writeAttribute(attrKey, attrValue); + xml.writeAttribute("xml", attrValue, "lang", attrValue); +// xml.writeAttribute(attrKey, attrValue); // xml.writeAttribute(nsAttr, attrKey, attrValue); xml.writeCharacters(value); xml.writeEndElement(); } + @Override + public void write(ItemClass itemClass, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/XSDFormatter.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/XSDFormatter.java new file mode 100644 index 00000000..449f1e73 --- /dev/null +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/format/XSDFormatter.java @@ -0,0 +1,654 @@ +/* + * /* + * * Copyright 2007,2016 EUROPEAN UNION + * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * * the European Commission - subsequent versions of the EUPL (the "Licence"); + * * You may not use this work except in compliance with the Licence. + * * You may obtain a copy of the Licence at: + * * + * * https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the Licence is distributed on an "AS IS" basis, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the Licence for the specific language governing permissions and + * * limitations under the Licence. + * * + * * Date: 2020/05/11 + * * Authors: + * * European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu + * * National Land Survey of Finland, SDI Services - inspire@nls.fi + * * + * * This work was supported by the Interoperability solutions for public + * * administrations, businesses and citizens programme (http://ec.europa.eu/isa2) + * * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) + * * for e-Government (ELISE) + */ +package eu.europa.ec.re3gistry2.restapi.format; + +import eu.europa.ec.re3gistry2.base.utility.BaseConstants; +import eu.europa.ec.re3gistry2.base.utility.Configuration; +import eu.europa.ec.re3gistry2.model.RegLanguagecode; +import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; +import eu.europa.ec.re3gistry2.restapi.util.IndentingXMLStreamWriter; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +public class XSDFormatter implements Formatter { + + private static final String XSISCHEMALOCATION = "http://www.w3.org/2001/XMLSchema"; + private static final String NS_XML = "http://www.w3.org/XML/1998/namespace"; + private RegLanguagecode languageFile; + + public String getFormatName() { + return "xsd"; + } + + public String getContentType() { + return "text/xsd"; + } + + public void write(ItemClass itemClass, OutputStream out) throws Exception { + switch (itemClass.getType()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + writeRegistry(out, itemClass); + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + writeRegister(out, itemClass); + break; + default: + writeItem(out, itemClass); + break; + } + } + + private XMLStreamWriter getXMLWriter(OutputStream out, ItemClass itemClass) throws XMLStreamException { + XMLStreamWriter xml = XMLFactory.XOF.createXMLStreamWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); + xml = new IndentingXMLStreamWriter(xml, XMLFactory.NEW_LINE, XMLFactory.INDENT); + + xml.writeStartDocument(); + switch (itemClass.getType()) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + xml.writeStartElement("xs:schema"); + xml.writeDefaultNamespace(itemClass.getBaseuri() + "/registry"); + xml.writeAttribute("targetNamespace", itemClass.getBaseuri() + "/registry"); + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + xml.writeStartElement("xs:schema"); + xml.writeDefaultNamespace(itemClass.getBaseuri() + "/" + itemClass.getId() + "_register"); + xml.writeAttribute("targetNamespace", itemClass.getBaseuri() + "/" + itemClass.getId() + "_register"); + break; + default: + String itemclassLocalId = itemClass.getId(); + xml.writeStartElement("xs:schema"); + xml.writeDefaultNamespace(itemClass.getBaseuri() + "/" + itemClass.getRegisterLocalId() + "_register/" + itemclassLocalId); + xml.writeAttribute("targetNamespace", itemClass.getBaseuri() + "/" + itemClass.getRegisterLocalId() + "_register/" + itemclassLocalId); + break; + } + + xml.writeAttribute("xmlns", XSISCHEMALOCATION, "xs", XSISCHEMALOCATION); + xml.writeAttribute("elementFormDefault", "qualified"); + xml.writeAttribute("attributeFormDefault", "unqualified"); + xml.writeAttribute("version", "1.0"); + + return xml; + } + + private void writeRegistry(OutputStream out, ItemClass itemClass) throws XMLStreamException { + XMLStreamWriter xml = getXMLWriter(out, itemClass); + + writeImport(xml); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", itemClass.getStartElement()); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeLanguage(xml); + writeDate(xml); +// writeVersions(xml); + writeFields(xml, itemClass.getType(), null, itemClass.getFields()); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "registers"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:sequence"); + writeRegisterWitoutContainedItems(xml, itemClass); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", "id"); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndDocument(); + xml.close(); + } + + private void writeRegisterWitoutContainedItems(XMLStreamWriter xml, ItemClass itemClass) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "register"); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "unbounded"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeLanguage(xml); + writeDate(xml); +// writeVersions(xml); + writeFields(xml, itemClass.getChildItemClassType(), null, itemClass.getContainedItemsFields()); + writeRegistryAndRegister(xml, itemClass.getChildItemClassType()); + + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", "id"); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + } + + private void writeRegister(OutputStream out, ItemClass itemClass) throws XMLStreamException { + XMLStreamWriter xml = getXMLWriter(out, itemClass); + + writeImport(xml); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", itemClass.getStartElement()); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeLanguage(xml); + writeDate(xml); +// writeVersions(xml); + writeFields(xml, itemClass.getType(), null, itemClass.getFields()); + writeRegistryAndRegister(xml, itemClass.getType()); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "containeditems"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:sequence"); + writeItemChildren(xml, itemClass, itemClass.getChildItemClassLocalId().toLowerCase()); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", "id"); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndDocument(); + xml.close(); + } + + private void writeItem(OutputStream out, ItemClass itemClass) throws XMLStreamException { + XMLStreamWriter xml = getXMLWriter(out, itemClass); + + writeImport(xml); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", itemClass.getStartElement()); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeLanguage(xml); + writeDate(xml); + writeVersions(xml); + writeFields(xml, itemClass.getType(), itemClass.getRegisterLocalId(), itemClass.getFields()); + writeItemclass(xml, itemClass.getType()); + writeRegistryAndRegister(xml, itemClass.getType()); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "containeditems"); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "1"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:sequence"); + if (!itemClass.getStartElement().equals("value")) { + writeItemSameItemClass(xml, itemClass); + } + writeItemChildren(xml, itemClass, "value"); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", "id"); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndDocument(); + + xml.close(); + } + + private void writeItemSameItemClass(XMLStreamWriter xml, ItemClass itemClass) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", itemClass.getStartElement()); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "unbounded"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeLanguage(xml); + writeDate(xml); + writeVersions(xml); + writeFields(xml, itemClass.getType(), itemClass.getRegisterLocalId(), itemClass.getFields()); + writeItemclass(xml, itemClass.getType()); + writeRegistryAndRegister(xml, itemClass.getType()); + + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", "id"); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + } + + private void writeItemChildren(XMLStreamWriter xml, ItemClass itemClass, String name) throws XMLStreamException { + if (itemClass.getChildItemClassType() != null && !itemClass.getContainedItemsFields().isEmpty()) { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", name); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "unbounded"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeLanguage(xml); + writeDate(xml); + writeVersions(xml); + String registerlocalid; + if (itemClass.getRegisterLocalId()!=null && !itemClass.getRegisterLocalId().isEmpty()) { + registerlocalid = itemClass.getRegisterLocalId(); + } else { + registerlocalid = itemClass.getStartElement(); + } + writeFields(xml, itemClass.getChildItemClassType(), registerlocalid, itemClass.getContainedItemsFields()); + writeItemclass(xml, itemClass.getChildItemClassType()); + writeRegistryAndRegister(xml, itemClass.getChildItemClassType()); + + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", "id"); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + } + } + + private void writeVersions(XMLStreamWriter xml) throws XMLStreamException { + writeEmptyElement(xml, "thisversion"); + writeEmptyElement(xml, "latestversion"); + createPreviousVersionsElement(xml, "previousversions", 0, 1, "version"); + } + + private void writeLanguage(XMLStreamWriter xml) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "language"); + xml.writeAttribute("type", "xs:string"); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "1"); + xml.writeEndElement(); + } + + private void writeDate(XMLStreamWriter xml) throws XMLStreamException { + writeEmptyElement(xml, "created"); + writeEmptyElement(xml, "issued"); + } + + private void writeFields(XMLStreamWriter xml, String itemClassType, String registerLocalId, HashMap localizedProperties) throws XMLStreamException { + // Get configuration properties + final Properties configurationProperties = Configuration.getInstance().getProperties(); + String legacyFlag = configurationProperties.getProperty(BaseConstants.KEY_APPLICATION_LEGACY_FLAG); + + for (Map.Entry entry : localizedProperties.entrySet()) { + String fieldLocalId = entry.getKey().replace("-item", "").toLowerCase(); + String fieldType = entry.getValue(); + + if (fieldLocalId != null + && ("registry".equals(fieldLocalId) || "register".equals(fieldLocalId) || itemClassType.equals(fieldLocalId))) { + continue; + } + + if (!legacyFlag.equals(BaseConstants.KEY_APPLICATION_LEGACY_FLAG_ON)) { + if (fieldLocalId != null && "successor".equals(fieldLocalId)) { + writeComplexeListElements(xml, "successors", 0, 1, "successor"); + } else if (fieldLocalId != null && "predecessor".equals(fieldLocalId)) { + writeComplexeListElements(xml, "predecessors", 0, 1, "predecessor"); + } else if (fieldLocalId != null && "parent".equals(fieldLocalId)) { + writeComplexeListElements(xml, "parents", 0, 1, "parent"); + } else { + writeSimpleElementWithLanguageAndOptionalId(xml, fieldLocalId, "id", "0", "1"); + } + } else { + if (fieldLocalId != null && "contactpoint".equals(fieldLocalId)) { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "contactpoint"); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "1"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeSimpleElementWithLanguage(xml, "label", "0", "1"); + writeSimpleElementWitoutLanguage(xml, "email", "0", "1"); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + } else if (fieldLocalId != null && "license".equals(fieldLocalId)) { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "license"); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "1"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeSimpleElementWithLanguage(xml, "label", "0", "1"); + writeSimpleElementWitoutLanguage(xml, "uri", "0", "1"); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + } else if (fieldLocalId != null && "collection".equals(fieldLocalId)) { + writeComplexeElementWithElementAndattribute(xml, registerLocalId, "id", "0", "1"); + } else if (fieldLocalId != null && "successor".equals(fieldLocalId)) { + writeComplexeListElements(xml, "successors", 0, 1, "successor"); + } else if (fieldLocalId != null && "predecessor".equals(fieldLocalId)) { + writeComplexeListElements(xml, "predecessors", 0, 1, "predecessor"); + } else if (fieldLocalId != null && "parent".equals(fieldLocalId)) { + writeComplexeListElements(xml, "parents", 0, 1, "parent"); + } else if (fieldLocalId != null + && ("status".equals(fieldLocalId) + || fieldType.equals(BaseConstants.KEY_FIELD_TYPE_RELATION) + || "referencelink".equals(fieldLocalId) + || "extensibility".equals(fieldLocalId) + || "governance-level".equals(fieldLocalId) + || "legislationuri".equals(fieldLocalId))) { + writeComplexeElementWithElementAndattribute(xml, fieldLocalId, "id", "0", "1"); + } else if (fieldLocalId != null && ("sourcedefinition".equals(fieldLocalId) || "eioneturi".equals(fieldLocalId))) { + writeSimpleElementWithLanguageAndOptionalId(xml, fieldLocalId, "id", "0", "1"); + } else if (fieldLocalId != null && ("annex".equals(fieldLocalId) || "themenumber".equals(fieldLocalId))) { + writeEmptyElement(xml, fieldLocalId); + } else { + writeSimpleElementWithLanguage(xml, fieldLocalId, "0", "1"); + } + } + } + } + + private void writeItemclass(XMLStreamWriter xml, String itemclassType) throws XMLStreamException { + switch (itemclassType) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + break; + default: + writeComplexeElementWithElementAndattribute(xml, "itemclass", "uriname", "1", "1"); + break; + } + } + + private void writeRegistryAndRegister(XMLStreamWriter xml, String itemClassType) throws XMLStreamException { + if (itemClassType != null) { + switch (itemClassType) { + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTRY: + break; + case BaseConstants.KEY_ITEMCLASS_TYPE_REGISTER: + writeComplexeElementWithElementAndattribute(xml, "registry", "id", "1", "1"); + break; + default: + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "register"); + xml.writeAttribute("minOccurs", "1"); + xml.writeAttribute("maxOccurs", "1"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + writeSimpleElementWithLanguage(xml, "label", "1", "1"); + writeComplexeElementWithElementAndattribute(xml, "registry", "id", "1", "1"); + + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", "id"); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + break; + } + } + } + + private void writeEmptyElement(XMLStreamWriter xml, String localName) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", localName); + xml.writeAttribute("type", "xs:string"); + xml.writeAttribute("minOccurs", "0"); + xml.writeAttribute("maxOccurs", "1"); + xml.writeEndElement(); + } + + private void writeImport(XMLStreamWriter xml) throws XMLStreamException { +// + xml.writeStartElement("xs:import"); + xml.writeAttribute("namespace", NS_XML); + xml.writeAttribute("schemaLocation", "http://www.w3.org/2001/xml.xsd"); + xml.writeEndElement(); + } + + private void createPreviousVersionsElement(XMLStreamWriter xml, String elementName, int minOccurs, int maxOccurs, String name) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", elementName); + xml.writeAttribute("minOccurs", String.valueOf(minOccurs)); + xml.writeAttribute("maxOccurs", String.valueOf(maxOccurs)); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:sequence"); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", name); + xml.writeAttribute("type", "xs:string"); + xml.writeAttribute("maxOccurs", "unbounded"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + } + + private void writeSimpleElementWithLanguage(XMLStreamWriter xml, String elementName, String minOccurs, String maxOccurs) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", elementName); + xml.writeAttribute("minOccurs", minOccurs); + xml.writeAttribute("maxOccurs", maxOccurs); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:simpleContent"); + + xml.writeStartElement("xs:extension"); + xml.writeAttribute("base", "xs:string"); + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("ref", "xml:lang"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + } + + private void writeSimpleElementWitoutLanguage(XMLStreamWriter xml, String elementName, String minOccurs, String maxOccurs) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", elementName); + xml.writeAttribute("minOccurs", minOccurs); + xml.writeAttribute("maxOccurs", maxOccurs); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:simpleContent"); + + xml.writeStartElement("xs:extension"); + xml.writeAttribute("base", "xs:string"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + } + + private void writeComplexeElementWithElementAndattribute(XMLStreamWriter xml, String elementName, String attributeName, String minOccurs, String maxOccurs) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", elementName); + xml.writeAttribute("minOccurs", minOccurs); + xml.writeAttribute("maxOccurs", maxOccurs); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:all"); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "label"); + xml.writeAttribute("minOccurs", "1"); + xml.writeAttribute("maxOccurs", "1"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:simpleContent"); + xml.writeStartElement("xs:extension"); + xml.writeAttribute("base", "xs:string"); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("ref", "xml:lang"); + xml.writeAttribute("use", "required"); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", attributeName); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "required"); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + } + + private void writeSimpleElementWithLanguageAndOptionalId(XMLStreamWriter xml, String elementName, String attributeName, String minOccurs, String maxOccurs) throws XMLStreamException { +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", elementName); + xml.writeAttribute("minOccurs", minOccurs); + xml.writeAttribute("maxOccurs", maxOccurs); + xml.writeStartElement("xs:complexType"); + xml.writeAttribute("mixed", "true"); + xml.writeStartElement("xs:sequence"); + + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", "label"); + xml.writeAttribute("minOccurs", "0"); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:simpleContent"); + xml.writeStartElement("xs:extension"); + xml.writeAttribute("base", "xs:string"); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("ref", "xml:lang"); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("name", attributeName); + xml.writeAttribute("type", "xs:anyURI"); + xml.writeAttribute("use", "optional"); + xml.writeEndElement(); + + xml.writeStartElement("xs:attribute"); + xml.writeAttribute("ref", "xml:lang"); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndElement(); + } + + private void writeComplexeListElements(XMLStreamWriter xml, String elementName, int minOccurs, int maxOccurs, String secondElement) throws XMLStreamException { + xml.writeStartElement("xs:element"); + xml.writeAttribute("name", elementName); + xml.writeAttribute("minOccurs", String.valueOf(minOccurs)); + xml.writeAttribute("maxOccurs", String.valueOf(maxOccurs)); + xml.writeStartElement("xs:complexType"); + xml.writeStartElement("xs:sequence"); + + writeComplexeElementWithElementAndattribute(xml, secondElement, "id", "0", "unbounded"); + + xml.writeEndElement(); + xml.writeEndElement(); + xml.writeEndElement(); + } + + @Override + public void write(Item item, RegLanguagecode lang, OutputStream out) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ApiError.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ApiError.java similarity index 94% rename from sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ApiError.java rename to sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ApiError.java index 39f8a084..5b7d9f35 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ApiError.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ApiError.java @@ -24,7 +24,7 @@ * * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) * * for e-Government (ELISE) */ -package eu.europa.ec.re3gistry2.restapi; +package eu.europa.ec.re3gistry2.restapi.util; import com.fasterxml.jackson.annotation.JsonFormat; @@ -32,6 +32,7 @@ public enum ApiError { UUID_URI_REQUIRED(400, "bad-request", "Either uri or uuid query parameter required"), + ITEMCLASS_REQUIRED(404, "not-found", "The requested itemclass is not available."), NOT_FOUND(404, "not-found", "Element not found"), VERSION_NOT_FOUND(404, "version-not-found", "Element with specified version not found"), FORMAT_NOT_SUPPORTED(406, "unknown-format", "The requested media type is not supported"), diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ApiResponse.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ApiResponse.java similarity index 97% rename from sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ApiResponse.java rename to sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ApiResponse.java index 929a6037..0531534c 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/ApiResponse.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ApiResponse.java @@ -24,7 +24,7 @@ * * through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) * * for e-Government (ELISE) */ -package eu.europa.ec.re3gistry2.restapi; +package eu.europa.ec.re3gistry2.restapi.util; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.java b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.java index a8a3d92d..bbc89452 100644 --- a/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.java +++ b/sources/Re3gistry2RestAPI/src/main/java/eu/europa/ec/re3gistry2/restapi/util/ResponseUtil.java @@ -33,11 +33,11 @@ import javax.servlet.http.HttpServletResponse; import eu.europa.ec.re3gistry2.model.RegLanguagecode; -import eu.europa.ec.re3gistry2.restapi.ApiError; -import eu.europa.ec.re3gistry2.restapi.ApiResponse; import eu.europa.ec.re3gistry2.restapi.format.Formatter; import eu.europa.ec.re3gistry2.restapi.format.JSONInternalFormatter; import eu.europa.ec.re3gistry2.javaapi.cache.model.Item; +import eu.europa.ec.re3gistry2.javaapi.cache.model.ItemClass; +import eu.europa.ec.re3gistry2.restapi.format.XSDFormatter; public class ResponseUtil { @@ -48,6 +48,14 @@ public static void ok(HttpServletResponse resp, Item item, RegLanguagecode lang, formatter.write(item, lang, baos); send(resp, sc, type, baos); } + + public static void ok(HttpServletResponse resp, ItemClass itemClass, Formatter formatter) throws Exception { + int sc = HttpServletResponse.SC_OK; + String type = formatter.getContentType(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ((XSDFormatter)formatter).write(itemClass, baos); + send(resp, sc, type, baos); + } public static void ok(HttpServletResponse resp, ApiResponse value) throws Exception { int sc = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; diff --git a/sources/Re3gistry2RestAPI/src/main/resources/META-INF/persistence.xml.orig b/sources/Re3gistry2RestAPI/src/main/resources/META-INF/persistence.xml.orig new file mode 100644 index 00000000..44097756 --- /dev/null +++ b/sources/Re3gistry2RestAPI/src/main/resources/META-INF/persistence.xml.orig @@ -0,0 +1,44 @@ + + + + org.eclipse.persistence.jpa.PersistenceProvider + + eu.europa.ec.re3gistry2.model.RegAction + eu.europa.ec.re3gistry2.model.RegField + eu.europa.ec.re3gistry2.model.RegFieldmapping + eu.europa.ec.re3gistry2.model.RegFieldtype + eu.europa.ec.re3gistry2.model.RegGroup + eu.europa.ec.re3gistry2.model.RegItemRegGroupRegRoleMapping + eu.europa.ec.re3gistry2.model.RegItem + eu.europa.ec.re3gistry2.model.RegItemclass + eu.europa.ec.re3gistry2.model.RegItemclasstype + eu.europa.ec.re3gistry2.model.RegItemhistory + eu.europa.ec.re3gistry2.model.RegItemproposed + eu.europa.ec.re3gistry2.model.RegLanguagecode + eu.europa.ec.re3gistry2.model.RegLocalization + eu.europa.ec.re3gistry2.model.RegLocalizationhistory + eu.europa.ec.re3gistry2.model.RegLocalizationproposed + eu.europa.ec.re3gistry2.model.RegRelation + eu.europa.ec.re3gistry2.model.RegRelationhistory + eu.europa.ec.re3gistry2.model.RegRelationpredicate + eu.europa.ec.re3gistry2.model.RegRelationproposed + eu.europa.ec.re3gistry2.model.RegRole + eu.europa.ec.re3gistry2.model.RegStatus + eu.europa.ec.re3gistry2.model.RegStatusgroup + eu.europa.ec.re3gistry2.model.RegStatuslocalization + eu.europa.ec.re3gistry2.model.RegUser + eu.europa.ec.re3gistry2.model.RegUserRegGroupMapping + eu.europa.ec.re3gistry2.model.RegItemproposedRegGroupRegRoleMapping + eu.europa.ec.re3gistry2.model.RegItemhistoryRegGroupRegRoleMapping + + false + + + + + + + + + + diff --git a/sources/Re3gistry2RestAPI/src/main/resources/configurations_files/configuration.properties b/sources/Re3gistry2RestAPI/src/main/resources/configurations_files/configuration.properties index 8c9aeed8..a1a7c3f7 100644 --- a/sources/Re3gistry2RestAPI/src/main/resources/configurations_files/configuration.properties +++ b/sources/Re3gistry2RestAPI/src/main/resources/configurations_files/configuration.properties @@ -45,3 +45,9 @@ application.cache.disk.maximum.space=25 # Status base URI application.status.maingroup=1 + +# XSD Schema to validate XML path +application.schemas.path=${application.schemas.path} + +# Legacy flag to manintain the old format of XSL +application.legacy.flag=${application.legacy.flag} diff --git a/sources/Re3gistry2RestAPI/src/main/resources/configurations_files/configuration.properties.orig b/sources/Re3gistry2RestAPI/src/main/resources/configurations_files/configuration.properties.orig new file mode 100644 index 00000000..2a5d4b4b --- /dev/null +++ b/sources/Re3gistry2RestAPI/src/main/resources/configurations_files/configuration.properties.orig @@ -0,0 +1,47 @@ +# +# Copyright 2007,2016 EUROPEAN UNION +# Licensed under the EUPL, Version 1.2 or - as soon they will be approved by +# the European Commission - subsequent versions of the EUPL (the "Licence"); +# You may not use this work except in compliance with the Licence. +# You may obtain a copy of the Licence at: +# +# https://ec.europa.eu/isa2/solutions/european-union-public-licence-eupl_en +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Licence is distributed on an "AS IS" basis, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the Licence for the specific language governing permissions and +# limitations under the Licence. +# +# Date: 2020/05/11 +# Authors: +# European Commission, Joint Research Centre - jrc-inspire-support@ec.europa.eu +# National Land Survey of Finland, SDI Services - inspire@nls.fi +# +# This work was supported by the Interoperability solutions for public +# administrations, businesses and citizens programme (http://ec.europa.eu/isa2) +# through Action 2016.10: European Location Interoperability Solutions for e-Government (ELISE) +# for e-Government (ELISE) +# + +############################################ +### Re3gistry2RestAPI Configuration file ### +############################################ + +#### Application properties #### + +# Persistence unit (DB connection reference specifyed in the persistence.xml file) +application.persistenceunitname=Re3gistry2 + +# This property specifies if the APIs needs to return also the empty values in +# the output +application.returnnullfields=true + +# Default date format for date creation and edit of the items +application.dateformat=yyyy-MM-dd + +application.cache.disk.path=diskcachepath +application.cache.disk.maximum.space=25 + +# Status base URI +application.status.maingroup=1 diff --git a/sources/Re3gistry2ServiceWebapp/public_html/index.html b/sources/Re3gistry2ServiceWebapp/public_html/index.html index ab0d3f8d..cf0c1c5c 100644 --- a/sources/Re3gistry2ServiceWebapp/public_html/index.html +++ b/sources/Re3gistry2ServiceWebapp/public_html/index.html @@ -94,86 +94,10 @@ diff --git a/sources/Re3gistry2ServiceWebapp/public_html/js-ecl-v2/app_common.js b/sources/Re3gistry2ServiceWebapp/public_html/js-ecl-v2/app_common.js index 7236558f..7aa4df59 100644 --- a/sources/Re3gistry2ServiceWebapp/public_html/js-ecl-v2/app_common.js +++ b/sources/Re3gistry2ServiceWebapp/public_html/js-ecl-v2/app_common.js @@ -55,6 +55,32 @@ const eventName_click = 'click'; var uriFromUrl = val_emptyString; var baseBreadcrumb = val_emptyString; +var unparsedLanguageJSON = ""; +var originalLanguages = new Dictionary(); + originalLanguages.add("en", "English"); + originalLanguages.add("es", "Español"); + originalLanguages.add("bg", "български"); + originalLanguages.add("cs", "čeština"); + originalLanguages.add("da", "dansk"); + originalLanguages.add("de", "Deutsch"); + originalLanguages.add("et", "eesti"); + originalLanguages.add("el", "ελληνικά"); + originalLanguages.add("fr", "français"); + originalLanguages.add("ga", "Gaeilge"); + originalLanguages.add("hr", "hrvatski"); + originalLanguages.add("it", "italiano"); + originalLanguages.add("lv", "latviešu"); + originalLanguages.add("lt", "lietuvių"); + originalLanguages.add("hu", "magyar"); + originalLanguages.add("mt", "Malti"); + originalLanguages.add("nl", "Nederlands"); + originalLanguages.add("pl", "polski"); + originalLanguages.add("pt", "português"); + originalLanguages.add("ro", "română"); + originalLanguages.add("sk", "slovenčina"); + originalLanguages.add("sl", "slovenščina"); + originalLanguages.add("fi", "suomi"); + originalLanguages.add("sv", "svenska"); // ** Script body ** // @@ -84,14 +110,12 @@ function setCookie(cookieName, cookieValue, expiringDays) { // Checks if the cookies are enabled in the system and if the EU cookie // consent has been accepted -// if (navigator.cookieEnabled && euCookieConsent && euCookieConsent.accepted("europa")) { - var cck1 = JSON.parse($wt.cookie.get("cck1")); - if (cck1.cm && cck1.all1st) { +// if ($wt.analytics.isTrackable()) { var d = new Date(); d.setTime(d.getTime() + (expiringDays * 24 * 60 * 60 * 1000)); var expires = key_cookieExpires + '=' + d.toUTCString(); document.cookie = cookieName + '=' + cookieValue + ';' + expires + ';' + key_cookiePath + '=/'; - } +// } } /* @@ -101,9 +125,7 @@ function setCookie(cookieName, cookieValue, expiringDays) { * @returns {String} The value of the cookie */ function getCookie(cookieName) { -// if (navigator.cookieEnabled) { - var cck1 = JSON.parse($wt.cookie.get("cck1")); - if (cck1.cm) { +// if ($wt.analytics.isTrackable()) { var name = cookieName + '='; var decodedCookie = decodeURIComponent(document.cookie); var ca = decodedCookie.split(';'); @@ -117,7 +139,7 @@ function getCookie(cookieName) { } } return val_emptyString; - } +// } } /* * Function to sort an array (to be used in Array.sort(function(a,b){})) @@ -154,7 +176,7 @@ function processUri() { // Getting the current URL let currentUrl = window.location.href; - if (currentUrl.endsWith("/")){ + if (currentUrl.endsWith("/")) { currentUrl = currentUrl.substring(0, currentUrl.length - 1); } // Getting the index of the lase occurence of "/" @@ -164,12 +186,12 @@ function processUri() { // let tmpUrl = currentUrl.substring(i); // Splitting this portion with "." to chech if it is specified a language // (e.g. elementName.en.html, but also the localID could contain a ".") - + //example localID: de.codelist.test //example localID with language and format: de.codelist.test.en.xml - - - + + + // let urlCheck = tmpUrl.split(val_dot); // If the lenght of the urlCheck is 3 @@ -191,14 +213,14 @@ function processUri() { // languageFromUrl = val_emptyString; // uriFromUrl = currentUrl; // } - - // Passing the current URL as the URI to be passed to the data service - languageFromUrl = val_emptyString; - uriFromUrl = currentUrl; - + + // Passing the current URL as the URI to be passed to the data service +// languageFromUrl = val_emptyString; + uriFromUrl = currentUrl; + // Check if the flag to force http is on - if(registryApp.forceHttpURIs){ - uriFromUrl = uriFromUrl.replace(key_https + '://', key_http + '://') + if (registryApp.forceHttpURIs) { + uriFromUrl = uriFromUrl.replace(key_https + '://', key_http + '://') } } @@ -227,13 +249,77 @@ function performSearch() { } /* Show or hide the loading overlay */ -function showLoadingOverlay(show){ - - let loadingOverlayElement = $('.'+elementClassName_loadingOverlay); - - if(show){ +function showLoadingOverlay(show) { + + let loadingOverlayElement = $('.' + elementClassName_loadingOverlay); + + if (show) { loadingOverlayElement.show(); - }else{ + } else { loadingOverlayElement.hide(); } -} \ No newline at end of file +} + +function Dictionary(){ + this.add = add; + this.dataStore = []; + this.find = find; + this.remove = remove; +} + +function add(key, value){ + this.dataStore[key] = value; +} + +function remove(key){ + delete this.dataStore[key]; +} + +function find(key){ + return this.dataStore[key]; +} + +function fillLanguageTable(){ + + //Check if languages have already been set in the table + // if(unparsedLanguageJSON == "" || document.getElementsByClassName("ecl-language-list__item").length == 0){ + + // Get the JSON that contains the active language list + let JSONLINK = registryApp.hostURL + "/rest?lang=active&format=jsonc" + unparsedLanguageJSON = $.ajax({ + url: JSONLINK, + async: false + }).responseText; + + var languageJSON = JSON.parse(unparsedLanguageJSON); + var columnChange = false; + + var finalLanguageColumns = document.getElementsByClassName("ecl-language-list__list"); + + for(var i=0; i{0}'; const htmlSnippet_li = '
  • {0}
  • '; const htmlSnippet_href = '{1}'; -const htmlSnippet_href_format = ' {1}'; +const htmlSnippet_href_external_link = '{1} '; +const htmlSnippet_href_format = ''; const htmlSnippet_field = '
    {0}
    {1}
    '; +const htmlSnippet_field_format = '
    {0}
    '; //const htmlSnippet_field = '
    {0}
    {1}
    '; const htmlSnippet_hx = '{2}'; const htmlSnippet_hr = '
    '; @@ -79,6 +81,10 @@ function fetchData(uri, lang) { if (lang === null || typeof lang === val_undefined || lang.length === 0) { lang = currentLanguage; } + + var url = new URL(uri); + var status = url.searchParams.get("status"); + uri = uri.split('?')[0]; // Show the loading overlay showLoadingOverlay(true); @@ -87,7 +93,7 @@ function fetchData(uri, lang) { // Base URL of the service taken from the configuration url: registryApp.dataServiceURL, - data: {uri: uri, lang: lang, format: key_jsonc} + data: {uri: uri, lang: lang, format: key_jsonc, status: status} }).done(function (responseData) { @@ -232,7 +238,7 @@ function renderVersionInfo(data) { htmlOutput += renderField(i18n[key_thisVersion], renderHref(version.uri, version.uri)); // Checking if the version history info are available - if (versionHistory !== null && versionHistory.length > 0) { + if (typeof versionHistory !== 'undefined' && versionHistory !== null && versionHistory.length > 0) { // Sorting the version history descending versionHistory.sort(function (a, b) { @@ -294,7 +300,17 @@ function renderProperties(data) { let href = values[0].href; tmpHtml = (href !== null && href !== val_emptyString) ? renderHref(value, href) : value; - htmlOutput += renderField(item.label, tmpHtml); + + if((item.id === "label" || item.id ==="definition") && data.language != item.lang){ + + let myJSON = getLanguageJSON(data.language); + let finalJSON = JSON.parse(myJSON); + let language = finalJSON.translatenotavailable; + let finalLabel = item.label + "
    "+" [" + language + "] "; + htmlOutput += renderField(finalLabel, tmpHtml); + }else{ + htmlOutput += renderField(item.label, tmpHtml); + } // If the property is the title, updating the page title. if (item.istitle !== null && item.istitle === val_true) { @@ -319,13 +335,16 @@ function renderProperties(data) { function renderFormats(data) { let href; + let hrefRor; let htmlOutput = val_emptyString; if (data.latest == true) { href = data.uri + "/" + data.localid + "." + currentLanguage + "."; + hrefRor = data.uri + "/" + data.localid + "."; } else { if (typeof data.version !== 'undefined') { href = data.uri + ":" + data.version.number + "/" + data.localid + "." + currentLanguage + "."; + hrefRor = data.uri + ":" + data.version.number + "/" + data.localid + "."; } } @@ -346,8 +365,11 @@ function renderFormats(data) { htmlInner += renderHrefFormat("CSV", href + "csv"); } + htmlInner += renderHrefFormat("ATOM", href + "atom"); + htmlInner += renderHrefFormat("ROR", hrefRor + "ror"); + //TODO to be completed - htmlOutput += renderField(i18n[key_otherFormats], htmlInner); + htmlOutput += renderFieldFormat(i18n[key_otherFormats], htmlInner); } return htmlOutput; } @@ -393,12 +415,27 @@ function renderCollections(data) { // The table header is rendered with the first element (the fields are // the same for all the elements) - tHead = renderTableHeader(containedItems[0].properties); + let max = -Infinity; + let index = -1; + containedItems.forEach(function (a, i) { + if (a.properties.length > max) { + max = a.properties.length; + index = i; + } + }); + var headerProperties = []; + $.each(containedItems[index].properties, function (index1, propertie) { + if (propertie.tablevisible !== null && propertie.tablevisible === val_true) { + headerProperties.push(propertie); + } + }); + + tHead = renderTableHeader(headerProperties); // The HTML of each contained item is rendered $.each(containedItems, function (index, item) { - htmlOutput += renderTableProperties(item); + htmlOutput += renderTableProperties(item, headerProperties); }); // The HTML of the table is rendered @@ -429,12 +466,27 @@ function renderNarrowers(data) { // The table header is rendered with the first element (the fields are // the same for all the elements) - tHead = renderTableHeader(narrowerItems[0].properties); + let max = -Infinity; + let index = -1; + narrowerItems.forEach(function (a, i) { + if (a.properties.length > max) { + max = a.properties.length; + index = i; + } + }); + var headerProperties = []; + $.each(narrowerItems[index].properties, function (index1, propertie) { + if (propertie.tablevisible !== null && propertie.tablevisible === val_true) { + headerProperties.push(propertie); + } + }); + + tHead = renderTableHeader(headerProperties); // The HTML of each narrower item is rendered $.each(narrowerItems, function (index, item) { - htmlOutput += renderTableProperties(item); + htmlOutput += renderTableProperties(item, headerProperties); }); // The HTML of the table is rendered @@ -452,12 +504,15 @@ function renderNarrowers(data) { * @returns {String} The rendered HTML of the field */ function renderField(label, value) { - if (label === 'label') { + if (label.toLowerCase() === 'label') { return htmlSnippet_field.replace('{0}', label).replace('{1}', value).replace('{2}', 'ecl-u-type-bold'); } else { return htmlSnippet_field.replace('{0}', label).replace('{1}', value).replace('{2}', ''); } } +function renderFieldFormat(label, value) { + return htmlSnippet_field_format.replace('{0}', label).replace('{1}', value).replace('{2}', ''); +} /* * Render the label and values (list) of the field in HTML @@ -516,6 +571,18 @@ function renderHref(value, href) { return htmlSnippet_href.replace('{0}', href).replace('{1}', value); } +/* + * Render the href of the field in HTML + * + * @param {String} value The value of the field + * @param {String} href The href of the field + * @returns {String} The rendered href HTML element of the field + * + */ +function renderHrefExternalLink(value, href) { + + return htmlSnippet_href_external_link.replace('{0}', href).replace('{1}', value); +} /* * Render the href of the field in HTML @@ -537,7 +604,7 @@ function renderHrefFormat(value, href) { * @param {Json} data The Re3gistry json data * @returns {String} The rendered HTML of the table properties */ -function renderTableProperties(data) { +function renderTableProperties(data, headerProperties) { let htmlOutput = val_emptyString; if (data.properties !== null && data.properties.length > 0) { @@ -547,30 +614,58 @@ function renderTableProperties(data) { return sortArray(a.order, b.order, key_ascOrdering); }); - // Rendering the HTML of each field for the table - $.each(data.properties, function (index, item) { - if (item.tablevisible !== null && item.tablevisible === val_true) { - let values = item.values; - let value = val_emptyString; - - if (values.length > 1) { - value = renderList(values); - } else if (values.length == 1) { - let tmpValue = values[0].value; - let href = values[0].href; - - // In the table view, in case the field is a 'titlefield' the href - // is added, in order to enable the link to the related element - // directly from the table - if (item.istitle !== null && item.istitle === val_true) { - value = renderHref(tmpValue, data.uri); - } else { - value = (href !== null && href !== val_emptyString) ? renderHref(tmpValue, href) : tmpValue; + $.each(headerProperties, function (index1, propertie) { + if (propertie.tablevisible !== null && propertie.tablevisible === val_true) { + + // Rendering the HTML of each field for the table + let valueAdded = false; + $.each(data.properties, function (index, item) { + if ((item.id === propertie.id) && !valueAdded) { + if (item.tablevisible !== null && item.tablevisible === val_true) { + let values = item.values; + let value = val_emptyString; + + if (values.length > 1) { + value = renderList(values); + } else if (values.length == 1) { + let tmpValue = values[0].value; + let href = values[0].href; + + // In the table view, in case the field is a 'titlefield' the href + // is added, in order to enable the link to the related element + // directly from the table + if (item.istitle !== null && item.istitle === val_true) { + + let windowlocation = window.location.href.replace("http://", "").replace("https://", ""); + let indexSlash = windowlocation.indexOf("/"); + let contain = windowlocation.substring(0, indexSlash); + + if (data.uri.includes(contain)) { + if(data.properties[2].values[0].value.toLowerCase() === "valid"){ + value = renderHref(tmpValue, data.uri); + } else { + value = renderHref(tmpValue, data.uri + "?status=" + data.properties[2].values[0].value.toLowerCase()); + } + } else { + value = renderHrefExternalLink(tmpValue, data.uri); + } + + } else { + value = (href !== null && href !== val_emptyString) ? renderHref(tmpValue, href) : tmpValue; + } + } + + // Rendering the HTML of the td + htmlOutput += renderTd(value, item.label); + valueAdded = true; + } } + }); + if (!valueAdded) { + // Rendering the HTML of the td + let value = val_emptyString; + htmlOutput += renderTd(value, propertie.label); } - - // Rendering the HTML of the td - htmlOutput += renderTd(value, item.label); } }); @@ -934,38 +1029,28 @@ function renderItemsBreadcrumb(data) { function generateBreadcrumbTrail(data) { let currentUri = data.uri; + let myBreadCrumbElements = []; + let myBreadCrumbElementsURL = []; + + //Rest of items + let topconcepts = true; + let currentLevel = data; + while(topconcepts){ + try { + myBreadCrumbElements.push(currentLevel.topConceptOf.values[0].value); + myBreadCrumbElementsURL.push(currentLevel.topConceptOf.uri) + currentLevel = currentLevel.topConceptOf; + } catch (error) { + topconcepts = false; + } + } - // Creating the list of additional elements for the breadcrumbs - let breadcrumbElements = currentUri.split('/'); - breadcrumbElements = breadcrumbElements.reverse(); - let breadcrumbUrls = []; - let tmpUrl = currentUri; - $.each(breadcrumbElements, function (index, item) { - breadcrumbUrls.push(tmpUrl); - tmpUrl = tmpUrl.replace('/' + item, ''); - }); - + myBreadCrumbElements.reverse(); + myBreadCrumbElementsURL.reverse(); let liHtml = val_emptyString; - breadcrumbUrls.reverse(); - $.each(breadcrumbUrls, function (index, item) { - - // Filtering elements not needed in the breadcrumbs or already - // processed (like registry and register or the current element) - if (item !== key_http + ':/' && - item !== key_http + ':' && - item !== key_https + ':' && - item !== key_http + ':/' && - item !== data.registry.uri && - item !== data.register.uri && - item !== data.uri) { - - // Filtering the base URI - if (data.registry.uri.indexOf(item) < 0 || data.register.uri.indexOf(item) < 0) { - - liHtml += renderBreadcrumbLink(getUriname(item), item); - } - } - }); + for(let i= 0 ; i
    @@ -225,10 +149,10 @@


    diff --git a/sources/Re3gistry2ServiceWebapp/support_files/re3gistry2-webapp.conf b/sources/Re3gistry2ServiceWebapp/support_files/re3gistry2-webapp.conf index 571f6113..adbb582f 100644 --- a/sources/Re3gistry2ServiceWebapp/support_files/re3gistry2-webapp.conf +++ b/sources/Re3gistry2ServiceWebapp/support_files/re3gistry2-webapp.conf @@ -40,6 +40,10 @@ RewriteCond %{REQUEST_URI} !^/registry/js RewriteRule ^/(.*)/(.*)\.([a-z]{2})\.(json|xml|atom|csv|rdf|iso19135xml|ror)$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=$3&uri=http://%{HTTP_HOST}/$1&format=$4 [P,L] + #Handling dynamic XSD + RewriteCond %{REQUEST_URI} ^/registry/schemas/2.0 + RewriteRule ^/registry/schemas/2.0/(.*)\.xsd$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?itemclass=$1&format=xsd [P,L] + ## Handling content negotiation: json RewriteCond %{REQUEST_URI} !^/registry/rest RewriteCond %{HTTP_ACCEPT} ^application/json$ @@ -68,20 +72,24 @@ RewriteCond %{HTTP:Accept-Language} ^()$ RewriteRule ^/(.*) %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=%1&uri=http://%{HTTP_HOST}/$1&format=atom [P,L] - ## Handling content negotiation: atom + ## Handling content negotiation: rdf RewriteCond %{REQUEST_URI} !^/registry/rest RewriteCond %{HTTP_ACCEPT} ^application/rdf+xml$ RewriteCond %{HTTP:Accept-Language} ([a-z]{2}) [OR] RewriteCond %{HTTP:Accept-Language} ^()$ RewriteRule ^/(.*) %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=%1&uri=http://%{HTTP_HOST}/$1&format=rdf [P,L] - ## Handling content negotiation: atom + ## Handling content negotiation: csv RewriteCond %{REQUEST_URI} !^/registry/rest RewriteCond %{HTTP_ACCEPT} ^text/csv$ RewriteCond %{HTTP:Accept-Language} ([a-z]{2}) [OR] RewriteCond %{HTTP:Accept-Language} ^()$ RewriteRule ^/(.*) %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?lang=%1&uri=http://%{HTTP_HOST}/$1&format=csv [P,L] + #Handling ROR format without specific language (defaults to ENG) + + RewriteRule ^/(.*)/((.*)\.(ror))$ %{REQUEST_SCHEME}://%{HTTP_HOST}/registry/rest?uri=http://%{HTTP_HOST}/$1&lang=en&format=ror [P,NE,L] + ## Generic request to the registry RewriteCond %{REQUEST_URI} !^/re3gistry2 RewriteCond %{REQUEST_URI} !^/registry/searchapi