From 384d674fad36c92c7c80b5de3152a10cd53a20af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20S=C3=A4rkikoski?= Date: Mon, 7 Nov 2022 08:26:09 +0200 Subject: [PATCH 1/3] Merge qa to master (#87) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cscttv 2132 (#24) * Add openshift config for api devel. * Modify Docker build path. * Add openshift config for identityserver devel. * Test permission change in container app folder. * Modify run time permissions * Change SDK version in Identityserver build. * Change build branch to devel. Change Rahti container image path. * Remove researchfi prefix from Openshift config. * Add README for api application. * Upgrade Nuget packages. * CSCTTV-2132_2 (#25) * Modify Openshift template and Dockerfile for Identityserver. * Modify image pull path. * Modify image pull path. * Add README in Identityserver project. * Update README.md * Update README.md * Modify Git source branch and container path in Identityserver template. * Remove Identityserver tempkey.jwk and add it in gitignore. * Change app folder permission in Identityserver container. * Do not initialize ElasticsearchService without required configuration values. * CSCTTV-2123 Elasticsearch sync (#26) * First draft of how data can be synced in Elasticsearch. * Elasticsearch connection parameters from configuration. * Add data types for Elasticsearch. * Add more data in Elasticsearch person index. * Delete tempkey.jwk * Add Elasticsearch environment variables in api README. Add identityserver's tempkey.jwk in .gitignore. * Simplify Elasticsearch service and change it to singleton instead of scoped service. * Use connection resiliency with SQL Server provider. * Always set field Modified to current timestamp when creating a new entry in database. * Add more logging in ElasticsearchService. * Improve handling of NEST error messages when adding or deleting entry in Elasticsearch. * Remove unused controller. * Use raw SQL query for getting DimUserProfile based on ORCID Id in DimPid. Clean up startup.cs dependencies. * Use raw SQL query when retrieving ORCID registered data source from DimRegisteredDataSource. * Upgrade Nuget packages in api project. * Code clean up, remove repetitive code. * Use raw sql when updating FactFieldValue table. Change created and modified timestamp to DateTime.UtcNow instead of DateTime.Now. Add namespace api.models.ProfileEditor. * Add unit tests for TtvSqlService. In TtvSqlService add own function for getting FactFieldValues FK column name based on field identifier. * Simplify queries which get data for profile editor and elasticsearch index. * When getting data for profile editor, get only those DimFieldDisplaySettings, which have at least one related FactFieldValue. * Update Elasticsearch in a background task. (#27) * When profile is created, include ORCID name automatically in profile. * In ORCID controller remove unnecessary retrieval of DimKnownPerson entity. * Remove multiple publicationIds from profile in the same api query. * Remove cached profiledata when publications are added or removed. * Use field identifier 550 with ORCID publications. When userprofile is deleted, remove cached profiledata and remove Elasticsearch entry in background task. * Enable split SQL query behaviour globally. Fix bug in ORCID publication handling. * Call SaveChangesAsync only once when ORCID data is imported. * Call SaveChangesAsync only once when adding demo data into user profile. * Modify Identityserver Javascript client configuration handling. * Add cooperation selection api (demo) (#28) * Add draft version of cooperation selection api. * Modify cooperation choice text content. * Add Swagger documentation. (#29) * Add Swagger documentation. * Simplify cooperationdemo api. Swagger documentation improvements. * Comment out Swagger, because the configuration does not work in Docker build. * Enable Swagger in development environment * Modify xml documentation generation. Modify environment handling in startup. * Add LanguageService. (#30) * Language service and name translation improvements. * Add DimUserChoice in Ttv model. Add DimUserChoice handling in CooperationChoicesController. (#31) * Compress api responses. * Profile API for adding and removing funding decisions. (#32) * Add controller and models for DimFundingDecision handling. * Add funding decision handling in user profile. * Add funding decision in demo data. * Add DimFundingDecision demo data. * Modify demo data. Modify FundingDecisionController functionality. * Simplify addFungingDecision API response. * CSCTTV-1703 research dataset handling in profile API (#33) * Add controller and models for DimResearchDataset handling. * Add relation between DimResearchDataset and FactFieldValues. Modify FundingDecisionController to use updated Ttv data model. * Add part of research dataset demo data. * Modify research dataset demo data handling. Modify demo data deletion. * Modify research dataset structure in profile data API response. * Comment out profile data query items. * Include ORCID ID in demodata source id field. * Modify research dataset demo data. * Fix bug in research dataset demodata creation. Modify user profile deletion. * Add funding decision and research dataset column names in TtvSqlService. * Optimize user profile and related data deletion. * Add department name in affiliation. Demo implementation, not production ready. (#34) * Improve handling of null values in name translation. * Restore previous functionality in user profile deletion. * Change funding decision handling to use projectId instead of funderProjectNumber. (#35) * Fix typo in funding decision property name. * Add OpenShift templates for demo version. * Change start and end date property names in funding decision item. * Add property datasetCreated in research dataset item. * Change demo version openshift build config git branch. * Comment out querying DimIdentifierlessData because of data model mismatch. * Add user profile id in cooperation item query. * Return API response immediately when there are no items to add or remove. * Add debug controller for getting debug data and statistics. * Sql model update 2021 12 (#37) * Remove log print of user profile existence check. * Add api response compression. * Ttv model update according to latest SQL database structure. Major change to handling of registered data source, work in progress. * Version which builds without errors. Elasticsearch synchronization and debug controller have some parts of code commented out. * Modify user profile creation, adding demo data and adding ORCID data. * Modify registered data source handling in profile data load. * Fix bug, which prevents deleting DimFieldDisplaySettings related to user profile. * Move profile editor data retrieval code into UserProfileService. * Modify profile data handling in Elasticsearch synchronization. * Modify SourceId field handling. Fix ORCID import bugs in DimName and DimResearcherDescription. * Handle missing name element when parsing ORCID record. * Upgrade Nuget packages. Modify DemoDataService. * Filter query to get only ORCID related entities. Remove manual adjustment of EntityState. * Get list of available FieldIdentifiers from UserProfileService. Add unit test to validate FieldIdentifier list content. * Add type code in publication and ORCID publication. (#38) * Fix bug where fromCache property was not set in ApiResponseProfileDataGet. * Take DOI from DimPublication.Doi instead of DimPublication.DoiHandle * Set ORCID member api scopes. Store and update user's ORCID access token on ORCID login. (#39) * Store ORCID tokens from Keycloak external IDP token endpoint. Add TokenService. (#40) * Add missing dependency injection in TokenService * Add Keycloak account linking. Add named http clients. Modify configuration parameters. (#41) * Require specific scope and orcid claim in controller authorization policy. (#42) * Add DuplicateHandlerService (#43) * Add DupplicateHandlerService and unit tests. Add temporary models and controller for testing purposes. * Modify DuplicateHandlerService unit tests * Logout and delete Keycloak user when user profile is deleted. (#44) * EF model update. Registered data source handling changes. (#45) * EF model scaffolded from test database on 5.5.2022. * Modify ORCID and Tiedejatutkimus.fi data source handling. * Add missing model files. * Store ORCID and TTV registered data source and organization values into a singleton service on startup. (#46) * Fixes suggested by Visual Studio code analyzer (#47) * Remove IdentityServer (#48) * Affiliation organization and department name handling in ORCID import (#49) * Add OrcidImportService. Move logic from controller to service. Modify Orcid json parser unit tests. * Add OrganizationHandlerService. Modify organization name handling in ORCID import and profile data deletion. * Modify affiliation related organization search in ORCID import. Modify affiliation deletion. * Modify affiliation department name handling. * Modify ORCID disambiguation source mapping, add unit test. * Move logic from controllers to services. (#50) * Improvements in background Elasticsearch index update (#51) * Queue background tasks according to Microsoft documentation. * Check if Elasticsearch sync feature is enabled before adding items to task queue. * Update NuGet packages (#52) * Add ” (test)” in temporary organization names * Update TTV model. Add DueDateDueTime into DimCallProgramme * Update TTV model change. PK in BrGrantedPermission * Remove demo data related code from user profile deletion. * Do not delete DimResearchDataset on profile deletion * Migrate from ASP.NET Core 5.0 to 6.0 (#53) * Add sharing API for profile editor (#54) * Add SharingController and SharingService * Profile sharing related database queries on application startup. * DimReferenceData handling when setting user profile default permissions. * Add SharingController and draft data * Add API endpoints for getting available sharing permissions and purposes. Language service bugfix. * Rename sharing purpose related model. * Add endpoints for listing, adding and removing of share permissions. * Change OKM organization id (#55) * Add Openshift template for Artifactory build. Remove unused templates. (#56) * Startup.cs code clean up. * Use interfaces in dependecy injection. (#57) * Add Serilog for logging. (#58) * Add GitHub codeql (#61) * Add GitHub action CodeQL. * Modify CodeQL GitHub action * Check only csharp in GitHub action CodeQL. * Modify log format. * Modify log message content. (#62) * CSCTTV-2724 Improvements in profile creation (#63) * Improved TTV database search when creating user profile. * Add research activity search on profile creation. * CSCTTV-2720 Improve testability (#64) * Add debug methods to create and delete a user profile to any ORCID ID in dim_pid. * Get ORCID record from public API when a specific flag is set in access token. * Fix bug in parsing of ORCID record education element * Import from ORCID only one entry of the same publication. (#65) * Enable database command logging * Use TagWith in queries to make debug log more readable. * Optimized profile data query using Dapper (#66) * Add Dapper. Get profiledata using raw sql. * Profile data person section query converted to raw sql. * Partially completed version of new profile data query using Dapper. * Add dim_education handling in new profile data query. * Add dim_publication and dim_orcid_publication handling in new profile data query. * Moved profile data SQL string into TtvSqlService. Use new profile data query in Elasticsearch index update. * Add missing ITtvSqlService.cs, which was dropped by malfunctioning IDE. * Removed old code from UserProfileService * Profile data deletion using Dapper (#67) * Initial code for deleting user profile using Dapper. Modify unit tests. Upgrade packages. * First working version of profile data deletion using Dapper. * Fix bug in deleting ORCID put codes from dim_pid. Add unit tests SQL statement creation. * Removed commented code. * Profile data response using new data structure. (#68) * New data structure in profile data response * Profile data personal section converted to new data structure. * Profile data activity section converted to new data structure. * Remove commented code and temporary modification in controller. * Modify DuplicateHandlerService unit tests. * Fix bug in Swagger documentation. Set controller route names explicitly. * Optimized profile deletion (#69) * Delete multiple IDs from dim_pid and dim_orcid_publication in the same SQL statement. * Delete multiple rows in one SQL statement using WHERE id IN condition * Update TTvSqlService unit tests. * Update field identifiers, unit tests and dependencies. * Use Automapper to create Elasticsearch data object from profile editor data object. (#70) * Limit profiledata in Elastic index to published items only. (#71) * Add property activitiesAndRewards into profiledata. Add property researchDatasets into Elasticsearch profiledata. (#72) * Update SQL query for dim_research_activity. Update profiledata contents. (#73) * Funding decision handling in SQL query and API response (#74) * Research dataset handling in SQL query and API response (#75) * Fix missing itemMeta in profile editor data. * Add API endpoint accountdelete for removing Keycloak user. (#76) * Cscttv 2799 hide national identification number (#77) * Add own folder structure for Keycloak related files. * Modify Java project naming. Add readme. * Modify Keycloak Dockerfile * Add custom Keycloak extension into build_dependencies folder. * Add OpenShift template for Keycloak devel. Modify Keycloak Dockerfile. (#78) * Rename Keycloak OpenShift template entries * Add Keycloak version number 19 in OpenShift template to avoid collision with old installation. * Fix bug in Keycloak custom provider URL. * Comment out automatic certificate generation in Keycloak container. * Add list of unique data sources in profile data and Elasticsearch index. (#79) * Add SectorId in organization data. (#80) * Add property primaryValue in Elasticsearch data. (#81) * Remove obsolete API code. Organize models. Update dependencies. (#82) * Remove DemoDataService * Reduce EntityFramework database log level. * Remove duplicate field PrimaryValue from elasticsearch model. (#83) * Remove duplicate PrimaryValue fields from Elasticsearch models. (#84) * Rename controller and route for profiledata. (#85) * Add OpenShift templates for QA and production. (#86) --- .github/workflows/codeql.yml | 72 + aspnetcore/.editorconfig | 4 + aspnetcore/mydata.sln | 18 +- aspnetcore/openshift/api/Dockerfile | 18 + .../openshift/api/template-api-demo.yml | 99 + .../api/template-api-devel-artifactory.yml | 58 + .../openshift/api/template-api-devel.yml | 99 + .../openshift/api/template-api-production.yml | 99 + aspnetcore/openshift/api/template-api-qa.yml | 99 + ...cidSandbox_0000-0002-9227-8514_record.json | 138 +- ...x_0000-0002-9227-8514_record_no_names.json | 2323 ++++ .../DuplicateHandlerServiceTest.cs | 355 + .../Services_Tests/LanguageServiceTest.cs | 165 + ...rTest.cs => OrcidJsonParserServiceTest.cs} | 628 +- .../OrganizationHandlerServiceTest.cs | 21 + .../Services_Tests/TtvSqlServiceTest.cs | 501 + .../Services_Tests/UserProfileServiceTest.cs | 35 + aspnetcore/src/api.Tests/api.Tests.csproj | 17 +- aspnetcore/src/api/.editorconfig | 3 + .../src/api/Automapper/MappingProfile.cs | 33 + .../api/Background/BackgroundProfileData.cs | 47 + .../src/api/Background/BackgroundTaskQueue.cs | 57 + .../api/Background/IBackgroundProfiledata.cs | 10 + .../src/api/Background/QueuedHostedService.cs | 58 + .../Controllers/AccountDeleteController.cs | 49 + .../api/Controllers/AccountLinkController.cs | 52 + .../CooperationChoicesController.cs | 173 + .../src/api/Controllers/DebugController.cs | 257 + .../Controllers/FundingDecisionController.cs | 231 + .../src/api/Controllers/OrcidController.cs | 755 +- .../api/Controllers/ProfileDataController.cs | 875 +- .../api/Controllers/PublicationController.cs | 190 +- .../Controllers/ResearchDatasetController.cs | 230 + .../src/api/Controllers/SharingController.cs | 217 + .../src/api/Controllers/TtvControllerBase.cs | 37 +- .../api/Controllers/UserProfileController.cs | 472 +- aspnetcore/src/api/Models/Api/ApiResponse.cs | 40 + .../Models/Api/ApiResponseCooperationGet.cs | 19 + .../Api/ApiResponseFundingDecisionPostMany.cs | 18 + .../ApiResponseFundingDecisionRemoveMany.cs | 18 + .../Models/Api/ApiResponseProfileDataGet.cs | 18 + .../Models/Api/ApiResponseProfileDataPatch.cs | 18 + ...sponseProfileSharingGivenPermissionsGet.cs | 18 + ...ApiResponseProfileSharingPermissionsGet.cs | 18 + .../ApiResponseProfileSharingPurposesGet.cs | 18 + .../Api/ApiResponsePublicationPostMany.cs | 18 + .../Api/ApiResponsePublicationRemoveMany.cs | 18 + .../Api/ApiResponseResearchDatasetPostMany.cs | 18 + .../ApiResponseResearchDatasetRemoveMany.cs | 18 + .../Models/Api/ApiResponseUserProfilePost.cs | 18 + aspnetcore/src/api/Models/ApiResponse.cs | 61 - aspnetcore/src/api/Models/Common/Constants.cs | 102 + .../NameTranslation.cs} | 10 +- .../src/api/Models/Common/Organization.cs | 18 + aspnetcore/src/api/Models/Constants.cs | 42 - .../Elasticsearch/ElasticsearchActivity.cs | 24 + .../ElasticsearchActivityAndReward.cs | 30 + .../Elasticsearch/ElasticsearchActor.cs | 16 + .../Elasticsearch/ElasticsearchAffiliation.cs | 36 + .../ElasticsearchDate.cs} | 10 +- .../Elasticsearch/ElasticsearchEducation.cs | 24 + .../Elasticsearch/ElasticsearchEmail.cs | 14 + .../ElasticsearchExternalIdentifier.cs | 16 + .../ElasticsearchFieldOfScience.cs} | 10 +- .../ElasticsearchFundingDecision.cs | 51 + .../Models/Elasticsearch/ElasticsearchItem.cs | 14 + .../Elasticsearch/ElasticsearchItemMeta.cs | 11 + .../Elasticsearch/ElasticsearchKeyword.cs | 14 + .../ElasticsearchName.cs} | 10 +- .../Elasticsearch/ElasticsearchPerson.cs | 28 + .../Elasticsearch/ElasticsearchPersonal.cs | 31 + .../ElasticsearchPreferredIdentifier.cs | 14 + .../ElasticsearchPublication.cs} | 12 +- .../ElasticsearchResearchDataset.cs | 33 + .../ElasticsearchResearcherDescription.cs} | 10 +- .../Elasticsearch/ElasticsearchSource.cs | 13 + .../ElasticsearchTelephoneNumber.cs | 14 + .../Elasticsearch/ElasticsearchWebLink.cs | 16 + .../src/api/Models/Orcid/OrcidEmployment.cs | 6 +- .../src/api/Models/Orcid/OrcidPublication.cs | 4 +- .../src/api/Models/Orcid/OrcidTokens.cs | 30 + .../Items/ProfileEditorActivityAndReward.cs | 28 + .../ProfileEditor/Items/ProfileEditorActor.cs | 16 + .../ProfileEditorAffiliation.cs} | 32 +- .../Items/ProfileEditorDataActivity.cs | 24 + .../Items/ProfileEditorDataPersonal.cs | 31 + .../Items/ProfileEditorDataResponse.cs | 18 + .../ProfileEditor/Items/ProfileEditorDate.cs | 16 + .../Items/ProfileEditorEducation.cs | 22 + .../ProfileEditor/Items/ProfileEditorEmail.cs | 12 + .../Items/ProfileEditorExternalIdentifier.cs | 14 + .../Items/ProfileEditorFieldOfScience.cs | 16 + .../Items/ProfileEditorFundingDecision.cs | 51 + .../{ => Items}/ProfileEditorItem.cs | 6 +- .../{ => Items}/ProfileEditorItemMeta.cs | 5 +- .../Items/ProfileEditorKeyword.cs | 12 + .../ProfileEditor/Items/ProfileEditorName.cs | 18 + .../Items/ProfileEditorPreferredIdentifier.cs | 14 + .../Items/ProfileEditorPublication.cs | 22 + .../Items/ProfileEditorResearchDataset.cs | 33 + .../ProfileEditorResearcherDescription.cs | 16 + .../{ => Items}/ProfileEditorSource.cs | 9 +- .../Items/ProfileEditorTelephoneNumber.cs | 12 + .../Items/ProfileEditorWebLink.cs | 14 + ...ProfileEditorAddFundingDecisionResponse.cs | 19 + .../ProfileEditorFundingDecisionToAdd.cs | 13 + ...fileEditorRemoveFundingDecisionResponse.cs | 15 + .../ProfileEditorAddPublicationResponse.cs | 6 +- .../ProfileEditorPublicationToAdd.cs | 3 +- .../ProfileEditorRemovePublicationResponse.cs | 15 + ...ProfileEditorAddResearchDatasetResponse.cs | 20 + ...fileEditorRemoveResearchDatasetResponse.cs | 15 + .../ProfileEditorResearchDatasetToAdd.cs | 14 + ...fileEditorSharingAddPermissionsResponse.cs | 21 + ...leEditorSharingGivenPermissionsResponse.cs | 13 + .../ManageSharing/ProfileEditorSharingItem.cs | 14 + .../ProfileEditorSharingItemMeta.cs | 20 + .../ProfileEditorSharingPermissionItem.cs | 18 + ...ileEditorSharingPermissionToAddOrDelete.cs | 12 + ...ProfileEditorSharingPermissionsResponse.cs | 13 + .../ProfileEditorSharingPurposeItem.cs | 24 + .../ProfileEditorSharingPurposesResponse.cs | 13 + .../ProfileEditor/ProfileDataFromSql.cs | 172 + .../ProfileEditorCooperationItem.cs | 15 + .../ProfileEditorDataActivity.cs | 19 - .../ProfileEditorDataModificationRequest.cs | 7 +- .../ProfileEditorDataModificationResponse.cs | 7 +- .../ProfileEditorDataPersonal.cs | 33 - .../ProfileEditorDataResponse.cs | 17 - .../ProfileEditor/ProfileEditorGroup.cs | 14 - .../ProfileEditorGroupAffiliation.cs | 16 - .../ProfileEditorGroupEducation.cs | 16 - .../ProfileEditor/ProfileEditorGroupEmail.cs | 16 - .../ProfileEditorGroupExternalIdentifier.cs | 16 - .../ProfileEditorGroupFieldOfScience.cs | 16 - .../ProfileEditorGroupKeyword.cs | 16 - .../ProfileEditor/ProfileEditorGroupMeta.cs | 5 +- .../ProfileEditor/ProfileEditorGroupName.cs | 16 - .../ProfileEditorGroupOtherName.cs | 16 - .../ProfileEditorGroupPublication.cs | 16 - ...ProfileEditorGroupResearcherDescription.cs | 16 - .../ProfileEditorGroupTelephoneNumber.cs | 16 - .../ProfileEditorGroupWebLink.cs | 16 - .../ProfileEditorItemEducation.cs | 26 - .../ProfileEditor/ProfileEditorItemEmail.cs | 16 - .../ProfileEditorItemExternalIdentifier.cs | 18 - .../ProfileEditor/ProfileEditorItemKeyword.cs | 16 - .../ProfileEditorItemTelephoneNumber.cs | 16 - .../ProfileEditor/ProfileEditorItemWebLink.cs | 18 - .../ProfileEditorPublicationId.cs | 12 - .../Ttv/BrArtpublicationTypecategory.cs | 16 + .../Ttv/BrDatasetDatasetRelationship.cs | 17 + .../src/api/Models/Ttv/BrGrantedPermission.cs | 18 + .../src/api/Models/Ttv/BrKeywordDimKeyword.cs | 18 + .../Models/Ttv/BrLanguageCodesForDataset.cs | 3 - .../Ttv/BrLicensesForResearchDataset.cs | 16 + .../Ttv/BrResearchDatasetDimFieldOfScience.cs | 3 - .../Models/Ttv/BrResearchDatasetDimKeyword.cs | 3 - .../Ttv/BrWordClusterDimFundingDecision.cs | 20 + .../api/Models/Ttv/BrWordsDefineACluster.cs | 4 + .../src/api/Models/Ttv/DimCallProgramme.cs | 14 +- .../src/api/Models/Ttv/DimFieldOfScience.cs | 2 - .../src/api/Models/Ttv/DimFundingDecision.cs | 2 + .../api/Models/Ttv/DimIdentifierlessDatum.cs | 5 +- .../src/api/Models/Ttv/DimInfrastructure.cs | 8 +- aspnetcore/src/api/Models/Ttv/DimKeyword.cs | 2 - .../src/api/Models/Ttv/DimKnownPerson.cs | 9 +- aspnetcore/src/api/Models/Ttv/DimMinedWord.cs | 4 + aspnetcore/src/api/Models/Ttv/DimName.cs | 4 +- .../src/api/Models/Ttv/DimOrganization.cs | 2 + .../src/api/Models/Ttv/DimPublication.cs | 9 +- aspnetcore/src/api/Models/Ttv/DimPurpose.cs | 32 + .../src/api/Models/Ttv/DimReferencedatum.cs | 14 +- .../api/Models/Ttv/DimRegisteredDataSource.cs | 2 + .../src/api/Models/Ttv/DimResearchActivity.cs | 2 + .../src/api/Models/Ttv/DimResearchDataset.cs | 17 +- .../src/api/Models/Ttv/DimServicePoint.cs | 8 +- .../src/api/Models/Ttv/DimUserChoice.cs | 22 + .../src/api/Models/Ttv/DimUserProfile.cs | 10 + aspnetcore/src/api/Models/Ttv/DimWebLink.cs | 4 +- .../src/api/Models/Ttv/DimWordCluster.cs | 8 +- .../src/api/Models/Ttv/FactFieldValue.cs | 4 + aspnetcore/src/api/Models/Ttv/TtvContext.cs | 630 +- aspnetcore/src/api/Program.cs | 32 +- .../src/api/Properties/launchSettings.json | 1 - aspnetcore/src/api/README.md | 19 + .../api/Services/DataSourceHelperService.cs | 107 + .../src/api/Services/DemoDataService.cs | 880 -- .../api/Services/DuplicateHandlerService.cs | 132 + .../src/api/Services/ElasticsearchService.cs | 115 +- .../api/Services/IDataSourceHelperService.cs | 19 + .../api/Services/IDuplicateHandlerService.cs | 14 + .../src/api/Services/IElasticsearchService.cs | 15 + .../api/Services/IKeycloakAdminApiService.cs | 21 + .../src/api/Services/ILanguageService.cs | 9 + .../src/api/Services/IOrcidApiService.cs | 11 + .../src/api/Services/IOrcidImportService.cs | 9 + .../api/Services/IOrcidJsonParserService.cs | 21 + .../Services/IOrganizationHandlerService.cs | 14 + .../src/api/Services/ISharingService.cs | 20 + .../src/api/Services/IStartupHelperService.cs | 11 + aspnetcore/src/api/Services/ITokenService.cs | 17 + aspnetcore/src/api/Services/ITtvSqlService.cs | 39 + .../src/api/Services/IUserProfileService.cs | 35 + .../src/api/Services/IUtilityService.cs | 9 + .../api/Services/KeycloakAdminApiService.cs | 218 + .../src/api/Services/LanguageService.cs | 84 + .../src/api/Services/OrcidApiService.cs | 60 +- .../src/api/Services/OrcidImportService.cs | 833 ++ .../api/Services/OrcidJsonParserService.cs | 387 +- .../Services/OrganizationHandlerService.cs | 140 + aspnetcore/src/api/Services/SharingService.cs | 302 + .../src/api/Services/StartupHelperService.cs | 93 + aspnetcore/src/api/Services/TokenService.cs | 96 + aspnetcore/src/api/Services/TtvSqlService.cs | 445 + .../src/api/Services/UserProfileService.cs | 1512 ++- aspnetcore/src/api/Services/UtilityService.cs | 20 + aspnetcore/src/api/Startup.cs | 279 +- aspnetcore/src/api/api.csproj | 64 +- .../src/api/appsettings.Development.json | 7 - aspnetcore/src/api/appsettings.json | 28 +- aspnetcore/src/identityserver/AspIdUsers.db | Bin 102400 -> 0 bytes .../Data/ApplicationDbContext.cs | 22 - ...0200930122959_InitialMigration.Designer.cs | 276 - .../20200930122959_InitialMigration.cs | 220 - .../ApplicationDbContextModelSnapshot.cs | 274 - ...ServerConfigurationDbMigration.Designer.cs | 905 -- ...lIdentityServerConfigurationDbMigration.cs | 657 - .../ConfigurationDbContextModelSnapshot.cs | 903 -- ...erverPersistedGrantDbMigration.Designer.cs | 129 - ...IdentityServerPersistedGrantDbMigration.cs | 85 - .../PersistedGrantDbContextModelSnapshot.cs | 127 - .../identityserver/Models/ApplicationUser.cs | 10 - aspnetcore/src/identityserver/Program.cs | 81 - .../Properties/launchSettings.json | 25 - .../Quickstart/Account/AccountController.cs | 343 - .../Quickstart/Account/AccountOptions.cs | 20 - .../Quickstart/Account/ExternalController.cs | 263 - .../Quickstart/Account/ExternalProvider.cs | 12 - .../Quickstart/Account/LoggedOutViewModel.cs | 19 - .../Quickstart/Account/LoginInputModel.cs | 18 - .../Quickstart/Account/LoginViewModel.cs | 22 - .../Quickstart/Account/LogoutInputModel.cs | 11 - .../Quickstart/Account/LogoutViewModel.cs | 11 - .../Quickstart/Account/RedirectViewModel.cs | 12 - .../Quickstart/Consent/ConsentController.cs | 262 - .../Quickstart/Consent/ConsentInputModel.cs | 17 - .../Quickstart/Consent/ConsentOptions.cs | 16 - .../Quickstart/Consent/ConsentViewModel.cs | 19 - .../Consent/ProcessConsentResult.cs | 21 - .../Quickstart/Consent/ScopeViewModel.cs | 16 - .../Device/DeviceAuthorizationInputModel.cs | 11 - .../Device/DeviceAuthorizationViewModel.cs | 12 - .../Quickstart/Device/DeviceController.cs | 232 - .../Diagnostics/DiagnosticsController.cs | 29 - .../Diagnostics/DiagnosticsViewModel.cs | 32 - .../identityserver/Quickstart/Extensions.cs | 27 - .../Quickstart/Grants/GrantsController.cs | 97 - .../Quickstart/Grants/GrantsViewModel.cs | 27 - .../Quickstart/Home/ErrorViewModel.cs | 22 - .../Quickstart/Home/HomeController.cs | 65 - .../Quickstart/SecurityHeadersAttribute.cs | 56 - .../identityserver/Quickstart/TestUsers.cs | 43 - aspnetcore/src/identityserver/SeedData.cs | 108 - .../identityserver/Services/ProfileService.cs | 46 - aspnetcore/src/identityserver/Startup.cs | 221 - .../Views/Account/AccessDenied.cshtml | 7 - .../Views/Account/LoggedOut.cshtml | 34 - .../identityserver/Views/Account/Login.cshtml | 92 - .../Views/Account/Logout.cshtml | 15 - .../identityserver/Views/Consent/Index.cshtml | 104 - .../Views/Device/Success.cshtml | 7 - .../Views/Device/UserCodeCapture.cshtml | 23 - .../Views/Device/UserCodeConfirmation.cshtml | 108 - .../Views/Diagnostics/Index.cshtml | 64 - .../identityserver/Views/Grants/Index.cshtml | 87 - .../identityserver/Views/Home/Index.cshtml | 32 - .../identityserver/Views/Shared/Error.cshtml | 40 - .../Views/Shared/Redirect.cshtml | 11 - .../Views/Shared/_Layout.cshtml | 28 - .../identityserver/Views/Shared/_Nav.cshtml | 33 - .../Views/Shared/_ScopeListItem.cshtml | 34 - .../Views/Shared/_ValidationSummary.cshtml | 7 - .../identityserver/Views/_ViewImports.cshtml | 2 - .../identityserver/Views/_ViewStart.cshtml | 3 - .../src/identityserver/appsettings.json | 5 - .../src/identityserver/identityserver.csproj | 39 - aspnetcore/src/identityserver/tempkey.jwk | 1 - aspnetcore/src/identityserver/updateUI.ps1 | 172 - .../src/identityserver/wwwroot/css/site.css | 24 - .../identityserver/wwwroot/css/site.min.css | 1 - .../src/identityserver/wwwroot/css/site.scss | 42 - .../src/identityserver/wwwroot/favicon.ico | Bin 1150 -> 0 bytes .../src/identityserver/wwwroot/icon.jpg | Bin 19482 -> 0 bytes .../src/identityserver/wwwroot/icon.png | Bin 20796 -> 0 bytes .../wwwroot/js/signin-redirect.js | 1 - .../wwwroot/js/signout-redirect.js | 6 - .../wwwroot/lib/bootstrap/README.md | 209 - .../lib/bootstrap/dist/css/bootstrap-grid.css | 3899 ------ .../bootstrap/dist/css/bootstrap-grid.css.map | 1 - .../bootstrap/dist/css/bootstrap-grid.min.css | 7 - .../dist/css/bootstrap-grid.min.css.map | 1 - .../bootstrap/dist/css/bootstrap-reboot.css | 327 - .../dist/css/bootstrap-reboot.css.map | 1 - .../dist/css/bootstrap-reboot.min.css | 8 - .../dist/css/bootstrap-reboot.min.css.map | 1 - .../lib/bootstrap/dist/css/bootstrap.css | 10224 --------------- .../lib/bootstrap/dist/css/bootstrap.css.map | 1 - .../lib/bootstrap/dist/css/bootstrap.min.css | 7 - .../bootstrap/dist/css/bootstrap.min.css.map | 1 - .../lib/bootstrap/dist/js/bootstrap.bundle.js | 7134 ---------- .../bootstrap/dist/js/bootstrap.bundle.js.map | 1 - .../bootstrap/dist/js/bootstrap.bundle.min.js | 7 - .../dist/js/bootstrap.bundle.min.js.map | 1 - .../lib/bootstrap/dist/js/bootstrap.js | 4521 ------- .../lib/bootstrap/dist/js/bootstrap.js.map | 1 - .../lib/bootstrap/dist/js/bootstrap.min.js | 7 - .../bootstrap/dist/js/bootstrap.min.js.map | 1 - .../wwwroot/lib/bootstrap/scss/_alert.scss | 51 - .../wwwroot/lib/bootstrap/scss/_badge.scss | 54 - .../lib/bootstrap/scss/_breadcrumb.scss | 42 - .../lib/bootstrap/scss/_button-group.scss | 163 - .../wwwroot/lib/bootstrap/scss/_buttons.scss | 139 - .../wwwroot/lib/bootstrap/scss/_card.scss | 278 - .../wwwroot/lib/bootstrap/scss/_carousel.scss | 197 - .../wwwroot/lib/bootstrap/scss/_close.scss | 41 - .../wwwroot/lib/bootstrap/scss/_code.scss | 48 - .../lib/bootstrap/scss/_custom-forms.scss | 521 - .../wwwroot/lib/bootstrap/scss/_dropdown.scss | 191 - .../wwwroot/lib/bootstrap/scss/_forms.scss | 338 - .../lib/bootstrap/scss/_functions.scss | 134 - .../wwwroot/lib/bootstrap/scss/_grid.scss | 69 - .../wwwroot/lib/bootstrap/scss/_images.scss | 42 - .../lib/bootstrap/scss/_input-group.scss | 191 - .../lib/bootstrap/scss/_jumbotron.scss | 17 - .../lib/bootstrap/scss/_list-group.scss | 158 - .../wwwroot/lib/bootstrap/scss/_media.scss | 8 - .../wwwroot/lib/bootstrap/scss/_mixins.scss | 47 - .../wwwroot/lib/bootstrap/scss/_modal.scss | 239 - .../wwwroot/lib/bootstrap/scss/_nav.scss | 120 - .../wwwroot/lib/bootstrap/scss/_navbar.scss | 324 - .../lib/bootstrap/scss/_pagination.scss | 73 - .../wwwroot/lib/bootstrap/scss/_popover.scss | 170 - .../wwwroot/lib/bootstrap/scss/_print.scss | 141 - .../wwwroot/lib/bootstrap/scss/_progress.scss | 46 - .../wwwroot/lib/bootstrap/scss/_reboot.scss | 482 - .../wwwroot/lib/bootstrap/scss/_root.scss | 20 - .../wwwroot/lib/bootstrap/scss/_spinners.scss | 55 - .../wwwroot/lib/bootstrap/scss/_tables.scss | 185 - .../wwwroot/lib/bootstrap/scss/_toasts.scss | 44 - .../wwwroot/lib/bootstrap/scss/_tooltip.scss | 115 - .../lib/bootstrap/scss/_transitions.scss | 20 - .../wwwroot/lib/bootstrap/scss/_type.scss | 125 - .../lib/bootstrap/scss/_utilities.scss | 17 - .../lib/bootstrap/scss/_variables.scss | 1143 -- .../lib/bootstrap/scss/bootstrap-grid.scss | 29 - .../lib/bootstrap/scss/bootstrap-reboot.scss | 12 - .../wwwroot/lib/bootstrap/scss/bootstrap.scss | 44 - .../lib/bootstrap/scss/mixins/_alert.scss | 13 - .../scss/mixins/_background-variant.scss | 22 - .../lib/bootstrap/scss/mixins/_badge.scss | 17 - .../bootstrap/scss/mixins/_border-radius.scss | 63 - .../bootstrap/scss/mixins/_box-shadow.scss | 20 - .../bootstrap/scss/mixins/_breakpoints.scss | 123 - .../lib/bootstrap/scss/mixins/_buttons.scss | 110 - .../lib/bootstrap/scss/mixins/_caret.scss | 62 - .../lib/bootstrap/scss/mixins/_clearfix.scss | 7 - .../lib/bootstrap/scss/mixins/_deprecate.scss | 10 - .../lib/bootstrap/scss/mixins/_float.scss | 14 - .../lib/bootstrap/scss/mixins/_forms.scss | 177 - .../lib/bootstrap/scss/mixins/_gradients.scss | 45 - .../scss/mixins/_grid-framework.scss | 71 - .../lib/bootstrap/scss/mixins/_grid.scss | 69 - .../lib/bootstrap/scss/mixins/_hover.scss | 37 - .../lib/bootstrap/scss/mixins/_image.scss | 36 - .../bootstrap/scss/mixins/_list-group.scss | 21 - .../lib/bootstrap/scss/mixins/_lists.scss | 7 - .../bootstrap/scss/mixins/_nav-divider.scss | 11 - .../bootstrap/scss/mixins/_pagination.scss | 22 - .../bootstrap/scss/mixins/_reset-text.scss | 17 - .../lib/bootstrap/scss/mixins/_resize.scss | 6 - .../bootstrap/scss/mixins/_screen-reader.scss | 34 - .../lib/bootstrap/scss/mixins/_size.scss | 7 - .../lib/bootstrap/scss/mixins/_table-row.scss | 39 - .../bootstrap/scss/mixins/_text-emphasis.scss | 17 - .../lib/bootstrap/scss/mixins/_text-hide.scss | 11 - .../bootstrap/scss/mixins/_text-truncate.scss | 8 - .../bootstrap/scss/mixins/_transition.scss | 16 - .../bootstrap/scss/mixins/_visibility.scss | 8 - .../lib/bootstrap/scss/utilities/_align.scss | 8 - .../bootstrap/scss/utilities/_background.scss | 19 - .../bootstrap/scss/utilities/_borders.scss | 75 - .../bootstrap/scss/utilities/_clearfix.scss | 3 - .../bootstrap/scss/utilities/_display.scss | 26 - .../lib/bootstrap/scss/utilities/_embed.scss | 39 - .../lib/bootstrap/scss/utilities/_flex.scss | 51 - .../lib/bootstrap/scss/utilities/_float.scss | 11 - .../bootstrap/scss/utilities/_overflow.scss | 5 - .../bootstrap/scss/utilities/_position.scss | 32 - .../scss/utilities/_screenreaders.scss | 11 - .../bootstrap/scss/utilities/_shadows.scss | 6 - .../lib/bootstrap/scss/utilities/_sizing.scss | 20 - .../bootstrap/scss/utilities/_spacing.scss | 73 - .../scss/utilities/_stretched-link.scss | 19 - .../lib/bootstrap/scss/utilities/_text.scss | 72 - .../bootstrap/scss/utilities/_visibility.scss | 13 - .../lib/bootstrap/scss/vendor/_rfs.scss | 204 - .../wwwroot/lib/jquery/LICENSE.txt | 20 - .../wwwroot/lib/jquery/README.md | 62 - .../wwwroot/lib/jquery/dist/jquery.js | 10872 ---------------- .../wwwroot/lib/jquery/dist/jquery.min.js | 2 - .../wwwroot/lib/jquery/dist/jquery.min.map | 1 - .../wwwroot/lib/jquery/dist/jquery.slim.js | 8777 ------------- .../lib/jquery/dist/jquery.slim.min.js | 2 - .../lib/jquery/dist/jquery.slim.min.map | 1 - .../researchfi.mapper-0.0.1-BETA.jar | Bin 0 -> 3524 bytes keycloak/custom/researchfi.mapper/.gitignore | 51 + keycloak/custom/researchfi.mapper/README.md | 10 + keycloak/custom/researchfi.mapper/pom.xml | 38 + .../ResearchfiUsernameTemplateMapper.java | 73 + ...oak.broker.provider.IdentityProviderMapper | 1 + keycloak/openshift/Dockerfile | 39 + keycloak/openshift/README.md | 3 + .../openshift/template-keycloak-devel.yml | 98 + .../template-keycloak-production.yml | 98 + keycloak/openshift/template-keycloak-qa.yml | 98 + 426 files changed, 14001 insertions(+), 65247 deletions(-) create mode 100644 .github/workflows/codeql.yml create mode 100644 aspnetcore/.editorconfig create mode 100644 aspnetcore/openshift/api/Dockerfile create mode 100644 aspnetcore/openshift/api/template-api-demo.yml create mode 100644 aspnetcore/openshift/api/template-api-devel-artifactory.yml create mode 100644 aspnetcore/openshift/api/template-api-devel.yml create mode 100644 aspnetcore/openshift/api/template-api-production.yml create mode 100644 aspnetcore/openshift/api/template-api-qa.yml create mode 100644 aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record_no_names.json create mode 100644 aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs create mode 100644 aspnetcore/src/api.Tests/Services_Tests/LanguageServiceTest.cs rename aspnetcore/src/api.Tests/Services_Tests/{OrcidJsonParserTest.cs => OrcidJsonParserServiceTest.cs} (78%) create mode 100644 aspnetcore/src/api.Tests/Services_Tests/OrganizationHandlerServiceTest.cs create mode 100644 aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs create mode 100644 aspnetcore/src/api.Tests/Services_Tests/UserProfileServiceTest.cs create mode 100644 aspnetcore/src/api/.editorconfig create mode 100644 aspnetcore/src/api/Automapper/MappingProfile.cs create mode 100644 aspnetcore/src/api/Background/BackgroundProfileData.cs create mode 100644 aspnetcore/src/api/Background/BackgroundTaskQueue.cs create mode 100644 aspnetcore/src/api/Background/IBackgroundProfiledata.cs create mode 100644 aspnetcore/src/api/Background/QueuedHostedService.cs create mode 100644 aspnetcore/src/api/Controllers/AccountDeleteController.cs create mode 100644 aspnetcore/src/api/Controllers/AccountLinkController.cs create mode 100644 aspnetcore/src/api/Controllers/CooperationChoicesController.cs create mode 100644 aspnetcore/src/api/Controllers/DebugController.cs create mode 100644 aspnetcore/src/api/Controllers/FundingDecisionController.cs create mode 100644 aspnetcore/src/api/Controllers/ResearchDatasetController.cs create mode 100644 aspnetcore/src/api/Controllers/SharingController.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponse.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseCooperationGet.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionPostMany.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionRemoveMany.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseProfileDataGet.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseProfileDataPatch.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseProfileSharingGivenPermissionsGet.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPermissionsGet.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPurposesGet.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponsePublicationPostMany.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponsePublicationRemoveMany.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetPostMany.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetRemoveMany.cs create mode 100644 aspnetcore/src/api/Models/Api/ApiResponseUserProfilePost.cs delete mode 100644 aspnetcore/src/api/Models/ApiResponse.cs create mode 100644 aspnetcore/src/api/Models/Common/Constants.cs rename aspnetcore/src/api/Models/{ProfileEditor/ProfileEditorItemFieldOfScience.cs => Common/NameTranslation.cs} (52%) create mode 100644 aspnetcore/src/api/Models/Common/Organization.cs delete mode 100644 aspnetcore/src/api/Models/Constants.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActor.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs rename aspnetcore/src/api/Models/{ProfileEditor/ProfileEditorItemDate.cs => Elasticsearch/ElasticsearchDate.cs} (55%) create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEducation.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEmail.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchExternalIdentifier.cs rename aspnetcore/src/api/Models/{ProfileEditor/ProfileEditorSourceOrganization.cs => Elasticsearch/ElasticsearchFieldOfScience.cs} (55%) create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchFundingDecision.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItem.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItemMeta.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchKeyword.cs rename aspnetcore/src/api/Models/{ProfileEditor/ProfileEditorItemName.cs => Elasticsearch/ElasticsearchName.cs} (56%) create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPersonal.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPreferredIdentifier.cs rename aspnetcore/src/api/Models/{ProfileEditor/ProfileEditorItemPublication.cs => Elasticsearch/ElasticsearchPublication.cs} (58%) create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchResearchDataset.cs rename aspnetcore/src/api/Models/{ProfileEditor/ProfileEditorItemResearcherDescription.cs => Elasticsearch/ElasticsearchResearcherDescription.cs} (58%) create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSource.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchTelephoneNumber.cs create mode 100644 aspnetcore/src/api/Models/Elasticsearch/ElasticsearchWebLink.cs create mode 100644 aspnetcore/src/api/Models/Orcid/OrcidTokens.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActor.cs rename aspnetcore/src/api/Models/ProfileEditor/{ProfileEditorItemAffiliation.cs => Items/ProfileEditorAffiliation.cs} (50%) create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataActivity.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataPersonal.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDate.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEducation.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEmail.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorExternalIdentifier.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFieldOfScience.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFundingDecision.cs rename aspnetcore/src/api/Models/ProfileEditor/{ => Items}/ProfileEditorItem.cs (54%) rename aspnetcore/src/api/Models/ProfileEditor/{ => Items}/ProfileEditorItemMeta.cs (79%) create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorKeyword.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorName.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPreferredIdentifier.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearchDataset.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearcherDescription.cs rename aspnetcore/src/api/Models/ProfileEditor/{ => Items}/ProfileEditorSource.cs (56%) create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorTelephoneNumber.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorWebLink.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorAddFundingDecisionResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorFundingDecisionToAdd.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorRemoveFundingDecisionResponse.cs rename aspnetcore/src/api/Models/ProfileEditor/{ => ManagePublications}/ProfileEditorAddPublicationResponse.cs (74%) rename aspnetcore/src/api/Models/ProfileEditor/{ => ManagePublications}/ProfileEditorPublicationToAdd.cs (91%) create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorRemovePublicationResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorAddResearchDatasetResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorRemoveResearchDatasetResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorResearchDatasetToAdd.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingAddPermissionsResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingGivenPermissionsResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItem.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItemMeta.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionItem.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionToAddOrDelete.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionsResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposeItem.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposesResponse.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs create mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorCooperationItem.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataActivity.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataPersonal.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataResponse.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroup.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupAffiliation.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEducation.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEmail.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupExternalIdentifier.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupFieldOfScience.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupKeyword.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupName.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupOtherName.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupPublication.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupResearcherDescription.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupTelephoneNumber.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupWebLink.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEducation.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEmail.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemExternalIdentifier.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemKeyword.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemTelephoneNumber.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemWebLink.cs delete mode 100644 aspnetcore/src/api/Models/ProfileEditor/ProfileEditorPublicationId.cs create mode 100644 aspnetcore/src/api/Models/Ttv/BrArtpublicationTypecategory.cs create mode 100644 aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs create mode 100644 aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs create mode 100644 aspnetcore/src/api/Models/Ttv/BrKeywordDimKeyword.cs create mode 100644 aspnetcore/src/api/Models/Ttv/BrLicensesForResearchDataset.cs create mode 100644 aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs create mode 100644 aspnetcore/src/api/Models/Ttv/DimPurpose.cs create mode 100644 aspnetcore/src/api/Models/Ttv/DimUserChoice.cs create mode 100644 aspnetcore/src/api/README.md create mode 100644 aspnetcore/src/api/Services/DataSourceHelperService.cs delete mode 100644 aspnetcore/src/api/Services/DemoDataService.cs create mode 100644 aspnetcore/src/api/Services/DuplicateHandlerService.cs create mode 100644 aspnetcore/src/api/Services/IDataSourceHelperService.cs create mode 100644 aspnetcore/src/api/Services/IDuplicateHandlerService.cs create mode 100644 aspnetcore/src/api/Services/IElasticsearchService.cs create mode 100644 aspnetcore/src/api/Services/IKeycloakAdminApiService.cs create mode 100644 aspnetcore/src/api/Services/ILanguageService.cs create mode 100644 aspnetcore/src/api/Services/IOrcidApiService.cs create mode 100644 aspnetcore/src/api/Services/IOrcidImportService.cs create mode 100644 aspnetcore/src/api/Services/IOrcidJsonParserService.cs create mode 100644 aspnetcore/src/api/Services/IOrganizationHandlerService.cs create mode 100644 aspnetcore/src/api/Services/ISharingService.cs create mode 100644 aspnetcore/src/api/Services/IStartupHelperService.cs create mode 100644 aspnetcore/src/api/Services/ITokenService.cs create mode 100644 aspnetcore/src/api/Services/ITtvSqlService.cs create mode 100644 aspnetcore/src/api/Services/IUserProfileService.cs create mode 100644 aspnetcore/src/api/Services/IUtilityService.cs create mode 100644 aspnetcore/src/api/Services/KeycloakAdminApiService.cs create mode 100644 aspnetcore/src/api/Services/LanguageService.cs create mode 100644 aspnetcore/src/api/Services/OrcidImportService.cs create mode 100644 aspnetcore/src/api/Services/OrganizationHandlerService.cs create mode 100644 aspnetcore/src/api/Services/SharingService.cs create mode 100644 aspnetcore/src/api/Services/StartupHelperService.cs create mode 100644 aspnetcore/src/api/Services/TokenService.cs create mode 100644 aspnetcore/src/api/Services/TtvSqlService.cs create mode 100644 aspnetcore/src/api/Services/UtilityService.cs delete mode 100644 aspnetcore/src/identityserver/AspIdUsers.db delete mode 100644 aspnetcore/src/identityserver/Data/ApplicationDbContext.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.Designer.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.Designer.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.Designer.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.cs delete mode 100644 aspnetcore/src/identityserver/Migrations/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs delete mode 100644 aspnetcore/src/identityserver/Models/ApplicationUser.cs delete mode 100644 aspnetcore/src/identityserver/Program.cs delete mode 100644 aspnetcore/src/identityserver/Properties/launchSettings.json delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/AccountController.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/AccountOptions.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/ExternalController.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/ExternalProvider.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/LoggedOutViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/LoginInputModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/LoginViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/LogoutInputModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/LogoutViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Account/RedirectViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Consent/ConsentController.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Consent/ConsentInputModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Consent/ConsentOptions.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Consent/ConsentViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Consent/ProcessConsentResult.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Consent/ScopeViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationInputModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Device/DeviceController.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsController.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Extensions.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Grants/GrantsController.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Grants/GrantsViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Home/ErrorViewModel.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/Home/HomeController.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/SecurityHeadersAttribute.cs delete mode 100644 aspnetcore/src/identityserver/Quickstart/TestUsers.cs delete mode 100644 aspnetcore/src/identityserver/SeedData.cs delete mode 100644 aspnetcore/src/identityserver/Services/ProfileService.cs delete mode 100644 aspnetcore/src/identityserver/Startup.cs delete mode 100644 aspnetcore/src/identityserver/Views/Account/AccessDenied.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Account/LoggedOut.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Account/Login.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Account/Logout.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Consent/Index.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Device/Success.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Device/UserCodeCapture.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Device/UserCodeConfirmation.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Diagnostics/Index.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Grants/Index.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Home/Index.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Shared/Error.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Shared/Redirect.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Shared/_Layout.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Shared/_Nav.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Shared/_ScopeListItem.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/Shared/_ValidationSummary.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/_ViewImports.cshtml delete mode 100644 aspnetcore/src/identityserver/Views/_ViewStart.cshtml delete mode 100644 aspnetcore/src/identityserver/appsettings.json delete mode 100644 aspnetcore/src/identityserver/identityserver.csproj delete mode 100644 aspnetcore/src/identityserver/tempkey.jwk delete mode 100644 aspnetcore/src/identityserver/updateUI.ps1 delete mode 100644 aspnetcore/src/identityserver/wwwroot/css/site.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/css/site.min.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/css/site.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/favicon.ico delete mode 100644 aspnetcore/src/identityserver/wwwroot/icon.jpg delete mode 100644 aspnetcore/src/identityserver/wwwroot/icon.png delete mode 100644 aspnetcore/src/identityserver/wwwroot/js/signin-redirect.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/js/signout-redirect.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/README.md delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_alert.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_badge.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_breadcrumb.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_button-group.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_buttons.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_card.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_carousel.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_close.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_code.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_custom-forms.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_dropdown.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_forms.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_functions.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_grid.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_images.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_input-group.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_jumbotron.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_list-group.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_media.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_mixins.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_modal.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_nav.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_navbar.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_pagination.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_popover.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_print.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_progress.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_reboot.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_root.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_spinners.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_tables.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_toasts.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_tooltip.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_transitions.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_type.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_utilities.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/_variables.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/bootstrap-grid.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/bootstrap-reboot.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/bootstrap.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_alert.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_background-variant.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_badge.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_border-radius.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_box-shadow.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_breakpoints.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_buttons.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_caret.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_clearfix.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_deprecate.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_float.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_forms.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_gradients.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_grid-framework.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_grid.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_hover.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_image.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_list-group.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_lists.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_nav-divider.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_pagination.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_reset-text.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_resize.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_screen-reader.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_size.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_table-row.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_text-emphasis.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_text-hide.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_text-truncate.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_transition.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/mixins/_visibility.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_align.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_background.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_borders.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_clearfix.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_display.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_embed.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_flex.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_float.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_overflow.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_position.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_screenreaders.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_shadows.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_sizing.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_spacing.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_stretched-link.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_text.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/utilities/_visibility.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/bootstrap/scss/vendor/_rfs.scss delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/LICENSE.txt delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/README.md delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/dist/jquery.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/dist/jquery.min.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/dist/jquery.min.map delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/dist/jquery.slim.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/dist/jquery.slim.min.js delete mode 100644 aspnetcore/src/identityserver/wwwroot/lib/jquery/dist/jquery.slim.min.map create mode 100644 keycloak/custom/build_dependencies/researchfi.mapper-0.0.1-BETA.jar create mode 100644 keycloak/custom/researchfi.mapper/.gitignore create mode 100644 keycloak/custom/researchfi.mapper/README.md create mode 100644 keycloak/custom/researchfi.mapper/pom.xml create mode 100644 keycloak/custom/researchfi.mapper/src/main/java/researchfi/mappers/ResearchfiUsernameTemplateMapper.java create mode 100644 keycloak/custom/researchfi.mapper/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper create mode 100644 keycloak/openshift/Dockerfile create mode 100644 keycloak/openshift/README.md create mode 100644 keycloak/openshift/template-keycloak-devel.yml create mode 100644 keycloak/openshift/template-keycloak-production.yml create mode 100644 keycloak/openshift/template-keycloak-qa.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..0b9bd479 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "devel" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "devel" ] + schedule: + - cron: '19 17 * * 0' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'csharp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/aspnetcore/.editorconfig b/aspnetcore/.editorconfig new file mode 100644 index 00000000..76e1c0f9 --- /dev/null +++ b/aspnetcore/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. +dotnet_diagnostic.CS8632.severity = silent diff --git a/aspnetcore/mydata.sln b/aspnetcore/mydata.sln index 7b253d9f..85b79788 100644 --- a/aspnetcore/mydata.sln +++ b/aspnetcore/mydata.sln @@ -8,11 +8,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{530E1C35-DCE src\.gitignore = src\.gitignore EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "identityserver", "src\identityserver\identityserver.csproj", "{F470F142-9BE6-4F58-ABBA-0688AADBA3DA}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api.Tests", "src\api.Tests\api.Tests.csproj", "{1C38915D-3D9C-4BF7-9C0F-B4568DE45493}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B24A6206-EA06-441C-811C-7C329851C4AA}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api", "src\api\api.csproj", "{9A67B131-39EA-4D65-A99C-7650B2238308}" EndProject @@ -29,18 +30,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Debug|x64.ActiveCfg = Debug|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Debug|x64.Build.0 = Debug|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Debug|x86.ActiveCfg = Debug|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Debug|x86.Build.0 = Debug|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Release|Any CPU.Build.0 = Release|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Release|x64.ActiveCfg = Release|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Release|x64.Build.0 = Release|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Release|x86.ActiveCfg = Release|Any CPU - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA}.Release|x86.Build.0 = Release|Any CPU {1C38915D-3D9C-4BF7-9C0F-B4568DE45493}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1C38915D-3D9C-4BF7-9C0F-B4568DE45493}.Debug|Any CPU.Build.0 = Debug|Any CPU {1C38915D-3D9C-4BF7-9C0F-B4568DE45493}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -67,7 +56,6 @@ Global {9A67B131-39EA-4D65-A99C-7650B2238308}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {F470F142-9BE6-4F58-ABBA-0688AADBA3DA} = {530E1C35-DCEA-4F12-B363-E62C2554D61E} {1C38915D-3D9C-4BF7-9C0F-B4568DE45493} = {530E1C35-DCEA-4F12-B363-E62C2554D61E} {9A67B131-39EA-4D65-A99C-7650B2238308} = {530E1C35-DCEA-4F12-B363-E62C2554D61E} EndGlobalSection diff --git a/aspnetcore/openshift/api/Dockerfile b/aspnetcore/openshift/api/Dockerfile new file mode 100644 index 00000000..55a3224d --- /dev/null +++ b/aspnetcore/openshift/api/Dockerfile @@ -0,0 +1,18 @@ +# Builder +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env +WORKDIR /app +# Copy csproj and restore as distinct layers +COPY aspnetcore/src/api/api.csproj ./ +RUN dotnet restore +# Copy everything else and build +COPY aspnetcore/src/api/ ./ +RUN dotnet publish -c Release -o out + + + +# Runtime image +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +WORKDIR /app +COPY --from=build-env /app/out . +EXPOSE 8080 +ENTRYPOINT ["dotnet", "api.dll"] diff --git a/aspnetcore/openshift/api/template-api-demo.yml b/aspnetcore/openshift/api/template-api-demo.yml new file mode 100644 index 00000000..694a95ac --- /dev/null +++ b/aspnetcore/openshift/api/template-api-demo.yml @@ -0,0 +1,99 @@ +# This file is part of the research.fi service +# +# Copyright 2021 Ministry of Education and Culture, Finland +# +# :author: CSC - IT Center for Science Ltd., Espoo Finland servicedesk@csc.fi +# :license: MIT +apiVersion: v1 +kind: Template +metadata: + labels: + app: mydata-api-demo + template: mydata-api-demo + name: mydata-api-demo +objects: + # Service (demo) + - apiVersion: v1 + kind: Service + metadata: + name: mydata-api-service-demo + labels: + app: mydata-api-demo + annotations: + description: Mydata api service for demo + spec: + ports: + - name: mydata-api-port-demo + port: 8080 + targetPort: 8080 + selector: + depcfg: mydata-api-depcfg-demo + + # ImageStream (demo) + - apiVersion: v1 + kind: ImageStream + metadata: + name: mydata-api-demo + labels: + app: mydata-api-demo + + # BuildConfig using Docker build strategy + - apiVersion: v1 + kind: BuildConfig + metadata: + name: mydata-api-build-demo + labels: + app: mydata-api-demo + spec: + source: + git: + uri: https://github.com/CSCfi/research-fi-mydata.git + ref: demo2021 + contextDir: . + strategy: + dockerStrategy: + dockerfilePath: aspnetcore/openshift/api/Dockerfile + output: + to: + kind: ImageStreamTag + name: mydata-api-demo:latest + successfulBuildsHistoryLimit: 2 + failedBuildsHistoryLimit: 2 + + # Deployment config (demo) + - apiVersion: v1 + kind: DeploymentConfig + metadata: + name: mydata-api-deployment-demo + labels: + app: mydata-api-demo + spec: + selector: + app: mydata-api-demo + depcfg: mydata-api-depcfg-demo + template: + metadata: + labels: + app: mydata-api-demo + depcfg: mydata-api-depcfg-demo + spec: + containers: + - name: mydata-api-container-demo + image: researchfi/mydata-api-demo + imagePullPolicy: Always + ports: + - containerPort: 8080 + protocol: TCP + replicas: 1 + triggers: + - type: ConfigChange + - type: ImageChange + imageChangeParams: + automatic: true + containerNames: + - mydata-api-container-demo + from: + kind: ImageStreamTag + name: mydata-api-demo:latest + strategy: + type: Rolling \ No newline at end of file diff --git a/aspnetcore/openshift/api/template-api-devel-artifactory.yml b/aspnetcore/openshift/api/template-api-devel-artifactory.yml new file mode 100644 index 00000000..4035a7a5 --- /dev/null +++ b/aspnetcore/openshift/api/template-api-devel-artifactory.yml @@ -0,0 +1,58 @@ +# This file is part of the research.fi service +# +# Copyright 2022 Ministry of Education and Culture, Finland +# +# :author: CSC - IT Center for Science Ltd., Espoo Finland servicedesk@csc.fi +# :license: MIT + +# Build mydata API devel version and push image to Artifactory + +apiVersion: v1 +kind: Template +metadata: + name: template-artifactory-build-mydata-api-devel + annotations: + description: This template adds build configuration, which builds mydata API devel version and pushes the image into Artifactory. + labels: + app: mydata-api-devel + +parameters: + - name: ARTIFACTORY_BASE_URL + displayName: Artifactory base URL. + description: Base URL of Artifactory. For example "myartifactory.mydomain.fi" + value: "" + required: true + +objects: + - apiVersion: v1 + kind: BuildConfig + metadata: + name: artifactory-build-mydata-api-devel + labels: + app: mydata-api-devel + spec: + source: + type: Git + git: + uri: https://github.com/CSCfi/research-fi-mydata.git + ref: devel + contextDir: . + strategy: + type: Docker + dockerStrategy: + dockerfilePath: aspnetcore/openshift/api/Dockerfile + output: + to: + kind: DockerImage + name: researchfi-mydata-api-docker-dev.${ARTIFACTORY_BASE_URL}/mydata-api-devel:latest + pushSecret: + name: artifactory-researchfi-mydata-api-docker-dev + nodeSelector: {} + triggers: + - type: ImageChange + imageChange: + from: + kind: ImageStreamTag + name: mydata-api-devel:latest + successfulBuildsHistoryLimit: 2 + failedBuildsHistoryLimit: 2 diff --git a/aspnetcore/openshift/api/template-api-devel.yml b/aspnetcore/openshift/api/template-api-devel.yml new file mode 100644 index 00000000..3d1b970e --- /dev/null +++ b/aspnetcore/openshift/api/template-api-devel.yml @@ -0,0 +1,99 @@ +# This file is part of the research.fi service +# +# Copyright 2021 Ministry of Education and Culture, Finland +# +# :author: CSC - IT Center for Science Ltd., Espoo Finland servicedesk@csc.fi +# :license: MIT +apiVersion: v1 +kind: Template +metadata: + labels: + app: mydata-api-devel + template: mydata-api-devel + name: mydata-api-devel +objects: + # Service (devel) + - apiVersion: v1 + kind: Service + metadata: + name: mydata-api-service-devel + labels: + app: mydata-api-devel + annotations: + description: Mydata api service for devel branch + spec: + ports: + - name: mydata-api-port-devel + port: 8080 + targetPort: 8080 + selector: + depcfg: mydata-api-depcfg-devel + + # ImageStream (development) + - apiVersion: v1 + kind: ImageStream + metadata: + name: mydata-api-devel + labels: + app: mydata-api-devel + + # BuildConfig using Docker build strategy + - apiVersion: v1 + kind: BuildConfig + metadata: + name: mydata-api-build-devel + labels: + app: mydata-api-devel + spec: + source: + git: + uri: https://github.com/CSCfi/research-fi-mydata.git + ref: devel + contextDir: . + strategy: + dockerStrategy: + dockerfilePath: aspnetcore/openshift/api/Dockerfile + output: + to: + kind: ImageStreamTag + name: mydata-api-devel:latest + successfulBuildsHistoryLimit: 4 + failedBuildsHistoryLimit: 4 + + # Deployment config (development) + - apiVersion: v1 + kind: DeploymentConfig + metadata: + name: mydata-api-deployment-devel + labels: + app: mydata-api-devel + spec: + selector: + app: mydata-api-devel + depcfg: mydata-api-depcfg-devel + template: + metadata: + labels: + app: mydata-api-devel + depcfg: mydata-api-depcfg-devel + spec: + containers: + - name: mydata-api-container-devel + image: researchfi/mydata-api-devel + imagePullPolicy: Always + ports: + - containerPort: 8080 + protocol: TCP + replicas: 1 + triggers: + - type: ConfigChange + - type: ImageChange + imageChangeParams: + automatic: true + containerNames: + - mydata-api-container-devel + from: + kind: ImageStreamTag + name: mydata-api-devel:latest + strategy: + type: Rolling \ No newline at end of file diff --git a/aspnetcore/openshift/api/template-api-production.yml b/aspnetcore/openshift/api/template-api-production.yml new file mode 100644 index 00000000..a67b6b8d --- /dev/null +++ b/aspnetcore/openshift/api/template-api-production.yml @@ -0,0 +1,99 @@ +# This file is part of the research.fi service +# +# Copyright 2022 Ministry of Education and Culture, Finland +# +# :author: CSC - IT Center for Science Ltd., Espoo Finland servicedesk@csc.fi +# :license: MIT +apiVersion: v1 +kind: Template +metadata: + labels: + app: mydata-api-production + template: mydata-api-production + name: mydata-api-production +objects: + # Service (production) + - apiVersion: v1 + kind: Service + metadata: + name: mydata-api-service-production + labels: + app: mydata-api-production + annotations: + description: Mydata api service for production branch (master) + spec: + ports: + - name: mydata-api-port-production + port: 8080 + targetPort: 8080 + selector: + depcfg: mydata-api-depcfg-production + + # ImageStream (production) + - apiVersion: v1 + kind: ImageStream + metadata: + name: mydata-api-production + labels: + app: mydata-api-production + + # BuildConfig using Docker build strategy + - apiVersion: v1 + kind: BuildConfig + metadata: + name: mydata-api-build-production + labels: + app: mydata-api-production + spec: + source: + git: + uri: https://github.com/CSCfi/research-fi-mydata.git + ref: master + contextDir: . + strategy: + dockerStrategy: + dockerfilePath: aspnetcore/openshift/api/Dockerfile + output: + to: + kind: ImageStreamTag + name: mydata-api-production:latest + successfulBuildsHistoryLimit: 2 + failedBuildsHistoryLimit: 2 + + # Deployment config (production) + - apiVersion: v1 + kind: DeploymentConfig + metadata: + name: mydata-api-deployment-production + labels: + app: mydata-api-production + spec: + selector: + app: mydata-api-production + depcfg: mydata-api-depcfg-production + template: + metadata: + labels: + app: mydata-api-production + depcfg: mydata-api-depcfg-production + spec: + containers: + - name: mydata-api-container-production + image: researchfi/mydata-api-production + imagePullPolicy: Always + ports: + - containerPort: 8080 + protocol: TCP + replicas: 1 + triggers: + - type: ConfigChange + - type: ImageChange + imageChangeParams: + automatic: true + containerNames: + - mydata-api-container-production + from: + kind: ImageStreamTag + name: mydata-api-production:latest + strategy: + type: Rolling \ No newline at end of file diff --git a/aspnetcore/openshift/api/template-api-qa.yml b/aspnetcore/openshift/api/template-api-qa.yml new file mode 100644 index 00000000..e729fa7c --- /dev/null +++ b/aspnetcore/openshift/api/template-api-qa.yml @@ -0,0 +1,99 @@ +# This file is part of the research.fi service +# +# Copyright 2022 Ministry of Education and Culture, Finland +# +# :author: CSC - IT Center for Science Ltd., Espoo Finland servicedesk@csc.fi +# :license: MIT +apiVersion: v1 +kind: Template +metadata: + labels: + app: mydata-api-qa + template: mydata-api-qa + name: mydata-api-qa +objects: + # Service (qa) + - apiVersion: v1 + kind: Service + metadata: + name: mydata-api-service-qa + labels: + app: mydata-api-qa + annotations: + description: Mydata api service for qa branch + spec: + ports: + - name: mydata-api-port-qa + port: 8080 + targetPort: 8080 + selector: + depcfg: mydata-api-depcfg-qa + + # ImageStream (qa) + - apiVersion: v1 + kind: ImageStream + metadata: + name: mydata-api-qa + labels: + app: mydata-api-qa + + # BuildConfig using Docker build strategy + - apiVersion: v1 + kind: BuildConfig + metadata: + name: mydata-api-build-qa + labels: + app: mydata-api-qa + spec: + source: + git: + uri: https://github.com/CSCfi/research-fi-mydata.git + ref: qa + contextDir: . + strategy: + dockerStrategy: + dockerfilePath: aspnetcore/openshift/api/Dockerfile + output: + to: + kind: ImageStreamTag + name: mydata-api-qa:latest + successfulBuildsHistoryLimit: 2 + failedBuildsHistoryLimit: 2 + + # Deployment config (qa) + - apiVersion: v1 + kind: DeploymentConfig + metadata: + name: mydata-api-deployment-qa + labels: + app: mydata-api-qa + spec: + selector: + app: mydata-api-qa + depcfg: mydata-api-depcfg-qa + template: + metadata: + labels: + app: mydata-api-qa + depcfg: mydata-api-depcfg-qa + spec: + containers: + - name: mydata-api-container-qa + image: researchfi/mydata-api-qa + imagePullPolicy: Always + ports: + - containerPort: 8080 + protocol: TCP + replicas: 1 + triggers: + - type: ConfigChange + - type: ImageChange + imageChangeParams: + automatic: true + containerNames: + - mydata-api-container-qa + from: + kind: ImageStreamTag + name: mydata-api-qa:latest + strategy: + type: Rolling \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json b/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json index af4097dd..655c03e7 100644 --- a/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json +++ b/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json @@ -635,6 +635,69 @@ "visibility": "public", "path": "/0000-0002-9227-8514/education/22423" } + }, + { + "education-summary": { + "created-date": { + "value": 1477583674474 + }, + "last-modified-date": { + "value": 1477583674474 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Another Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 42345, + "department-name": "Managing Department", + "role-title": "MSc", + "start-date": { + "year": { + "value": "1998" + }, + "month": { + "value": "10" + }, + "day": { + "value": "04" + } + }, + "end-date": { + "year": { + "value": "2002" + }, + "month": { + "value": "06" + }, + "day": { + "value": "16" + } + }, + "organization": { + "name": "Test university without disambiguated organization", + "address": { + "city": "London", + "region": null, + "country": "GI" + }, + "disambiguated-organization": null + }, + "url": null, + "external-ids": null, + "display-index": "0", + "visibility": "public", + "path": "/0000-0002-9227-8514/education/22423" + } } ] } @@ -707,6 +770,69 @@ "visibility": "public", "path": "/0000-0002-9227-8514/employment/22411" } + }, + { + "employment-summary": { + "created-date": { + "value": 1653029011477 + }, + "last-modified-date": { + "value": 1653298285245 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0001-6790-8767", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Timo J S" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 47431, + "department-name": "Astrophysics", + "role-title": "Professor", + "start-date": { + "year": { + "value": "2018" + }, + "month": { + "value": "01" + }, + "day": { + "value": "21" + } + }, + "end-date": { + "year": { + "value": "2019" + }, + "month": { + "value": "12" + }, + "day": { + "value": "31" + } + }, + "organization": { + "name": "Test university without disambiguated organization", + "address": { + "city": "London", + "region": null, + "country": "GI" + }, + "disambiguated-organization": null + }, + "url": null, + "external-ids": null, + "display-index": "1", + "visibility": "public", + "path": "/0000-0001-6790-8767/employment/47431" + } } ] } @@ -2218,7 +2344,7 @@ }, "title": { "title": { - "value": "ORCID: a system to uniquely identify researchers" + "value": "My research paper" }, "subtitle": null, "translated-title": null @@ -2239,7 +2365,7 @@ ] }, "url": null, - "type": "journal-article", + "type": "journal-article B", "publication-date": { "year": { "value": "2019" @@ -2302,7 +2428,7 @@ }, "title": { "title": { - "value": "ORCID: a system to uniquely identify researchers" + "value": "Another publication" }, "subtitle": null, "translated-title": null @@ -2334,7 +2460,7 @@ ] }, "url": null, - "type": "journal-article", + "type": "journal-article C", "publication-date": { "year": { "value": "2012" @@ -2377,7 +2503,7 @@ }, "title": { "title": { - "value": "ORCID: a system to uniquely identify researchers" + "value": "Another publication" }, "subtitle": null, "translated-title": null @@ -2398,7 +2524,7 @@ ] }, "url": null, - "type": "journal-article", + "type": "journal-article C", "publication-date": { "year": { "value": "2012" diff --git a/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record_no_names.json b/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record_no_names.json new file mode 100644 index 00000000..9338f09c --- /dev/null +++ b/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record_no_names.json @@ -0,0 +1,2323 @@ +{ + "orcid-identifier": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "preferences": { + "locale": "en" + }, + "history": { + "creation-method": "MEMBER_REFERRED", + "completion-date": null, + "submission-date": { + "value": 1423033401286 + }, + "last-modified-date": { + "value": 1617028188658 + }, + "claimed": true, + "source": null, + "deactivation-date": null, + "verified-email": true, + "verified-primary-email": true + }, + "person": { + "last-modified-date": { + "value": 1512136401301 + }, + "name": null, + "other-names": { + "last-modified-date": null, + "other-name": [], + "path": "/0000-0002-9227-8514/other-names" + }, + "biography": { + "created-date": { + "value": 1460669280211 + }, + "last-modified-date": { + "value": 1460669280211 + }, + "content": "Sofia Maria Hernandez Garcia is the researcher that is used as an example ORCID record holder.", + "visibility": "public", + "path": "/0000-0002-9227-8514/biography" + }, + "researcher-urls": { + "last-modified-date": { + "value": 1477423579805 + }, + "researcher-url": [ + { + "created-date": { + "value": 1477423573347 + }, + "last-modified-date": { + "value": 1477423579805 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "url-name": "Twitter", + "url": { + "value": "https://twitter.com/ORCIDsofia" + }, + "visibility": "public", + "path": "/0000-0002-9227-8514/researcher-urls/41387", + "put-code": 41387, + "display-index": 1 + } + ], + "path": "/0000-0002-9227-8514/researcher-urls" + }, + "emails": { + "last-modified-date": { + "value": 1512136401301 + }, + "email": [ + { + "created-date": { + "value": 1477501385707 + }, + "last-modified-date": { + "value": 1512136401301 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "email": "s.garcia@orcid.org", + "path": null, + "visibility": "public", + "verified": true, + "primary": true, + "put-code": null + } + ], + "path": "/0000-0002-9227-8514/email" + }, + "addresses": { + "last-modified-date": { + "value": 1477506367602 + }, + "address": [ + { + "created-date": { + "value": 1477423504065 + }, + "last-modified-date": { + "value": 1477506367566 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "country": { + "value": "GB" + }, + "visibility": "public", + "path": "/0000-0002-9227-8514/address/4556", + "put-code": 4556, + "display-index": 2 + }, + { + "created-date": { + "value": 1477506337295 + }, + "last-modified-date": { + "value": 1477506367602 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "country": { + "value": "US" + }, + "visibility": "public", + "path": "/0000-0002-9227-8514/address/4560", + "put-code": 4560, + "display-index": 1 + } + ], + "path": "/0000-0002-9227-8514/address" + }, + "keywords": { + "last-modified-date": { + "value": 1477423527039 + }, + "keyword": [ + { + "created-date": { + "value": 1461170250189 + }, + "last-modified-date": { + "value": 1477423527029 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-2UX70ON5NEEMCGCV", + "path": "APP-2UX70ON5NEEMCGCV", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Frontiers ORCID Sandbox" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "content": "QA and testing", + "visibility": "public", + "path": "/0000-0002-9227-8514/keywords/4504", + "put-code": 4504, + "display-index": 3 + }, + { + "created-date": { + "value": 1477423515476 + }, + "last-modified-date": { + "value": 1477423527037 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "content": "QA and testing", + "visibility": "public", + "path": "/0000-0002-9227-8514/keywords/4603", + "put-code": 4603, + "display-index": 2 + }, + { + "created-date": { + "value": 1477423527039 + }, + "last-modified-date": { + "value": 1477423527039 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "content": "Additional keyword", + "visibility": "public", + "path": "/0000-0002-9227-8514/keywords/4604", + "put-code": 4604, + "display-index": 1 + } + ], + "path": "/0000-0002-9227-8514/keywords" + }, + "external-identifiers": { + "last-modified-date": { + "value": 1477423883379 + }, + "external-identifier": [ + { + "created-date": { + "value": 1461170250171 + }, + "last-modified-date": { + "value": 1477423619621 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-2UX70ON5NEEMCGCV", + "path": "APP-2UX70ON5NEEMCGCV", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Frontiers ORCID Sandbox" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "external-id-type": "Loop profile", + "external-id-value": "558", + "external-id-url": { + "value": "http://loop.frontiers-sandbox-int.info/people/559/overview?referrer=orcid_profile" + }, + "external-id-relationship": "self", + "visibility": "public", + "path": "/0000-0002-9227-8514/external-identifiers/3193", + "put-code": 3193, + "display-index": 1 + }, + { + "created-date": { + "value": 1477423883378 + }, + "last-modified-date": { + "value": 1477423883379 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "external-id-type": "Personal External Identifier", + "external-id-value": "506", + "external-id-url": { + "value": "www.6.com" + }, + "external-id-relationship": "self", + "visibility": "public", + "path": "/0000-0002-9227-8514/external-identifiers/3294", + "put-code": 3294, + "display-index": 0 + } + ], + "path": "/0000-0002-9227-8514/external-identifiers" + }, + "path": "/0000-0002-9227-8514/person" + }, + "activities-summary": { + "last-modified-date": { + "value": 1617028188646 + }, + "distinctions": { + "last-modified-date": { + "value": 1518040998211 + }, + "affiliation-group": [ + { + "last-modified-date": { + "value": 1518040998211 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "ext-id-ABC", + "external-id-normalized": { + "value": "ext-id-abc", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": { + "value": "http://tempuri.orgABC" + }, + "external-id-relationship": "self" + } + ] + }, + "summaries": [ + { + "distinction-summary": { + "created-date": { + "value": 1518040998211 + }, + "last-modified-date": { + "value": 1518040998211 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 29770, + "department-name": "Department", + "role-title": "Role", + "start-date": { + "year": { + "value": "2012" + }, + "month": { + "value": "07" + }, + "day": { + "value": "01" + } + }, + "end-date": null, + "organization": { + "name": "Andrew W. Mellon Foundation", + "address": { + "city": "New York", + "region": null, + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "http://dx.doi.org/10.13039/100000873", + "disambiguation-source": "FUNDREF" + } + }, + "url": null, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "ext-id-ABC", + "external-id-normalized": { + "value": "ext-id-abc", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": { + "value": "http://tempuri.orgABC" + }, + "external-id-relationship": "self" + } + ] + }, + "display-index": "0", + "visibility": "public", + "path": "/0000-0002-9227-8514/distinction/29770" + } + } + ] + } + ], + "path": "/0000-0002-9227-8514/distinctions" + }, + "educations": { + "last-modified-date": { + "value": 1477583674474 + }, + "affiliation-group": [ + { + "last-modified-date": { + "value": 1477583674474 + }, + "external-ids": { + "external-id": [] + }, + "summaries": [ + { + "education-summary": { + "created-date": { + "value": 1477583674474 + }, + "last-modified-date": { + "value": 1477583674474 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 22423, + "department-name": "Testing Department", + "role-title": "BA", + "start-date": { + "year": { + "value": "1997" + }, + "month": { + "value": "09" + }, + "day": { + "value": "02" + } + }, + "end-date": { + "year": { + "value": "2001" + }, + "month": { + "value": "05" + }, + "day": { + "value": "15" + } + }, + "organization": { + "name": "Massachusetts Institute of Technology", + "address": { + "city": "Cambridge", + "region": "MA", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "2167", + "disambiguation-source": "RINGGOLD" + } + }, + "url": null, + "external-ids": null, + "display-index": "0", + "visibility": "public", + "path": "/0000-0002-9227-8514/education/22423" + } + } + ] + } + ], + "path": "/0000-0002-9227-8514/educations" + }, + "employments": { + "last-modified-date": { + "value": 1477418605431 + }, + "affiliation-group": [ + { + "last-modified-date": { + "value": 1477418605431 + }, + "external-ids": { + "external-id": [] + }, + "summaries": [ + { + "employment-summary": { + "created-date": { + "value": 1477418605431 + }, + "last-modified-date": { + "value": 1477418605431 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 22411, + "department-name": "QA and Testing", + "role-title": "Test account holder", + "start-date": { + "year": { + "value": "2012" + }, + "month": { + "value": "10" + }, + "day": null + }, + "end-date": null, + "organization": { + "name": "ORCID", + "address": { + "city": "Bethesda", + "region": "MD", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "385488", + "disambiguation-source": "RINGGOLD" + } + }, + "url": null, + "external-ids": null, + "display-index": "0", + "visibility": "public", + "path": "/0000-0002-9227-8514/employment/22411" + } + } + ] + } + ], + "path": "/0000-0002-9227-8514/employments" + }, + "fundings": { + "last-modified-date": { + "value": 1490615782714 + }, + "group": [ + { + "last-modified-date": { + "value": 1490615782714 + }, + "external-ids": { + "external-id": [] + }, + "funding-summary": [ + { + "created-date": { + "value": 1490615782714 + }, + "last-modified-date": { + "value": 1490615782714 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "title": { + "title": { + "value": "Excellence Grant" + }, + "translated-title": null + }, + "external-ids": null, + "url": null, + "type": "grant", + "start-date": { + "year": { + "value": "2017" + }, + "month": { + "value": "03" + }, + "day": null + }, + "end-date": { + "year": { + "value": "2019" + }, + "month": { + "value": "03" + }, + "day": null + }, + "organization": { + "name": "Funding Submission System", + "address": { + "city": "London", + "region": null, + "country": "GB" + }, + "disambiguated-organization": null + }, + "visibility": "public", + "put-code": 6388, + "path": "/0000-0002-9227-8514/funding/6388", + "display-index": "1" + } + ] + }, + { + "last-modified-date": { + "value": 1477578536846 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "12345", + "external-id-normalized": null, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "funding-summary": [ + { + "created-date": { + "value": 1477578536846 + }, + "last-modified-date": { + "value": 1477578536846 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-DAM7BV5YGLGHWOCH", + "path": "APP-DAM7BV5YGLGHWOCH", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Grant System Sandbox Test" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "title": { + "title": { + "value": "Grant title" + }, + "translated-title": null + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "12345", + "external-id-normalized": null, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "url": { + "value": "http://tempuri.org" + }, + "type": "grant", + "start-date": { + "year": { + "value": "1999" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "end-date": { + "year": { + "value": "2001" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "organization": { + "name": "Wellcome Trust", + "address": { + "city": "London", + "region": null, + "country": "GB" + }, + "disambiguated-organization": null + }, + "visibility": "public", + "put-code": 4413, + "path": "/0000-0002-9227-8514/funding/4413", + "display-index": "0" + } + ] + } + ], + "path": "/0000-0002-9227-8514/fundings" + }, + "invited-positions": { + "last-modified-date": { + "value": 1518120857978 + }, + "affiliation-group": [ + { + "last-modified-date": { + "value": 1518120857978 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "grant-identifier-value", + "external-id-normalized": { + "value": "grant-identifier-value", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": { + "value": "http://tempuri.org" + }, + "external-id-relationship": "self" + } + ] + }, + "summaries": [ + { + "invited-position-summary": { + "created-date": { + "value": 1518120857978 + }, + "last-modified-date": { + "value": 1518120857978 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 29778, + "department-name": "Dept Name", + "role-title": "Invited Position Title", + "start-date": { + "year": { + "value": "2018" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "end-date": null, + "organization": { + "name": "University of Michigan", + "address": { + "city": "Ann Arbor", + "region": "MI", + "country": "GB" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "1259", + "disambiguation-source": "RINGGOLD" + } + }, + "url": { + "value": "http://orcid.org/umich" + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "grant-identifier-value", + "external-id-normalized": { + "value": "grant-identifier-value", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": { + "value": "http://tempuri.org" + }, + "external-id-relationship": "self" + } + ] + }, + "display-index": "0", + "visibility": "public", + "path": "/0000-0002-9227-8514/invited-position/29778" + } + } + ] + } + ], + "path": "/0000-0002-9227-8514/invited-positions" + }, + "memberships": { + "last-modified-date": { + "value": 1518121061309 + }, + "affiliation-group": [ + { + "last-modified-date": { + "value": 1518121061309 + }, + "external-ids": { + "external-id": [] + }, + "summaries": [ + { + "membership-summary": { + "created-date": { + "value": 1518121061309 + }, + "last-modified-date": { + "value": 1518121061309 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 29779, + "department-name": null, + "role-title": null, + "start-date": { + "year": { + "value": "2017" + }, + "month": null, + "day": null + }, + "end-date": null, + "organization": { + "name": "Research Data Alliance", + "address": { + "city": "Dublin", + "region": null, + "country": "IE" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "grid.484715.b", + "disambiguation-source": "GRID" + } + }, + "url": null, + "external-ids": null, + "display-index": "0", + "visibility": "public", + "path": "/0000-0002-9227-8514/membership/29779" + } + } + ] + } + ], + "path": "/0000-0002-9227-8514/memberships" + }, + "peer-reviews": { + "last-modified-date": { + "value": 1560828391350 + }, + "group": [ + { + "last-modified-date": { + "value": 1560828391350 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "peer-review", + "external-id-value": "issn:12345678", + "external-id-normalized": null, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": null + } + ] + }, + "peer-review-group": [ + { + "last-modified-date": { + "value": 1547745329373 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1c", + "external-id-normalized": { + "value": "dec1c", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1547745329373 + }, + "last-modified-date": { + "value": 1547745329373 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1c", + "external-id-normalized": { + "value": "dec1c", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2016" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "review-group-id": "issn:12345678", + "convening-organization": { + "name": "ORCID", + "address": { + "city": "Providence", + "region": "RI", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "grid.455335.1", + "disambiguation-source": "GRID" + } + }, + "visibility": "public", + "put-code": 3466, + "path": "/0000-0002-9227-8514/peer-review/3466", + "display-index": "0" + } + ] + }, + { + "last-modified-date": { + "value": 1512151928076 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1b", + "external-id-normalized": { + "value": "dec1b", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1512151928076 + }, + "last-modified-date": { + "value": 1512151928076 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1b", + "external-id-normalized": { + "value": "dec1b", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2016" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "review-group-id": "issn:12345678", + "convening-organization": { + "name": "ORCID", + "address": { + "city": "Providence", + "region": "RI", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "grid.455335.1", + "disambiguation-source": "GRID" + } + }, + "visibility": "public", + "put-code": 2129, + "path": "/0000-0002-9227-8514/peer-review/2129", + "display-index": "0" + } + ] + }, + { + "last-modified-date": { + "value": 1512151819656 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1a", + "external-id-normalized": { + "value": "dec1a", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1512151819656 + }, + "last-modified-date": { + "value": 1512151819656 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1a", + "external-id-normalized": { + "value": "dec1a", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2016" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "review-group-id": "issn:12345678", + "convening-organization": { + "name": "ORDER", + "address": { + "city": "Providence", + "region": "RI", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "1234", + "disambiguation-source": "RINGGOLD" + } + }, + "visibility": "public", + "put-code": 2128, + "path": "/0000-0002-9227-8514/peer-review/2128", + "display-index": "0" + } + ] + }, + { + "last-modified-date": { + "value": 1560828391350 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1d", + "external-id-normalized": { + "value": "dec1d", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1560828391350 + }, + "last-modified-date": { + "value": 1560828391350 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1d", + "external-id-normalized": { + "value": "dec1d", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2016" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "review-group-id": "issn:12345678", + "convening-organization": { + "name": "ORCID", + "address": { + "city": "Providence", + "region": "RI", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "grid.455335.1", + "disambiguation-source": "GRID" + } + }, + "visibility": "public", + "put-code": 3746, + "path": "/0000-0002-9227-8514/peer-review/3746", + "display-index": "0" + } + ] + }, + { + "last-modified-date": { + "value": 1512151792037 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1", + "external-id-normalized": { + "value": "dec1", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1512151792037 + }, + "last-modified-date": { + "value": 1512151792037 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "dec1", + "external-id-normalized": { + "value": "dec1", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2016" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "review-group-id": "issn:12345678", + "convening-organization": { + "name": "ORDER", + "address": { + "city": "Providence", + "region": "RI", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "1234", + "disambiguation-source": "RINGGOLD" + } + }, + "visibility": "public", + "put-code": 2127, + "path": "/0000-0002-9227-8514/peer-review/2127", + "display-index": "0" + } + ] + } + ] + }, + { + "last-modified-date": { + "value": 1477420417267 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "peer-review", + "external-id-value": "issn:1234=1234", + "external-id-normalized": null, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": null + } + ] + }, + "peer-review-group": [ + { + "last-modified-date": { + "value": 1477420417267 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "12345", + "external-id-normalized": { + "value": "12345", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1477420417267 + }, + "last-modified-date": { + "value": 1477420417267 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "12345", + "external-id-normalized": { + "value": "12345", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2016" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "review-group-id": "issn:1234=1234", + "convening-organization": { + "name": "Journal of Psychoceramics", + "address": { + "city": "Providence", + "region": "RI", + "country": "US" + }, + "disambiguated-organization": null + }, + "visibility": "public", + "put-code": 1374, + "path": "/0000-0002-9227-8514/peer-review/1374", + "display-index": "0" + } + ] + } + ] + }, + { + "last-modified-date": { + "value": 1559326773265 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "peer-review", + "external-id-value": "issn:1234-5678", + "external-id-normalized": null, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": null + } + ] + }, + "peer-review-group": [ + { + "last-modified-date": { + "value": 1559326737739 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "doi", + "external-id-value": "10.1000/183_grant", + "external-id-normalized": { + "value": "10.1000/183_grant", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1559326737739 + }, + "last-modified-date": { + "value": 1559326737739 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "doi", + "external-id-value": "10.1000/183_grant", + "external-id-normalized": { + "value": "10.1000/183_grant", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2006" + }, + "month": null, + "day": null + }, + "review-group-id": "issn:1234-5678", + "convening-organization": { + "name": "ORCID", + "address": { + "city": "Bethesda", + "region": "MD", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "385488", + "disambiguation-source": "RINGGOLD" + } + }, + "visibility": "public", + "put-code": 3701, + "path": "/0000-0002-9227-8514/peer-review/3701", + "display-index": "0" + } + ] + }, + { + "last-modified-date": { + "value": 1559326773265 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "55", + "external-id-normalized": { + "value": "55", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "peer-review-summary": [ + { + "created-date": { + "value": 1559326773265 + }, + "last-modified-date": { + "value": 1559326773265 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "reviewer-role": "reviewer", + "external-ids": { + "external-id": [ + { + "external-id-type": "source-work-id", + "external-id-value": "55", + "external-id-normalized": { + "value": "55", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "review-url": null, + "review-type": "review", + "completion-date": { + "year": { + "value": "2006" + }, + "month": null, + "day": null + }, + "review-group-id": "issn:1234-5678", + "convening-organization": { + "name": "ORCID", + "address": { + "city": "Bethesda", + "region": "MD", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "385488", + "disambiguation-source": "RINGGOLD" + } + }, + "visibility": "public", + "put-code": 3702, + "path": "/0000-0002-9227-8514/peer-review/3702", + "display-index": "0" + } + ] + } + ] + } + ], + "path": "/0000-0002-9227-8514/peer-reviews" + }, + "qualifications": { + "last-modified-date": { + "value": 1518040786404 + }, + "affiliation-group": [ + { + "last-modified-date": { + "value": 1518040786404 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "external-identifier-value3", + "external-id-normalized": { + "value": "external-identifier-value3", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": { + "value": "http://tempuri.org3" + }, + "external-id-relationship": "self" + } + ] + }, + "summaries": [ + { + "qualification-summary": { + "created-date": { + "value": 1518040786404 + }, + "last-modified-date": { + "value": 1518040786404 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "put-code": 29769, + "department-name": "Dept Name", + "role-title": "Title", + "start-date": { + "year": { + "value": "2017" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "end-date": { + "year": { + "value": "2018" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "organization": { + "name": "Program 973", + "address": { + "city": "Beijing", + "region": null, + "country": "CN" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "grid.454688.3", + "disambiguation-source": "GRID" + } + }, + "url": { + "value": "http://www.most.gov.cn/eng/" + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "grant_number", + "external-id-value": "external-identifier-value3", + "external-id-normalized": { + "value": "external-identifier-value3", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": { + "value": "http://tempuri.org3" + }, + "external-id-relationship": "self" + } + ] + }, + "display-index": "0", + "visibility": "public", + "path": "/0000-0002-9227-8514/qualification/29769" + } + } + ] + } + ], + "path": "/0000-0002-9227-8514/qualifications" + }, + "research-resources": { + "last-modified-date": { + "value": 1528835735683 + }, + "group": [ + { + "last-modified-date": { + "value": 1528835735683 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "handle", + "external-id-value": "https://grants.net/123456c", + "external-id-normalized": { + "value": "https://grants.net/123456c", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "research-resource-summary": [ + { + "created-date": { + "value": 1528835735683 + }, + "last-modified-date": { + "value": 1528835735683 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "proposal": { + "title": { + "title": { + "value": "The best ever Giant Laser Award" + }, + "translated-title": { + "value": "Giant Laser Award", + "language-code": "en" + } + }, + "hosts": { + "organization": [ + { + "name": "National Science Foundation", + "address": { + "city": "Arlington", + "region": "VA", + "country": "US" + }, + "disambiguated-organization": { + "disambiguated-organization-identifier": "grid.431093.c", + "disambiguation-source": "GRID" + } + } + ] + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "handle", + "external-id-value": "https://grants.net/123456c", + "external-id-normalized": { + "value": "https://grants.net/123456c", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "start-date": { + "year": { + "value": "2018" + }, + "month": { + "value": "02" + }, + "day": { + "value": "02" + } + }, + "end-date": { + "year": { + "value": "2018" + }, + "month": { + "value": "02" + }, + "day": { + "value": "10" + } + }, + "url": { + "value": "http://xsede.org/GiantLaserAward" + } + }, + "visibility": "public", + "put-code": 1000, + "path": "/0000-0002-9227-8514/research-resource/1000", + "display-index": "0" + } + ] + } + ], + "path": "/0000-0002-9227-8514/research-resources" + }, + "services": { + "last-modified-date": null, + "affiliation-group": [], + "path": "/0000-0002-9227-8514/services" + }, + "works": { + "last-modified-date": { + "value": 1617028188646 + }, + "group": [ + { + "last-modified-date": { + "value": 1556533430249 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "handle", + "external-id-value": "12345678", + "external-id-normalized": { + "value": "12345678", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "work-summary": [ + { + "put-code": 1022665, + "created-date": { + "value": 1556533430249 + }, + "last-modified-date": { + "value": 1556533430249 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "title": { + "title": { + "value": "ORCID: a system to uniquely identify researchers" + }, + "subtitle": null, + "translated-title": null + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "handle", + "external-id-value": "12345678", + "external-id-normalized": { + "value": "12345678", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "url": null, + "type": "journal-article", + "publication-date": { + "year": { + "value": "2019" + }, + "month": { + "value": "01" + }, + "day": { + "value": "01" + } + }, + "journal-title": null, + "visibility": "public", + "path": "/0000-0002-9227-8514/work/1022665", + "display-index": "1" + } + ] + }, + { + "last-modified-date": { + "value": 1561445646673 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "doi", + "external-id-value": "10.1111/test.12241", + "external-id-normalized": { + "value": "10.1111/test.12241", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "work-summary": [ + { + "put-code": 1045646, + "created-date": { + "value": 1561445646673 + }, + "last-modified-date": { + "value": 1561445646673 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "title": { + "title": { + "value": "ORCID: a system to uniquely identify researchers" + }, + "subtitle": null, + "translated-title": null + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "doi", + "external-id-value": "10.1111/test.12241", + "external-id-normalized": { + "value": "10.1111/test.12241", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "url": null, + "type": "journal-article", + "publication-date": { + "year": { + "value": "2019" + }, + "month": { + "value": "01" + }, + "day": { + "value": "01" + } + }, + "journal-title": null, + "visibility": "public", + "path": "/0000-0002-9227-8514/work/1045646", + "display-index": "1" + } + ] + }, + { + "last-modified-date": { + "value": 1617028188646 + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "doi", + "external-id-value": "10.1087/20120404", + "external-id-normalized": { + "value": "10.1087/20120404", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "work-summary": [ + { + "put-code": 733536, + "created-date": { + "value": 1477419746443 + }, + "last-modified-date": { + "value": 1617028188646 + }, + "source": { + "source-orcid": { + "uri": "https://sandbox.orcid.org/0000-0002-9227-8514", + "path": "0000-0002-9227-8514", + "host": "sandbox.orcid.org" + }, + "source-client-id": null, + "source-name": { + "value": "Sofia Maria Hernandez Garcia" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "title": { + "title": { + "value": "ORCID: a system to uniquely identify researchers" + }, + "subtitle": null, + "translated-title": null + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "doi", + "external-id-value": "10.1087/20120404", + "external-id-normalized": { + "value": "10.1087/20120404", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + }, + { + "external-id-type": "issn", + "external-id-value": "1741-4857", + "external-id-normalized": { + "value": "1741-4857", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "part-of" + } + ] + }, + "url": null, + "type": "journal-article", + "publication-date": { + "year": { + "value": "2012" + }, + "month": { + "value": "10" + }, + "day": { + "value": "01" + } + }, + "journal-title": { + "value": "Learned Publishing" + }, + "visibility": "public", + "path": "/0000-0002-9227-8514/work/733536", + "display-index": "7" + }, + { + "put-code": 733535, + "created-date": { + "value": 1477419565672 + }, + "last-modified-date": { + "value": 1617028178125 + }, + "source": { + "source-orcid": null, + "source-client-id": { + "uri": "https://sandbox.orcid.org/client/APP-674MCQQR985VZZQ2", + "path": "APP-674MCQQR985VZZQ2", + "host": "sandbox.orcid.org" + }, + "source-name": { + "value": "Regional University" + }, + "assertion-origin-orcid": null, + "assertion-origin-client-id": null, + "assertion-origin-name": null + }, + "title": { + "title": { + "value": "ORCID: a system to uniquely identify researchers" + }, + "subtitle": null, + "translated-title": null + }, + "external-ids": { + "external-id": [ + { + "external-id-type": "doi", + "external-id-value": "10.1087/20120404", + "external-id-normalized": { + "value": "10.1087/20120404", + "transient": true + }, + "external-id-normalized-error": null, + "external-id-url": null, + "external-id-relationship": "self" + } + ] + }, + "url": null, + "type": "journal-article", + "publication-date": { + "year": { + "value": "2012" + }, + "month": { + "value": "10" + }, + "day": { + "value": "01" + } + }, + "journal-title": null, + "visibility": "public", + "path": "/0000-0002-9227-8514/work/733535", + "display-index": "6" + } + ] + } + ], + "path": "/0000-0002-9227-8514/works" + }, + "path": "/0000-0002-9227-8514/activities" + }, + "path": "/0000-0002-9227-8514" +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs new file mode 100644 index 00000000..f885080d --- /dev/null +++ b/aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs @@ -0,0 +1,355 @@ +using Xunit; +using System.Collections.Generic; +using api.Services; +using api.Models; +using api.Models.Common; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; +using api.Models.Ttv; + +namespace api.Tests +{ + [Collection("Duplicate handler service tests.")] + public class DuplicateHandlerServiceTests_HasSameDoiButIsDifferentPublication + { + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication type code is not A3, A4, B2, B3, D2, D3 or E1")] + public void hasSameDoiButIsDifferentPublication_010() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "code123" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as different publications: Virta publication has type code A3, names differ.")] + public void hasSameDoiButIsDifferentPublication_020() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name456", TypeCode = "A3" }; + Assert.True(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication has type code A3, both have the same name.")] + public void hasSameDoiButIsDifferentPublication_030() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "A3" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as different publications: Virta publication has type code A4, names differ.")] + public void hasSameDoiButIsDifferentPublication_040() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name456", TypeCode = "A4" }; + Assert.True(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication has type code A4, both have the same name.")] + public void hasSameDoiButIsDifferentPublication_050() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "A4" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as different publications: Virta publication has type code B2, names differ.")] + public void hasSameDoiButIsDifferentPublication_060() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name456", TypeCode = "B2" }; + Assert.True(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication has type code B2, both have the same name.")] + public void hasSameDoiButIsDifferentPublication_070() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "B2" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as different publications: Virta publication has type code B3, names differ.")] + public void hasSameDoiButIsDifferentPublication_080() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name456", TypeCode = "B3" }; + Assert.True(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication has type code B3, both have the same name.")] + public void hasSameDoiButIsDifferentPublication_090() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "B3" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as different publications: Virta publication has type code D2, names differ.")] + public void hasSameDoiButIsDifferentPublication_100() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name456", TypeCode = "D2" }; + Assert.True(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication has type code D2, both have the same name.")] + public void hasSameDoiButIsDifferentPublication_110() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "D2" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as different publications: Virta publication has type code D3, names differ.")] + public void hasSameDoiButIsDifferentPublication_120() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name456", TypeCode = "D3" }; + Assert.True(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication has type code D3, both have the same name.")] + public void hasSameDoiButIsDifferentPublication_130() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "D3" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as different publications: Virta publication has type code E1, names differ.")] + public void hasSameDoiButIsDifferentPublication_140() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name456", TypeCode = "E1" }; + Assert.True(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + [Fact(DisplayName = "Virta and ORCID publication have the same DOI, they are considered as the same publications: Virta publication has type code E1, both have the same name.")] + public void hasSameDoiButIsDifferentPublication_150() + { + DuplicateHandlerService duplicateHandlerService = new(); + DimOrcidPublication dimOrcidPublication = new() { DoiHandle = "doi123", PublicationName = "name123" }; + ProfileEditorPublication profileEditorPublication = new() { Doi = "doi123", PublicationName = "name123", TypeCode = "E1" }; + Assert.False(duplicateHandlerService.HasSameDoiButIsDifferentPublication(dimOrcidPublication.PublicationName, profileEditorPublication)); + } + + + + + [Fact(DisplayName = "AddPublicationToProfileEditorData_HandlePublicationIdDuplicates")] + public void addPublicationToProfileEditorData_010() + { + DuplicateHandlerService duplicateHandlerService = new(); + + // Datasources + ProfileEditorSource profileEditorSourceA = new() + { + Id = 1, + RegisteredDataSource = "Source A", + Organization = new Organization() { NameEn = "Organization name A" } + }; + ProfileEditorSource profileEditorSourceB = new() + { + Id = 2, + RegisteredDataSource = "Source B", + Organization = new Organization() { NameEn = "Organization name B" } + }; + + // Create ProfileDataRaw for Virta publication 1 + ProfileDataFromSql profileDataVirta1 = new() + { + DimPublication_PublicationId = "publicationId123", + DimPublication_Doi = "doi123", + DimPublication_PublicationName = "name123", + DimPublication_PublicationTypeCode = "A1", + DimFieldDisplaySettings_FieldIdentifier = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION + }; + + // Create ProfileDataRaw for Virta publication 2 + ProfileDataFromSql profileDataVirta2 = new() + { + DimPublication_PublicationId = "publicationId456", + DimPublication_Doi = "doi456", + DimPublication_PublicationName = "name456", + DimPublication_PublicationTypeCode = "A2", + DimFieldDisplaySettings_FieldIdentifier = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION + }; + + // Create empty list of publications + List publications = new(); + + // + // Add publication 1st time + // + List publications1 = duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSourceA, + profileData: profileDataVirta1, + publications: publications + ); + // Check that publication list contains one publication + Assert.Single(publications1); + // Check publication values + Assert.Equal("publicationId123", publications1[0].PublicationId); + Assert.Equal("doi123", publications1[0].Doi); + Assert.Equal("name123", publications1[0].PublicationName); + Assert.Single(publications1[0].DataSources); + Assert.Equal(profileEditorSourceA.RegisteredDataSource, publications1[0].DataSources[0].RegisteredDataSource); + + // + // Add the same publication 2nd time, the same data source + // + List publications2 = duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSourceA, + profileData: profileDataVirta1, + publications: publications1 + ); + // Check that publication list contains one publication + Assert.Single(publications2); + // Check publication values + Assert.Equal("publicationId123", publications2[0].PublicationId); + Assert.Equal("doi123", publications2[0].Doi); + Assert.Equal("name123", publications2[0].PublicationName); + Assert.Single(publications2[0].DataSources); + Assert.Equal(profileEditorSourceA.RegisteredDataSource, publications2[0].DataSources[0].RegisteredDataSource); + + // + // Add the same publication 3rd time, different data source (profileEditorSourceB) + // + List publications3 = duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSourceB, + profileData: profileDataVirta1, + publications: publications2 + ); + + // Check that publication list contains one publication + Assert.Single(publications3); + // Check publication values + Assert.Equal("publicationId123", publications3[0].PublicationId); + Assert.Equal("doi123", publications3[0].Doi); + Assert.Equal("name123", publications3[0].PublicationName); + Assert.Equal(2, publications3[0].DataSources.Count); + Assert.Equal(profileEditorSourceA.RegisteredDataSource, publications3[0].DataSources[0].RegisteredDataSource); + Assert.Equal(profileEditorSourceB.RegisteredDataSource, publications3[0].DataSources[1].RegisteredDataSource); + + // + // Add different publication + // + List publications4 = duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSourceA, + profileData: profileDataVirta2, + publications: publications3 + ); + // Check that publication list contains two publications + Assert.Equal(2, publications4.Count); + // Check 1st publication values + Assert.Equal("publicationId123", publications4[0].PublicationId); + Assert.Equal("doi123", publications4[0].Doi); + Assert.Equal("name123", publications4[0].PublicationName); + Assert.Equal(2, publications4[0].DataSources.Count); + Assert.Equal(profileEditorSourceA.RegisteredDataSource, publications4[0].DataSources[0].RegisteredDataSource); + Assert.Equal(profileEditorSourceB.RegisteredDataSource, publications4[0].DataSources[1].RegisteredDataSource); + // Check 2nd publication values + Assert.Equal("publicationId456", publications4[1].PublicationId); + Assert.Equal("doi456", publications4[1].Doi); + Assert.Equal("name456", publications4[1].PublicationName); + Assert.Single(publications4[1].DataSources); + Assert.Equal(profileEditorSourceA.RegisteredDataSource, publications4[0].DataSources[0].RegisteredDataSource); + } + + + + + [Fact(DisplayName = "AddPublicationToProfileEditorData_HandleDoiDuplicates")] + public void addPublicationToProfileEditorData_020() + { + DuplicateHandlerService duplicateHandlerService = new(); + + // Datasources + ProfileEditorSource profileEditorSourceVirta = new() + { + Id = 1, + RegisteredDataSource = "Virta", + Organization = new Organization() {} + }; + ProfileEditorSource profileEditorSourceOrcid = new() + { + Id = 2, + RegisteredDataSource = "ORCID", + Organization = new Organization() {} + }; + + // Create ProfileDataRaw for Virta publication 1 + ProfileDataFromSql profileDataVirta1 = new() + { + DimPublication_PublicationId = "publicationId123", + DimPublication_Doi = "doi123", + DimPublication_PublicationName = "name123", + DimPublication_PublicationTypeCode = "A4", + DimFieldDisplaySettings_FieldIdentifier = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION + }; + + // Create ProfileDataRaw for ORCID publication 1. The same DOI and name as in Virta publication. + ProfileDataFromSql profileDataOrcid1 = new() + { + DimOrcidPublication_PublicationId = "publicationId456", + DimOrcidPublication_Doi = "doi123", + DimOrcidPublication_PublicationName = "name123", + DimFieldDisplaySettings_FieldIdentifier = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID + }; + + // Create ProfileDataRaw for ORCID publication 2. The same DOI as in Virta publication but different name. + ProfileDataFromSql profileDataOrcid2 = new() + { + DimOrcidPublication_PublicationId = "publicationId789", + DimOrcidPublication_Doi = "doi123", + DimOrcidPublication_PublicationName = "name456", + DimFieldDisplaySettings_FieldIdentifier = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID + }; + + // Create empty list of publications + List publications = new(); + + // Add Virta publication + List publications1 = duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSourceVirta, + profileData: profileDataVirta1, + publications: publications + ); + // Add ORCID publication with the same DOI and name + List publications2 = duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSourceOrcid, + profileData: profileDataOrcid1, + publications: publications1 + ); + // Check that publication list contains one publication + Assert.Single(publications2); + // Check that publication has two data sources + Assert.Equal(2, publications2[0].DataSources.Count); + Assert.Equal(profileEditorSourceVirta.RegisteredDataSource, publications2[0].DataSources[0].RegisteredDataSource); + Assert.Equal(profileEditorSourceOrcid.RegisteredDataSource, publications2[0].DataSources[1].RegisteredDataSource); + + // Add ORCID publication with the same DOI but different name + List publications3 = duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSourceOrcid, + profileData: profileDataOrcid2, + publications: publications2 + ); + // Check that publication list contains two publications + Assert.Equal(2, publications3.Count); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/LanguageServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/LanguageServiceTest.cs new file mode 100644 index 00000000..49489bfe --- /dev/null +++ b/aspnetcore/src/api.Tests/Services_Tests/LanguageServiceTest.cs @@ -0,0 +1,165 @@ +using Xunit; +using api.Services; + +namespace api.Tests +{ + [Collection("Language service tests")] + public class LanguageServiceTests + { + [Fact(DisplayName = "Get NameTranslation: FI, EN and SV are defined.")] + public void getNameTranslation_01() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "A", nameEn: "B", nameSv: "C"); + + Assert.Equal("A", nameTranslation.NameFi); + Assert.Equal("B", nameTranslation.NameEn); + Assert.Equal("C", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of FI is used for EN and SV, when EN and SV are empty strings.")] + public void getNameTranslation_02() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "A", nameEn: "", nameSv: ""); + + Assert.Equal("A", nameTranslation.NameFi); + Assert.Equal("A", nameTranslation.NameEn); + Assert.Equal("A", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of FI is used for EN and SV, when EN and SV are null.")] + public void getNameTranslation_03() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "A", nameEn: null, nameSv: null); + + Assert.Equal("A", nameTranslation.NameFi); + Assert.Equal("A", nameTranslation.NameEn); + Assert.Equal("A", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of EN is used for FI and SV, when FI and SV are empty strings.")] + public void getNameTranslation_04() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "", nameEn: "B", nameSv: ""); + + Assert.Equal("B", nameTranslation.NameFi); + Assert.Equal("B", nameTranslation.NameEn); + Assert.Equal("B", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of EN is used for FI and SV, when FI and SV are null.")] + public void getNameTranslation_05() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: null, nameEn: "B", nameSv: null); + + Assert.Equal("B", nameTranslation.NameFi); + Assert.Equal("B", nameTranslation.NameEn); + Assert.Equal("B", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of SV is used for FI and EN, when FI and EN are empty strings.")] + public void getNameTranslation_06() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "", nameEn: "", nameSv: "C"); + + Assert.Equal("C", nameTranslation.NameFi); + Assert.Equal("C", nameTranslation.NameEn); + Assert.Equal("C", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of SV is used for FI and EN, when FI and EN are null.")] + public void getNameTranslation_07() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: null, nameEn: null, nameSv: "C"); + + Assert.Equal("C", nameTranslation.NameFi); + Assert.Equal("C", nameTranslation.NameEn); + Assert.Equal("C", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of FI is used for SV, when SV is an empty string.")] + public void getNameTranslation_08() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "A", nameEn: "B", nameSv: ""); + + Assert.Equal("A", nameTranslation.NameFi); + Assert.Equal("B", nameTranslation.NameEn); + Assert.Equal("A", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of FI is used for SV, when SV is null.")] + public void getNameTranslation_09() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "A", nameEn: "B", nameSv: null); + + Assert.Equal("A", nameTranslation.NameFi); + Assert.Equal("B", nameTranslation.NameEn); + Assert.Equal("A", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of FI is used for EN, when EN is an empty string.")] + public void getNameTranslation_10() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "A", nameEn: "", nameSv: "B"); + + Assert.Equal("A", nameTranslation.NameFi); + Assert.Equal("A", nameTranslation.NameEn); + Assert.Equal("B", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of FI is used for EN, when EN is null.")] + public void getNameTranslation_11() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "A", nameEn: null, nameSv: "B"); + + Assert.Equal("A", nameTranslation.NameFi); + Assert.Equal("A", nameTranslation.NameEn); + Assert.Equal("B", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of SV is used for FI, when FI is an empty string.")] + public void getNameTranslation_12() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: "", nameEn: "A", nameSv: "B"); + + Assert.Equal("B", nameTranslation.NameFi); + Assert.Equal("A", nameTranslation.NameEn); + Assert.Equal("B", nameTranslation.NameSv); + } + + [Fact(DisplayName = "Get NameTranslation: value of SV is used for FI, when FI is null.")] + public void getNameTranslation_13() + { + var languageService = new LanguageService(); + + var nameTranslation = languageService.GetNameTranslation(nameFi: null, nameEn: "A", nameSv: "B"); + + Assert.Equal("B", nameTranslation.NameFi); + Assert.Equal("A", nameTranslation.NameEn); + Assert.Equal("B", nameTranslation.NameSv); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserTest.cs b/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserServiceTest.cs similarity index 78% rename from aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserTest.cs rename to aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserServiceTest.cs index 134d1354..29474a91 100644 --- a/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserTest.cs +++ b/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserServiceTest.cs @@ -1,17 +1,17 @@ -using System; -using Xunit; -using api.Services; -using api.Models.Orcid; +using System; +using Xunit; +using api.Services; +using api.Models.Orcid; using System.IO; using System.Collections.Generic; -namespace api.Tests -{ - [Collection("Parsing data from ORCID record json")] - public class OrcidJsonParserTests +namespace api.Tests +{ + [Collection("Parsing data from ORCID record json")] + public class OrcidJsonParserServiceTests { // Get ORCID record. - // Test file is a copy of ORCID's sandbox https://pub.sandbox.orcid.org/v3.0/0000-0002-9227-8514/record + // Test file is based on ORCID's sandbox record https://pub.sandbox.orcid.org/v3.0/0000-0002-9227-8514/record private string getOrcidJsonRecord() { var filePath = $@"{Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName}/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json"; @@ -19,8 +19,16 @@ private string getOrcidJsonRecord() return reader.ReadToEnd(); } + // Get ORCID record which does not contain name or other name. + private string getOrcidJsonRecord_NoNames() + { + var filePath = $@"{Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName}/Infrastructure/orcidSandbox_0000-0002-9227-8514_record_no_names.json"; + var reader = new StreamReader(filePath); + return reader.ReadToEnd(); + } + // Get ORCID personal details. - // Test file is a copy of ORCID's sandbox https://pub.sandbox.orcid.org/v3.0/0000-0002-9227-8514/personal-details + // Test file is based on ORCID's sandbox record https://pub.sandbox.orcid.org/v3.0/0000-0002-9227-8514/personal-details private string getOrcidJsonPersonalDetails() { var filePath = $@"{Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName}/Infrastructure/orcidSandbox_0000-0002-9227-8514_personal-details.json"; @@ -28,78 +36,108 @@ private string getOrcidJsonPersonalDetails() return reader.ReadToEnd(); } - [Fact(DisplayName = "Get given names from full ORCID record")] - public void TestGetGivenNamesFromRecord() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedGivenNames = new OrcidGivenNames("Sofia"); - var actualGivenNames = orcidJsonParserService.GetGivenNames(jsonStr); - Assert.Equal(expectedGivenNames.Value, actualGivenNames.Value); + [Fact(DisplayName = "Get given names from full ORCID record")] + public void TestGetGivenNamesFromRecord() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedGivenNames = new OrcidGivenNames("Sofia"); + var actualGivenNames = orcidJsonParserService.GetGivenNames(jsonStr); + Assert.Equal(expectedGivenNames.Value, actualGivenNames.Value); + } + + [Fact(DisplayName = "Get given names from full ORCID record - handle missing name")] + public void TestGetGivenNames_NameIsNull() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord_NoNames(); + var expectedGivenNames = new OrcidGivenNames(""); + var actualGivenNames = orcidJsonParserService.GetGivenNames(jsonStr); + Assert.Equal(expectedGivenNames.Value, actualGivenNames.Value); } - [Fact(DisplayName = "Get given names from personal details")] - public void TestGetGivenNamesFromPersonalDetails() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonPersonalDetails(); - var expectedGivenNames = new OrcidGivenNames("Sofia"); - var actualGivenNames = orcidJsonParserService.GetGivenNames(jsonStr); - Assert.Equal(expectedGivenNames.Value, actualGivenNames.Value); + [Fact(DisplayName = "Get given names from personal details")] + public void TestGetGivenNamesFromPersonalDetails() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonPersonalDetails(); + var expectedGivenNames = new OrcidGivenNames("Sofia"); + var actualGivenNames = orcidJsonParserService.GetGivenNames(jsonStr); + Assert.Equal(expectedGivenNames.Value, actualGivenNames.Value); } - [Fact(DisplayName = "Get family name from full ORCID record")] - public void TestGetFamilyNameFromRecord() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedFamilyName = new OrcidFamilyName("Garcia"); - var actualFamilyName = orcidJsonParserService.GetFamilyName(jsonStr); - Assert.Equal(expectedFamilyName.Value, actualFamilyName.Value); + [Fact(DisplayName = "Get family name from full ORCID record")] + public void TestGetFamilyNameFromRecord() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedFamilyName = new OrcidFamilyName("Garcia"); + var actualFamilyName = orcidJsonParserService.GetFamilyName(jsonStr); + Assert.Equal(expectedFamilyName.Value, actualFamilyName.Value); } - [Fact(DisplayName = "Get family name from personal details")] - public void TestGetFamilyNameFromPersonalDetails() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonPersonalDetails(); - var expectedFamilyName = new OrcidFamilyName("Garcia"); - var actualFamilyName = orcidJsonParserService.GetFamilyName(jsonStr); - Assert.Equal(expectedFamilyName.Value, actualFamilyName.Value); + [Fact(DisplayName = "Get family name from full ORCID record - handle missing name")] + public void TestGetFamilyName_NameIsNull() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord_NoNames(); + var expectedFamilyName = new OrcidFamilyName(""); + var actualFamilyName = orcidJsonParserService.GetFamilyName(jsonStr); + Assert.Equal(expectedFamilyName.Value, actualFamilyName.Value); } - [Fact(DisplayName = "Get credit name from full ORCID record")] - public void TestGetCreditNameFromRecord() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedCreditName = new OrcidCreditName("Sofia Maria Hernandez Garcia"); - var actualCreditName = orcidJsonParserService.GetCreditName(jsonStr); - Assert.Equal(expectedCreditName.Value, actualCreditName.Value); + [Fact(DisplayName = "Get family name from personal details")] + public void TestGetFamilyNameFromPersonalDetails() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonPersonalDetails(); + var expectedFamilyName = new OrcidFamilyName("Garcia"); + var actualFamilyName = orcidJsonParserService.GetFamilyName(jsonStr); + Assert.Equal(expectedFamilyName.Value, actualFamilyName.Value); } - [Fact(DisplayName = "Get credit name from personal details")] - public void TestGetCreditNameFromPersonalDetails() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonPersonalDetails(); - var expectedCreditName = new OrcidCreditName("Sofia Maria Hernandez Garcia"); - var actualCreditName = orcidJsonParserService.GetCreditName(jsonStr); - Assert.Equal(expectedCreditName.Value, actualCreditName.Value); + [Fact(DisplayName = "Get credit name from full ORCID record")] + public void TestGetCreditNameFromRecord() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedCreditName = new OrcidCreditName("Sofia Maria Hernandez Garcia"); + var actualCreditName = orcidJsonParserService.GetCreditName(jsonStr); + Assert.Equal(expectedCreditName.Value, actualCreditName.Value); } - [Fact(DisplayName = "Get other names from full ORCID record")] - public void TestGetOtherNamesFromRecord() - { - var jsonStr = getOrcidJsonRecord(); - var orcidJsonParserService = new OrcidJsonParserService(); - var actualOtherNames = orcidJsonParserService.GetOtherNames(jsonStr); - var expectedOtherNames = new List { }; - expectedOtherNames.Add( - new OrcidOtherName( - "Sofia Maria Garcia", - new OrcidPutCode(15812) - ) + [Fact(DisplayName = "Get credit name from full ORCID record - handle missing name")] + public void TestGetCreditName_NameIsNull() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord_NoNames(); + var expectedCreditName = new OrcidCreditName(""); + var actualCreditName = orcidJsonParserService.GetCreditName(jsonStr); + Assert.Equal(expectedCreditName.Value, actualCreditName.Value); + } + + [Fact(DisplayName = "Get credit name from personal details")] + public void TestGetCreditNameFromPersonalDetails() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonPersonalDetails(); + var expectedCreditName = new OrcidCreditName("Sofia Maria Hernandez Garcia"); + var actualCreditName = orcidJsonParserService.GetCreditName(jsonStr); + Assert.Equal(expectedCreditName.Value, actualCreditName.Value); + } + + [Fact(DisplayName = "Get other names from full ORCID record")] + public void TestGetOtherNamesFromRecord() + { + var jsonStr = getOrcidJsonRecord(); + var orcidJsonParserService = new OrcidJsonParserService(); + var actualOtherNames = orcidJsonParserService.GetOtherNames(jsonStr); + var expectedOtherNames = new List { }; + expectedOtherNames.Add( + new OrcidOtherName( + "Sofia Maria Garcia", + new OrcidPutCode(15812) + ) ); expectedOtherNames.Add( new OrcidOtherName( @@ -112,27 +150,27 @@ public void TestGetOtherNamesFromRecord() "索菲亚玛丽亚 加西亚", new OrcidPutCode(15814) ) - ); - Assert.Equal(expectedOtherNames[0].Value, actualOtherNames[0].Value); - Assert.Equal(expectedOtherNames[1].Value, actualOtherNames[1].Value); - Assert.Equal(expectedOtherNames[2].Value, actualOtherNames[2].Value); - Assert.Equal(expectedOtherNames[0].PutCode.Value, actualOtherNames[0].PutCode.Value); - Assert.Equal(expectedOtherNames[1].PutCode.Value, actualOtherNames[1].PutCode.Value); - Assert.Equal(expectedOtherNames[2].PutCode.Value, actualOtherNames[2].PutCode.Value); + ); + Assert.Equal(expectedOtherNames[0].Value, actualOtherNames[0].Value); + Assert.Equal(expectedOtherNames[1].Value, actualOtherNames[1].Value); + Assert.Equal(expectedOtherNames[2].Value, actualOtherNames[2].Value); + Assert.Equal(expectedOtherNames[0].PutCode.Value, actualOtherNames[0].PutCode.Value); + Assert.Equal(expectedOtherNames[1].PutCode.Value, actualOtherNames[1].PutCode.Value); + Assert.Equal(expectedOtherNames[2].PutCode.Value, actualOtherNames[2].PutCode.Value); } - [Fact(DisplayName = "Get other names from personal details")] - public void TestGetOtherNamesFromPersonalDetails() - { - var jsonStr = getOrcidJsonPersonalDetails(); - var orcidJsonParserService = new OrcidJsonParserService(); - var actualOtherNames = orcidJsonParserService.GetOtherNames(jsonStr); - var expectedOtherNames = new List { }; - expectedOtherNames.Add( - new OrcidOtherName( - "Sofia Maria Garcia", - new OrcidPutCode(15812) - ) + [Fact(DisplayName = "Get other names from personal details")] + public void TestGetOtherNamesFromPersonalDetails() + { + var jsonStr = getOrcidJsonPersonalDetails(); + var orcidJsonParserService = new OrcidJsonParserService(); + var actualOtherNames = orcidJsonParserService.GetOtherNames(jsonStr); + var expectedOtherNames = new List { }; + expectedOtherNames.Add( + new OrcidOtherName( + "Sofia Maria Garcia", + new OrcidPutCode(15812) + ) ); expectedOtherNames.Add( new OrcidOtherName( @@ -145,222 +183,248 @@ public void TestGetOtherNamesFromPersonalDetails() "索菲亚玛丽亚 加西亚", new OrcidPutCode(15814) ) - ); - Assert.Equal(expectedOtherNames[0].Value, actualOtherNames[0].Value); - Assert.Equal(expectedOtherNames[1].Value, actualOtherNames[1].Value); - Assert.Equal(expectedOtherNames[2].Value, actualOtherNames[2].Value); - Assert.Equal(expectedOtherNames[0].PutCode.Value, actualOtherNames[0].PutCode.Value); - Assert.Equal(expectedOtherNames[1].PutCode.Value, actualOtherNames[1].PutCode.Value); - Assert.Equal(expectedOtherNames[2].PutCode.Value, actualOtherNames[2].PutCode.Value); - } - - [Fact(DisplayName = "Get biography from full ORCID record")] - public void TestGetBiography() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedBiography = new OrcidBiography( - "Sofia Maria Hernandez Garcia is the researcher that is used as an example ORCID record holder." - ); - var actualBiography = orcidJsonParserService.GetBiography(jsonStr); - Assert.Equal(expectedBiography.Value, actualBiography.Value); + ); + Assert.Equal(expectedOtherNames[0].Value, actualOtherNames[0].Value); + Assert.Equal(expectedOtherNames[1].Value, actualOtherNames[1].Value); + Assert.Equal(expectedOtherNames[2].Value, actualOtherNames[2].Value); + Assert.Equal(expectedOtherNames[0].PutCode.Value, actualOtherNames[0].PutCode.Value); + Assert.Equal(expectedOtherNames[1].PutCode.Value, actualOtherNames[1].PutCode.Value); + Assert.Equal(expectedOtherNames[2].PutCode.Value, actualOtherNames[2].PutCode.Value); + } + + [Fact(DisplayName = "Get biography from full ORCID record")] + public void TestGetBiography() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedBiography = new OrcidBiography( + "Sofia Maria Hernandez Garcia is the researcher that is used as an example ORCID record holder." + ); + var actualBiography = orcidJsonParserService.GetBiography(jsonStr); + Assert.Equal(expectedBiography.Value, actualBiography.Value); } - [Fact(DisplayName = "Get biography from personal details")] - public void TestGetBiographyFromPersonalDetails() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonPersonalDetails(); - var expectedBiography = new OrcidBiography( - "Sofia Maria Hernandez Garcia is the researcher that is used as an example ORCID record holder." - ); - var actualBiography = orcidJsonParserService.GetBiography(jsonStr); - Assert.Equal(expectedBiography.Value, actualBiography.Value); + [Fact(DisplayName = "Get biography from personal details")] + public void TestGetBiographyFromPersonalDetails() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonPersonalDetails(); + var expectedBiography = new OrcidBiography( + "Sofia Maria Hernandez Garcia is the researcher that is used as an example ORCID record holder." + ); + var actualBiography = orcidJsonParserService.GetBiography(jsonStr); + Assert.Equal(expectedBiography.Value, actualBiography.Value); } - [Fact(DisplayName = "Get researcher urls")] - public void TestGetResearcherUrls() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedResearcherUrls = new List { - new OrcidResearcherUrl( - "Twitter", - "https://twitter.com/ORCIDsofia", - new OrcidPutCode(41387) - ) - }; - var actualResearcherUrls = orcidJsonParserService.GetResearcherUrls(jsonStr); - - Assert.Equal(expectedResearcherUrls[0].UrlName, actualResearcherUrls[0].UrlName); - Assert.Equal(expectedResearcherUrls[0].Url, actualResearcherUrls[0].Url); - Assert.Equal(expectedResearcherUrls[0].PutCode.Value, actualResearcherUrls[0].PutCode.Value); + [Fact(DisplayName = "Get researcher urls")] + public void TestGetResearcherUrls() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedResearcherUrls = new List { + new OrcidResearcherUrl( + "Twitter", + "https://twitter.com/ORCIDsofia", + new OrcidPutCode(41387) + ) + }; + var actualResearcherUrls = orcidJsonParserService.GetResearcherUrls(jsonStr); + + Assert.Equal(expectedResearcherUrls[0].UrlName, actualResearcherUrls[0].UrlName); + Assert.Equal(expectedResearcherUrls[0].Url, actualResearcherUrls[0].Url); + Assert.Equal(expectedResearcherUrls[0].PutCode.Value, actualResearcherUrls[0].PutCode.Value); } - [Fact(DisplayName = "Get emails")] - public void TestGetEmails() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedEmails = new List { - new OrcidEmail( - "s.garcia@orcid.org", - new OrcidPutCode(null) + [Fact(DisplayName = "Get emails")] + public void TestGetEmails() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedEmails = new List { + new OrcidEmail( + "s.garcia@orcid.org", + new OrcidPutCode(null) ) - }; - var actualEmails = orcidJsonParserService.GetEmails(jsonStr); - - Assert.Equal(expectedEmails[0].Value, actualEmails[0].Value); - Assert.Equal(expectedEmails[0].PutCode.Value, actualEmails[0].PutCode.Value); + }; + var actualEmails = orcidJsonParserService.GetEmails(jsonStr); + + Assert.Equal(expectedEmails[0].Value, actualEmails[0].Value); + Assert.Equal(expectedEmails[0].PutCode.Value, actualEmails[0].PutCode.Value); } - [Fact(DisplayName = "Get keywords")] - public void TestGetKeywords() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedKeywords = new List { - new OrcidKeyword( - "QA and testing", - new OrcidPutCode(4504) - ), - // "QA and testing" is intentionally twice, because the example record has a duplicate - new OrcidKeyword( - "QA and testing", - new OrcidPutCode(4603) - ), - new OrcidKeyword( - "Additional keyword", - new OrcidPutCode(4604) - ) - }; - var actualKeywords = orcidJsonParserService.GetKeywords(jsonStr); - - Assert.Equal(expectedKeywords[0].Value, actualKeywords[0].Value); - Assert.Equal(expectedKeywords[1].Value, actualKeywords[1].Value); - Assert.Equal(expectedKeywords[2].Value, actualKeywords[2].Value); - Assert.Equal(expectedKeywords[0].PutCode.Value, actualKeywords[0].PutCode.Value); - Assert.Equal(expectedKeywords[1].PutCode.Value, actualKeywords[1].PutCode.Value); - Assert.Equal(expectedKeywords[2].PutCode.Value, actualKeywords[2].PutCode.Value); + [Fact(DisplayName = "Get keywords")] + public void TestGetKeywords() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedKeywords = new List { + new OrcidKeyword( + "QA and testing", + new OrcidPutCode(4504) + ), + // "QA and testing" is intentionally twice, because the example record has a duplicate + new OrcidKeyword( + "QA and testing", + new OrcidPutCode(4603) + ), + new OrcidKeyword( + "Additional keyword", + new OrcidPutCode(4604) + ) + }; + var actualKeywords = orcidJsonParserService.GetKeywords(jsonStr); + + Assert.Equal(expectedKeywords[0].Value, actualKeywords[0].Value); + Assert.Equal(expectedKeywords[1].Value, actualKeywords[1].Value); + Assert.Equal(expectedKeywords[2].Value, actualKeywords[2].Value); + Assert.Equal(expectedKeywords[0].PutCode.Value, actualKeywords[0].PutCode.Value); + Assert.Equal(expectedKeywords[1].PutCode.Value, actualKeywords[1].PutCode.Value); + Assert.Equal(expectedKeywords[2].PutCode.Value, actualKeywords[2].PutCode.Value); } - [Fact(DisplayName = "Get external identifiers")] - public void TestGetExternalIdentifiers() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var expectedExternalIdentifiers = new List { - new OrcidExternalIdentifier( - "Loop profile", - "558", - "http://loop.frontiers-sandbox-int.info/people/559/overview?referrer=orcid_profile", - new OrcidPutCode(3193) - ), - - new OrcidExternalIdentifier( - "Personal External Identifier", - "506", - "www.6.com", - new OrcidPutCode(3294) - ) - }; - var actualExternalIdentifiers = orcidJsonParserService.GetExternalIdentifiers(jsonStr); - - Assert.Equal(expectedExternalIdentifiers[0].ExternalIdType, actualExternalIdentifiers[0].ExternalIdType); - Assert.Equal(expectedExternalIdentifiers[0].ExternalIdValue, actualExternalIdentifiers[0].ExternalIdValue); - Assert.Equal(expectedExternalIdentifiers[0].ExternalIdUrl, actualExternalIdentifiers[0].ExternalIdUrl); + [Fact(DisplayName = "Get external identifiers")] + public void TestGetExternalIdentifiers() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var expectedExternalIdentifiers = new List { + new OrcidExternalIdentifier( + "Loop profile", + "558", + "http://loop.frontiers-sandbox-int.info/people/559/overview?referrer=orcid_profile", + new OrcidPutCode(3193) + ), + + new OrcidExternalIdentifier( + "Personal External Identifier", + "506", + "www.6.com", + new OrcidPutCode(3294) + ) + }; + var actualExternalIdentifiers = orcidJsonParserService.GetExternalIdentifiers(jsonStr); + + Assert.Equal(expectedExternalIdentifiers[0].ExternalIdType, actualExternalIdentifiers[0].ExternalIdType); + Assert.Equal(expectedExternalIdentifiers[0].ExternalIdValue, actualExternalIdentifiers[0].ExternalIdValue); + Assert.Equal(expectedExternalIdentifiers[0].ExternalIdUrl, actualExternalIdentifiers[0].ExternalIdUrl); Assert.Equal(expectedExternalIdentifiers[0].PutCode.Value, actualExternalIdentifiers[0].PutCode.Value); - Assert.Equal(expectedExternalIdentifiers[1].ExternalIdType, actualExternalIdentifiers[1].ExternalIdType); - Assert.Equal(expectedExternalIdentifiers[1].ExternalIdValue, actualExternalIdentifiers[1].ExternalIdValue); - Assert.Equal(expectedExternalIdentifiers[1].ExternalIdUrl, actualExternalIdentifiers[1].ExternalIdUrl); - Assert.Equal(expectedExternalIdentifiers[1].PutCode.Value, actualExternalIdentifiers[1].PutCode.Value); + Assert.Equal(expectedExternalIdentifiers[1].ExternalIdType, actualExternalIdentifiers[1].ExternalIdType); + Assert.Equal(expectedExternalIdentifiers[1].ExternalIdValue, actualExternalIdentifiers[1].ExternalIdValue); + Assert.Equal(expectedExternalIdentifiers[1].ExternalIdUrl, actualExternalIdentifiers[1].ExternalIdUrl); + Assert.Equal(expectedExternalIdentifiers[1].PutCode.Value, actualExternalIdentifiers[1].PutCode.Value); } - [Fact(DisplayName = "Get educations")] - public void TestGetEducations() - { - var orcidJsonParserService = new OrcidJsonParserService(); - var jsonStr = getOrcidJsonRecord(); - var actualEducations = orcidJsonParserService.GetEducations(jsonStr); - Assert.True(actualEducations.Count == 1, "Educations: should parse 1 education"); - Assert.Equal("Massachusetts Institute of Technology", actualEducations[0].OrganizationName); - Assert.Equal("Testing Department", actualEducations[0].DepartmentName); - Assert.Equal("BA", actualEducations[0].RoleTitle); - Assert.Equal(1997, actualEducations[0].StartDate.Year); - Assert.Equal(9, actualEducations[0].StartDate.Month); - Assert.Equal(2, actualEducations[0].StartDate.Day); - Assert.Equal(2001, actualEducations[0].EndDate.Year); - Assert.Equal(5, actualEducations[0].EndDate.Month); - Assert.Equal(15, actualEducations[0].EndDate.Day); - Assert.Equal(new OrcidPutCode(22423).Value, actualEducations[0].PutCode.Value); + [Fact(DisplayName = "Get educations")] + public void TestGetEducations() + { + var orcidJsonParserService = new OrcidJsonParserService(); + var jsonStr = getOrcidJsonRecord(); + var actualEducations = orcidJsonParserService.GetEducations(jsonStr); + Assert.True(actualEducations.Count == 2, "Educations: should parse 2 education"); + + Assert.Equal("Massachusetts Institute of Technology", actualEducations[0].OrganizationName); + Assert.Equal("Testing Department", actualEducations[0].DepartmentName); + Assert.Equal("BA", actualEducations[0].RoleTitle); + Assert.Equal(1997, actualEducations[0].StartDate.Year); + Assert.Equal(9, actualEducations[0].StartDate.Month); + Assert.Equal(2, actualEducations[0].StartDate.Day); + Assert.Equal(2001, actualEducations[0].EndDate.Year); + Assert.Equal(5, actualEducations[0].EndDate.Month); + Assert.Equal(15, actualEducations[0].EndDate.Day); + Assert.Equal(new OrcidPutCode(22423).Value, actualEducations[0].PutCode.Value); + + Assert.Equal("Test university without disambiguated organization", actualEducations[1].OrganizationName); + Assert.Equal("Managing Department", actualEducations[1].DepartmentName); + Assert.Equal("MSc", actualEducations[1].RoleTitle); + Assert.Equal(1998, actualEducations[1].StartDate.Year); + Assert.Equal(10, actualEducations[1].StartDate.Month); + Assert.Equal(4, actualEducations[1].StartDate.Day); + Assert.Equal(2002, actualEducations[1].EndDate.Year); + Assert.Equal(6, actualEducations[1].EndDate.Month); + Assert.Equal(16, actualEducations[1].EndDate.Day); + Assert.Equal(new OrcidPutCode(42345).Value, actualEducations[1].PutCode.Value); } - [Fact(DisplayName = "Get employments")] - public void TestGetEmployments() - { - var orcidJsonParserService = new OrcidJsonParserService(); + [Fact(DisplayName = "Get employments")] + public void TestGetEmployments() + { + var orcidJsonParserService = new OrcidJsonParserService(); var jsonStr = getOrcidJsonRecord(); - var actualEmployments = orcidJsonParserService.GetEmployments(jsonStr); - Assert.True(actualEmployments.Count == 1, "Educations: should parse 1 employment"); - Assert.Equal("ORCID", actualEmployments[0].OrganizationName); - Assert.Equal("QA and Testing", actualEmployments[0].DepartmentName); - Assert.Equal("Test account holder", actualEmployments[0].RoleTitle); - Assert.Equal(2012, actualEmployments[0].StartDate.Year); - Assert.Equal(10, actualEmployments[0].StartDate.Month); - Assert.Equal(0, actualEmployments[0].StartDate.Day); - Assert.Equal(0, actualEmployments[0].EndDate.Year); - Assert.Equal(0, actualEmployments[0].EndDate.Month); - Assert.Equal(0, actualEmployments[0].EndDate.Day); - Assert.Equal(new OrcidPutCode(22411).Value, actualEmployments[0].PutCode.Value); + var actualEmployments = orcidJsonParserService.GetEmployments(jsonStr); + + Assert.True(actualEmployments.Count == 2, "Educations: should parse 2 employments"); + + Assert.Equal("ORCID", actualEmployments[0].OrganizationName); + Assert.Equal("385488", actualEmployments[0].DisambiguatedOrganizationIdentifier); + Assert.Equal("RINGGOLD", actualEmployments[0].DisambiguationSource); + Assert.Equal("QA and Testing", actualEmployments[0].DepartmentName); + Assert.Equal("Test account holder", actualEmployments[0].RoleTitle); + Assert.Equal(2012, actualEmployments[0].StartDate.Year); + Assert.Equal(10, actualEmployments[0].StartDate.Month); + Assert.Equal(0, actualEmployments[0].StartDate.Day); + Assert.Equal(0, actualEmployments[0].EndDate.Year); + Assert.Equal(0, actualEmployments[0].EndDate.Month); + Assert.Equal(0, actualEmployments[0].EndDate.Day); + Assert.Equal(new OrcidPutCode(22411).Value, actualEmployments[0].PutCode.Value); + + Assert.Equal("Test university without disambiguated organization", actualEmployments[1].OrganizationName); + Assert.Equal("", actualEmployments[1].DisambiguatedOrganizationIdentifier); + Assert.Equal("Astrophysics", actualEmployments[1].DepartmentName); + Assert.Equal("Professor", actualEmployments[1].RoleTitle); + Assert.Equal(2018, actualEmployments[1].StartDate.Year); + Assert.Equal(1, actualEmployments[1].StartDate.Month); + Assert.Equal(21, actualEmployments[1].StartDate.Day); + Assert.Equal(2019, actualEmployments[1].EndDate.Year); + Assert.Equal(12, actualEmployments[1].EndDate.Month); + Assert.Equal(31, actualEmployments[1].EndDate.Day); + Assert.Equal(new OrcidPutCode(47431).Value, actualEmployments[1].PutCode.Value); } - [Fact(DisplayName = "Get publications")] - public void TestGetPublications() - { - var orcidJsonParserService = new OrcidJsonParserService(); + [Fact(DisplayName = "Get publications")] + public void TestGetPublications() + { + var orcidJsonParserService = new OrcidJsonParserService(); var jsonStr = getOrcidJsonRecord(); - var actualPublications = orcidJsonParserService.GetPublications(jsonStr); - Assert.True(actualPublications.Count == 4, "Publications: should parse 4 publications"); - Assert.Equal("ORCID: a system to uniquely identify researchers", actualPublications[0].PublicationName); + var actualPublications = orcidJsonParserService.GetPublications(jsonStr); + Assert.True(actualPublications.Count == 3, "Publications: should parse 3 publications"); + + Assert.Equal("ORCID: a system to uniquely identify researchers", actualPublications[0].PublicationName); Assert.Equal(new OrcidPutCode(1022665).Value, actualPublications[0].PutCode.Value); - Assert.Equal(2019, actualPublications[0].PublicationYear); - Assert.Equal("", actualPublications[0].DoiHandle); - Assert.Equal("journal-article", actualPublications[0].Type); - Assert.Equal("ORCID: a system to uniquely identify researchers", actualPublications[1].PublicationName); - Assert.Equal(new OrcidPutCode(1045646).Value, actualPublications[1].PutCode.Value); - Assert.Equal(2019, actualPublications[1].PublicationYear); - Assert.Equal("10.1111/test.12241", actualPublications[1].DoiHandle); - Assert.Equal("journal-article", actualPublications[1].Type); - Assert.Equal("ORCID: a system to uniquely identify researchers", actualPublications[2].PublicationName); - Assert.Equal(new OrcidPutCode(733536).Value, actualPublications[2].PutCode.Value); - Assert.Equal(2012, actualPublications[2].PublicationYear); - Assert.Equal("10.1087/20120404", actualPublications[2].DoiHandle); - Assert.Equal("journal-article", actualPublications[2].Type); - Assert.Equal("ORCID: a system to uniquely identify researchers", actualPublications[3].PublicationName); - Assert.Equal(new OrcidPutCode(733535).Value, actualPublications[3].PutCode.Value); - Assert.Equal(2012, actualPublications[3].PublicationYear); - Assert.Equal("10.1087/20120404", actualPublications[3].DoiHandle); - Assert.Equal("journal-article", actualPublications[3].Type); + Assert.Equal(2019, actualPublications[0].PublicationYear); + Assert.Equal("", actualPublications[0].Doi); + Assert.Equal("journal-article", actualPublications[0].Type); + + Assert.Equal("My research paper", actualPublications[1].PublicationName); + Assert.Equal(new OrcidPutCode(1045646).Value, actualPublications[1].PutCode.Value); + Assert.Equal(2019, actualPublications[1].PublicationYear); + Assert.Equal("10.1111/test.12241", actualPublications[1].Doi); + Assert.Equal("journal-article B", actualPublications[1].Type); + + Assert.Equal("Another publication", actualPublications[2].PublicationName); + Assert.Equal(new OrcidPutCode(733536).Value, actualPublications[2].PutCode.Value); + Assert.Equal(2012, actualPublications[2].PublicationYear); + Assert.Equal("10.1087/20120404", actualPublications[2].Doi); + Assert.Equal("journal-article C", actualPublications[2].Type); } - //[Fact(DisplayName = "Template")] - //public void TestTemplate() - //{ - // var orcidJsonParserService = new OrcidJsonParserService(); + //[Fact(DisplayName = "Template")] + //public void TestTemplate() + //{ + // var orcidJsonParserService = new OrcidJsonParserService(); // var jsonStr = getOrcidJsonRecord(); - - // var biography = orcidJsonParserService.GetBiography(jsonStr); - // var givenNames = orcidJsonParserService.GetGivenNames(jsonStr); - // var familyName = orcidJsonParserService.GetFamilyName(jsonStr); - // var creditName = orcidJsonParserService.GetCreditName(jsonStr); - // var otherNames = orcidJsonParserService.GetOtherNames(jsonStr); - // var researcherUrls = orcidJsonParserService.GetResearcherUrls(jsonStr); - // var emails = orcidJsonParserService.GetEmails(jsonStr); - // var keywords = orcidJsonParserService.GetKeywords(jsonStr); - // var externalIdentifiers = orcidJsonParserService.GetExternalIdentifiers(jsonStr); - // var educations = orcidJsonParserService.GetEducations(jsonStr); - // var employments = orcidJsonParserService.GetEmployments(jsonStr); + + // var biography = orcidJsonParserService.GetBiography(jsonStr); + // var givenNames = orcidJsonParserService.GetGivenNames(jsonStr); + // var familyName = orcidJsonParserService.GetFamilyName(jsonStr); + // var creditName = orcidJsonParserService.GetCreditName(jsonStr); + // var otherNames = orcidJsonParserService.GetOtherNames(jsonStr); + // var researcherUrls = orcidJsonParserService.GetResearcherUrls(jsonStr); + // var emails = orcidJsonParserService.GetEmails(jsonStr); + // var keywords = orcidJsonParserService.GetKeywords(jsonStr); + // var externalIdentifiers = orcidJsonParserService.GetExternalIdentifiers(jsonStr); + // var educations = orcidJsonParserService.GetEducations(jsonStr); + // var employments = orcidJsonParserService.GetEmployments(jsonStr); // Assert.True(employments.Count == 0, "Educations: parsed correct number of employments"); - //} - } + //} + } } \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/OrganizationHandlerServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/OrganizationHandlerServiceTest.cs new file mode 100644 index 00000000..d6f6dcfd --- /dev/null +++ b/aspnetcore/src/api.Tests/Services_Tests/OrganizationHandlerServiceTest.cs @@ -0,0 +1,21 @@ +using Xunit; +using api.Services; + +namespace api.Tests +{ + [Collection("Organization handler service tests")] + public class OrganizationHandlerServiceTests + { + [Fact(DisplayName = "Get correct DimPid.PidType value for ORCID disambiguation source")] + public void getPidTypeFromOrcidDisambiguationSource() + { + var organizationHandlerService = new OrganizationHandlerService(); + + Assert.Equal("GRIDID", organizationHandlerService.MapOrcidDisambiguationSourceToPidType("GRID")); + Assert.Equal("RinggoldID", organizationHandlerService.MapOrcidDisambiguationSourceToPidType("RINGGOLD")); + Assert.Equal("RORID", organizationHandlerService.MapOrcidDisambiguationSourceToPidType("ROR")); + // Unknown disambiguation source must be mapped to empty string + Assert.Equal("", organizationHandlerService.MapOrcidDisambiguationSourceToPidType("foobar123")); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs new file mode 100644 index 00000000..17b50264 --- /dev/null +++ b/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs @@ -0,0 +1,501 @@ +using Xunit; +using api.Services; +using api.Models.Common; +using api.Models.Ttv; +using api.Models.ProfileEditor.Items; +using System; +using System.Collections.Generic; + +namespace api.Tests +{ + [Collection("Get SQL query from TtvSqlService")] + public class TtvSqlServiceTests + { + public FactFieldValue GetFactFieldValueForTest() + { + // Get FactFieldValue for test + return new FactFieldValue() + { + DimAffiliationId = -1, + DimCompetenceId = -1, + DimEducationId = -1, + DimEmailAddrressId = -1, + DimEventId = -1, + DimFieldDisplaySettingsId = -1, + DimFieldOfScienceId = -1, + DimFundingDecisionId = -1, + DimIdentifierlessDataId = -1, + DimKeywordId = -1, + DimNameId = -1, + DimOrcidPublicationId = -1, + DimPidId = -1, + DimPidIdOrcidPutCode = -1, + DimPublicationId = -1, + DimResearchActivityId = -1, + DimResearchCommunityId = -1, + DimResearchDatasetId = -1, + DimResearcherDescriptionId = -1, + DimResearcherToResearchCommunityId = -1, + DimTelephoneNumberId = -1, + DimUserProfileId = -1, + DimWebLinkId = -1 + }; + } + + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_name_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_name_id() + { + TtvSqlService ttvSqlService = new(); + // Names + Assert.Equal( + "dim_name_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.PERSON_NAME) + ); + // Other names + Assert.Equal( + "dim_name_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.PERSON_OTHER_NAMES) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_researcher_description_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_researcher_description_id() + { + TtvSqlService ttvSqlService = new(); + // Researcer description + Assert.Equal( + "dim_researcher_description_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_web_link_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_web_link_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_web_link_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.PERSON_WEB_LINK) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_email_addrress_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_email_addrress_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_email_addrress_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_keyword_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_keyword_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_keyword_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.PERSON_KEYWORD) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_telephone_number_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_telephone_number_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_telephone_number_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_affiliation_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_affiliation_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_affiliation_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.ACTIVITY_AFFILIATION) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_education_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_education_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_education_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.ACTIVITY_EDUCATION) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_publication_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_publication_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_publication_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.ACTIVITY_PUBLICATION) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_orcid_publication_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_orcid_publication_id() + { + TtvSqlService ttvSqlService = new(); + // Web link + Assert.Equal( + "dim_orcid_publication_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_funding_decision_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_funding_decision_id() + { + TtvSqlService ttvSqlService = new(); + // Funding decision + Assert.Equal( + "dim_funding_decision_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION) + ); + } + + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_research_dataset_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_research_dataset_id() + { + TtvSqlService ttvSqlService = new(); + // Research dataset + Assert.Equal( + "dim_research_dataset_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET) + ); + } + + [Fact(DisplayName = "Test that list of integers is converted to a comma separated string")] + public void Test_ConvertListOfIntsToCommaSeparatedString() + { + TtvSqlService ttvSqlService = new(); + List listOfInts = new() + { + 3489, + 432523, + 2, + 45, + 5345, + 98752, + 1 + }; + string expectedString = "3489,432523,2,45,5345,98752,1"; + Assert.Equal( + expectedString, + ttvSqlService.ConvertListOfIntsToCommaSeparatedString(listOfInts) + ); + } + + [Fact(DisplayName = "Get SQL query for updating FactFieldValues, first name")] + public void Test_getSqlQuery_Update_FactFieldValues_first_name() + { + TtvSqlService ttvSqlService = new(); + int userProfileId = 80; + ProfileEditorItemMeta profileEditorItemMeta = new() + { + Id = 321, + Type = Constants.FieldIdentifiers.PERSON_NAME, + PrimaryValue = false, + Show = true + }; + + string expectedSqlString = @"UPDATE fact_field_values + SET + show=1, + primary_value=0, + modified=GETDATE() + WHERE + dim_user_profile_id=80 AND + dim_name_id=321"; + string actualSqlString = ttvSqlService.GetSqlQuery_Update_FactFieldValues(userProfileId, profileEditorItemMeta); + + Assert.Equal( + expectedSqlString.Replace("\n", String.Empty).Replace(" ", String.Empty), + actualSqlString.Replace("\n", String.Empty).Replace(" ", String.Empty) + ); + } + + [Fact(DisplayName = "Get SQL query for updating FactFieldValues, researcher description")] + public void Test_getSqlQuery_Update_FactFieldValues_researcher_description() + { + TtvSqlService ttvSqlService = new(); + int userProfileId = 5678; + ProfileEditorItemMeta profileEditorItemMeta = new() + { + Id = 254, + Type = Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION, + PrimaryValue = true, + Show = false + }; + + string expectedSqlString = @"UPDATE fact_field_values + SET + show=0, + primary_value=1, + modified=GETDATE() + WHERE + dim_user_profile_id=5678 AND + dim_researcher_description_id=254"; + string actualSqlString = ttvSqlService.GetSqlQuery_Update_FactFieldValues(userProfileId, profileEditorItemMeta); + + Assert.Equal( + expectedSqlString.Replace("\n", String.Empty).Replace(" ", String.Empty), + actualSqlString.Replace("\n", String.Empty).Replace(" ", String.Empty) + ); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, affiliation")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_affiliation() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 12, 23, 34}; + string expectedSqlString = "DELETE FROM dim_affiliation WHERE id IN (12,23,34)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimAffiliations(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, competence")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_competence() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 23, 34, 45 }; + string expectedSqlString = "DELETE FROM dim_competence WHERE id IN (23,34,45)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimCompetences(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, education")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_education() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 34, 45, 56 }; + string expectedSqlString = "DELETE FROM dim_education WHERE id IN (34,45,56)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimEducations(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, email")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_email() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 45, 56, 67 }; + string expectedSqlString = "DELETE FROM dim_email_addrress WHERE id IN (45,56,67)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimEmailAddrresses(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, event")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_event() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 56, 67, 78 }; + string expectedSqlString = "DELETE FROM dim_event WHERE id IN (56,67,78)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimEvents(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, field of science")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_field_of_science() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 67, 78, 89 }; + string expectedSqlString = "DELETE FROM dim_field_of_science WHERE id IN (67,78,89)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimFieldsOfScience(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, funding decision")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_funding_decision() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 78, 89, 90 }; + string expectedSqlString = "DELETE FROM dim_funding_decision WHERE id IN (78,89,90)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimFundingDecisions(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, keyword")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_keyword() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 100, 101, 102 }; + string expectedSqlString = "DELETE FROM dim_keyword WHERE id IN (100,101,102)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimKeyword(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, name")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_name() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 101, 102, 103 }; + string expectedSqlString = "DELETE FROM dim_name WHERE id IN (101,102,103)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimNames(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, ORCID publication")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_ORCID_publication() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 102, 103, 104 }; + string expectedSqlString = "DELETE FROM dim_orcid_publication WHERE id IN (102,103,104)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimOrcidPublications(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, pid")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_pid() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 103, 104, 105 }; + string expectedSqlString = "DELETE FROM dim_pid WHERE id IN (103,104,105)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimPids(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, research activity")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_research_activity() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 104, 105, 106 }; + string expectedSqlString = "DELETE FROM dim_research_activity WHERE id IN (104,105,106)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimResearchActivities(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, research community")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_research_community() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 105, 106, 107 }; + string expectedSqlString = "DELETE FROM dim_research_community WHERE id IN (105,106,107)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimResearchCommunities(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, research dataset")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_research_dataset() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 106, 107, 108 }; + string expectedSqlString = "DELETE FROM dim_research_dataset WHERE id IN (106,107,108)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimResearchDatasets(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, researcher description")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_researcher_description() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 107, 108, 109 }; + string expectedSqlString = "DELETE FROM dim_researcher_description WHERE id IN (107,108,109)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimResearchDescriptions(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, researcher to research community")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_researcher_to_research_community() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 108, 109, 110 }; + string expectedSqlString = "DELETE FROM dim_researcher_to_research_community WHERE id IN (108,109,110)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimResearcherToResearchCommunities(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, telephone number")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_telephone_number() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 109, 110, 111 }; + string expectedSqlString = "DELETE FROM dim_telephone_number WHERE id IN (109,110,111)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimTelephoneNumbers(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting FactFieldValues related data, web link")] + public void Test_getSqlQuery_Delete_FactFieldValues_related_web_link() + { + TtvSqlService ttvSqlService = new(); + List ids = new() { 110, 111, 112 }; + string expectedSqlString = "DELETE FROM dim_telephone_number WHERE id IN (110,111,112)"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimTelephoneNumbers(ids); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for selecting fact_field_values")] + public void Test_GetSqlQuery_Select_FactFieldValues() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "SELECT * FROM fact_field_values WHERE dim_user_profile_id=332211"; + string actualSqlString = ttvSqlService.GetSqlQuery_Select_FactFieldValues(332211); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting fact_field_values")] + public void Test_GetSqlQuery_Delete_FactFieldValues() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "DELETE FROM fact_field_values WHERE dim_user_profile_id=443322"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_FactFieldValues(443322); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting dim_identifierless_data children")] + public void Test_GetSqlQuery_Delete_DimIdentifierlessData_Children() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "DELETE FROM dim_identifierless_data where dim_identifierless_data_id=554433"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimIdentifierlessData_Children(554433); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting dim_identifierless_data parent")] + public void Test_GetSqlQuery_Delete_DimIdentifierlessData_Parent() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "DELETE FROM dim_identifierless_data where id=665544"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimIdentifierlessData_Parent(665544); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting dim_field_display_settings")] + public void Test_GetSqlQuery_Delete_DimFieldDisplaySettings() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "DELETE FROM dim_field_display_settings WHERE dim_user_profile_id=887766"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimFieldDisplaySettings(887766); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting br_granted_permissions")] + public void Test_GetSqlQuery_Delete_BrGrantedPermissions() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "DELETE FROM br_granted_permissions WHERE dim_user_profile_id=998877"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_BrGrantedPermissions(998877); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting dim_user_choices")] + public void Test_GetSqlQuery_Delete_DimUserChoices() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "DELETE FROM dim_user_choices WHERE dim_user_profile_id=119988"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimUserChoices(119988); + Assert.Equal(expectedSqlString, actualSqlString); + } + + [Fact(DisplayName = "Get SQL query for deleting dim_user_profile")] + public void Test_GetSqlQuery_Delete_DimUserProfile() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = "DELETE FROM dim_user_profile WHERE id=221199"; + string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimUserProfile(221199); + Assert.Equal(expectedSqlString, actualSqlString); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/UserProfileServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/UserProfileServiceTest.cs new file mode 100644 index 00000000..3f8541f3 --- /dev/null +++ b/aspnetcore/src/api.Tests/Services_Tests/UserProfileServiceTest.cs @@ -0,0 +1,35 @@ +using Xunit; +using api.Services; +using api.Models.Common; + +namespace api.Tests +{ + [Collection("User profile service tests")] + public class UserProfileServiceTests + { + [Fact(DisplayName = "Get FieldIdentifiers")] + public void getFieldIdentifiers_01() + { + var userProfileService = new UserProfileService(); + var fieldIdentifiers = userProfileService.GetFieldIdentifiers(); + + Assert.Equal(16, fieldIdentifiers.Count); + Assert.Contains(Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_KEYWORD, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_NAME, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_OTHER_NAMES, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.PERSON_WEB_LINK, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.ACTIVITY_EDUCATION, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET, fieldIdentifiers); + Assert.Contains(Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY, fieldIdentifiers); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/api.Tests.csproj b/aspnetcore/src/api.Tests/api.Tests.csproj index 2a6274ba..e01e08d1 100644 --- a/aspnetcore/src/api.Tests/api.Tests.csproj +++ b/aspnetcore/src/api.Tests/api.Tests.csproj @@ -1,16 +1,20 @@ - net5.0 + net6.0 false - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive +all + + runtime; build; native; contentfiles; analyzers; buildtransitive +all + @@ -20,4 +24,7 @@ + + + diff --git a/aspnetcore/src/api/.editorconfig b/aspnetcore/src/api/.editorconfig new file mode 100644 index 00000000..06331618 --- /dev/null +++ b/aspnetcore/src/api/.editorconfig @@ -0,0 +1,3 @@ +[*.cs] +# IDE0058 +csharp_style_unused_value_expression_statement_preference = discard_variable:none \ No newline at end of file diff --git a/aspnetcore/src/api/Automapper/MappingProfile.cs b/aspnetcore/src/api/Automapper/MappingProfile.cs new file mode 100644 index 00000000..9062d07b --- /dev/null +++ b/aspnetcore/src/api/Automapper/MappingProfile.cs @@ -0,0 +1,33 @@ +using api.Models.ProfileEditor.Items; +using api.Models.Elasticsearch; +using AutoMapper; + +public class MappingProfile : Profile +{ + public MappingProfile() + { + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Background/BackgroundProfileData.cs b/aspnetcore/src/api/Background/BackgroundProfileData.cs new file mode 100644 index 00000000..927a3bc4 --- /dev/null +++ b/aspnetcore/src/api/Background/BackgroundProfileData.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; +using api.Models.Ttv; +using api.Models.Elasticsearch; +using Microsoft.Extensions.DependencyInjection; +using api.Models.ProfileEditor.Items; +using AutoMapper; + +namespace api.Services +{ + /* + * BackgroundProfiledata gets user profile data and constructs an entry for Elasticsearch person index. + * + * In normal controller code the request context has access to database via ttvContext. + * In a background task that is not available, since it is disposed when the response is sent. + * Here a local scope is created and database context can be taken from that scope. + */ + public class BackgroundProfiledata : IBackgroundProfiledata + { + private readonly IServiceScopeFactory _serviceScopeFactory; + + public BackgroundProfiledata(IServiceScopeFactory serviceScopeFactory) + { + _serviceScopeFactory = serviceScopeFactory; + } + + + /* + * Get userprofile data from TTV database and construct entry for Elasticsearch person index. + */ + public async Task GetProfiledataForElasticsearch(string orcidId, int userprofileId) + { + // Create a scope and get TtvContext for data query. + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IUserProfileService localUserProfileService = scope.ServiceProvider.GetRequiredService(); + IMapper mapper = scope.ServiceProvider.GetRequiredService(); + + ProfileEditorDataResponse profileEditorDataResponse = await localUserProfileService.GetProfileDataAsync2(userprofileId: userprofileId, forElasticsearch: true); + + // Convert profile editor model into Elasticsearch model using Automapper. + // Set id to ORCID ID + ElasticsearchPerson elasticsearchPerson = mapper.Map(profileEditorDataResponse); + elasticsearchPerson.id = orcidId; + + return elasticsearchPerson; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Background/BackgroundTaskQueue.cs b/aspnetcore/src/api/Background/BackgroundTaskQueue.cs new file mode 100644 index 00000000..7a7cba21 --- /dev/null +++ b/aspnetcore/src/api/Background/BackgroundTaskQueue.cs @@ -0,0 +1,57 @@ +/* + * Background processing code from: + * https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#queued-background-tasks + */ + +using System; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; + +public interface IBackgroundTaskQueue +{ + ValueTask QueueBackgroundWorkItemAsync(Func workItem); + + ValueTask> DequeueAsync( + CancellationToken cancellationToken); +} + +public class BackgroundTaskQueue : IBackgroundTaskQueue +{ + private readonly Channel> _queue; + + public BackgroundTaskQueue() + { + // Capacity should be set based on the expected application load and + // number of concurrent threads accessing the queue. + // BoundedChannelFullMode.Wait will cause calls to WriteAsync() to return a task, + // which completes only when space became available. This leads to backpressure, + // in case too many publishers/calls start accumulating. + + // TODO: Hard coded capacity for now. Add to configuration? + var options = new BoundedChannelOptions(capacity: 100) + { + FullMode = BoundedChannelFullMode.Wait + }; + _queue = Channel.CreateBounded>(options); + } + + public async ValueTask QueueBackgroundWorkItemAsync( + Func workItem) + { + if (workItem == null) + { + throw new ArgumentNullException(nameof(workItem)); + } + + await _queue.Writer.WriteAsync(workItem); + } + + public async ValueTask> DequeueAsync( + CancellationToken cancellationToken) + { + var workItem = await _queue.Reader.ReadAsync(cancellationToken); + + return workItem; + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Background/IBackgroundProfiledata.cs b/aspnetcore/src/api/Background/IBackgroundProfiledata.cs new file mode 100644 index 00000000..bd2ff6d3 --- /dev/null +++ b/aspnetcore/src/api/Background/IBackgroundProfiledata.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using api.Models.Elasticsearch; + +namespace api.Services +{ + public interface IBackgroundProfiledata + { + Task GetProfiledataForElasticsearch(string orcidId, int userprofileId); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Background/QueuedHostedService.cs b/aspnetcore/src/api/Background/QueuedHostedService.cs new file mode 100644 index 00000000..1fa73a1e --- /dev/null +++ b/aspnetcore/src/api/Background/QueuedHostedService.cs @@ -0,0 +1,58 @@ +/* + * Background processing code from: + * https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#queued-background-tasks + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +public class QueuedHostedService : BackgroundService +{ + private readonly ILogger _logger; + + public QueuedHostedService(IBackgroundTaskQueue taskQueue, + ILogger logger) + { + TaskQueue = taskQueue; + _logger = logger; + } + + public IBackgroundTaskQueue TaskQueue { get; } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation( + $"Queued Hosted Service is running."); + + await BackgroundProcessing(stoppingToken); + } + + private async Task BackgroundProcessing(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + var workItem = + await TaskQueue.DequeueAsync(stoppingToken); + + try + { + await workItem(stoppingToken); + } + catch (Exception ex) + { + _logger.LogError(ex, + "Error occurred executing {WorkItem}.", nameof(workItem)); + } + } + } + + public override async Task StopAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("Queued Hosted Service is stopping."); + + await base.StopAsync(stoppingToken); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/AccountDeleteController.cs b/aspnetcore/src/api/Controllers/AccountDeleteController.cs new file mode 100644 index 00000000..da56dd9a --- /dev/null +++ b/aspnetcore/src/api/Controllers/AccountDeleteController.cs @@ -0,0 +1,49 @@ +using api.Services; +using api.Models.Api; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; + +namespace api.Controllers +{ + /* + * AccountDeleteController handles Keycloak user deletion. + */ + [Route("api/accountdelete")] + [ApiController] + [Authorize] + public class AccountDeleteController : TtvControllerBase + { + private readonly IKeycloakAdminApiService _keycloakAdminApiService; + private readonly ITokenService _tokenService; + private readonly ILogger _logger; + + public AccountDeleteController(IKeycloakAdminApiService keycloakAdminApiService, ITokenService tokenService, ILogger logger) + { + _keycloakAdminApiService = keycloakAdminApiService; + _tokenService = tokenService; + _logger = logger; + } + + /// + /// Trigger backend to remove user from Keycloak. + /// + [HttpDelete] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task Delete() + { + // Log request. + _logger.LogInformation(this.GetLogPrefix() + " delete user from Keycloak."); + + // Keycloak: logout user + await _keycloakAdminApiService.LogoutUser(this.GetBearerTokenFromHttpRequest()); + + // Keycloak: remove user + await _keycloakAdminApiService.RemoveUser(this.GetBearerTokenFromHttpRequest()); + + return Ok(new ApiResponse(success: true)); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/AccountLinkController.cs b/aspnetcore/src/api/Controllers/AccountLinkController.cs new file mode 100644 index 00000000..4e339104 --- /dev/null +++ b/aspnetcore/src/api/Controllers/AccountLinkController.cs @@ -0,0 +1,52 @@ +using api.Services; +using api.Models.Api; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; + +namespace api.Controllers +{ + /* + * AccountLinkController handles Suomi.fi and ORCID identity linking related functionality. + */ + [Route("api/accountlink")] + [ApiController] + [Authorize] + public class AccountLinkController : TtvControllerBase + { + private readonly IKeycloakAdminApiService _keycloakAdminApiService; + private readonly ITokenService _tokenService; + private readonly ILogger _logger; + + public AccountLinkController(IKeycloakAdminApiService keycloakAdminApiService, ITokenService tokenService, ILogger logger) + { + _keycloakAdminApiService = keycloakAdminApiService; + _tokenService = tokenService; + _logger = logger; + } + + /// + /// Trigger backend to set ORCID ID as a user attribute in Keycloak. + /// + [HttpGet] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task Get() + { + // Log request. + _logger.LogInformation(this.GetLogPrefix() + " set ORCID ID attribute in Keycloak."); + + // Decode JWT. + System.IdentityModel.Tokens.Jwt.JwtSecurityToken kcJwt = _tokenService.GetJwtFromString(this.GetBearerTokenFromHttpRequest()); + // Return immediately if JWT already contains ORCID ID. + if (TokenService.JwtContainsOrcid(kcJwt)) + { + return Ok(new ApiResponse(success: true)); + } + // Set ORCID ID as a user attribute in Keycloak. + bool setOrcidAttributeSuccess = await _keycloakAdminApiService.SetOrcidAttributedInKeycloak(kcJwt); + return Ok(new ApiResponse(success: setOrcidAttributeSuccess)); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/CooperationChoicesController.cs b/aspnetcore/src/api/Controllers/CooperationChoicesController.cs new file mode 100644 index 00000000..ff35d90d --- /dev/null +++ b/aspnetcore/src/api/Controllers/CooperationChoicesController.cs @@ -0,0 +1,173 @@ +using api.Services; +using api.Models.Api; +using api.Models.Common; +using api.Models.ProfileEditor; +using api.Models.Ttv; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Memory; +using System; + +namespace api.Controllers +{ + /* + * CooperationChoicesController implements profile editor API for setting user choices for cooperation. + */ + [Route("api/cooperationchoices")] + [ApiController] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] + public class CooperationChoicesController : TtvControllerBase + { + private readonly TtvContext _ttvContext; + private readonly IUserProfileService _userProfileService; + private readonly ITtvSqlService _ttvSqlService; + private readonly IUtilityService _utilityService; + private readonly IMemoryCache _cache; + + public CooperationChoicesController(TtvContext ttvContext, IUserProfileService userProfileService, + ITtvSqlService ttvSqlService, IUtilityService utilityService, IMemoryCache memoryCache) + { + _ttvContext = ttvContext; + _userProfileService = userProfileService; + _ttvSqlService = ttvSqlService; + _utilityService = utilityService; + _cache = memoryCache; + } + + /// + /// Get cooperation selections. + /// + [HttpGet] + [ProducesResponseType(typeof(ApiResponseCooperationGet), StatusCodes.Status200OK)] + public async Task Get() + { + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // Send cached response, if exists. Cache key is ORCID ID + "_choices" + string cacheKey = orcidId + "_choices"; + if (_cache.TryGetValue(cacheKey, out List cachedResponse)) + { + return Ok(new ApiResponseCooperationGet(success: true, reason: "", data: cachedResponse, fromCache: true)); + } + + // Get choices from DimReferencedata by code scheme. + // MUST NOT use AsNoTracking, because it is possible that DimUserChoise entities will be added. + List dimReferenceDataUserChoices = await _ttvContext.DimReferencedata.TagWith("Get user choices").Where(dr => dr.CodeScheme == Constants.CodeSchemes.USER_CHOICES) + .Include(dr => dr.DimUserChoices.Where(duc => duc.DimUserProfileId == userprofileId)).ToListAsync(); + + // Chech that all available choices have DimUserChoice for this user profile. + foreach (DimReferencedatum dimReferenceDataUserChoice in dimReferenceDataUserChoices) + { + DimUserChoice dimUserChoice = dimReferenceDataUserChoice.DimUserChoices.FirstOrDefault(); + if (dimUserChoice == null) + { + // Add new DimUserChoice + dimUserChoice = new DimUserChoice() + { + UserChoiceValue = false, + DimUserProfileId = userprofileId, + DimReferencedataIdAsUserChoiceLabelNavigation = dimReferenceDataUserChoice, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = _utilityService.GetCurrentDateTime(), + Modified = _utilityService.GetCurrentDateTime() + }; + _ttvContext.DimUserChoices.Add(dimUserChoice); + } + } + await _ttvContext.SaveChangesAsync(); + + + // Collect data for API response. + List cooperationItems = new(); + foreach (DimReferencedatum dimReferenceDataUserChoice in dimReferenceDataUserChoices) + { + DimUserChoice dimUserChoice = dimReferenceDataUserChoice.DimUserChoices.First(); + cooperationItems.Add( + new ProfileEditorCooperationItem() + { + Id = dimUserChoice.Id, + NameFi = dimUserChoice.DimReferencedataIdAsUserChoiceLabelNavigation.NameFi, + NameEn = dimUserChoice.DimReferencedataIdAsUserChoiceLabelNavigation.NameEn, + NameSv = dimUserChoice.DimReferencedataIdAsUserChoiceLabelNavigation.NameSv, + Selected = dimUserChoice.UserChoiceValue + } + ); + } + + // Save response in cache + MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() + // Keep in cache for this time, reset time if accessed. + .SetSlidingExpiration(TimeSpan.FromSeconds(Constants.Cache.MEMORY_CACHE_EXPIRATION_SECONDS)); + + // Save data in cache. Cache key is ORCID ID. + _cache.Set(cacheKey, cooperationItems, cacheEntryOptions); + + return Ok(new ApiResponseCooperationGet(success: true, reason: "", data: cooperationItems, fromCache: false)); + } + + + /// + /// Modify cooperation selections. + /// + [HttpPatch] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task PatchMany([FromBody] List profileEditorCooperationItems) + { + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to modify. + if (profileEditorCooperationItems.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to modify")); + } + + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // Remove cached profile data response. Cache key is ORCID ID + "_choices" + _cache.Remove(orcidId + "_choices"); + + // Save cooperation selections + foreach (ProfileEditorCooperationItem profileEditorCooperationItem in profileEditorCooperationItems) + { + DimUserChoice dimUserChoice = await _ttvContext.DimUserChoices.Where(duc => duc.DimUserProfileId == userprofileId && duc.Id == profileEditorCooperationItem.Id).FirstOrDefaultAsync(); + if (dimUserChoice != null) + { + dimUserChoice.UserChoiceValue = profileEditorCooperationItem.Selected; + } + } + + await _ttvContext.SaveChangesAsync(); + + return Ok(new ApiResponse(success: true)); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/DebugController.cs b/aspnetcore/src/api/Controllers/DebugController.cs new file mode 100644 index 00000000..e2a57b89 --- /dev/null +++ b/aspnetcore/src/api/Controllers/DebugController.cs @@ -0,0 +1,257 @@ +using api.Services; +using api.Models.Api; +using api.Models.Common; +using api.Models.Ttv; +using api.Models.ProfileEditor.Items; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Caching.Memory; +using System; + +namespace api.Controllers +{ + /* + * DebugController implements API for debugging. + */ + [ApiController] + public class DebugController: TtvControllerBase + { + private readonly TtvContext _ttvContext; + private readonly IUserProfileService _userProfileService; + private readonly IElasticsearchService _elasticsearchService; + private readonly ILogger _logger; + private readonly IMemoryCache _cache; + private readonly IBackgroundTaskQueue _taskQueue; + public IConfiguration Configuration { get; } + + public DebugController( + IConfiguration configuration, + TtvContext ttvContext, + IUserProfileService userProfileService, + ILogger logger, + IMemoryCache memoryCache, + IBackgroundTaskQueue taskQueue, + IElasticsearchService elasticsearchService) + { + _ttvContext = ttvContext; + _userProfileService = userProfileService; + _logger = logger; + _cache = memoryCache; + _taskQueue = taskQueue; + _elasticsearchService = elasticsearchService; + Configuration = configuration; + } + + + /// + /// Debug: Get number of user profiles. Requires correct "debugtoken" header value. + /// + [HttpGet] + [Route("/[controller]/profilecount")] + public async Task GetNumberOfProfiles() + { + _logger.LogInformation(this.GetLogPrefix() + " DEBUG get number of profiles"); + + // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. + if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + { + return Unauthorized(); + } + + int count = await _ttvContext.DimUserProfiles.Where(up => up.Id != -1).AsNoTracking().CountAsync(); + return Ok(count); + } + + + /// + /// Debug: Get list of ORCID ID which have a user profile. Requires correct "debugtoken" header value. + /// + [HttpGet] + [Route("/[controller]/orcids")] + public async Task GetListOfORCIDs() + { + _logger.LogInformation(this.GetLogPrefix() + " DEBUG get list of ORCID IDs which have a user profile"); + + // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. + if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + { + return Unauthorized(); + } + + List orcidIds = new(); + foreach (DimUserProfile up in await _ttvContext.DimUserProfiles.Where(up => up.Id != -1).AsNoTracking().ToListAsync()) + { + orcidIds.Add(up.OrcidId); + } + + return Ok(orcidIds); + } + + + /// + /// Debug: Get any user profile data. Requires correct "debugtoken" header value. + /// + [HttpGet] + [Route("/[controller]/profiledata/{orcidId}")] + [ProducesResponseType(typeof(ApiResponseProfileDataGet), StatusCodes.Status200OK)] + public async Task Get(string orcidId) + { + _logger.LogInformation(this.GetLogPrefix() + " DEBUG get profile data for ORCID ID: " + orcidId); + + // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. + if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + { + return Unauthorized(); + } + + // Validate request data + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Check that user profile exists. + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + if (userprofileId == -1) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get profile data + ProfileEditorDataResponse profileDataResponse = await _userProfileService.GetProfileDataAsync2(userprofileId); + + return Ok(new ApiResponseProfileDataGet(success: true, reason: "", data: profileDataResponse, fromCache: false)); + } + + + /// + /// Debug: Create user profile for given ORCID ID. + /// Preconditions: ORCID ID must must already exist in dim_pid and it must be linked to a row in dim_known_person. + /// Requires correct "debugtoken" header value. + /// + [HttpPost] + [Route("/[controller]/userprofile/{orcidId}")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task CreateProfile(string orcidId) + { + _logger.LogInformation(this.GetLogPrefix() + " DEBUG create user profile, ORCID ID: " + orcidId); + + // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. + if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + { + return Unauthorized(); + } + + // Validate request data + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Check if user profile already exists. + if (await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: true, reason: "profile already exists")); + } + + // Check that ORCID ID exists in DimPid and is linked to DimKnownPerson. + // New rows must not be added to DimPid or DimKnownPerson. + DimPid dimPid = await _ttvContext.DimPids + .FirstOrDefaultAsync(dp => dp.PidContent == orcidId && dp.PidType == Constants.PidTypes.ORCID && dp.DimKnownPersonId != -1); + + if (dimPid == null) + { + return Ok(new ApiResponse(success: false, reason: "DEBUG Either ORCID ID does not exist in dim_pid, or it is not linked to any dim_known_person.")); + } + + // Create profile + try + { + await _userProfileService.CreateProfile(orcidId: orcidId); + } + catch + { + string msg = " DEBUG profile creation failed"; + _logger.LogError(this.GetLogPrefix() + msg); + return Ok(new ApiResponse(success: false, reason: msg)); + } + + _logger.LogInformation(this.GetLogPrefix() + " DEBUG profile created"); + return Ok(new ApiResponse(success: true)); + } + + + /// + /// Debug: Delete user profile for given ORCID ID. Requires correct "debugtoken" header value. + /// + [HttpDelete] + [Route("/[controller]/userprofile/{orcidId}")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task DeleteProfile(string orcidId) + { + _logger.LogInformation(this.GetLogPrefix() + " DEBUG delete user profile, ORCID ID: " + orcidId); + + // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. + if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + { + return Unauthorized(); + } + + // Validate request data + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately, if profile does not exist. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + string msg = " DEBUG profile does not exist for ORCID ID: " + orcidId; + _logger.LogInformation(this.GetLogPrefix() + msg); + return Ok(new ApiResponse(success: false, reason: msg)); + } + + // Get userprofile id. + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // Remove cached profile data response. Cache key is ORCID ID. + _cache.Remove(orcidId); + + // Remove entry from Elasticsearch index in a background task. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) + { + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation($"Elasticsearch removal of {orcidId} started {DateTime.UtcNow}"); + // Update Elasticsearch person index. + await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId); + _logger.LogInformation($"Elasticsearch removal of {orcidId} completed {DateTime.UtcNow}"); + }); + } + + // Delete profile data from database + try + { + await _userProfileService.DeleteProfileDataAsync(userprofileId: userprofileId); + } + catch + { + // Log error + string msg = " DEBUG profile deletion failed"; + _logger.LogError(this.GetLogPrefix() + msg); + return Ok(new ApiResponse(success: false, reason: msg)); + } + + // Log deletion + _logger.LogInformation(this.GetLogPrefix() + " DEBUG profile deleted"); + + return Ok(new ApiResponse(success: true)); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/FundingDecisionController.cs b/aspnetcore/src/api/Controllers/FundingDecisionController.cs new file mode 100644 index 00000000..8c1abc27 --- /dev/null +++ b/aspnetcore/src/api/Controllers/FundingDecisionController.cs @@ -0,0 +1,231 @@ +using api.Services; +using api.Models.Api; +using api.Models.Ttv; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Collections.Generic; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; +using api.Models.Common; + +namespace api.Controllers +{ + /* + * FundingController implements profile editor API commands for adding and deleting profile's fundings. + */ + [Route("api/fundingdecision")] + [ApiController] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] + public class FundingDecisionController : TtvControllerBase + { + private readonly TtvContext _ttvContext; + private readonly IUserProfileService _userProfileService; + private readonly IUtilityService _utilityService; + private readonly IDataSourceHelperService _dataSourceHelperService; + private readonly ILanguageService _languageService; + private readonly IMemoryCache _cache; + + public FundingDecisionController(TtvContext ttvContext, IUserProfileService userProfileService, + IUtilityService utilityService, IDataSourceHelperService dataSourceHelperService, + IMemoryCache memoryCache, ILanguageService languageService) + { + _ttvContext = ttvContext; + _userProfileService = userProfileService; + _utilityService = utilityService; + _dataSourceHelperService = dataSourceHelperService; + _languageService = languageService; + _cache = memoryCache; + } + + /// + /// Add funding decision(s) to user profile. + /// + [HttpPost] + [ProducesResponseType(typeof(ApiResponseFundingDecisionPostMany), StatusCodes.Status200OK)] + public async Task PostMany([FromBody] List profileEditorFundingDecisionsToAdd) + { + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to add + if (profileEditorFundingDecisionsToAdd.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to add")); + } + + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + DimUserProfile dimUserProfile = await _ttvContext.DimUserProfiles.Where(dup => dup.Id == userprofileId) + .Include(dup => dup.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION)) + .ThenInclude(dfds => dfds.FactFieldValues) + .ThenInclude(ffv => ffv.DimRegisteredDataSource) + .ThenInclude(drds => drds.DimOrganization).AsNoTracking() + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimFundingDecisionId != -1)) + .ThenInclude(ffv => ffv.DimFundingDecision).FirstOrDefaultAsync(); + + // TODO: Currently all added funding decisions get the same data source (Tiedejatutkimus.fi) + + // Get DimFieldDisplaySetting for funding decision + DimFieldDisplaySetting dimFieldDisplaySettingsFundingDecision = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION); + + // Registered data source organization name translation + NameTranslation nameTranslation_OrganizationName = _languageService.GetNameTranslation( + nameFi: _dataSourceHelperService.DimOrganizationNameFi_TTV, + nameSv: _dataSourceHelperService.DimOrganizationNameSv_TTV, + nameEn: _dataSourceHelperService.DimOrganizationNameEn_TTV + ); + + // Response object + ProfileEditorAddFundingDecisionResponse profileEditorAddFundingDecisionResponse = new() + { + source = new ProfileEditorSource() + { + RegisteredDataSource = _dataSourceHelperService.DimRegisteredDataSourceName_TTV, + Organization = new Organization() + { + NameFi = nameTranslation_OrganizationName.NameFi, + NameSv = nameTranslation_OrganizationName.NameSv, + NameEn = nameTranslation_OrganizationName.NameEn + } + } + }; + + + // Loop funding decisions + foreach (ProfileEditorFundingDecisionToAdd fundingDecisionToAdd in profileEditorFundingDecisionsToAdd) + { + bool fundingDecisionProcessed = false; + // Check if userprofile already includes given funding decision + foreach (FactFieldValue ffv in dimUserProfile.FactFieldValues) + { + if (ffv.DimFundingDecision.Id == fundingDecisionToAdd.ProjectId) + { + // Funding decision is already in profile + profileEditorAddFundingDecisionResponse.fundingDecisionsAlreadyInProfile.Add(fundingDecisionToAdd.ProjectId); + fundingDecisionProcessed = true; + break; + } + } + + if (!fundingDecisionProcessed) + { + // Get DimFundingDecision + DimFundingDecision dimFundingDecision = await _ttvContext.DimFundingDecisions.AsNoTracking().FirstOrDefaultAsync(dfd => dfd.Id == fundingDecisionToAdd.ProjectId); + // Check if exists + if (dimFundingDecision == null) + { + // Does not exist + profileEditorAddFundingDecisionResponse.fundingDecisionsNotFound.Add(fundingDecisionToAdd.ProjectId); + } + else + { + // Add FactFieldValue + FactFieldValue factFieldValueFunding = _userProfileService.GetEmptyFactFieldValue(); + factFieldValueFunding.Show = fundingDecisionToAdd.Show != null ? fundingDecisionToAdd.Show : false; + factFieldValueFunding.PrimaryValue = fundingDecisionToAdd.PrimaryValue != null ? fundingDecisionToAdd.PrimaryValue : false; + factFieldValueFunding.DimUserProfileId = dimUserProfile.Id; + factFieldValueFunding.DimFieldDisplaySettingsId = dimFieldDisplaySettingsFundingDecision.Id; + factFieldValueFunding.DimFundingDecisionId = dimFundingDecision.Id; + factFieldValueFunding.DimRegisteredDataSourceId = _dataSourceHelperService.DimRegisteredDataSourceId_TTV; + factFieldValueFunding.SourceId = Constants.SourceIdentifiers.TIEDEJATUTKIMUS; + factFieldValueFunding.Created = _utilityService.GetCurrentDateTime(); + factFieldValueFunding.Modified = _utilityService.GetCurrentDateTime(); + _ttvContext.FactFieldValues.Add(factFieldValueFunding); + await _ttvContext.SaveChangesAsync(); + + profileEditorAddFundingDecisionResponse.fundingDecisionsAdded.Add(fundingDecisionToAdd.ProjectId); + } + } + } + + // TODO: add Elasticsearch sync? + + // Remove cached profile data response. Cache key is ORCID ID. + _cache.Remove(orcidId); + + return Ok(new ApiResponseFundingDecisionPostMany(success: true, reason:"", data: profileEditorAddFundingDecisionResponse, fromCache: false)); + } + + /// + /// Remove funding decision(s) from user profile. + /// + [HttpPost] + [Route("remove")] + [ProducesResponseType(typeof(ApiResponseFundingDecisionRemoveMany), StatusCodes.Status200OK)] + public async Task RemoveMany([FromBody] List projectIds) + { + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to remove + if (projectIds.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to remove")); + } + + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // Response object + ProfileEditorRemoveFundingDecisionResponse profileEditorRemoveFundingDecisionResponse = new(); + + // Remove FactFieldValues + foreach(int projectId in projectIds.Distinct()) + { + FactFieldValue factFieldValue = await _ttvContext.FactFieldValues + .Where( + ffv => ffv.DimUserProfileId == userprofileId && + ffv.DimFundingDecisionId != -1 && + ffv.DimFundingDecision.Id == projectId + ).FirstOrDefaultAsync(); + + if (factFieldValue != null) + { + _ttvContext.FactFieldValues.Remove(factFieldValue); + profileEditorRemoveFundingDecisionResponse.fundingDecisionsRemoved.Add(projectId); + } + else + { + profileEditorRemoveFundingDecisionResponse.fundingDecisionsNotFound.Add(projectId); + } + } + await _ttvContext.SaveChangesAsync(); + + // TODO: add Elasticsearch sync? + + // Remove cached profile data response. Cache key is ORCID ID. + _cache.Remove(orcidId); + + return Ok(new ApiResponseFundingDecisionRemoveMany(success: true, reason: "removed", data: profileEditorRemoveFundingDecisionResponse, fromCache: false)); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/OrcidController.cs b/aspnetcore/src/api/Controllers/OrcidController.cs index d1df6e78..fa222ff1 100644 --- a/aspnetcore/src/api/Controllers/OrcidController.cs +++ b/aspnetcore/src/api/Controllers/OrcidController.cs @@ -1,15 +1,12 @@ using api.Services; -using api.Models; -using api.Models.Ttv; +using api.Models.Api; using api.Models.Orcid; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Linq; using System.Threading.Tasks; -using System; -using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; +using System; namespace api.Controllers { @@ -18,740 +15,114 @@ namespace api.Controllers */ [Route("api/orcid")] [ApiController] - [Authorize] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] public class OrcidController : TtvControllerBase { - private readonly TtvContext _ttvContext; - private readonly UserProfileService _userProfileService; - private readonly OrcidApiService _orcidApiService; - private readonly OrcidJsonParserService _orcidJsonParserService; + private readonly IUserProfileService _userProfileService; + private readonly IOrcidApiService _orcidApiService; + private readonly IOrcidImportService _orcidImportService; + private readonly ITokenService _tokenService; private readonly ILogger _logger; - public OrcidController(TtvContext ttvContext, UserProfileService userProfileService, OrcidApiService orcidApiService, OrcidJsonParserService orcidJsonParserService, ILogger logger) + public OrcidController(IUserProfileService userProfileService, IOrcidApiService orcidApiService, + IOrcidImportService orcidImportService, ILogger logger, ITokenService tokenService) { - _ttvContext = ttvContext; _userProfileService = userProfileService; _orcidApiService = orcidApiService; - _orcidJsonParserService = orcidJsonParserService; + _orcidImportService = orcidImportService; + _tokenService = tokenService; _logger = logger; } + /// + /// Trigger backend to get ORCID record and save data into TTV database. + /// [HttpGet] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] // TODO: Currently adding and updating ORCID data works, but detecting deleted ORCID data and deleting them is TTV database is not implemented. public async Task Get() { // Get ORCID id. - var orcidId = this.GetOrcidId(); + string orcidId = this.GetOrcidId(); + // Log request. - _logger.LogInformation(this.GetLogPrefix() + " get ORCID data request"); + //_logger.LogInformation(this.GetLogPrefix() + " get ORCID data request"); // Check that userprofile exists. - var userprofileId = await _userProfileService.GetUserprofileId(orcidId); - if (userprofileId == -1) + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: GetOrcidId())) { - // Userprofile not found return Ok(new ApiResponse(success: false, reason: "profile not found")); } - // Get record JSON from ORCID - var json = await _orcidApiService.GetRecord(orcidId); - - // Get DimUserProfile and related entities - var dimUserProfile = await _ttvContext.DimUserProfiles - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.BrFieldDisplaySettingsDimRegisteredDataSources) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimName) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimWebLink) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimFundingDecision) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimPublication) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimOrcidPublication) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimPid) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimPidIdOrcidPutCodeNavigation) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearchActivity) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimEvent) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimEducation) - .ThenInclude(de => de.DimStartDateNavigation) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimEducation) - .ThenInclude(de => de.DimEndDateNavigation) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.DimOrganization) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.StartDateNavigation) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.EndDateNavigation) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimCompetence) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearchCommunity) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimTelephoneNumber) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimEmailAddrress) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearcherDescription) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimIdentifierlessData) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimWebLink) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimKeyword).AsSplitQuery().FirstOrDefaultAsync(up => up.Id == userprofileId); - - // Get DimKnownPerson - var dimKnownPerson = await _ttvContext.DimKnownPeople - .Include(dkp => dkp.DimNameDimKnownPersonIdConfirmedIdentityNavigations).AsSplitQuery().AsNoTracking().FirstOrDefaultAsync(dkp => dkp.Id == dimUserProfile.DimKnownPersonId); - - // Get ORCID registered data source id - var orcidRegisteredDataSourceId = await _userProfileService.GetOrcidRegisteredDataSourceId(); - - - // Must use "Constants.SourceIdentifiers.ORCID" as value for "FactFieldValue.SourceId". It is used to identify what data can be deleted when userprofile is deleted. + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + // Get ORCID record from ORCID member or public API. + // If user access token has claim "use_orcid_public_api", then the record is requested from public API. + // In all other cases the ORCID member API will be used. + string orcidRecordJson = ""; - // Name - var dimFieldDisplaySettingsName = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaysettingsName => dimFieldDisplaysettingsName.FieldIdentifier == Constants.FieldIdentifiers.PERSON_NAME && dimFieldDisplaysettingsName.SourceId == Constants.SourceIdentifiers.ORCID); - // FactFieldValues - var factFieldValuesName = dimUserProfile.FactFieldValues.FirstOrDefault(factFieldValuesName => factFieldValuesName.DimFieldDisplaySettingsId == dimFieldDisplaySettingsName.Id); - if (factFieldValuesName != null) + if (this.GetOrcidPublicApiFlag() != null) { - // Update existing DimName - var dimName = factFieldValuesName.DimName; - dimName.LastName = _orcidJsonParserService.GetFamilyName(json).Value; - dimName.FirstNames = _orcidJsonParserService.GetGivenNames(json).Value; - dimName.Modified = DateTime.Now; - _ttvContext.Entry(dimName).State = EntityState.Modified; - // Update existing FactFieldValue - factFieldValuesName.Modified = DateTime.Now; - await _ttvContext.SaveChangesAsync(); - } - else - { - // Create new DimName - var dimName = new DimName() + // ORCID public API should be used + try { - LastName = _orcidJsonParserService.GetFamilyName(json).Value, - FirstNames = _orcidJsonParserService.GetGivenNames(json).Value, - DimKnownPersonIdConfirmedIdentity = dimKnownPerson.Id, - DimKnownPersonidFormerNames = -1, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = orcidRegisteredDataSourceId - }; - _ttvContext.DimNames.Add(dimName); - await _ttvContext.SaveChangesAsync(); - - factFieldValuesName = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesName.DimUserProfileId = dimUserProfile.Id; - factFieldValuesName.DimFieldDisplaySettingsId = dimFieldDisplaySettingsName.Id; - factFieldValuesName.DimNameId = dimName.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesName); - await _ttvContext.SaveChangesAsync(); - } - - - // Other names - var otherNames = _orcidJsonParserService.GetOtherNames(json); - foreach (OrcidOtherName otherName in otherNames) - { - // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid - var factFieldValuesOtherName = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == otherName.PutCode.Value.ToString()); - - if (factFieldValuesOtherName != null) - { - // Update existing DimName - var dimName_otherName = factFieldValuesOtherName.DimName; - dimName_otherName.FullName = otherName.Value; - dimName_otherName.Modified = DateTime.Now; - _ttvContext.Entry(dimName_otherName).State = EntityState.Modified; - // Update existing FactFieldValue - factFieldValuesOtherName.Modified = DateTime.Now; - await _ttvContext.SaveChangesAsync(); + orcidRecordJson = await _orcidApiService.GetRecordFromPublicApi(orcidId); + _logger.LogInformation(this.GetLogPrefix() + " get ORCID record json from ORCID public API OK"); } - else + catch (Exception ex) { - // Create new DimName for other name - var dimName_otherName = new DimName() - { - FullName = otherName.Value, - DimKnownPersonIdConfirmedIdentity = dimKnownPerson.Id, - DimKnownPersonidFormerNames = -1, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - DimRegisteredDataSourceId = orcidRegisteredDataSourceId, - Created = DateTime.Now - }; - _ttvContext.DimNames.Add(dimName_otherName); - await _ttvContext.SaveChangesAsync(); - - // Add other name ORCID put code into DimPid - var dimPidOrcidPutCodeOtherName = _userProfileService.GetEmptyDimPid(); - dimPidOrcidPutCodeOtherName.PidContent = otherName.PutCode.GetDbValue(); - dimPidOrcidPutCodeOtherName.PidType = "ORCID put code"; - dimPidOrcidPutCodeOtherName.DimKnownPersonId = dimKnownPerson.Id; - dimPidOrcidPutCodeOtherName.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPidOrcidPutCodeOtherName); - await _ttvContext.SaveChangesAsync(); - - // Get DimFieldDisplaySettings for other name - var dimFieldDisplaySettingsOtherName = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_OTHER_NAMES && dfdsWebLink.SourceId == Constants.SourceIdentifiers.ORCID); - - // Create FactFieldValues for other name - factFieldValuesOtherName = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesOtherName.DimUserProfileId = dimUserProfile.Id; - factFieldValuesOtherName.DimFieldDisplaySettingsId = dimFieldDisplaySettingsOtherName.Id; - factFieldValuesOtherName.DimNameId = dimName_otherName.Id; - factFieldValuesOtherName.DimPidIdOrcidPutCode = dimPidOrcidPutCodeOtherName.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesOtherName); - await _ttvContext.SaveChangesAsync(); + _logger.LogError(this.GetLogPrefix() + " get ORCID record json from ORCID public API failed: " + ex); + return Ok(new ApiResponse(success: false)); } } - - - // Researcher urls - var researcherUrls = _orcidJsonParserService.GetResearcherUrls(json); - foreach (OrcidResearcherUrl researchUrl in researcherUrls) - { - // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid - var factFieldValuesWebLink = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == researchUrl.PutCode.Value.ToString()); - - if (factFieldValuesWebLink != null) - { - // Update existing DimWebLink - var dimWebLink = factFieldValuesWebLink.DimWebLink; - dimWebLink.Url = researchUrl.Url; - dimWebLink.LinkLabel = researchUrl.UrlName; - dimWebLink.Modified = DateTime.Now; - _ttvContext.Entry(dimWebLink).State = EntityState.Modified; - - // Update existing FactFieldValue - factFieldValuesWebLink.Modified = DateTime.Now; - - await _ttvContext.SaveChangesAsync(); - } - else - { - // Create new DimWebLink - var dimWebLink = new DimWebLink() - { - Url = researchUrl.Url, - LinkLabel = researchUrl.UrlName, - DimOrganizationId = -1, - DimKnownPersonId = dimKnownPerson.Id, - DimCallProgrammeId = -1, - DimFundingDecisionId = -1, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - }; - _ttvContext.DimWebLinks.Add(dimWebLink); - await _ttvContext.SaveChangesAsync(); - - // Add web link ORCID put code into DimPid - var dimPidOrcidPutCodeWebLink = _userProfileService.GetEmptyDimPid(); - dimPidOrcidPutCodeWebLink.PidContent = researchUrl.PutCode.GetDbValue(); - dimPidOrcidPutCodeWebLink.PidType = "ORCID put code"; - dimPidOrcidPutCodeWebLink.DimKnownPersonId = dimKnownPerson.Id; - dimPidOrcidPutCodeWebLink.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPidOrcidPutCodeWebLink); - await _ttvContext.SaveChangesAsync(); - - // Get DimFieldDisplaySettings for weblink - var dimFieldDisplaySettingsWebLink = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_WEB_LINK && dfdsWebLink.SourceId == Constants.SourceIdentifiers.ORCID); - - // Create FactFieldValues for weblink - factFieldValuesWebLink = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesWebLink.DimUserProfileId = dimUserProfile.Id; - factFieldValuesWebLink.DimFieldDisplaySettingsId = dimFieldDisplaySettingsWebLink.Id; - factFieldValuesWebLink.DimWebLinkId = dimWebLink.Id; - factFieldValuesWebLink.DimPidIdOrcidPutCode = dimPidOrcidPutCodeWebLink.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesWebLink); - await _ttvContext.SaveChangesAsync(); - } - } - - // Researcher description - var biography = _orcidJsonParserService.GetBiography(json); - if (biography != null) - { - var dimResearcherDescription = await _userProfileService.AddOrUpdateDimResearcherDescription( - "", - _orcidJsonParserService.GetBiography(json).Value, - "", - dimKnownPerson.Id, - orcidRegisteredDataSourceId - ); - - // Researcher description: DimFieldDisplaySettings - var dimFieldDisplaySettingsResearcherDescription = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaySettingsResearcherDescription => dimFieldDisplaySettingsResearcherDescription.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION && dimFieldDisplaySettingsResearcherDescription.SourceId == Constants.SourceIdentifiers.ORCID); - - // Researcher description: FactFieldValues - var factFieldValuesResearcherDescription = dimUserProfile.FactFieldValues.FirstOrDefault(factFieldValuesResearcherDescription => factFieldValuesResearcherDescription.DimFieldDisplaySettingsId == dimFieldDisplaySettingsResearcherDescription.Id); - if (factFieldValuesResearcherDescription == null) - { - factFieldValuesResearcherDescription = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesResearcherDescription.DimUserProfileId = dimUserProfile.Id; - factFieldValuesResearcherDescription.DimFieldDisplaySettingsId = dimFieldDisplaySettingsResearcherDescription.Id; - factFieldValuesResearcherDescription.DimResearcherDescriptionId = dimResearcherDescription.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesResearcherDescription); - } - else - { - factFieldValuesResearcherDescription.Modified = DateTime.Now; - } - await _ttvContext.SaveChangesAsync(); - } - - - // Email - var emails = _orcidJsonParserService.GetEmails(json); - foreach (OrcidEmail email in emails) - { - // Email: DimEmailAddrressess - var dimEmailAddress = await _userProfileService.AddOrUpdateDimEmailAddress( - email.Value, - dimKnownPerson.Id, - orcidRegisteredDataSourceId - ); - - // Email: DimFieldDisplaySettings - var dimFieldDisplaySettingsEmailAddress = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaySettingsEmailAddress => dimFieldDisplaySettingsEmailAddress.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS && dimFieldDisplaySettingsEmailAddress.SourceId == Constants.SourceIdentifiers.ORCID); - - // Email: FactFieldValues - var factFieldValuesEmailAddress = dimUserProfile.FactFieldValues.FirstOrDefault(factFieldValuesEmailAddress => factFieldValuesEmailAddress.DimFieldDisplaySettingsId == dimFieldDisplaySettingsEmailAddress.Id); - if (factFieldValuesEmailAddress == null) - { - factFieldValuesEmailAddress = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesEmailAddress.DimUserProfileId = dimUserProfile.Id; - factFieldValuesEmailAddress.DimFieldDisplaySettingsId = dimFieldDisplaySettingsEmailAddress.Id; - factFieldValuesEmailAddress.DimEmailAddrressId = dimEmailAddress.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesEmailAddress); - } - else - { - factFieldValuesEmailAddress.Modified = DateTime.Now; - } - await _ttvContext.SaveChangesAsync(); - } - - // Keyword - var keywords = _orcidJsonParserService.GetKeywords(json); - // Get DimFieldDisplaySettings for keyword - var dimFieldDisplaySettingsKeyword = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_KEYWORD && dfdsKeyword.SourceId == Constants.SourceIdentifiers.ORCID); - // Collect list of processed FactFieldValues related to keyword. Needed when deleting keywords. - var processedKeywordFactFieldValues = new List (); - foreach (OrcidKeyword keyword in keywords) - { - // Check if FactFieldValues contains entry, which points to ORCID put code value in DimKeyword - var factFieldValuesKeyword = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == keyword.PutCode.Value.ToString()); - - if (factFieldValuesKeyword != null) - { - // Update existing DimKeyword - var dimKeyword = factFieldValuesKeyword.DimKeyword; - dimKeyword.Keyword = keyword.Value; - dimKeyword.Modified = DateTime.Now; - _ttvContext.Entry(dimKeyword).State = EntityState.Modified; - // Update existing FactFieldValue - factFieldValuesKeyword.Modified = DateTime.Now; - await _ttvContext.SaveChangesAsync(); - } - else - { - // Create new DimKeyword - var dimKeyword = new DimKeyword() - { - Keyword = keyword.Value, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - DimRegisteredDataSourceId = orcidRegisteredDataSourceId, - Created = DateTime.Now - }; - _ttvContext.DimKeywords.Add(dimKeyword); - await _ttvContext.SaveChangesAsync(); - - // Add keyword ORCID put code into DimPid - var dimPidOrcidPutCodeKeyword = _userProfileService.GetEmptyDimPid(); - dimPidOrcidPutCodeKeyword.PidContent = keyword.PutCode.GetDbValue(); - dimPidOrcidPutCodeKeyword.PidType = "ORCID put code"; - dimPidOrcidPutCodeKeyword.DimKnownPersonId = dimKnownPerson.Id; - dimPidOrcidPutCodeKeyword.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPidOrcidPutCodeKeyword); - await _ttvContext.SaveChangesAsync(); - - // Create FactFieldValues for keyword - factFieldValuesKeyword = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesKeyword.DimUserProfileId = dimUserProfile.Id; - factFieldValuesKeyword.DimFieldDisplaySettingsId = dimFieldDisplaySettingsKeyword.Id; - factFieldValuesKeyword.DimKeywordId = dimKeyword.Id; - factFieldValuesKeyword.DimPidIdOrcidPutCode = dimPidOrcidPutCodeKeyword.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesKeyword); - await _ttvContext.SaveChangesAsync(); - } - processedKeywordFactFieldValues.Add(factFieldValuesKeyword); - } - // Remove existing keywords which were not in ORCID data. - foreach (FactFieldValue ffvKeyword in dimFieldDisplaySettingsKeyword.FactFieldValues) - { - if (!processedKeywordFactFieldValues.Contains(ffvKeyword)) - { - _ttvContext.FactFieldValues.Remove(ffvKeyword); - _ttvContext.DimKeywords.Remove(ffvKeyword.DimKeyword); - } - } - await _ttvContext.SaveChangesAsync(); - - - - // External identifier (=DimPid) - var externalIdentifiers = _orcidJsonParserService.GetExternalIdentifiers(json); - // Get DimFieldDisplaySettings for keyword - var dimFieldDisplaySettingsExternalIdentifier = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER && dfdsKeyword.SourceId == Constants.SourceIdentifiers.ORCID); - foreach (OrcidExternalIdentifier externalIdentifier in externalIdentifiers) - { - // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid - var factFieldValuesExternalIdentifier = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == externalIdentifier.PutCode.Value.ToString()); - - if (factFieldValuesExternalIdentifier != null) - { - // Update existing DimPid - var dimPid = factFieldValuesExternalIdentifier.DimPid; - dimPid.PidContent = externalIdentifier.ExternalIdValue; - dimPid.PidType = externalIdentifier.ExternalIdType; - dimPid.Modified = DateTime.Now; - _ttvContext.Entry(dimPid).State = EntityState.Modified; - // Update existing FactFieldValue - factFieldValuesExternalIdentifier.Modified = DateTime.Now; - await _ttvContext.SaveChangesAsync(); - } - else - { - // Create new DimPid (external identifier is stored into DimPid) - var dimPid = _userProfileService.GetEmptyDimPid(); - dimPid.PidContent = externalIdentifier.ExternalIdValue; - dimPid.PidType = externalIdentifier.ExternalIdType; - dimPid.DimKnownPersonId = dimKnownPerson.Id; - dimPid.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPid); - await _ttvContext.SaveChangesAsync(); - - // Add ORCID put code into DimPid - var dimPidOrcidPutCodeExternalIdentifier = _userProfileService.GetEmptyDimPid(); - dimPidOrcidPutCodeExternalIdentifier.PidContent = externalIdentifier.PutCode.GetDbValue(); - dimPidOrcidPutCodeExternalIdentifier.PidType = "ORCID put code"; - dimPidOrcidPutCodeExternalIdentifier.DimKnownPersonId = dimKnownPerson.Id; - dimPidOrcidPutCodeExternalIdentifier.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPidOrcidPutCodeExternalIdentifier); - await _ttvContext.SaveChangesAsync(); - - // Create FactFieldValues for external identifier - factFieldValuesExternalIdentifier = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesExternalIdentifier.DimUserProfileId = dimUserProfile.Id; - factFieldValuesExternalIdentifier.DimFieldDisplaySettingsId = dimFieldDisplaySettingsExternalIdentifier.Id; - factFieldValuesExternalIdentifier.DimPidId = dimPid.Id; - factFieldValuesExternalIdentifier.DimPidIdOrcidPutCode = dimPidOrcidPutCodeExternalIdentifier.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesExternalIdentifier); - await _ttvContext.SaveChangesAsync(); - } - } - - - - // Education - var educations = _orcidJsonParserService.GetEducations(json); - foreach (OrcidEducation education in educations) + else { - // Check if FactFieldValues contains entry, which points to ORCID put code value in DimEducation - var factFieldValuesEducation = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == education.PutCode.Value.ToString()); + // ORCID member API should be used - // Start date - var startDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == education.StartDate.Year && dd.Month == education.StartDate.Month && dd.Day == education.StartDate.Day); - if (startDate == null) + // User's ORCID access token handling + OrcidTokens orcidTokens; + try { - startDate = new DimDate() - { - Year = education.StartDate.Year, - Month = education.StartDate.Month, - Day = education.StartDate.Day, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(startDate); - await _ttvContext.SaveChangesAsync(); + // Get ORCID access token from Keycloak + string orcidTokensJson = await _tokenService.GetOrcidTokensJsonFromKeycloak(this.GetBearerTokenFromHttpRequest()); + // Parse json from Keycloak into EF model + orcidTokens = _tokenService.ParseOrcidTokensJson(orcidTokensJson); + // Update ORCID tokens in TTV database. + await _userProfileService.UpdateOrcidTokensInDimUserProfile(userprofileId, orcidTokens); + _logger.LogInformation(this.GetLogPrefix() + " get ORCID tokens from Keycloak OK"); } - - // End date - var endDate = await _ttvContext.DimDates.FirstOrDefaultAsync(ed => ed.Year == education.EndDate.Year && ed.Month == education.EndDate.Month && ed.Day == education.EndDate.Day); - if (endDate == null) + catch (Exception ex) { - endDate = new DimDate() - { - Year = education.EndDate.Year, - Month = education.EndDate.Month, - Day = education.EndDate.Day, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(endDate); - await _ttvContext.SaveChangesAsync(); + _logger.LogError(this.GetLogPrefix() + " get ORCID tokens from Keycloak failed: " + ex); + return Ok(new ApiResponse(success: false)); } - if (factFieldValuesEducation != null) + // Get record json from ORCID member API + try { - // Update existing DimEducation - var dimEducation = factFieldValuesEducation.DimEducation; - dimEducation.NameEn = education.RoleTitle; - dimEducation.DegreeGrantingInstitutionName = education.OrganizationName; - dimEducation.DimStartDate = startDate.Id; - dimEducation.DimEndDate = endDate.Id; - _ttvContext.Entry(dimEducation).State = EntityState.Modified; - dimEducation.Modified = DateTime.Now; - - // Update existing FactFieldValue - factFieldValuesEducation.Modified = DateTime.Now; - - await _ttvContext.SaveChangesAsync(); + orcidRecordJson = await _orcidApiService.GetRecordFromMemberApi(orcidId, orcidTokens.AccessToken); + _logger.LogInformation(this.GetLogPrefix() + " get ORCID record json from ORCID member API OK"); } - else + catch (Exception ex) { - // Create new DimEducation - var dimEducation = new DimEducation() - { - NameEn = education.RoleTitle, - DegreeGrantingInstitutionName = education.OrganizationName, - DimStartDate = startDate.Id, - DimEndDate = endDate.Id, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - DimKnownPersonId = dimKnownPerson.Id, - DimRegisteredDataSourceId = orcidRegisteredDataSourceId, - Created = DateTime.Now - }; - _ttvContext.DimEducations.Add(dimEducation); - await _ttvContext.SaveChangesAsync(); - - // Add education ORCID put code into DimPid - var dimPidOrcidPutCodeEducation = _userProfileService.GetEmptyDimPid(); - dimPidOrcidPutCodeEducation.PidContent = education.PutCode.GetDbValue(); - dimPidOrcidPutCodeEducation.PidType = "ORCID put code"; - dimPidOrcidPutCodeEducation.DimKnownPersonId = dimKnownPerson.Id; - dimPidOrcidPutCodeEducation.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPidOrcidPutCodeEducation); - await _ttvContext.SaveChangesAsync(); - - // Get DimFieldDisplaySettings for education - var dimFieldDisplaySettingsEducation = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsEducation => dfdsEducation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION && dfdsEducation.SourceId == Constants.SourceIdentifiers.ORCID); - - // Create FactFieldValues for education - factFieldValuesEducation = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesEducation.DimUserProfileId = dimUserProfile.Id; - factFieldValuesEducation.DimFieldDisplaySettingsId = dimFieldDisplaySettingsEducation.Id; - factFieldValuesEducation.DimEducationId = dimEducation.Id; - factFieldValuesEducation.DimPidIdOrcidPutCode = dimPidOrcidPutCodeEducation.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesEducation); - await _ttvContext.SaveChangesAsync(); + _logger.LogError(this.GetLogPrefix() + " get ORCID record json from ORCID member API failed: " + ex); + return Ok(new ApiResponse(success: false)); } } - - // Employment (Affiliation in Ttv database) - // TODO: Handling of relations DimOrganization and AffiliationType - var employments = _orcidJsonParserService.GetEmployments(json); - foreach (OrcidEmployment employment in employments) + // Import record json into userprofile + try { - // Check if FactFieldValues contains entry, which points to ORCID put code value in DimAffiliation - var factFieldValuesAffiliation = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == employment.PutCode.Value.ToString()); - - // Start date - var startDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.StartDate.Year && dd.Month == employment.StartDate.Month && dd.Day == employment.StartDate.Day); - if (startDate == null) - { - startDate = new DimDate() - { - Year = employment.StartDate.Year, - Month = employment.StartDate.Month, - Day = employment.StartDate.Day, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(startDate); - await _ttvContext.SaveChangesAsync(); - } - - // End date - var endDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.EndDate.Year && dd.Month == employment.EndDate.Month && dd.Day == employment.EndDate.Day); - if (endDate == null) - { - endDate = new DimDate() - { - Year = employment.EndDate.Year, - Month = employment.EndDate.Month, - Day = employment.EndDate.Day, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(endDate); - await _ttvContext.SaveChangesAsync(); - } - - // TODO: DimOrganization handling - - if (factFieldValuesAffiliation != null) - { - // Update existing DimAffiliation - var dimAffiliation = factFieldValuesAffiliation.DimAffiliation; - dimAffiliation.PositionNameEn = employment.RoleTitle; - dimAffiliation.StartDate = startDate.Id; - dimAffiliation.EndDate = endDate.Id; - _ttvContext.Entry(dimAffiliation).State = EntityState.Modified; - dimAffiliation.Modified = DateTime.Now; - - // Update related DimOrganization - // TODO: DimOrganization handling - var dimOrganization = dimAffiliation.DimOrganization; - dimOrganization.NameEn = employment.OrganizationName; - _ttvContext.Entry(dimOrganization).State = EntityState.Modified; - - // Update existing FactFieldValue - factFieldValuesAffiliation.Modified = DateTime.Now; - - await _ttvContext.SaveChangesAsync(); - } - else - { - // Create new related DimOrganization - // TODO: DimOrganization handling - var dimOrganization = new DimOrganization() - { - DimSectorid = -1, - NameEn = employment.OrganizationName, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - DimRegisteredDataSourceId = orcidRegisteredDataSourceId - }; - - _ttvContext.DimOrganizations.Add(dimOrganization); - await _ttvContext.SaveChangesAsync(); - - - // Create new DimAffiliation - var dimAffiliation = new DimAffiliation() - { - DimOrganizationId = dimOrganization.Id, - StartDate = startDate.Id, - EndDate = endDate.Id, - PositionNameEn = employment.RoleTitle, - AffiliationType = -1, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - DimKnownPersonId = dimKnownPerson.Id, - DimRegisteredDataSourceId = orcidRegisteredDataSourceId, - Created = DateTime.Now - }; - _ttvContext.DimAffiliations.Add(dimAffiliation); - await _ttvContext.SaveChangesAsync(); - - // Add employment (=affiliation) ORCID put code into DimPid - var dimPidOrcidPutCodeEmployment = _userProfileService.GetEmptyDimPid(); - dimPidOrcidPutCodeEmployment.PidContent = employment.PutCode.GetDbValue(); - dimPidOrcidPutCodeEmployment.PidType = "ORCID put code"; - dimPidOrcidPutCodeEmployment.DimKnownPersonId = dimKnownPerson.Id; - dimPidOrcidPutCodeEmployment.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPidOrcidPutCodeEmployment); - await _ttvContext.SaveChangesAsync(); - - // Get DimFieldDisplaySettings for affiliation - var dimFieldDisplaySettingsAffiliation = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsAffiliation => dfdsAffiliation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION && dfdsAffiliation.SourceId == Constants.SourceIdentifiers.ORCID); - - // Create FactFieldValues for affiliation - factFieldValuesAffiliation = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesAffiliation.DimUserProfileId = dimUserProfile.Id; - factFieldValuesAffiliation.DimFieldDisplaySettingsId = dimFieldDisplaySettingsAffiliation.Id; - factFieldValuesAffiliation.DimAffiliationId = dimAffiliation.Id; - factFieldValuesAffiliation.DimPidIdOrcidPutCode = dimPidOrcidPutCodeEmployment.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesAffiliation); - await _ttvContext.SaveChangesAsync(); - } + await _orcidImportService.ImportOrcidRecordJsonIntoUserProfile(userprofileId, orcidRecordJson); + _logger.LogInformation(this.GetLogPrefix() + " import ORCID record to userprofile OK"); } - - - // Publication - var orcidPublications = _orcidJsonParserService.GetPublications(json); - foreach (OrcidPublication orcidPublication in orcidPublications) + catch (Exception ex) { - // Check if FactFieldValues contains entry, which points to ORCID put code value in DimOrcidPublication - var factFieldValuesPublication = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == orcidPublication.PutCode.Value.ToString()); - - if (factFieldValuesPublication != null) - { - // Update existing DimOrcidPublication - var dimOrcidPublication = factFieldValuesPublication.DimOrcidPublication; - dimOrcidPublication.OrcidWorkType = orcidPublication.Type; - dimOrcidPublication.PublicationName = orcidPublication.PublicationName; - dimOrcidPublication.PublicationYear = orcidPublication.PublicationYear; - dimOrcidPublication.DoiHandle = orcidPublication.DoiHandle; - dimOrcidPublication.Modified = DateTime.Now; - _ttvContext.Entry(dimOrcidPublication).State = EntityState.Modified; - // Update existing FactFieldValue - factFieldValuesPublication.Modified = DateTime.Now; - await _ttvContext.SaveChangesAsync(); - } - else - { - // Create new DimOrcidPublication - var dimOrcidPublication = _userProfileService.GetEmptyDimOrcidPublication(); - dimOrcidPublication.OrcidWorkType = orcidPublication.Type; - dimOrcidPublication.PublicationName = orcidPublication.PublicationName; - dimOrcidPublication.PublicationYear = orcidPublication.PublicationYear; - dimOrcidPublication.DoiHandle = orcidPublication.DoiHandle; - dimOrcidPublication.SourceId = Constants.SourceIdentifiers.ORCID; - dimOrcidPublication.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; - dimOrcidPublication.Created = DateTime.Now; - _ttvContext.DimOrcidPublications.Add(dimOrcidPublication); - await _ttvContext.SaveChangesAsync(); - - // Add publication's ORCID put code into DimPid - var dimPidOrcidPutCodePublication = _userProfileService.GetEmptyDimPid(); - dimPidOrcidPutCodePublication.PidContent = orcidPublication.PutCode.GetDbValue(); - dimPidOrcidPutCodePublication.PidType = "ORCID put code"; - dimPidOrcidPutCodePublication.DimKnownPersonId = dimKnownPerson.Id; - dimPidOrcidPutCodePublication.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPidOrcidPutCodePublication); - await _ttvContext.SaveChangesAsync(); - - // Get DimFieldDisplaySettings for orcid publication - var dimFieldDisplaySettingsPublication = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsPublication => dfdsPublication.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION && dfdsPublication.SourceId == Constants.SourceIdentifiers.ORCID); - - // Create FactFieldValues for orcid publication - factFieldValuesPublication = _userProfileService.GetEmptyFactFieldValueOrcid(); - factFieldValuesPublication.DimUserProfileId = dimUserProfile.Id; - factFieldValuesPublication.DimFieldDisplaySettingsId = dimFieldDisplaySettingsPublication.Id; - factFieldValuesPublication.DimOrcidPublicationId = dimOrcidPublication.Id; - factFieldValuesPublication.DimPidIdOrcidPutCode = dimPidOrcidPutCodePublication.Id; - _ttvContext.FactFieldValues.Add(factFieldValuesPublication); - await _ttvContext.SaveChangesAsync(); - } + _logger.LogError(this.GetLogPrefix() + " import ORCID record to userprofile failed: " + ex); + return Ok(new ApiResponse(success: false)); } - _logger.LogInformation(this.GetLogPrefix() + " get ORCID data success"); - return Ok(new ApiResponse(success: true)); } } diff --git a/aspnetcore/src/api/Controllers/ProfileDataController.cs b/aspnetcore/src/api/Controllers/ProfileDataController.cs index 5c5b82f1..a660f810 100644 --- a/aspnetcore/src/api/Controllers/ProfileDataController.cs +++ b/aspnetcore/src/api/Controllers/ProfileDataController.cs @@ -1,775 +1,102 @@ using api.Services; -using api.Models; +using api.Models.Api; +using api.Models.Common; using api.Models.Ttv; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using System; using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; namespace api.Controllers { /* * ProfileDataController implements profile editor API commands, such as getting editor data and setting data visibility. */ - [Route("api/[controller]")] + [Route("api/profiledata")] [ApiController] - [Authorize] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] public class ProfileDataController : TtvControllerBase { - private readonly TtvContext _ttvContext; - private readonly UserProfileService _userProfileService; - private readonly ElasticsearchService _elasticsearchService; - private IMemoryCache _cache; + private readonly IUserProfileService _userProfileService; + private readonly IElasticsearchService _elasticsearchService; + private readonly ITtvSqlService _ttvSqlService; + private readonly IMemoryCache _cache; private readonly ILogger _logger; - - public ProfileDataController(TtvContext ttvContext, UserProfileService userProfileService, ElasticsearchService elasticsearchService, IMemoryCache memoryCache, ILogger logger) + private readonly IBackgroundProfiledata _backgroundProfiledata; + private readonly IBackgroundTaskQueue _taskQueue; + + public ProfileDataController(IUserProfileService userProfileService, + IElasticsearchService elasticsearchService, + ITtvSqlService ttvSqlService, + IMemoryCache memoryCache, + ILogger logger, + IBackgroundProfiledata backgroundProfiledata, + IBackgroundTaskQueue taskQueue) { - _ttvContext = ttvContext; _userProfileService = userProfileService; _cache = memoryCache; _elasticsearchService = elasticsearchService; + _ttvSqlService = ttvSqlService; + _backgroundProfiledata = backgroundProfiledata; _logger = logger; + _taskQueue = taskQueue; } + /// + /// Get profile data. New version using different data structure. + /// [HttpGet] + [ProducesResponseType(typeof(ApiResponseProfileDataGet), StatusCodes.Status200OK)] public async Task Get() { - // Get userprofile - var orcidId = this.GetOrcidId(); - var userprofileId = await _userProfileService.GetUserprofileId(orcidId); - if (userprofileId == -1) - { - return Ok(new ApiResponse(success: false, reason: "profile not found")); - } + // Get ORCID id + string orcidId = GetOrcidId(); - // Send cached response, if exists. Cache key is ORCID ID - ProfileEditorDataResponse cachedResponse; - if (_cache.TryGetValue(orcidId, out cachedResponse)) + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - return Ok(new ApiResponse(success: true, data: cachedResponse, fromCache: true)); + return Ok(new ApiResponse(success: false, reason: "profile not found")); } + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); - // Get DimUserProfile and related entities - var dimUserProfile = await _ttvContext.DimUserProfiles - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.BrFieldDisplaySettingsDimRegisteredDataSources) - .ThenInclude(br => br.DimRegisteredDataSource) - .ThenInclude(drds => drds.DimOrganization).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimName).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimWebLink).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimFundingDecision).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimPublication).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimPid).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimPidIdOrcidPutCodeNavigation).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearchActivity).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimEvent).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimEducation) - .ThenInclude(de => de.DimStartDateNavigation).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimEducation) - .ThenInclude(de => de.DimEndDateNavigation).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.StartDateNavigation).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.EndDateNavigation).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.DimOrganization).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.AffiliationTypeNavigation).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimCompetence).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearchCommunity).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearcherToResearchCommunity) - .ThenInclude(drtrc => drtrc.DimResearchCommunity).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimTelephoneNumber).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimEmailAddrress).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearcherDescription).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimIdentifierlessData).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimOrcidPublication).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimKeyword).AsNoTracking() - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.FactFieldValues) - .ThenInclude(ffv => ffv.DimFieldOfScience).AsNoTracking().AsSplitQuery().FirstOrDefaultAsync(up => up.Id == userprofileId); - - var profileDataResponse = new ProfileEditorDataResponse() {}; + // Cache key + string cacheKey = orcidId + "_2"; - // Collect data from DimFieldDisplaySettings and FactFieldValues entities - foreach (DimFieldDisplaySetting dfds in dimUserProfile.DimFieldDisplaySettings) + // Send cached response, if exists. + if (_cache.TryGetValue(cacheKey, out ProfileEditorDataResponse cachedResponse)) { - // FieldIdentifier defines what type of data the field contains. - switch (dfds.FieldIdentifier) - { - case Constants.FieldIdentifiers.PERSON_NAME: - var nameGroup = new ProfileEditorGroupName() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_NAME, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - nameGroup.items.Add( - new ProfileEditorItemName() - { - FirstNames = ffv.DimName.FirstNames, - LastName = ffv.DimName.LastName, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimNameId, - Type = Constants.FieldIdentifiers.PERSON_FIRST_NAMES, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (nameGroup.items.Count > 0) - { - profileDataResponse.personal.nameGroups.Add(nameGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_OTHER_NAMES: - var otherNameGroup = new ProfileEditorGroupOtherName() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_OTHER_NAMES, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - otherNameGroup.items.Add( - new ProfileEditorItemName() - { - FullName = ffv.DimName.FullName, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimNameId, - Type = Constants.FieldIdentifiers.PERSON_OTHER_NAMES, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (otherNameGroup.items.Count > 0) - { - profileDataResponse.personal.otherNameGroups.Add(otherNameGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION: - var researcherDescriptionGroup = new ProfileEditorGroupResearcherDescription() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - researcherDescriptionGroup.items.Add( - new ProfileEditorItemResearcherDescription() - { - ResearchDescriptionEn = ffv.DimResearcherDescription.ResearchDescriptionEn, - ResearchDescriptionFi = ffv.DimResearcherDescription.ResearchDescriptionFi, - ResearchDescriptionSv = ffv.DimResearcherDescription.ResearchDescriptionSv, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimResearcherDescriptionId, - Type = Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (researcherDescriptionGroup.items.Count > 0) - { - profileDataResponse.personal.researcherDescriptionGroups.Add(researcherDescriptionGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_WEB_LINK: - var webLinkGroup = new ProfileEditorGroupWebLink() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_WEB_LINK, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - webLinkGroup.items.Add( - new ProfileEditorItemWebLink() - { - Url = ffv.DimWebLink.Url, - LinkLabel = ffv.DimWebLink.LinkLabel, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimWebLinkId, - Type = Constants.FieldIdentifiers.PERSON_WEB_LINK, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (webLinkGroup.items.Count > 0) - { - profileDataResponse.personal.webLinkGroups.Add(webLinkGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS: - var emailGroup = new ProfileEditorGroupEmail() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - emailGroup.items.Add( - new ProfileEditorItemEmail() - { - Value = ffv.DimEmailAddrress.Email, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimEmailAddrressId, - Type = Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (emailGroup.items.Count > 0) - { - profileDataResponse.personal.emailGroups.Add(emailGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER: - var telephoneNumberGroup = new ProfileEditorGroupTelephoneNumber() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - telephoneNumberGroup.items.Add( - new ProfileEditorItemTelephoneNumber() - { - Value = ffv.DimTelephoneNumber.TelephoneNumber, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimTelephoneNumberId, - Type = Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (telephoneNumberGroup.items.Count > 0) - { - profileDataResponse.personal.telephoneNumberGroups.Add(telephoneNumberGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE: - var fieldOfScienceGroup = new ProfileEditorGroupFieldOfScience() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - fieldOfScienceGroup.items.Add( - new ProfileEditorItemFieldOfScience() - { - NameFi = ffv.DimFieldOfScience.NameFi, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimFieldOfScienceId, - Type = Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (fieldOfScienceGroup.items.Count > 0) - { - profileDataResponse.personal.fieldOfScienceGroups.Add(fieldOfScienceGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_KEYWORD: - var keywordGroup = new ProfileEditorGroupKeyword() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_KEYWORD, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - keywordGroup.items.Add( - new ProfileEditorItemKeyword() - { - Value = ffv.DimKeyword.Keyword, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimKeywordId, - Type = Constants.FieldIdentifiers.PERSON_KEYWORD, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (keywordGroup.items.Count > 0) - { - profileDataResponse.personal.keywordGroups.Add(keywordGroup); - } - break; - case Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER: - var externalIdentifierGroup = new ProfileEditorGroupExternalIdentifier() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - externalIdentifierGroup.items.Add( - new ProfileEditorItemExternalIdentifier() - { - PidContent = ffv.DimPid.PidContent, - PidType = ffv.DimPid.PidType, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimPidId, - Type = Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - ); - } - if (externalIdentifierGroup.items.Count > 0) - { - profileDataResponse.personal.externalIdentifierGroups.Add(externalIdentifierGroup); - } - break; - case Constants.FieldIdentifiers.ACTIVITY_ROLE_IN_RESERCH_COMMUNITY: - // TODO - break; - case Constants.FieldIdentifiers.ACTIVITY_AFFILIATION: - var affiliationGroup = new ProfileEditorGroupAffiliation() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - var affiliation = new ProfileEditorItemAffiliation() - { - // TODO: DimOrganization handling - OrganizationNameFi = ffv.DimAffiliation.DimOrganization.NameFi, - OrganizationNameEn = ffv.DimAffiliation.DimOrganization.NameEn, - OrganizationNameSv = ffv.DimAffiliation.DimOrganization.NameSv, - PositionNameFi = ffv.DimAffiliation.PositionNameFi, - PositionNameEn = ffv.DimAffiliation.PositionNameEn, - PositionNameSv = ffv.DimAffiliation.PositionNameSv, - Type = ffv.DimAffiliation.AffiliationTypeNavigation.NameFi, - StartDate = new ProfileEditorItemDate() - { - Year = ffv.DimAffiliation.StartDateNavigation.Year, - Month = ffv.DimAffiliation.StartDateNavigation.Month, - Day = ffv.DimAffiliation.StartDateNavigation.Day - }, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimAffiliationId, - Type = Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - }; - - // Affiliation EndDate can be null - if (ffv.DimAffiliation.EndDateNavigation != null) - { - affiliation.EndDate = new ProfileEditorItemDate() - { - Year = ffv.DimAffiliation.EndDateNavigation.Year, - Month = ffv.DimAffiliation.EndDateNavigation.Month, - Day = ffv.DimAffiliation.EndDateNavigation.Day - }; - } - affiliationGroup.items.Add(affiliation); - } - if (affiliationGroup.items.Count > 0) - { - profileDataResponse.activity.affiliationGroups.Add(affiliationGroup); - } - break; - case Constants.FieldIdentifiers.ACTIVITY_EDUCATION: - var educationGroup = new ProfileEditorGroupEducation() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.ACTIVITY_EDUCATION, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - var education = new ProfileEditorItemEducation() - { - NameFi = ffv.DimEducation.NameFi, - NameEn = ffv.DimEducation.NameEn, - NameSv = ffv.DimEducation.NameSv, - DegreeGrantingInstitutionName = ffv.DimEducation.DegreeGrantingInstitutionName, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimEducationId, - Type = Constants.FieldIdentifiers.ACTIVITY_EDUCATION, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - }; - // Education StartDate can be null - if (ffv.DimEducation.DimStartDateNavigation != null) - { - education.StartDate = new ProfileEditorItemDate() - { - Year = ffv.DimEducation.DimStartDateNavigation.Year, - Month = ffv.DimEducation.DimStartDateNavigation.Month, - Day = ffv.DimEducation.DimStartDateNavigation.Day - }; - } - // Education EndDate can be null - if (ffv.DimEducation.DimEndDateNavigation != null) - { - education.EndDate = new ProfileEditorItemDate() - { - Year = ffv.DimEducation.DimEndDateNavigation.Year, - Month = ffv.DimEducation.DimEndDateNavigation.Month, - Day = ffv.DimEducation.DimEndDateNavigation.Day - }; - } - educationGroup.items.Add(education); - } - if (educationGroup.items.Count > 0) - { - profileDataResponse.activity.educationGroups.Add(educationGroup); - } - break; - case Constants.FieldIdentifiers.ACTIVITY_PUBLICATION: - var publicationGroup = new ProfileEditorGroupPublication() - { - source = new ProfileEditorSource() - { - Id = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() - { - NameFi = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv - } - }, - items = new List() { }, - groupMeta = new ProfileEditorGroupMeta() - { - Id = dfds.Id, - Type = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, - Show = dfds.Show - } - }; - foreach (FactFieldValue ffv in dfds.FactFieldValues) - { - // DimPublication - if (ffv.DimPublicationId != -1) - { - publicationGroup.items.Add( - - new ProfileEditorItemPublication() - { - PublicationId = ffv.DimPublication.PublicationId, - PublicationName = ffv.DimPublication.PublicationName, - PublicationYear = ffv.DimPublication.PublicationYear, - Doi = ffv.DimPublication.Doi, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimPublicationId, - Type = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - - ); - } - - // DimOrcidPublication - if (ffv.DimOrcidPublicationId != -1) - { - publicationGroup.items.Add( - - new ProfileEditorItemPublication() - { - PublicationId = ffv.DimOrcidPublication.PublicationId, - PublicationName = ffv.DimOrcidPublication.PublicationName, - PublicationYear = ffv.DimOrcidPublication.PublicationYear, - Doi = ffv.DimOrcidPublication.DoiHandle, - itemMeta = new ProfileEditorItemMeta() - { - Id = ffv.DimOrcidPublicationId, - Type = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, - Show = ffv.Show, - PrimaryValue = ffv.PrimaryValue - } - } - - ); - } - } - if (publicationGroup.items.Count > 0) - { - profileDataResponse.activity.publicationGroups.Add(publicationGroup); - } - break; - default: - break; - } + return Ok(new ApiResponseProfileDataGet(success: true, reason: "", data: cachedResponse, fromCache: true)); } + // Get profile data + ProfileEditorDataResponse profileDataResponse = await _userProfileService.GetProfileDataAsync2(userprofileId); + // Save response in cache - var cacheEntryOptions = new MemoryCacheEntryOptions() + MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() // Keep in cache for this time, reset time if accessed. - .SetSlidingExpiration(TimeSpan.FromSeconds(60)); + .SetSlidingExpiration(TimeSpan.FromSeconds(Constants.Cache.MEMORY_CACHE_EXPIRATION_SECONDS)); - // Save data in cache. Cache key is ORCID ID. - _cache.Set(orcidId, profileDataResponse, cacheEntryOptions); + // Save data in cache + _cache.Set(cacheKey, profileDataResponse, cacheEntryOptions); - return Ok(new ApiResponse(success: true, data: profileDataResponse)); + return Ok(new ApiResponseProfileDataGet(success: true, reason: "", data: profileDataResponse, fromCache: false)); } - // PATCH: api/ProfileData/ + /// + /// Modify profile data. + /// [HttpPatch] + [ProducesResponseType(typeof(ApiResponseProfileDataPatch), StatusCodes.Status200OK)] public async Task PatchMany([FromBody] ProfileEditorDataModificationRequest profileEditorDataModificationRequest) { // Return immediately if there is nothing to change. @@ -778,98 +105,48 @@ public async Task PatchMany([FromBody] ProfileEditorDataModificat return Ok(new ApiResponse(success: true)); } - // Get userprofile - var orcidId = this.GetOrcidId(); - var userprofileId = await _userProfileService.GetUserprofileId(orcidId); - if (userprofileId == -1) + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { return Ok(new ApiResponse(success: false, reason: "profile not found")); } + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); - var dimUserProfile = await _ttvContext.DimUserProfiles - .Include(dup => dup.DimFieldDisplaySettings) - .Include(dup => dup.FactFieldValues).AsSplitQuery().FirstOrDefaultAsync(up => up.Id == userprofileId); - - - var profileEditorDataModificationResponse = new ProfileEditorDataModificationResponse(); - - // Set 'Show' in DimFieldDisplaySettings - foreach (ProfileEditorGroupMeta profileEditorGroupMeta in profileEditorDataModificationRequest.groups.ToList()) - { - var dimFieldDisplaySettings = dimUserProfile.DimFieldDisplaySettings.Where(d => d.Id == profileEditorGroupMeta.Id).FirstOrDefault(); - if (dimFieldDisplaySettings != null) - { - dimFieldDisplaySettings.Show = profileEditorGroupMeta.Show; - profileEditorDataModificationResponse.groups.Add(profileEditorGroupMeta); - } - } + // Collect information about updated items to a response object, which will be sent in response. + ProfileEditorDataModificationResponse profileEditorDataModificationResponse = new(); // Set 'Show' and 'PrimaryValue' in FactFieldValues foreach (ProfileEditorItemMeta profileEditorItemMeta in profileEditorDataModificationRequest.items.ToList()) { - FactFieldValue factFieldValue = null; - switch (profileEditorItemMeta.Type) - { - case Constants.FieldIdentifiers.PERSON_FIRST_NAMES: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimNameId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.PERSON_LAST_NAME: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimNameId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.PERSON_OTHER_NAMES: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimNameId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimResearcherDescriptionId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.PERSON_WEB_LINK: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimWebLinkId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimEmailAddrressId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.PERSON_KEYWORD: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimKeywordId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimTelephoneNumberId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.ACTIVITY_AFFILIATION: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimAffiliationId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.ACTIVITY_EDUCATION: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimEducationId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - case Constants.FieldIdentifiers.ACTIVITY_PUBLICATION: - factFieldValue = dimUserProfile.FactFieldValues.Where(ffv => ffv.DimPublicationId == profileEditorItemMeta.Id || ffv.DimOrcidPublicationId == profileEditorItemMeta.Id).FirstOrDefault(); - break; - default: - break; - } - - if (factFieldValue != null) - { - factFieldValue.Show = profileEditorItemMeta.Show; - factFieldValue.PrimaryValue = profileEditorItemMeta.PrimaryValue; - profileEditorDataModificationResponse.items.Add(profileEditorItemMeta); - } + string updateSql = _ttvSqlService.GetSqlQuery_Update_FactFieldValues(userprofileId, profileEditorItemMeta); + await _userProfileService.ExecuteRawSql(updateSql); + profileEditorDataModificationResponse.items.Add(profileEditorItemMeta); } - await _ttvContext.SaveChangesAsync(); - - // Update entry in Elasticsearch index - // TODO use BackgroundService to handle Elasticsearch API call. - // TODO create data structure for Elasticsearch person index. + // Update Elasticsearch index in a background task. if (_elasticsearchService.IsElasticsearchSyncEnabled()) { - await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, null); + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation($"Elasticsearch index update for {orcidId} started {DateTime.UtcNow}"); + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = await _backgroundProfiledata.GetProfiledataForElasticsearch(orcidId, userprofileId); + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person); + _logger.LogInformation($"Elasticsearch index update for {orcidId} completed {DateTime.UtcNow}"); + }); } - return Ok(new ApiResponse(success: true, data: profileEditorDataModificationResponse)); + return Ok(new ApiResponseProfileDataPatch(success: true, reason: "", data: profileEditorDataModificationResponse, fromCache: false)); } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/PublicationController.cs b/aspnetcore/src/api/Controllers/PublicationController.cs index ca27df99..db9aa05f 100644 --- a/aspnetcore/src/api/Controllers/PublicationController.cs +++ b/aspnetcore/src/api/Controllers/PublicationController.cs @@ -1,12 +1,17 @@ using api.Services; -using api.Models; +using api.Models.Api; using api.Models.Ttv; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using System.Linq; using System.Collections.Generic; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.AspNetCore.Http; +using api.Models.Common; namespace api.Controllers { @@ -15,71 +20,101 @@ namespace api.Controllers */ [Route("api/publication")] [ApiController] - [Authorize] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] public class PublicationController : TtvControllerBase { private readonly TtvContext _ttvContext; - private readonly UserProfileService _userProfileService; - - public PublicationController(TtvContext ttvContext, UserProfileService userProfileService) + private readonly IUserProfileService _userProfileService; + private readonly IUtilityService _utilityService; + private readonly IDataSourceHelperService _dataSourceHelperService; + private readonly ILanguageService _languageService; + private readonly IMemoryCache _cache; + + public PublicationController(TtvContext ttvContext, IUserProfileService userProfileService, + IUtilityService utilityService, IDataSourceHelperService dataSourceHelperService, + IMemoryCache memoryCache, ILanguageService languageService) { _ttvContext = ttvContext; - _userProfileService = userProfileService; + _userProfileService = userProfileService; + _utilityService = utilityService; + _dataSourceHelperService = dataSourceHelperService; + _languageService = languageService; + _cache = memoryCache; } - /* - * Add publication(s) to profile. - */ + /// + /// Add publicaton(s) to user profile. + /// [HttpPost] + [ProducesResponseType(typeof(ApiResponsePublicationPostMany), StatusCodes.Status200OK)] public async Task PostMany([FromBody] List profileEditorPublicationsToAdd) { if (!ModelState.IsValid) { - return Ok(new ApiResponse(success: false, reason: "invalid request data", data: profileEditorPublicationsToAdd)); + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to add + if (profileEditorPublicationsToAdd.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to add")); } - // Get userprofile - var orcidId = this.GetOrcidId(); - var userprofileId = await _userProfileService.GetUserprofileId(orcidId); - if (userprofileId == -1) + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - // Userprofile not found return Ok(new ApiResponse(success: false, reason: "profile not found")); } - var dimUserProfile = await _ttvContext.DimUserProfiles + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + DimUserProfile dimUserProfile = await _ttvContext.DimUserProfiles .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.BrFieldDisplaySettingsDimRegisteredDataSources) - .ThenInclude(br => br.DimRegisteredDataSource) + .ThenInclude(dfds => dfds.FactFieldValues) + .ThenInclude(ffv => ffv.DimRegisteredDataSource) .ThenInclude(drds => drds.DimOrganization).AsNoTracking() .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimPublication).AsNoTracking().AsSplitQuery().FirstOrDefaultAsync(dup => dup.Id == userprofileId); + .ThenInclude(ffv => ffv.DimPublication).AsNoTracking().FirstOrDefaultAsync(dup => dup.Id == userprofileId); - // TODO: Currently all added publications get the same data source. + // TODO: Currently all added publications get the same data source (Tiedejatutkimus.fi) - // Get Tiedejatutkimus.fi registered data source id - var tiedejatutkimusRegisteredDataSourceId = await _userProfileService.GetTiedejatutkimusFiRegisteredDataSourceId(); - // Get DimFieldDisplaySetting for Tiedejatutkimus.fi - var dimFieldDisplaySettingsPublication = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION && dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSourceId == tiedejatutkimusRegisteredDataSourceId); + // Get DimFieldDisplaySetting for publication + DimFieldDisplaySetting dimFieldDisplaySettingsPublication = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION); - // Response object - var profileEditorAddPublicationResponse = new ProfileEditorAddPublicationResponse(); - profileEditorAddPublicationResponse.source = new ProfileEditorSource() + // Registered data source organization name translation + NameTranslation nameTranslation_OrganizationName = _languageService.GetNameTranslation( + nameFi: _dataSourceHelperService.DimOrganizationNameFi_TTV, + nameSv: _dataSourceHelperService.DimOrganizationNameSv_TTV, + nameEn: _dataSourceHelperService.DimOrganizationNameEn_TTV + ); + + // Data source + ProfileEditorSource dataSource = new ProfileEditorSource() { - Id = dimFieldDisplaySettingsPublication.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Id, - RegisteredDataSource = dimFieldDisplaySettingsPublication.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.Name, - Organization = new ProfileEditorSourceOrganization() + RegisteredDataSource = _dataSourceHelperService.DimRegisteredDataSourceName_TTV, + Organization = new Organization() { - NameFi = dimFieldDisplaySettingsPublication.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameFi, - NameEn = dimFieldDisplaySettingsPublication.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameEn, - NameSv = dimFieldDisplaySettingsPublication.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSource.DimOrganization.NameSv + NameFi = nameTranslation_OrganizationName.NameFi, + NameSv = nameTranslation_OrganizationName.NameSv, + NameEn = nameTranslation_OrganizationName.NameEn } }; + // Response object + ProfileEditorAddPublicationResponse profileEditorAddPublicationResponse = new() + { + source = dataSource + }; + // Loop publications foreach (ProfileEditorPublicationToAdd publicationToAdd in profileEditorPublicationsToAdd) { - var publicationProcessed = false; + bool publicationProcessed = false; // Check if userprofile already includes given publication foreach (FactFieldValue ffv in dimUserProfile.FactFieldValues.Where(ffv => ffv.DimPublicationId != -1)) { @@ -95,7 +130,7 @@ public async Task PostMany([FromBody] List dp.PublicationId == publicationToAdd.PublicationId); + DimPublication dimPublication = await _ttvContext.DimPublications.AsNoTracking().FirstOrDefaultAsync(dp => dp.PublicationId == publicationToAdd.PublicationId); // Check if DimPublication exists if (dimPublication == null) { @@ -105,30 +140,37 @@ public async Task PostMany([FromBody] List + { + dataSource } }; @@ -136,43 +178,73 @@ public async Task PostMany([FromBody] List DeletePublicationFromProfile(string publicationId) + /// + /// Remove publication(s) from user profile. + /// + [HttpPost] + [Route("remove")] + [ProducesResponseType(typeof(ApiResponsePublicationRemoveMany), StatusCodes.Status200OK)] + public async Task RemoveMany([FromBody] List publicationIds) { if (!ModelState.IsValid) { - return Ok(new ApiResponse(success: false, reason: "publicationId invalid", data: publicationId)); + return Ok(new ApiResponse(success: false, reason: "invalid request data")); } - // Get id of userprofile - var orcidId = this.GetOrcidId(); - var userprofileId = await _userProfileService.GetUserprofileId(orcidId); - if (userprofileId == -1) + // Return immediately if there is nothing to remove + if (publicationIds.Count == 0) { - // Userprofile not found - return Ok(new ApiResponse(success: false, reason: "profile not found")); + return Ok(new ApiResponse(success: false, reason: "nothing to remove")); } - // Remove FactFieldValue - var factFieldValue = await _ttvContext.FactFieldValues - .Include(ffv => ffv.DimPublication).AsNoTracking().AsSplitQuery().FirstOrDefaultAsync(ffv => ffv.DimUserProfileId == userprofileId && ffv.DimPublicationId != -1 && ffv.DimPublication.PublicationId == publicationId); + // Get ORCID id + string orcidId = GetOrcidId(); - if (factFieldValue == null) + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - // Publication is not in profile - return Ok(new ApiResponse(success: false, reason: "publicationId not found in profile", data: publicationId)); + return Ok(new ApiResponse(success: false, reason: "profile not found")); } - _ttvContext.FactFieldValues.Remove(factFieldValue); + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // Response object + ProfileEditorRemovePublicationResponse profileEditorRemovePublicationResponse = new(); + + // Remove FactFieldValues + foreach(string publicationId in publicationIds.Distinct()) + { + FactFieldValue factFieldValue = await _ttvContext.FactFieldValues.Where(ffv => ffv.DimUserProfileId == userprofileId && ffv.DimPublicationId != -1 && ffv.DimPublication.PublicationId == publicationId) + .Include(ffv => ffv.DimPublication).AsNoTracking().FirstOrDefaultAsync(); + + if (factFieldValue != null) + { + profileEditorRemovePublicationResponse.publicationsRemoved.Add(publicationId); + _ttvContext.FactFieldValues.Remove(factFieldValue); + } + else + { + profileEditorRemovePublicationResponse.publicationsNotFound.Add(publicationId); + } + } await _ttvContext.SaveChangesAsync(); - return Ok(new ApiResponse(success: true, reason: "removed", data: publicationId)); + // TODO: add Elasticsearch sync? + + // Remove cached profile data response. Cache key is ORCID ID. + _cache.Remove(orcidId); + + return Ok(new ApiResponsePublicationRemoveMany(success: true, reason: "removed", data: profileEditorRemovePublicationResponse, fromCache: false)); } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/ResearchDatasetController.cs b/aspnetcore/src/api/Controllers/ResearchDatasetController.cs new file mode 100644 index 00000000..62699f37 --- /dev/null +++ b/aspnetcore/src/api/Controllers/ResearchDatasetController.cs @@ -0,0 +1,230 @@ +using api.Services; +using api.Models.Api; +using api.Models.Ttv; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Collections.Generic; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.AspNetCore.Http; +using api.Models.Common; + +namespace api.Controllers +{ + /* + * ResearchDatasetController implements profile editor API commands for adding and deleting profile's research dataset. + */ + [Route("api/researchdataset")] + [ApiController] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] + public class ResearchDatasetController : TtvControllerBase + { + private readonly TtvContext _ttvContext; + private readonly IUserProfileService _userProfileService; + private readonly IUtilityService _utilityService; + private readonly IDataSourceHelperService _dataSourceHelperService; + private readonly ILanguageService _languageService; + private readonly IMemoryCache _cache; + + public ResearchDatasetController(TtvContext ttvContext, IUserProfileService userProfileService, + IUtilityService utilityService, IDataSourceHelperService dataSourceHelperService, + IMemoryCache memoryCache, ILanguageService languageService) + { + _ttvContext = ttvContext; + _userProfileService = userProfileService; + _utilityService = utilityService; + _dataSourceHelperService = dataSourceHelperService; + _languageService = languageService; + _cache = memoryCache; + } + + /// + /// Add research dataset to user profile. + /// + [HttpPost] + [ProducesResponseType(typeof(ApiResponseResearchDatasetPostMany), StatusCodes.Status200OK)] + public async Task PostMany([FromBody] List profileEditorResearchdatasetToAdd) + { + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to add + if (profileEditorResearchdatasetToAdd.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to add")); + } + + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // TODO: FactFieldValues relation to DimResearchDataset + DimUserProfile dimUserProfile = await _ttvContext.DimUserProfiles.Where(dup => dup.Id == userprofileId) + .Include(dup => dup.DimFieldDisplaySettings) + .ThenInclude(dfds => dfds.FactFieldValues) + .ThenInclude(ffv => ffv.DimRegisteredDataSource) + .ThenInclude(drds => drds.DimOrganization).AsNoTracking() + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimResearchDatasetId != -1)) + .ThenInclude(ffv => ffv.DimResearchDataset).AsNoTracking().FirstOrDefaultAsync(); + + // TODO: Currently all added research data get the same data source (Tiedejatutkimus.fi) + + // Get DimFieldDisplaySetting for Tiedejatutkimus.fi + DimFieldDisplaySetting dimFieldDisplaySettingsResearchDataset = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET); + + // Registered data source organization name translation + NameTranslation nameTranslation_OrganizationName = _languageService.GetNameTranslation( + nameFi: _dataSourceHelperService.DimOrganizationNameFi_TTV, + nameSv: _dataSourceHelperService.DimOrganizationNameSv_TTV, + nameEn: _dataSourceHelperService.DimOrganizationNameEn_TTV + ); + + // Response object + ProfileEditorAddResearchDatasetResponse profileEditorAddResearchDatasetResponse = new() + { + source = new ProfileEditorSource() + { + RegisteredDataSource = _dataSourceHelperService.DimRegisteredDataSourceName_TTV, + Organization = new Organization() + { + NameFi = nameTranslation_OrganizationName.NameFi, + NameSv = nameTranslation_OrganizationName.NameSv, + NameEn = nameTranslation_OrganizationName.NameEn + } + } + }; + + // Loop data sets + foreach (ProfileEditorResearchDatasetToAdd researchDatasetToAdd in profileEditorResearchdatasetToAdd) + { + bool researchDatasetProcessed = false; + // Check if userprofile already includes given research dataset + foreach (FactFieldValue ffv in dimUserProfile.FactFieldValues) + { + if (ffv.DimResearchDataset.LocalIdentifier == researchDatasetToAdd.LocalIdentifier) + { + // Research dataset is already in profile + profileEditorAddResearchDatasetResponse.researchDatasetAlreadyInProfile.Add(researchDatasetToAdd.LocalIdentifier); + researchDatasetProcessed = true; + break; + } + } + + if (!researchDatasetProcessed) + { + // Get DimResearchDataset + DimResearchDataset dimResearchDataset = await _ttvContext.DimResearchDatasets.AsNoTracking().FirstOrDefaultAsync(drd => drd.LocalIdentifier == researchDatasetToAdd.LocalIdentifier); + // Check if DimResearchDataset exists + if (dimResearchDataset == null) + { + // DimResearchDataset does not exist + profileEditorAddResearchDatasetResponse.researchDatasetNotFound.Add(researchDatasetToAdd.LocalIdentifier); + } + else + { + // Add FactFieldValue + FactFieldValue factFieldValueResearchDataset = _userProfileService.GetEmptyFactFieldValue(); + factFieldValueResearchDataset.Show = researchDatasetToAdd.Show != null ? researchDatasetToAdd.Show : false; + factFieldValueResearchDataset.PrimaryValue = researchDatasetToAdd.PrimaryValue != null ? researchDatasetToAdd.PrimaryValue : false; + factFieldValueResearchDataset.DimUserProfileId = dimUserProfile.Id; + factFieldValueResearchDataset.DimFieldDisplaySettingsId = dimFieldDisplaySettingsResearchDataset.Id; + factFieldValueResearchDataset.DimResearchDatasetId = dimResearchDataset.Id; + factFieldValueResearchDataset.DimRegisteredDataSourceId = _dataSourceHelperService.DimRegisteredDataSourceId_TTV; + factFieldValueResearchDataset.SourceId = Constants.SourceIdentifiers.TIEDEJATUTKIMUS; + factFieldValueResearchDataset.Created = _utilityService.GetCurrentDateTime(); + factFieldValueResearchDataset.Modified = _utilityService.GetCurrentDateTime(); + _ttvContext.FactFieldValues.Add(factFieldValueResearchDataset); + await _ttvContext.SaveChangesAsync(); + + profileEditorAddResearchDatasetResponse.researchDatasetAdded.Add(researchDatasetToAdd.LocalIdentifier); + } + } + } + + // TODO: add Elasticsearch sync? + + // Remove cached profile data response. Cache key is ORCID ID. + _cache.Remove(orcidId); + + return Ok(new ApiResponseResearchDatasetPostMany(success: true, reason:"", data: profileEditorAddResearchDatasetResponse, fromCache: false)); + } + + /// + /// Remove research dataset(s) from user profile. + /// + [HttpPost] + [Route("remove")] + [ProducesResponseType(typeof(ApiResponseResearchDatasetRemoveMany), StatusCodes.Status200OK)] + public async Task RemoveMany([FromBody] List localIdentifiers) + { + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to remove + if (localIdentifiers.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to remove")); + } + + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // Response object + ProfileEditorRemoveResearchDatasetResponse profileEditorRemoveResearchDatasetResponse = new(); + + // Remove FactFieldValues + foreach(string localIdentifier in localIdentifiers.Distinct()) + { + FactFieldValue factFieldValue = await _ttvContext.FactFieldValues + .Where( + ffv => ffv.DimUserProfileId == userprofileId && + ffv.DimResearchDatasetId != -1 && + ffv.DimResearchDataset.LocalIdentifier == localIdentifier + ).FirstOrDefaultAsync(); + + if (factFieldValue != null) + { + _ttvContext.FactFieldValues.Remove(factFieldValue); + profileEditorRemoveResearchDatasetResponse.researchDatasetsRemoved.Add(localIdentifier); + } + else + { + profileEditorRemoveResearchDatasetResponse.researchDatasetsNotFound.Add(localIdentifier); + } + } + await _ttvContext.SaveChangesAsync(); + + // TODO: add Elasticsearch sync? + + // Remove cached profile data response. Cache key is ORCID ID. + _cache.Remove(orcidId); + + return Ok(new ApiResponseResearchDatasetRemoveMany(success: true, reason: "removed", data: profileEditorRemoveResearchDatasetResponse, fromCache: false)); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/SharingController.cs b/aspnetcore/src/api/Controllers/SharingController.cs new file mode 100644 index 00000000..1d844c98 --- /dev/null +++ b/aspnetcore/src/api/Controllers/SharingController.cs @@ -0,0 +1,217 @@ +using api.Services; +using api.Models.Api; +using api.Models.Common; +using api.Models.Ttv; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Collections.Generic; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.AspNetCore.Http; +using api.Models.ProfileEditor; +using System; + +namespace api.Controllers +{ + /* + * SharingController implements profile editor API commands for sharing settings. + */ + [Route("api/sharing")] + [ApiController] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] + public class SharingController : TtvControllerBase + { + private readonly TtvContext _ttvContext; + private readonly IUserProfileService _userProfileService; + private readonly ISharingService _sharingService; + private readonly IMemoryCache _cache; + + public SharingController(TtvContext ttvContext, + IUserProfileService userProfileService, + ISharingService sharingService, + IMemoryCache memoryCache) + { + _ttvContext = ttvContext; + _userProfileService = userProfileService; + _sharingService = sharingService; + _cache = memoryCache; + } + + /// + /// Get list of sharing purposes. + /// + [HttpGet] + [Route("purposes")] + [ProducesResponseType(typeof(ApiResponseProfileSharingPurposesGet), StatusCodes.Status200OK)] + public async Task GetPurposes() + { + string cacheKey = "share_purposes"; + + // Send cached response, if exists. + if (_cache.TryGetValue(cacheKey, out ProfileEditorSharingPurposesResponse cachedResponse)) + { + return Ok(new ApiResponseProfileSharingPurposesGet(success: true, reason: "", data: cachedResponse, fromCache: true)); + } + + // Get purposes + ProfileEditorSharingPurposesResponse profileSharingPurposesResponse = await _sharingService.GetProfileEditorSharingPurposesResponse(); + + // Save response in cache. Cache life time can be long, singe the values are the same for all users. + MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() + .SetSlidingExpiration(TimeSpan.FromSeconds(Constants.Cache.MEMORY_CACHE_EXPIRATION_SECONDS_LONG)); + _cache.Set(cacheKey, profileSharingPurposesResponse, cacheEntryOptions); + + return Ok(new ApiResponseProfileSharingPurposesGet(success: true, reason: "", data: profileSharingPurposesResponse, fromCache: false)); + } + + /// + /// Get list of sharing permissions. + /// + [HttpGet] + [Route("permissions")] + [ProducesResponseType(typeof(ApiResponseProfileSharingPermissionsGet), StatusCodes.Status200OK)] + public async Task GetPermissions() + { + string cacheKey = "share_permissions"; + + // Send cached response, if exists. + if (_cache.TryGetValue(cacheKey, out ProfileEditorSharingPermissionsResponse cachedResponse)) + { + return Ok(new ApiResponseProfileSharingPermissionsGet(success: true, reason: "", data: cachedResponse, fromCache: true)); + } + + // Get permissions + ProfileEditorSharingPermissionsResponse profileSharingPermissionsResponse = await _sharingService.GetProfileEditorSharingPermissionsResponse(); + + // Save response in cache. Cache life time can be long, singe the values are the same for all users. + MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() + .SetSlidingExpiration(TimeSpan.FromSeconds(Constants.Cache.MEMORY_CACHE_EXPIRATION_SECONDS_LONG)); + _cache.Set(cacheKey, profileSharingPermissionsResponse, cacheEntryOptions); + + return Ok(new ApiResponseProfileSharingPermissionsGet(success: true, reason: "", data: profileSharingPermissionsResponse, fromCache: false)); + } + + + /// + /// Get list of given permissions. + /// + [HttpGet] + [Route("givenpermissions")] + [ProducesResponseType(typeof(ApiResponseProfileSharingGivenPermissionsGet), StatusCodes.Status200OK)] + public async Task GetShares() + { + // Get ORCID id + string orcidId = GetOrcidId(); + + // Cache key + string cacheKey = orcidId + "_given_permissions"; + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Send cached response, if exists. + if (_cache.TryGetValue(cacheKey, out ProfileEditorSharingGivenPermissionsResponse cachedResponse)) + { + return Ok(new ApiResponseProfileSharingGivenPermissionsGet(success: true, reason: "", data: cachedResponse, fromCache: true)); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + // Get profile data + ProfileEditorSharingGivenPermissionsResponse profileSharingResponse = await _sharingService.GetProfileEditorSharingResponse(userprofileId); + + // Save response in cache + MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() + .SetSlidingExpiration(TimeSpan.FromSeconds(Constants.Cache.MEMORY_CACHE_EXPIRATION_SECONDS)); + _cache.Set(cacheKey, profileSharingResponse, cacheEntryOptions); + + return Ok(new ApiResponseProfileSharingGivenPermissionsGet(success: true, reason: "", data: profileSharingResponse, fromCache: false)); + } + + /// + /// Add permission(s). + /// + [HttpPost] + [Route("add")] + public async Task PostMany([FromBody] List permissionsToAdd) + { + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to add + if (permissionsToAdd.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to add")); + } + + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + await _sharingService.AddPermissions(userprofileId, permissionsToAdd); + + await _ttvContext.SaveChangesAsync(); + + // Remove cached given permissions list. + _cache.Remove(orcidId + "_given_permissions"); + + return Ok(new ApiResponse(success: true)); + } + + /// + /// Remove permission(s). + /// + [HttpPost] + [Route("remove")] + public async Task RemoveMany([FromBody] List permissionsToDelete) + { + if (!ModelState.IsValid) + { + return Ok(new ApiResponse(success: false, reason: "invalid request data")); + } + + // Return immediately if there is nothing to delete + if (permissionsToDelete.Count == 0) + { + return Ok(new ApiResponse(success: false, reason: "nothing to remove")); + } + + // Get ORCID id + string orcidId = GetOrcidId(); + + // Check that userprofile exists. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } + + // Get userprofile id + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); + + await _sharingService.DeletePermissions(userprofileId, permissionsToDelete); + + await _ttvContext.SaveChangesAsync(); + + // Remove cached given permissions list. + _cache.Remove(orcidId + "_given_permissions"); + + return Ok(new ApiResponse(success: true)); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/TtvControllerBase.cs b/aspnetcore/src/api/Controllers/TtvControllerBase.cs index 84ff0441..7bb776af 100644 --- a/aspnetcore/src/api/Controllers/TtvControllerBase.cs +++ b/aspnetcore/src/api/Controllers/TtvControllerBase.cs @@ -1,22 +1,55 @@ using System; using System.Linq; using Microsoft.AspNetCore.Mvc; +using Microsoft.Net.Http.Headers; /* * TtvControllerBase implements utility methods which can be used by all controllers. */ public abstract class TtvControllerBase : ControllerBase { + // Get Keycloak user ID from user claims + [NonAction] + protected string GetKeycloakUserId() + { + //return User.Claims.FirstOrDefault(x => x.Type == "sub")?.Value; + return User.Claims.FirstOrDefault(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; + } + // Get ORCID ID from user claims + [NonAction] protected string GetOrcidId() { return User.Claims.FirstOrDefault(x => x.Type == "orcid")?.Value; } + // Get access token from HttpRequest header Authorization. + [NonAction] + protected string GetBearerTokenFromHttpRequest() + { + return Request.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", ""); + } + + // Get ORCID access token from user claims + [NonAction] + protected string GetOrcidAccessToken() + { + return User.Claims.FirstOrDefault(x => x.Type == "orcid_access_token")?.Value; + } + // Get prefix for log message - // [timestamp][ORCID ID][ip address] + // [Keycloak user ID][ORCID ID][ip address] + [NonAction] public string GetLogPrefix() { - return "[" + DateTime.UtcNow.ToString("s") + "][" + this.GetOrcidId() + "][" + HttpContext.Connection.RemoteIpAddress?.ToString() + "]"; + return "[ORCID=" + this.GetOrcidId() + "][IP=" + HttpContext.Connection.RemoteIpAddress?.ToString() + "][Keycloak ID=" + this.GetKeycloakUserId() + "]"; + } + + // Get ORCID public API flag from user claims. + // This flag is used in testing phase only. + [NonAction] + protected string GetOrcidPublicApiFlag() + { + return User.Claims.FirstOrDefault(x => x.Type == "use_orcid_public_api")?.Value; } } \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/UserProfileController.cs b/aspnetcore/src/api/Controllers/UserProfileController.cs index c1e80ff4..9b0f7622 100644 --- a/aspnetcore/src/api/Controllers/UserProfileController.cs +++ b/aspnetcore/src/api/Controllers/UserProfileController.cs @@ -1,445 +1,173 @@ -using api.Services; -using api.Models; -using api.Models.Ttv; +using api.Services; +using api.Models.Api; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Linq; using System.Threading.Tasks; using System; -using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.AspNetCore.Http; -namespace api.Controllers +namespace api.Controllers { /* * UserProfileController handles creation, existence check and deletion of userprofile. */ - [Route("api/[controller]")] + [Route("api/userprofile")] [ApiController] - [Authorize] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] public class UserProfileController : TtvControllerBase { - private readonly TtvContext _ttvContext; - private readonly DemoDataService _demoDataService; - private readonly UserProfileService _userProfileService; - private readonly ElasticsearchService _elasticsearchService; + private readonly IUserProfileService _userProfileService; + private readonly IElasticsearchService _elasticsearchService; + private readonly IKeycloakAdminApiService _keycloakAdminApiService; private readonly ILogger _logger; - - public UserProfileController(TtvContext ttvContext, DemoDataService demoDataService, ElasticsearchService elasticsearchService, UserProfileService userProfileService, ILogger logger) + private readonly IMemoryCache _cache; + private readonly IBackgroundTaskQueue _taskQueue; + + public UserProfileController( + IElasticsearchService elasticsearchService, + IUserProfileService userProfileService, + IKeycloakAdminApiService keycloakAdminApiService, + ILogger logger, + IMemoryCache memoryCache, + IBackgroundTaskQueue taskQueue) { - _ttvContext = ttvContext; - _demoDataService = demoDataService; _userProfileService = userProfileService; _elasticsearchService = elasticsearchService; + _keycloakAdminApiService = keycloakAdminApiService; _logger = logger; + _cache = memoryCache; + _taskQueue = taskQueue; } - // Check if profile exists. + /// + /// Check if user profile exists. + /// [HttpGet] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task Get() { - // Get ORCID id. - var orcidId = this.GetOrcidId(); - // Log request - _logger.LogInformation(this.GetLogPrefix() + " check profile exists"); - - // Get userprofile id from ORCID id. - var userprofileId = await _userProfileService.GetUserprofileId(orcidId); - - // Userprofile id must be positive. - if (userprofileId > 0) + if (await _userProfileService.UserprofileExistsForOrcidId(orcidId: GetOrcidId())) { return Ok(new ApiResponse(success: true)); } - return Ok(new ApiResponse(success: false, reason: "profile not found")); + else + { + return Ok(new ApiResponse(success: false, reason: "profile not found")); + } } - // Create profile + /// + /// Create user profile. + /// [HttpPost] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task Create() { // Get ORCID id. - var orcidId = this.GetOrcidId(); + string orcidId = GetOrcidId(); // Log request _logger.LogInformation(this.GetLogPrefix() + " create profile request"); - // Get DimPid by ORCID id. - // Also get related entities. Needed when searching existing data that should be automatically included in profile. - var dimPid = await _ttvContext.DimPids - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimNameDimKnownPersonIdConfirmedIdentityNavigations) - .ThenInclude(dn => dn.FactContributions).AsNoTracking() - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimNameDimKnownPersonIdConfirmedIdentityNavigations) - .ThenInclude(dn => dn.DimRegisteredDataSource).AsNoTracking() - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimTelephoneNumbers) - .ThenInclude(dtn => dtn.DimRegisteredDataSource).AsNoTracking() - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimUserProfiles).AsNoTracking().AsSplitQuery().FirstOrDefaultAsync(p => p.PidContent == orcidId && p.PidType == Constants.PidTypes.ORCID); - - // Check if DimPid - if (dimPid == null) + // Return immediately, if profile already exist. + if (await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - // DimPid was not found. - - // Add new DimPid, add new DimKnownPerson - dimPid = _userProfileService.GetEmptyDimPid(); - dimPid.PidContent = orcidId; - dimPid.PidType = Constants.PidTypes.ORCID; - - // Since new DimPid is added, then new DimKnownPerson must be added - dimPid.DimKnownPerson = new DimKnownPerson() { - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - dimPid.SourceId = Constants.SourceIdentifiers.ORCID; - _ttvContext.DimPids.Add(dimPid); - await _ttvContext.SaveChangesAsync(); + _logger.LogInformation(this.GetLogPrefix() + " profile already exists"); + return Ok(new ApiResponse(success: true)); } - else if (dimPid.DimKnownPerson == null || dimPid.DimKnownPersonId == -1) + + // Create profile + try { - // DimPid was found but it does not have related DimKnownPerson. - var kp = new DimKnownPerson() - { - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimKnownPeople.Add(kp); - dimPid.DimKnownPerson = kp; - await _ttvContext.SaveChangesAsync(); + await _userProfileService.CreateProfile(orcidId: orcidId); } - - - // Add DimUserProfile - var dimUserProfile = dimPid.DimKnownPerson.DimUserProfiles.FirstOrDefault(); - if (dimUserProfile == null) + catch { - dimUserProfile = new DimUserProfile() { - DimKnownPersonId = dimPid.DimKnownPerson.Id, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - AllowAllSubscriptions = false - }; - _ttvContext.DimUserProfiles.Add(dimUserProfile); - await _ttvContext.SaveChangesAsync(); - - - // Add DimFieldDisplaySettings for data sources ORCID and DEMO - var orcidRegisteredDataSourceId = await _userProfileService.GetOrcidRegisteredDataSourceId(); - var demoOrganization1RegisteredDataSource = await _demoDataService.GetOrganization1RegisteredDataSourceAsync(); - var demoOrganization2RegisteredDataSource = await _demoDataService.GetOrganization2RegisteredDataSourceAsync(); - var demoOrganization3RegisteredDataSource = await _demoDataService.GetOrganization3RegisteredDataSourceAsync(); - - // TODO: Field identifiers used in demo. In production this list should be extended. - var fieldIdentifiers = new List - { - Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS, - Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER, - Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE, - Constants.FieldIdentifiers.PERSON_KEYWORD, - Constants.FieldIdentifiers.PERSON_NAME, - Constants.FieldIdentifiers.PERSON_OTHER_NAMES, - Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION, - Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER, - Constants.FieldIdentifiers.PERSON_WEB_LINK, - Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, - Constants.FieldIdentifiers.ACTIVITY_EDUCATION, - Constants.FieldIdentifiers.ACTIVITY_PUBLICATION - }; - - // DimFieldDisplaySettings for ORCID registered data source - foreach (int fieldIdentifier in fieldIdentifiers) - { - var dimFieldDisplaySetting = new DimFieldDisplaySetting() - { - DimUserProfileId = dimUserProfile.Id, - FieldIdentifier = fieldIdentifier, - Show = false, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - dimFieldDisplaySetting.BrFieldDisplaySettingsDimRegisteredDataSources.Add( - new BrFieldDisplaySettingsDimRegisteredDataSource() - { - DimFieldDisplaySettingsId = dimFieldDisplaySetting.Id, - DimRegisteredDataSourceId = orcidRegisteredDataSourceId - } - ); - _ttvContext.DimFieldDisplaySettings.Add(dimFieldDisplaySetting); - } - await _ttvContext.SaveChangesAsync(); - - - // DimFieldDisplaySettings for demo: Organization 1 registered data source - foreach (int fieldIdentifier in fieldIdentifiers) - { - var dimFieldDisplaySetting = new DimFieldDisplaySetting() - { - DimUserProfileId = dimUserProfile.Id, - FieldIdentifier = fieldIdentifier, - Show = false, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = _demoDataService.GetDemoOrganization1Name(), // In demo must use Org1 name here - Created = DateTime.Now - }; - dimFieldDisplaySetting.BrFieldDisplaySettingsDimRegisteredDataSources.Add( - new BrFieldDisplaySettingsDimRegisteredDataSource() - { - DimFieldDisplaySettingsId = dimFieldDisplaySetting.Id, - DimRegisteredDataSourceId = demoOrganization1RegisteredDataSource.Id - } - ); - _ttvContext.DimFieldDisplaySettings.Add(dimFieldDisplaySetting); - } - await _ttvContext.SaveChangesAsync(); - - // DimFieldDisplaySettings for demo: Organization 2 registered data source - foreach (int fieldIdentifier in fieldIdentifiers) - { - var dimFieldDisplaySetting = new DimFieldDisplaySetting() - { - DimUserProfileId = dimUserProfile.Id, - FieldIdentifier = fieldIdentifier, - Show = false, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = _demoDataService.GetDemoOrganization2Name(), // In demo must use Org2 name here - Created = DateTime.Now - }; - dimFieldDisplaySetting.BrFieldDisplaySettingsDimRegisteredDataSources.Add( - new BrFieldDisplaySettingsDimRegisteredDataSource() - { - DimFieldDisplaySettingsId = dimFieldDisplaySetting.Id, - DimRegisteredDataSourceId = demoOrganization2RegisteredDataSource.Id - } - ); - _ttvContext.DimFieldDisplaySettings.Add(dimFieldDisplaySetting); - } - await _ttvContext.SaveChangesAsync(); - - // DimFieldDisplaySettings for demo: Organization 3 (Tiedejatutkimus.fi) publications - var dimFieldDisplaySettingDemoOrganization3 = new DimFieldDisplaySetting() - { - DimUserProfileId = dimUserProfile.Id, - FieldIdentifier = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, - Show = false, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - dimFieldDisplaySettingDemoOrganization3.BrFieldDisplaySettingsDimRegisteredDataSources.Add( - new BrFieldDisplaySettingsDimRegisteredDataSource() - { - DimFieldDisplaySettingsId = dimFieldDisplaySettingDemoOrganization3.Id, - DimRegisteredDataSourceId = demoOrganization3RegisteredDataSource.Id - } - ); - _ttvContext.DimFieldDisplaySettings.Add(dimFieldDisplaySettingDemoOrganization3); - await _ttvContext.SaveChangesAsync(); - - // Add demo data - await _demoDataService.AddDemoDataToUserProfile(dimUserProfile); - await _userProfileService.AddTtvDataToUserProfile(dimPid.DimKnownPerson, dimUserProfile); + string msg = " create profile failed"; + _logger.LogError(this.GetLogPrefix() + msg); + return Ok(new ApiResponse(success: false, reason: msg)); } - _logger.LogInformation(this.GetLogPrefix() + " profile created"); + _logger.LogInformation(this.GetLogPrefix() + " create profile OK"); return Ok(new ApiResponse(success: true)); } - // Delete profile + /// + /// Delete user profile. + /// [HttpDelete] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task Delete() { // Get ORCID id. - var orcidId = this.GetOrcidId(); + string orcidId = GetOrcidId(); // Log request. _logger.LogInformation(this.GetLogPrefix() + " delete profile request"); - // Check that userprofile exists. - var userprofileId = await _userProfileService.GetUserprofileId(orcidId); - if (userprofileId == -1) + // Return immediately, if profile does not exist. + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - return Ok(new ApiResponse(success: false, reason: "profile not found")); + _logger.LogInformation(this.GetLogPrefix() + " nothing deleted, profile does not exist"); + return Ok(new ApiResponse(success: true)); } - // Get DimUserProfile and related data that should be removed. - var dimUserProfile = await _ttvContext.DimUserProfiles - .Include(dup => dup.DimFieldDisplaySettings) - .ThenInclude(dfds => dfds.BrFieldDisplaySettingsDimRegisteredDataSources) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimName) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimWebLink) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimPid) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimPidIdOrcidPutCodeNavigation) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimAffiliation) - .ThenInclude(da => da.DimOrganization) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimEducation) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimTelephoneNumber) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimEmailAddrress) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimResearcherDescription) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimOrcidPublication) - .Include(dup => dup.FactFieldValues) - .ThenInclude(ffv => ffv.DimKeyword).AsSplitQuery().FirstOrDefaultAsync(up => up.Id == userprofileId); + // Get userprofile id. + int userprofileId = await _userProfileService.GetUserprofileId(orcidId); - foreach (FactFieldValue ffv in dimUserProfile.FactFieldValues) + // Delete profile data from database + bool deleteSuccess = false; + try { - // DimAffiliation. In demo version the related DimOrganization is removed. - if (ffv.DimAffiliationId != -1) - { - var dimOrganization = ffv.DimAffiliation.DimOrganization; - _ttvContext.FactFieldValues.Remove(ffv); - - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimAffiliations.Remove(ffv.DimAffiliation); - } - - // Remove related DimOrganization - // TODO: Removal of DimOrganization only in demo version, if sourceId is ORCID - if (dimOrganization.SourceId == Constants.SourceIdentifiers.ORCID) - { - _ttvContext.DimOrganizations.Remove(dimOrganization); - } - } else - { - // Always remove FactFieldValue - _ttvContext.FactFieldValues.Remove(ffv); - } - - // ORCID put code - if (ffv.DimPidIdOrcidPutCode != -1) - { - _ttvContext.DimPids.Remove(ffv.DimPidIdOrcidPutCodeNavigation); - } - - // DimName - if (ffv.DimNameId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimNames.Remove(ffv.DimName); - } - } - - // DimPid - // DimPids related to FactFieldValue store person's external identifiers - if (ffv.DimPidId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimPids.Remove(ffv.DimPid); - } - } - - // DimWebLink - else if (ffv.DimWebLinkId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimWebLinks.Remove(ffv.DimWebLink); - } - } + deleteSuccess = await _userProfileService.DeleteProfileDataAsync(userprofileId: userprofileId); + } + catch (Exception ex) + { + _logger.LogError(this.GetLogPrefix() + ex.ToString()); + } - // DimOrcidPublication - else if (ffv.DimOrcidPublicationId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimOrcidPublications.Remove(ffv.DimOrcidPublication); - } - } + if (deleteSuccess) + { + // Log deletion + _logger.LogInformation(this.GetLogPrefix() + " delete profile from database OK"); - // DimKeyword - else if (ffv.DimKeywordId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimKeywords.Remove(ffv.DimKeyword); - } - } + // Remove cached profile data response. Cache key is ORCID ID. + _cache.Remove(orcidId); - // DimEducation - else if (ffv.DimEducationId != -1) + // Remove entry from Elasticsearch index in a background task. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) + await _taskQueue.QueueBackgroundWorkItemAsync(async token => { - _ttvContext.DimEducations.Remove(ffv.DimEducation); - } - } - - // DimEmail - else if (ffv.DimEmailAddrressId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimEmailAddrresses.Remove(ffv.DimEmailAddrress); - } + // Update Elasticsearch person index. + bool deleteSuccess = await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId); + if (deleteSuccess) + { + _logger.LogInformation($"Elasticsearch: {orcidId} delete OK."); + } + else + { + _logger.LogError($"Elasticsearch: {orcidId} delete failed."); + } + }); } - // DimResearcherDescription - else if (ffv.DimResearcherDescriptionId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimResearcherDescriptions.Remove(ffv.DimResearcherDescription); - } - } + // Keycloak: logout user + await _keycloakAdminApiService.LogoutUser(this.GetBearerTokenFromHttpRequest()); - // DimTelephoneNumber - else if (ffv.DimTelephoneNumberId != -1) - { - if (_userProfileService.CanDeleteFactFieldValueRelatedData(ffv)) - { - _ttvContext.DimTelephoneNumbers.Remove(ffv.DimTelephoneNumber); - } - } - } - await _ttvContext.SaveChangesAsync(); + // Keycloak: remove user + await _keycloakAdminApiService.RemoveUser(this.GetBearerTokenFromHttpRequest()); - // Remove profile's DimFieldDisplaySettings relation to DimRegisteredDataSource - foreach (DimFieldDisplaySetting dimFieldDisplaySetting in dimUserProfile.DimFieldDisplaySettings) - { - _ttvContext.BrFieldDisplaySettingsDimRegisteredDataSources.RemoveRange(dimFieldDisplaySetting.BrFieldDisplaySettingsDimRegisteredDataSources); + return Ok(new ApiResponse(success: true)); } - - // Remove DimFieldDisplaySettings - _ttvContext.DimFieldDisplaySettings.RemoveRange(dimUserProfile.DimFieldDisplaySettings); - - // Remove DimUserProfile - _ttvContext.DimUserProfiles.Remove(dimUserProfile); - - // Must not remove DimKnownPerson. - // Must not remove DimPid. - - await _ttvContext.SaveChangesAsync(); - - - // Remove entry from Elasticsearch index - // TODO use BackgroundService to handle Elasticsearch API call. - if (_elasticsearchService.IsElasticsearchSyncEnabled()) + else { - await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId); + // Log error + string msg = " delete profile from database failed"; + _logger.LogError(this.GetLogPrefix() + msg); + return Ok(new ApiResponse(success: false, reason: msg)); } - - // Log deletion - _logger.LogInformation(this.GetLogPrefix() + " profile deleted"); - - return Ok(new ApiResponse(success: true)); } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Models/Api/ApiResponse.cs b/aspnetcore/src/api/Models/Api/ApiResponse.cs new file mode 100644 index 00000000..96ba8f15 --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponse.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Api +{ + public class ApiResponse + { + public ApiResponse() + { + Success = true; + Reason = ""; + FromCache = false; + } + + public ApiResponse(bool success) + { + Success = success; + Reason = ""; + FromCache = false; + } + + public ApiResponse(bool success, string reason) + { + Success = success; + Reason = reason; + FromCache = false; + } + + public ApiResponse(bool success, string reason, bool fromCache) + { + Success = success; + Reason = reason; + FromCache = fromCache; + } + + public bool Success { get; set; } + public string Reason { get; set; } + public bool FromCache { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseCooperationGet.cs b/aspnetcore/src/api/Models/Api/ApiResponseCooperationGet.cs new file mode 100644 index 00000000..fbcc4dcd --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseCooperationGet.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseCooperationGet : ApiResponse + { + + public ApiResponseCooperationGet(bool success, string reason, List data, bool fromCache) + { + Success = success; + Reason = reason; + Data = data; + FromCache = fromCache; + } + + public List Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionPostMany.cs b/aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionPostMany.cs new file mode 100644 index 00000000..72dfc28d --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionPostMany.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor.Items; + +namespace api.Models.Api +{ + public class ApiResponseFundingDecisionPostMany : ApiResponse + { + + public ApiResponseFundingDecisionPostMany(bool success, string reason, ProfileEditorAddFundingDecisionResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorAddFundingDecisionResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionRemoveMany.cs b/aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionRemoveMany.cs new file mode 100644 index 00000000..0a7dcb7d --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseFundingDecisionRemoveMany.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseFundingDecisionRemoveMany : ApiResponse + { + + public ApiResponseFundingDecisionRemoveMany(bool success, string reason, ProfileEditorRemoveFundingDecisionResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorRemoveFundingDecisionResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseProfileDataGet.cs b/aspnetcore/src/api/Models/Api/ApiResponseProfileDataGet.cs new file mode 100644 index 00000000..e1bb932b --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseProfileDataGet.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor.Items; + +namespace api.Models.Api +{ + public class ApiResponseProfileDataGet : ApiResponse + { + + public ApiResponseProfileDataGet(bool success, string reason, ProfileEditorDataResponse data, bool fromCache) + { + Success = success; + Reason = reason; + Data = data; + FromCache = fromCache; + } + + public ProfileEditorDataResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseProfileDataPatch.cs b/aspnetcore/src/api/Models/Api/ApiResponseProfileDataPatch.cs new file mode 100644 index 00000000..e6122bf9 --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseProfileDataPatch.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseProfileDataPatch : ApiResponse + { + + public ApiResponseProfileDataPatch(bool success, string reason, ProfileEditorDataModificationResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorDataModificationResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingGivenPermissionsGet.cs b/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingGivenPermissionsGet.cs new file mode 100644 index 00000000..f11b1f68 --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingGivenPermissionsGet.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseProfileSharingGivenPermissionsGet : ApiResponse + { + + public ApiResponseProfileSharingGivenPermissionsGet(bool success, string reason, ProfileEditorSharingGivenPermissionsResponse data, bool fromCache) + { + Success = success; + Reason = reason; + Data = data; + FromCache = fromCache; + } + + public ProfileEditorSharingGivenPermissionsResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPermissionsGet.cs b/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPermissionsGet.cs new file mode 100644 index 00000000..656ec45f --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPermissionsGet.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseProfileSharingPermissionsGet : ApiResponse + { + + public ApiResponseProfileSharingPermissionsGet(bool success, string reason, ProfileEditorSharingPermissionsResponse data, bool fromCache) + { + Success = success; + Reason = reason; + Data = data; + FromCache = fromCache; + } + + public ProfileEditorSharingPermissionsResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPurposesGet.cs b/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPurposesGet.cs new file mode 100644 index 00000000..b43be4b4 --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseProfileSharingPurposesGet.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseProfileSharingPurposesGet : ApiResponse + { + + public ApiResponseProfileSharingPurposesGet(bool success, string reason, ProfileEditorSharingPurposesResponse data, bool fromCache) + { + Success = success; + Reason = reason; + Data = data; + FromCache = fromCache; + } + + public ProfileEditorSharingPurposesResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponsePublicationPostMany.cs b/aspnetcore/src/api/Models/Api/ApiResponsePublicationPostMany.cs new file mode 100644 index 00000000..69f295ce --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponsePublicationPostMany.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor.Items; + +namespace api.Models.Api +{ + public class ApiResponsePublicationPostMany : ApiResponse + { + + public ApiResponsePublicationPostMany(bool success, string reason, ProfileEditorAddPublicationResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorAddPublicationResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponsePublicationRemoveMany.cs b/aspnetcore/src/api/Models/Api/ApiResponsePublicationRemoveMany.cs new file mode 100644 index 00000000..67d76158 --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponsePublicationRemoveMany.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponsePublicationRemoveMany : ApiResponse + { + + public ApiResponsePublicationRemoveMany(bool success, string reason, ProfileEditorRemovePublicationResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorRemovePublicationResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetPostMany.cs b/aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetPostMany.cs new file mode 100644 index 00000000..bbff6bfd --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetPostMany.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseResearchDatasetPostMany : ApiResponse + { + + public ApiResponseResearchDatasetPostMany(bool success, string reason, ProfileEditorAddResearchDatasetResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorAddResearchDatasetResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetRemoveMany.cs b/aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetRemoveMany.cs new file mode 100644 index 00000000..b8ec8020 --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseResearchDatasetRemoveMany.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseResearchDatasetRemoveMany : ApiResponse + { + + public ApiResponseResearchDatasetRemoveMany(bool success, string reason, ProfileEditorRemoveResearchDatasetResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorRemoveResearchDatasetResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Api/ApiResponseUserProfilePost.cs b/aspnetcore/src/api/Models/Api/ApiResponseUserProfilePost.cs new file mode 100644 index 00000000..3b077d89 --- /dev/null +++ b/aspnetcore/src/api/Models/Api/ApiResponseUserProfilePost.cs @@ -0,0 +1,18 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Api +{ + public class ApiResponseUserProfilePost : ApiResponse + { + + public ApiResponseUserProfilePost(bool success, string reason, ProfileEditorRemovePublicationResponse data, bool fromCache) + { + Success = true; + Reason = null; + Data = data; + FromCache = false; + } + + public ProfileEditorRemovePublicationResponse Data { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ApiResponse.cs b/aspnetcore/src/api/Models/ApiResponse.cs deleted file mode 100644 index 4a217f54..00000000 --- a/aspnetcore/src/api/Models/ApiResponse.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace api.Models -{ - public class ApiResponse - { - public ApiResponse(bool success) - { - Success = success; - Reason = null; - Data = null; - FromCache = false; - } - - public ApiResponse(object data) - { - Success = true; - Reason = null; - Data = data; - FromCache = false; - } - - public ApiResponse(bool success, string reason) - { - Success = success; - Reason = reason; - Data = null; - FromCache = false; - } - - public ApiResponse(bool success, object data) - { - Success = success; - Reason = null; - Data = data; - FromCache = false; - } - - public ApiResponse(bool success, object data, bool fromCache) - { - Success = success; - Reason = null; - Data = data; - FromCache = fromCache; - } - - public ApiResponse(bool success, string reason, object data) - { - Success = success; - Reason = reason; - Data = data; - FromCache = false; - } - - public bool Success { get; set; } - public string Reason { get; set; } - public object Data { get; set; } - public bool FromCache { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/Common/Constants.cs b/aspnetcore/src/api/Models/Common/Constants.cs new file mode 100644 index 00000000..60603453 --- /dev/null +++ b/aspnetcore/src/api/Models/Common/Constants.cs @@ -0,0 +1,102 @@ +namespace api.Models.Common +{ + public static class Constants + { + /* + * FieldIdentifiers should be according to + * https://koodistot.suomi.fi/codescheme;registryCode=research;schemeCode=ttv_field_identifiers + */ + public static class FieldIdentifiers + { + public const int PERSON_NAME = 110; + public const int PERSON_OTHER_NAMES = 120; + public const int PERSON_EXTERNAL_IDENTIFIER = 130; + public const int PERSON_RESEARCHER_DESCRIPTION = 140; + public const int PERSON_KEYWORD = 150; + public const int PERSON_FIELD_OF_SCIENCE = 160; + public const int PERSON_EMAIL_ADDRESS = 171; + public const int PERSON_TELEPHONE_NUMBER = 172; + public const int PERSON_WEB_LINK = 180; + public const int ACTIVITY_ROLE_IN_RESERCH_COMMUNITY = 200; + public const int ACTIVITY_AFFILIATION = 300; + public const int ACTIVITY_EDUCATION = 400; + public const int ACTIVITY_PUBLICATION = 500; + public const int ACTIVITY_PUBLICATION_ORCID = 550; + public const int ACTIVITY_RESEARCH_DATASET = 600; + public const int ACTIVITY_INFRASTRUCTURE = 700; + public const int ACTIVITY_FUNDING_DECISION = 800; + public const int ACTIVITY_RESEARCH_ACTIVITY = 900; + } + + public static class SourceIdentifiers + { + public const string DEMO_COMMON = "DEMO common"; + public const string DEMO = "DEMO"; + public const string TIEDEJATUTKIMUS = "Tiedejatutkimus.fi"; + public const string PROFILE_API = "Profile API"; + } + + public static class SourceDescriptions + { + public const string PROFILE_API = "Profile API"; + public const string ORCID = "ORCID"; + } + + public static class PidTypes + { + public const string ORCID = "ORCID"; + public const string ORCID_PUT_CODE = "ORCID put code"; + } + + public static class CodeSchemes + { + public const string USER_CHOICES = "user choices"; + } + + public static class Cache + { + public const int MEMORY_CACHE_EXPIRATION_SECONDS = 60; + public const int MEMORY_CACHE_EXPIRATION_SECONDS_LONG = 3600; + } + + public static class OrganizationIds + { + public const string OKM = "02w52zt87"; + } + + public static class OrganizationNames + { + // TODO: check correct name, when these are properly populated in the database + public const string ORCID = "ORCID (test)"; + public const string TTV = "Tiedejatutkimus.fi (test)"; + } + + public static class PurposeNames + { + public const string TTV = "Tiedejatutkimus.fi (test)"; + } + + public static class IdentifierlessDataTypes + { + public const string ORGANIZATION_NAME = "organization_name"; + public const string ORGANIZATION_UNIT = "organization_unit"; + } + + public static class ReferenceDataCodeSchemes + { + public const string PROFILE_SHARING = "tutkijaprofiilin_luvitus"; + } + + public static class ReferenceDataCodeValues + { + public const string PROFILE_SHARING_PROFILE_INFORMATION = "01"; + public const string PROFILE_SHARING_EMAIL_ADDRESS = "02"; + public const string PROFILE_SHARING_PHONE_NUMBER = "03"; + public const string PROFILE_SHARING_AFFILIATION_AND_EDUCATION = "04"; + public const string PROFILE_SHARING_PUBLICATIONS = "05"; + public const string PROFILE_SHARING_DATASETS = "06"; + public const string PROFILE_SHARING_GRANTS = "07"; + public const string PROFILE_SHARING_ACTIVITIES_AND_DISTINCTIONS = "08"; + } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemFieldOfScience.cs b/aspnetcore/src/api/Models/Common/NameTranslation.cs similarity index 52% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemFieldOfScience.cs rename to aspnetcore/src/api/Models/Common/NameTranslation.cs index af2482a3..07dc0939 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemFieldOfScience.cs +++ b/aspnetcore/src/api/Models/Common/NameTranslation.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models +namespace api.Models.Common { - public partial class ProfileEditorItemFieldOfScience : ProfileEditorItem + public partial class NameTranslation { - public ProfileEditorItemFieldOfScience() + public NameTranslation() { NameFi = ""; NameEn = ""; diff --git a/aspnetcore/src/api/Models/Common/Organization.cs b/aspnetcore/src/api/Models/Common/Organization.cs new file mode 100644 index 00000000..a01289d1 --- /dev/null +++ b/aspnetcore/src/api/Models/Common/Organization.cs @@ -0,0 +1,18 @@ +namespace api.Models.Common +{ + public partial class Organization + { + public Organization() + { + NameFi = ""; + NameEn = ""; + NameSv = ""; + SectorId = ""; + } + + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + public string SectorId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Constants.cs b/aspnetcore/src/api/Models/Constants.cs deleted file mode 100644 index 39575eec..00000000 --- a/aspnetcore/src/api/Models/Constants.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -namespace api.Models -{ - public static class Constants - { - public static class FieldIdentifiers - { - public const int PERSON_NAME = 110; - public const int PERSON_LAST_NAME = 111; - public const int PERSON_FIRST_NAMES = 112; - public const int PERSON_OTHER_NAMES = 120; - public const int PERSON_EXTERNAL_IDENTIFIER = 130; - public const int PERSON_RESEARCHER_DESCRIPTION = 140; - public const int PERSON_KEYWORD = 150; - public const int PERSON_FIELD_OF_SCIENCE = 160; - public const int PERSON_EMAIL_ADDRESS = 171; - public const int PERSON_TELEPHONE_NUMBER = 172; - public const int PERSON_WEB_LINK = 180; - public const int ACTIVITY_ROLE_IN_RESERCH_COMMUNITY = 200; - public const int ACTIVITY_AFFILIATION = 300; - public const int ACTIVITY_EDUCATION = 400; - public const int ACTIVITY_PUBLICATION = 500; - } - - public static class SourceIdentifiers - { - public const string ORCID = "ORCID"; - public const string DEMO = "DEMO"; - public const string TIEDEJATUTKIMUS = "Tiedejatutkimus.fi"; - } - - public static class SourceDescriptions - { - public const string PROFILE_API = "Profile API"; - } - - public static class PidTypes - { - public const string ORCID = "ORCID"; - } - } -} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs new file mode 100644 index 00000000..37a8cd16 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchActivity + { + public ElasticsearchActivity() + { + affiliations = new List(); + educations = new List(); + publications = new List(); + fundingDecisions = new List(); + researchDatasets = new List(); + activitiesAndRewards = new List(); + } + + public List affiliations { get; set; } + public List educations { get; set; } + public List publications { get; set; } + public List fundingDecisions { get; set; } + public List researchDatasets { get; set; } + public List activitiesAndRewards { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs new file mode 100644 index 00000000..2141e9ed --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs @@ -0,0 +1,30 @@ +using api.Models.ProfileEditor; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchActivityAndReward : ElasticsearchItem + { + public ElasticsearchActivityAndReward() + { + NameFi = ""; + NameEn = ""; + NameSv = ""; + DescriptionFi = ""; + DescriptionEn = ""; + DescriptionSv = ""; + InternationalCollaboration = false; + StartDate = new ElasticsearchDate(); + EndDate = new ElasticsearchDate(); + } + + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + public string DescriptionFi { get; set; } + public string DescriptionEn { get; set; } + public string DescriptionSv { get; set; } + public bool InternationalCollaboration { get; set; } + public ElasticsearchDate StartDate { get; set; } + public ElasticsearchDate EndDate { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActor.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActor.cs new file mode 100644 index 00000000..8d1a8b2b --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActor.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchActor + { + public ElasticsearchActor() + { + } + + public int actorRole { get; set; } + public string actorRoleNameFi { get; set; } + public string actorRoleNameSv { get; set; } + public string actorRoleNameEn { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs new file mode 100644 index 00000000..a76f15ab --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchAffiliation : ElasticsearchItem + { + public ElasticsearchAffiliation() + { + OrganizationNameFi = ""; + OrganizationNameSv = ""; + OrganizationNameEn = ""; + DepartmentNameFi = ""; + DepartmentNameSv = ""; + DepartmentNameEn = ""; + PositionNameFi = ""; + PositionNameSv = ""; + PositionNameEn = ""; + Type = ""; + StartDate = new ElasticsearchDate(); + EndDate = new ElasticsearchDate(); + } + + public string OrganizationNameFi { get; set; } + public string OrganizationNameSv { get; set; } + public string OrganizationNameEn { get; set; } + public string DepartmentNameFi { get; set; } + public string DepartmentNameSv { get; set; } + public string DepartmentNameEn { get; set; } + public string PositionNameFi { get; set; } + public string PositionNameSv { get; set; } + public string PositionNameEn { get; set; } + public string Type { get; set; } + public ElasticsearchDate StartDate { get; set; } + public ElasticsearchDate EndDate { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemDate.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchDate.cs similarity index 55% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemDate.cs rename to aspnetcore/src/api/Models/Elasticsearch/ElasticsearchDate.cs index 9854b40a..493cfaa5 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemDate.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchDate.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models +namespace api.Models.Elasticsearch { - public partial class ProfileEditorItemDate + public partial class ElasticsearchDate { - public ProfileEditorItemDate() + public ElasticsearchDate() { Year = 0; Month = 0; diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEducation.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEducation.cs new file mode 100644 index 00000000..9b6e473e --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEducation.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchEducation : ElasticsearchItem + { + public ElasticsearchEducation() + { + NameFi = ""; + NameEn = ""; + NameSv = ""; + DegreeGrantingInstitutionName = ""; + StartDate = new ElasticsearchDate(); + EndDate = new ElasticsearchDate(); + } + + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + public string DegreeGrantingInstitutionName { get; set; } + public ElasticsearchDate StartDate { get; set; } + public ElasticsearchDate EndDate { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEmail.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEmail.cs new file mode 100644 index 00000000..d4ea649a --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchEmail.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchEmail : ElasticsearchItem + { + public ElasticsearchEmail() + { + Value = ""; + } + + public string Value { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchExternalIdentifier.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchExternalIdentifier.cs new file mode 100644 index 00000000..03d0db98 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchExternalIdentifier.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchExternalIdentifier : ElasticsearchItem + { + public ElasticsearchExternalIdentifier() + { + PidContent = ""; + PidType = ""; + } + + public string PidContent { get; set; } + public string PidType { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorSourceOrganization.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchFieldOfScience.cs similarity index 55% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorSourceOrganization.cs rename to aspnetcore/src/api/Models/Elasticsearch/ElasticsearchFieldOfScience.cs index f695d750..c8e50558 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorSourceOrganization.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchFieldOfScience.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using api.Models; +using System.Collections.Generic; -namespace api.Models +namespace api.Models.Elasticsearch { - public partial class ProfileEditorSourceOrganization + public partial class ElasticsearchFieldOfScience : ElasticsearchItem { - public ProfileEditorSourceOrganization() + public ElasticsearchFieldOfScience() { NameFi = ""; NameEn = ""; diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchFundingDecision.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchFundingDecision.cs new file mode 100644 index 00000000..97fe9eb1 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchFundingDecision.cs @@ -0,0 +1,51 @@ +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchFundingDecision : ElasticsearchItem + { + public ElasticsearchFundingDecision() + { + ProjectAcronym = ""; + ProjectNameFi = ""; + ProjectNameSv = ""; + ProjectNameEn = ""; + ProjectDescriptionFi = ""; + ProjectDescriptionSv = ""; + ProjectDescriptionEn = ""; + FunderNameFi = ""; + FunderNameSv = ""; + FunderNameEn = ""; + FunderProjectNumber = ""; + TypeOfFundingNameFi = ""; + TypeOfFundingNameSv = ""; + TypeOfFundingNameEn = ""; + CallProgrammeNameFi = ""; + CallProgrammeNameSv = ""; + CallProgrammeNameEn = ""; + FundingStartYear = null; + FundingEndYear = null; + AmountInEur = -1; + } + + public int ProjectId { get; set; } + public string ProjectAcronym { get; set; } + public string ProjectNameFi { get; set; } + public string ProjectNameSv { get; set; } + public string ProjectNameEn { get; set; } + public string ProjectDescriptionFi { get; set; } + public string ProjectDescriptionSv { get; set; } + public string ProjectDescriptionEn { get; set; } + public string FunderNameFi { get; set; } + public string FunderNameSv { get; set; } + public string FunderNameEn { get; set; } + public string FunderProjectNumber { get; set; } + public string TypeOfFundingNameFi { get; set; } + public string TypeOfFundingNameSv { get; set; } + public string TypeOfFundingNameEn { get; set; } + public string CallProgrammeNameFi { get; set; } + public string CallProgrammeNameSv { get; set; } + public string CallProgrammeNameEn { get; set; } + public int? FundingStartYear { get; set; } + public int? FundingEndYear { get; set; } + public decimal AmountInEur { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItem.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItem.cs new file mode 100644 index 00000000..de3d46d6 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItem.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchItem + { + public ElasticsearchItem() + { + } + + public ElasticsearchItemMeta itemMeta { get; set; } + public List DataSources { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItemMeta.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItemMeta.cs new file mode 100644 index 00000000..6b1dd103 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchItemMeta.cs @@ -0,0 +1,11 @@ +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchItemMeta + { + public ElasticsearchItemMeta() + { + } + + public bool? PrimaryValue { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchKeyword.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchKeyword.cs new file mode 100644 index 00000000..131c131a --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchKeyword.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchKeyword : ElasticsearchItem + { + public ElasticsearchKeyword() + { + Value = ""; + } + + public string Value { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemName.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchName.cs similarity index 56% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemName.cs rename to aspnetcore/src/api/Models/Elasticsearch/ElasticsearchName.cs index aaf52692..4a46c4e2 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemName.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchName.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using api.Models; +using System.Collections.Generic; -namespace api.Models +namespace api.Models.Elasticsearch { - public partial class ProfileEditorItemName : ProfileEditorItem + public partial class ElasticsearchName : ElasticsearchItem { - public ProfileEditorItemName() + public ElasticsearchName() { FirstNames = ""; LastName = ""; diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs new file mode 100644 index 00000000..8ea57fe0 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchPerson + { + public ElasticsearchPerson() + { + id = ""; + personal = new(); + activity = new(); + uniqueDataSources = new(); + } + + public ElasticsearchPerson(string orcidId) + { + id = orcidId; + personal = new(); + activity = new(); + uniqueDataSources = new(); + } + + public string id { get; set; } + public ElasticsearchPersonal personal { get; set; } + public ElasticsearchActivity activity { get; set; } + public List uniqueDataSources { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPersonal.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPersonal.cs new file mode 100644 index 00000000..4d29486e --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPersonal.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchPersonal + { + public ElasticsearchPersonal() + { + names = new List(); + otherNames = new List(); + emails = new List(); + telephoneNumbers = new List(); + webLinks = new List(); + keywords = new List(); + fieldOfSciences = new List(); + researcherDescriptions = new List(); + externalIdentifiers = new List(); + } + + public List names { get; set; } + public List otherNames { get; set; } + public List emails { get; set; } + public List telephoneNumbers { get; set; } + public List webLinks { get; set; } + public List keywords { get; set; } + public List fieldOfSciences { get; set; } + public List researcherDescriptions { get; set; } + public List externalIdentifiers { get; set; } + } +} + diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPreferredIdentifier.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPreferredIdentifier.cs new file mode 100644 index 00000000..1afac8e0 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPreferredIdentifier.cs @@ -0,0 +1,14 @@ +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchPreferredIdentifier + { + public ElasticsearchPreferredIdentifier() + { + PidContent = ""; + PidType = ""; + } + + public string PidContent { get; set; } + public string PidType { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemPublication.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPublication.cs similarity index 58% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemPublication.cs rename to aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPublication.cs index 7a640ed4..3d083847 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemPublication.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPublication.cs @@ -1,22 +1,22 @@ -using System; -using System.Collections.Generic; -using api.Models; +using System.Collections.Generic; -namespace api.Models +namespace api.Models.Elasticsearch { - public partial class ProfileEditorItemPublication : ProfileEditorItem + public partial class ElasticsearchPublication : ElasticsearchItem { - public ProfileEditorItemPublication() + public ElasticsearchPublication() { PublicationId = ""; PublicationName = ""; PublicationYear = null; Doi = ""; + TypeCode = ""; } public string PublicationId { get; set; } public string PublicationName { get; set; } public int? PublicationYear { get; set; } public string Doi { get; set; } + public string TypeCode { get; set; } } } diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchResearchDataset.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchResearchDataset.cs new file mode 100644 index 00000000..96c842ae --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchResearchDataset.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchResearchDataset : ElasticsearchItem + { + public ElasticsearchResearchDataset() + { + Actor = new List(); + Identifier = ""; + NameFi = ""; + NameSv = ""; + NameEn = ""; + DescriptionFi = ""; + DescriptionSv = ""; + DescriptionEn = ""; + DatasetCreated = null; + PreferredIdentifiers = new List(); + } + + // Properties are according to ElasticSearch index, not according to model DimResearchDataset + public List Actor { get; set; } + public string Identifier { get; set; } + public string NameFi { get; set; } + public string NameSv { get; set; } + public string NameEn { get; set; } + public string DescriptionFi { get; set; } + public string DescriptionSv { get; set; } + public string DescriptionEn { get; set; } + public int? DatasetCreated { get; set; } + public List PreferredIdentifiers { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemResearcherDescription.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchResearcherDescription.cs similarity index 58% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemResearcherDescription.cs rename to aspnetcore/src/api/Models/Elasticsearch/ElasticsearchResearcherDescription.cs index a52f0e50..ce90ac23 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemResearcherDescription.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchResearcherDescription.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models +namespace api.Models.Elasticsearch { - public partial class ProfileEditorItemResearcherDescription : ProfileEditorItem + public partial class ElasticsearchResearcherDescription : ElasticsearchItem { - public ProfileEditorItemResearcherDescription() + public ElasticsearchResearcherDescription() { ResearchDescriptionFi = ""; ResearchDescriptionEn = ""; diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSource.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSource.cs new file mode 100644 index 00000000..d787284f --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSource.cs @@ -0,0 +1,13 @@ +using api.Models.Common; +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchSource + { + public ElasticsearchSource() + { + } + + public string RegisteredDataSource { get; set; } + public Organization Organization { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchTelephoneNumber.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchTelephoneNumber.cs new file mode 100644 index 00000000..e3a540c4 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchTelephoneNumber.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchTelephoneNumber : ElasticsearchItem + { + public ElasticsearchTelephoneNumber() + { + Value = ""; + } + + public string Value { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchWebLink.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchWebLink.cs new file mode 100644 index 00000000..7e896974 --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchWebLink.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchWebLink : ElasticsearchItem + { + public ElasticsearchWebLink() + { + Url = ""; + LinkLabel = ""; + } + + public string Url { get; set; } + public string LinkLabel { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Orcid/OrcidEmployment.cs b/aspnetcore/src/api/Models/Orcid/OrcidEmployment.cs index c4e216e6..ae5605bf 100644 --- a/aspnetcore/src/api/Models/Orcid/OrcidEmployment.cs +++ b/aspnetcore/src/api/Models/Orcid/OrcidEmployment.cs @@ -1,9 +1,11 @@ namespace api.Models.Orcid { public partial class OrcidEmployment { - public OrcidEmployment(string organizationName, string departmentName, string roleTitle, OrcidDate startDate, OrcidDate endDate, OrcidPutCode putCode) + public OrcidEmployment(string organizationName, string disambiguatedOrganizationIdentifier, string disambiguationSource, string departmentName, string roleTitle, OrcidDate startDate, OrcidDate endDate, OrcidPutCode putCode) { OrganizationName = organizationName; + DisambiguatedOrganizationIdentifier = disambiguatedOrganizationIdentifier; + DisambiguationSource = disambiguationSource; DepartmentName = departmentName; RoleTitle = roleTitle; StartDate = startDate; @@ -12,6 +14,8 @@ public OrcidEmployment(string organizationName, string departmentName, string ro } public string OrganizationName { get; set; } + public string DisambiguatedOrganizationIdentifier { get; set; } + public string DisambiguationSource { get; set; } public string DepartmentName { get; set; } public string RoleTitle { get; set; } public OrcidDate StartDate { get; set; } diff --git a/aspnetcore/src/api/Models/Orcid/OrcidPublication.cs b/aspnetcore/src/api/Models/Orcid/OrcidPublication.cs index 92144661..10f42f15 100644 --- a/aspnetcore/src/api/Models/Orcid/OrcidPublication.cs +++ b/aspnetcore/src/api/Models/Orcid/OrcidPublication.cs @@ -5,14 +5,14 @@ public OrcidPublication() { PublicationName = ""; PublicationYear = null; - DoiHandle = ""; + Doi = ""; Type = ""; PutCode = new OrcidPutCode(0) { }; } public string PublicationName { get; set; } public int? PublicationYear { get; set; } - public string DoiHandle { get; set; } + public string Doi { get; set; } public string Type { get; set; } public OrcidPutCode PutCode { get; set; } } diff --git a/aspnetcore/src/api/Models/Orcid/OrcidTokens.cs b/aspnetcore/src/api/Models/Orcid/OrcidTokens.cs new file mode 100644 index 00000000..5d5ea8b2 --- /dev/null +++ b/aspnetcore/src/api/Models/Orcid/OrcidTokens.cs @@ -0,0 +1,30 @@ +using System; + +namespace api.Models.Orcid +{ + public class OrcidTokens { + public OrcidTokens() { } + + public OrcidTokens(string? accessToken, string? refreshToken, long? expiresSeconds, DateTime? expiresDatetime, string? scope) + { + AccessToken = accessToken; + RefreshToken = refreshToken; + ExpiresSeconds = expiresSeconds; + ExpiresDatetime = expiresDatetime; + Scope = scope; + + // Calculate ExpiresDatetime from ExpiresSeconds + if (ExpiresSeconds != null && ExpiresDatetime == null) + { + DateTimeOffset dateTimeOffSet = DateTimeOffset.FromUnixTimeSeconds((long)ExpiresSeconds); + ExpiresDatetime = dateTimeOffSet.DateTime; + } + } + + public string? AccessToken { get; set; } + public string? RefreshToken { get; set; } + public long? ExpiresSeconds { get; set; } + public DateTime? ExpiresDatetime { get; set; } + public string? Scope { get; set; } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs new file mode 100644 index 00000000..135fba5f --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs @@ -0,0 +1,28 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorActivityAndReward : ProfileEditorItem + { + public ProfileEditorActivityAndReward() + { + NameFi = ""; + NameEn = ""; + NameSv = ""; + DescriptionFi = ""; + DescriptionEn = ""; + DescriptionSv = ""; + InternationalCollaboration = false; + StartDate = new ProfileEditorDate(); + EndDate = new ProfileEditorDate(); + } + + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + public string DescriptionFi { get; set; } + public string DescriptionEn { get; set; } + public string DescriptionSv { get; set; } + public bool InternationalCollaboration { get; set; } + public ProfileEditorDate StartDate { get; set; } + public ProfileEditorDate EndDate { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActor.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActor.cs new file mode 100644 index 00000000..d06396d5 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActor.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorActor + { + public ProfileEditorActor() + { + } + + public int actorRole { get; set; } + public string actorRoleNameFi { get; set; } + public string actorRoleNameSv { get; set; } + public string actorRoleNameEn { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemAffiliation.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorAffiliation.cs similarity index 50% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemAffiliation.cs rename to aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorAffiliation.cs index 316c0aa0..be6909de 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemAffiliation.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorAffiliation.cs @@ -1,32 +1,34 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models +namespace api.Models.ProfileEditor.Items { - public partial class ProfileEditorItemAffiliation : ProfileEditorItem + public partial class ProfileEditorAffiliation : ProfileEditorItem { - public ProfileEditorItemAffiliation() + public ProfileEditorAffiliation() { OrganizationNameFi = ""; - OrganizationNameEn = ""; OrganizationNameSv = ""; + OrganizationNameEn = ""; + DepartmentNameFi = ""; + DepartmentNameSv = ""; + DepartmentNameEn = ""; PositionNameFi = ""; - PositionNameEn = ""; PositionNameSv = ""; + PositionNameEn = ""; Type = ""; - StartDate = new ProfileEditorItemDate(); - EndDate = new ProfileEditorItemDate(); + StartDate = new ProfileEditorDate(); + EndDate = new ProfileEditorDate(); } public string OrganizationNameFi { get; set; } - public string OrganizationNameEn { get; set; } public string OrganizationNameSv { get; set; } + public string OrganizationNameEn { get; set; } + public string DepartmentNameFi { get; set; } + public string DepartmentNameSv { get; set; } + public string DepartmentNameEn { get; set; } public string PositionNameFi { get; set; } - public string PositionNameEn { get; set; } public string PositionNameSv { get; set; } + public string PositionNameEn { get; set; } public string Type { get; set; } - public ProfileEditorItemDate StartDate { get; set; } - public ProfileEditorItemDate EndDate { get; set; } + public ProfileEditorDate StartDate { get; set; } + public ProfileEditorDate EndDate { get; set; } } } diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataActivity.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataActivity.cs new file mode 100644 index 00000000..003b3662 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataActivity.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorDataActivity + { + public ProfileEditorDataActivity() + { + affiliations = new List(); + educations = new List(); + publications = new List(); + fundingDecisions = new List(); + researchDatasets = new List(); + activitiesAndRewards = new List(); + } + + public List affiliations { get; set; } + public List educations { get; set; } + public List publications { get; set; } + public List fundingDecisions { get; set; } + public List researchDatasets { get; set; } + public List activitiesAndRewards { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataPersonal.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataPersonal.cs new file mode 100644 index 00000000..737e4b59 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataPersonal.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorDataPersonal + { + public ProfileEditorDataPersonal() + { + names = new List (); + otherNames = new List(); + emails = new List(); + telephoneNumbers = new List(); + webLinks = new List (); + keywords = new List(); + fieldOfSciences = new List(); + researcherDescriptions = new List(); + externalIdentifiers = new List(); + } + + public List names { get; set; } + public List otherNames { get; set; } + public List emails { get; set; } + public List telephoneNumbers { get; set; } + public List webLinks { get; set; } + public List keywords { get; set; } + public List fieldOfSciences { get; set; } + public List researcherDescriptions { get; set; } + public List externalIdentifiers { get; set; } + } +} + diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataResponse.cs new file mode 100644 index 00000000..529bf1e2 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDataResponse.cs @@ -0,0 +1,18 @@ +using api.Models.Elasticsearch; +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorDataResponse { + public ProfileEditorDataResponse() + { + personal = new(); + activity = new(); + uniqueDataSources = new(); + } + + public ProfileEditorDataPersonal personal { get; set; } + public ProfileEditorDataActivity activity { get; set; } + public List uniqueDataSources { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDate.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDate.cs new file mode 100644 index 00000000..00f810ca --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorDate.cs @@ -0,0 +1,16 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorDate + { + public ProfileEditorDate() + { + Year = 0; + Month = 0; + Day = 0; + } + + public int Year { get; set; } + public int Month { get; set; } + public int Day { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEducation.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEducation.cs new file mode 100644 index 00000000..aff857d8 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEducation.cs @@ -0,0 +1,22 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorEducation : ProfileEditorItem + { + public ProfileEditorEducation() + { + NameFi = ""; + NameEn = ""; + NameSv = ""; + DegreeGrantingInstitutionName = ""; + StartDate = new ProfileEditorDate(); + EndDate = new ProfileEditorDate(); + } + + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + public string DegreeGrantingInstitutionName { get; set; } + public ProfileEditorDate StartDate { get; set; } + public ProfileEditorDate EndDate { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEmail.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEmail.cs new file mode 100644 index 00000000..268f22cd --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorEmail.cs @@ -0,0 +1,12 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorEmail : ProfileEditorItem + { + public ProfileEditorEmail() + { + Value = ""; + } + + public string Value { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorExternalIdentifier.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorExternalIdentifier.cs new file mode 100644 index 00000000..a0339384 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorExternalIdentifier.cs @@ -0,0 +1,14 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorExternalIdentifier : ProfileEditorItem + { + public ProfileEditorExternalIdentifier() + { + PidContent = ""; + PidType = ""; + } + + public string PidContent { get; set; } + public string PidType { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFieldOfScience.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFieldOfScience.cs new file mode 100644 index 00000000..3df3c398 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFieldOfScience.cs @@ -0,0 +1,16 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorFieldOfScience : ProfileEditorItem + { + public ProfileEditorFieldOfScience() + { + NameFi = ""; + NameEn = ""; + NameSv = ""; + } + + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFundingDecision.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFundingDecision.cs new file mode 100644 index 00000000..86fa3ba2 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorFundingDecision.cs @@ -0,0 +1,51 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorFundingDecision : ProfileEditorItem + { + public ProfileEditorFundingDecision() + { + ProjectAcronym = ""; + ProjectNameFi = ""; + ProjectNameSv = ""; + ProjectNameEn = ""; + ProjectDescriptionFi = ""; + ProjectDescriptionSv = ""; + ProjectDescriptionEn = ""; + FunderNameFi = ""; + FunderNameSv = ""; + FunderNameEn = ""; + FunderProjectNumber = ""; + TypeOfFundingNameFi = ""; + TypeOfFundingNameSv = ""; + TypeOfFundingNameEn = ""; + CallProgrammeNameFi = ""; + CallProgrammeNameSv = ""; + CallProgrammeNameEn = ""; + FundingStartYear = null; + FundingEndYear = null; + AmountInEur = -1; + } + + public int ProjectId { get; set; } + public string ProjectAcronym { get; set; } + public string ProjectNameFi { get; set; } + public string ProjectNameSv { get; set; } + public string ProjectNameEn { get; set; } + public string ProjectDescriptionFi { get; set; } + public string ProjectDescriptionSv { get; set; } + public string ProjectDescriptionEn { get; set; } + public string FunderNameFi { get; set; } + public string FunderNameSv { get; set; } + public string FunderNameEn { get; set; } + public string FunderProjectNumber { get; set; } + public string TypeOfFundingNameFi { get; set; } + public string TypeOfFundingNameSv { get; set; } + public string TypeOfFundingNameEn { get; set; } + public string CallProgrammeNameFi { get; set; } + public string CallProgrammeNameSv { get; set; } + public string CallProgrammeNameEn { get; set; } + public int? FundingStartYear { get; set; } + public int? FundingEndYear { get; set; } + public decimal AmountInEur { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItem.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorItem.cs similarity index 54% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItem.cs rename to aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorItem.cs index 1019d2bd..6b1aa9c1 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItem.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorItem.cs @@ -1,7 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; -namespace api.Models +namespace api.Models.ProfileEditor.Items { public partial class ProfileEditorItem { @@ -10,5 +9,6 @@ public ProfileEditorItem() } public ProfileEditorItemMeta itemMeta { get; set; } + public List DataSources { get; set; } } } diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemMeta.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorItemMeta.cs similarity index 79% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemMeta.cs rename to aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorItemMeta.cs index 756523f6..221a3f6c 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemMeta.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorItemMeta.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace api.Models +namespace api.Models.ProfileEditor.Items { public partial class ProfileEditorItemMeta { diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorKeyword.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorKeyword.cs new file mode 100644 index 00000000..1f00e2c7 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorKeyword.cs @@ -0,0 +1,12 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorKeyword : ProfileEditorItem + { + public ProfileEditorKeyword() + { + Value = ""; + } + + public string Value { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorName.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorName.cs new file mode 100644 index 00000000..022ea097 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorName.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorName : ProfileEditorItem + { + public ProfileEditorName() + { + FirstNames = ""; + LastName = ""; + FullName = ""; + } + + public string FirstNames { get; set; } + public string LastName { get; set; } + public string FullName { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPreferredIdentifier.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPreferredIdentifier.cs new file mode 100644 index 00000000..5ece5479 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPreferredIdentifier.cs @@ -0,0 +1,14 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorPreferredIdentifier + { + public ProfileEditorPreferredIdentifier() + { + PidContent = ""; + PidType = ""; + } + + public string PidContent { get; set; } + public string PidType { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs new file mode 100644 index 00000000..f2ef079b --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorPublication : ProfileEditorItem + { + public ProfileEditorPublication() + { + PublicationId = ""; + PublicationName = ""; + PublicationYear = null; + Doi = ""; + TypeCode = ""; + } + + public string PublicationId { get; set; } + public string PublicationName { get; set; } + public int? PublicationYear { get; set; } + public string Doi { get; set; } + public string TypeCode { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearchDataset.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearchDataset.cs new file mode 100644 index 00000000..44286a60 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearchDataset.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorResearchDataset : ProfileEditorItem + { + public ProfileEditorResearchDataset() + { + Actor = new List(); + Identifier = ""; + NameFi = ""; + NameSv = ""; + NameEn = ""; + DescriptionFi = ""; + DescriptionSv = ""; + DescriptionEn = ""; + DatasetCreated = null; + PreferredIdentifiers = new List(); + } + + // Properties are according to ElasticSearch index, not according to model DimResearchDataset + public List Actor { get; set; } + public string Identifier { get; set; } + public string NameFi { get; set; } + public string NameSv { get; set; } + public string NameEn { get; set; } + public string DescriptionFi { get; set; } + public string DescriptionSv { get; set; } + public string DescriptionEn { get; set; } + public int? DatasetCreated { get; set; } + public List PreferredIdentifiers { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearcherDescription.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearcherDescription.cs new file mode 100644 index 00000000..899a6934 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorResearcherDescription.cs @@ -0,0 +1,16 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorResearcherDescription : ProfileEditorItem + { + public ProfileEditorResearcherDescription() + { + ResearchDescriptionFi = ""; + ResearchDescriptionEn = ""; + ResearchDescriptionSv = ""; + } + + public string ResearchDescriptionFi { get; set; } + public string ResearchDescriptionEn { get; set; } + public string ResearchDescriptionSv { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorSource.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSource.cs similarity index 56% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorSource.cs rename to aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSource.cs index 53dc6119..01bba490 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorSource.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSource.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models +using api.Models.Common; +namespace api.Models.ProfileEditor.Items { public partial class ProfileEditorSource { @@ -12,6 +9,6 @@ public ProfileEditorSource() public int Id { get; set; } public string RegisteredDataSource { get; set; } - public ProfileEditorSourceOrganization Organization { get; set; } + public Organization Organization { get; set; } } } diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorTelephoneNumber.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorTelephoneNumber.cs new file mode 100644 index 00000000..cf535684 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorTelephoneNumber.cs @@ -0,0 +1,12 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorTelephoneNumber : ProfileEditorItem + { + public ProfileEditorTelephoneNumber() + { + Value = ""; + } + + public string Value { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorWebLink.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorWebLink.cs new file mode 100644 index 00000000..0ddae054 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorWebLink.cs @@ -0,0 +1,14 @@ +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorWebLink : ProfileEditorItem + { + public ProfileEditorWebLink() + { + Url = ""; + LinkLabel = ""; + } + + public string Url { get; set; } + public string LinkLabel { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorAddFundingDecisionResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorAddFundingDecisionResponse.cs new file mode 100644 index 00000000..8e51cba1 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorAddFundingDecisionResponse.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +namespace api.Models.ProfileEditor.Items + +{ + public partial class ProfileEditorAddFundingDecisionResponse { + public ProfileEditorAddFundingDecisionResponse() + { + source = new ProfileEditorSource(); + fundingDecisionsAdded = new List(); + fundingDecisionsAlreadyInProfile = new List(); + fundingDecisionsNotFound = new List(); + } + + public ProfileEditorSource source { get; set; } + public List fundingDecisionsAdded { get; set; } + public List fundingDecisionsAlreadyInProfile { get; set; } + public List fundingDecisionsNotFound { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorFundingDecisionToAdd.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorFundingDecisionToAdd.cs new file mode 100644 index 00000000..d303e404 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorFundingDecisionToAdd.cs @@ -0,0 +1,13 @@ +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorFundingDecisionToAdd + { + public ProfileEditorFundingDecisionToAdd() + { + } + + public int ProjectId { get; set; } + public bool? Show { get; set; } + public bool? PrimaryValue { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorRemoveFundingDecisionResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorRemoveFundingDecisionResponse.cs new file mode 100644 index 00000000..d923e29e --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageFundingDecisions/ProfileEditorRemoveFundingDecisionResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +namespace api.Models.ProfileEditor + +{ + public partial class ProfileEditorRemoveFundingDecisionResponse { + public ProfileEditorRemoveFundingDecisionResponse() + { + fundingDecisionsRemoved = new List(); + fundingDecisionsNotFound = new List(); + } + + public List fundingDecisionsRemoved { get; set; } + public List fundingDecisionsNotFound { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorAddPublicationResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorAddPublicationResponse.cs similarity index 74% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorAddPublicationResponse.cs rename to aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorAddPublicationResponse.cs index 5665e9d5..640e9100 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorAddPublicationResponse.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorAddPublicationResponse.cs @@ -1,18 +1,18 @@ using System.Collections.Generic; -namespace api.Models +namespace api.Models.ProfileEditor.Items { public partial class ProfileEditorAddPublicationResponse { public ProfileEditorAddPublicationResponse() { source = new ProfileEditorSource(); - publicationsAdded = new List(); + publicationsAdded = new List(); publicationsAlreadyInProfile = new List(); publicationsNotFound = new List(); } public ProfileEditorSource source { get; set; } - public List publicationsAdded { get; set; } + public List publicationsAdded { get; set; } public List publicationsAlreadyInProfile { get; set; } public List publicationsNotFound { get; set; } } diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorPublicationToAdd.cs b/aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorPublicationToAdd.cs similarity index 91% rename from aspnetcore/src/api/Models/ProfileEditor/ProfileEditorPublicationToAdd.cs rename to aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorPublicationToAdd.cs index 2bf2b178..9d2ebb96 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorPublicationToAdd.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorPublicationToAdd.cs @@ -1,5 +1,4 @@ - -namespace api.Models +namespace api.Models.ProfileEditor { public partial class ProfileEditorPublicationToAdd { diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorRemovePublicationResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorRemovePublicationResponse.cs new file mode 100644 index 00000000..311eac38 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManagePublications/ProfileEditorRemovePublicationResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +namespace api.Models.ProfileEditor + +{ + public partial class ProfileEditorRemovePublicationResponse { + public ProfileEditorRemovePublicationResponse() + { + publicationsRemoved = new List(); + publicationsNotFound = new List(); + } + + public List publicationsRemoved { get; set; } + public List publicationsNotFound { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorAddResearchDatasetResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorAddResearchDatasetResponse.cs new file mode 100644 index 00000000..0d6eda36 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorAddResearchDatasetResponse.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using api.Models.ProfileEditor.Items; + +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorAddResearchDatasetResponse { + public ProfileEditorAddResearchDatasetResponse() + { + source = new ProfileEditorSource(); + researchDatasetAdded = new List(); + researchDatasetAlreadyInProfile = new List(); + researchDatasetNotFound = new List(); + } + + public ProfileEditorSource source { get; set; } + public List researchDatasetAdded { get; set; } + public List researchDatasetAlreadyInProfile { get; set; } + public List researchDatasetNotFound { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorRemoveResearchDatasetResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorRemoveResearchDatasetResponse.cs new file mode 100644 index 00000000..2418314e --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorRemoveResearchDatasetResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +namespace api.Models.ProfileEditor + +{ + public partial class ProfileEditorRemoveResearchDatasetResponse { + public ProfileEditorRemoveResearchDatasetResponse() + { + researchDatasetsRemoved = new List(); + researchDatasetsNotFound = new List(); + } + + public List researchDatasetsRemoved { get; set; } + public List researchDatasetsNotFound { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorResearchDatasetToAdd.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorResearchDatasetToAdd.cs new file mode 100644 index 00000000..63716d7c --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageResearchDatasets/ProfileEditorResearchDatasetToAdd.cs @@ -0,0 +1,14 @@ +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorResearchDatasetToAdd + { + public ProfileEditorResearchDatasetToAdd() + { + } + + public string LocalIdentifier { get; set; } + public bool? Show { get; set; } + // TODO: "PrimaryValue" is used in demo to indicate manually selected publication. Need to add dedicated property in the database model. + public bool? PrimaryValue { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingAddPermissionsResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingAddPermissionsResponse.cs new file mode 100644 index 00000000..8c53efa2 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingAddPermissionsResponse.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using api.Models.ProfileEditor.Items; + +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingAddPermissionsResponse + { + public ProfileEditorSharingAddPermissionsResponse() + { + source = new ProfileEditorSource(); + publicationsAdded = new List(); + publicationsAlreadyInProfile = new List(); + publicationsNotFound = new List(); + } + + public ProfileEditorSource source { get; set; } + public List publicationsAdded { get; set; } + public List publicationsAlreadyInProfile { get; set; } + public List publicationsNotFound { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingGivenPermissionsResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingGivenPermissionsResponse.cs new file mode 100644 index 00000000..15cb7acc --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingGivenPermissionsResponse.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingGivenPermissionsResponse { + public ProfileEditorSharingGivenPermissionsResponse(List items) + { + givenPermissions = items; + } + + public List givenPermissions { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItem.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItem.cs new file mode 100644 index 00000000..1252f6bd --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItem.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingItem + { + public ProfileEditorSharingItem() + { + } + + public ProfileEditorSharingPurposeItem Purpose { get; set; } + public List Permissions { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItemMeta.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItemMeta.cs new file mode 100644 index 00000000..e352e2b0 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingItemMeta.cs @@ -0,0 +1,20 @@ +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingItemMeta + { + public ProfileEditorSharingItemMeta() + { + PurposeId = -1; + PermittedFieldGroupId = -1; + } + + public ProfileEditorSharingItemMeta(int purposeId, int permittedFieldGroupId) + { + PurposeId = purposeId; + PermittedFieldGroupId = permittedFieldGroupId; + } + + public int PurposeId { get; set; } + public int PermittedFieldGroupId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionItem.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionItem.cs new file mode 100644 index 00000000..b8a417a9 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionItem.cs @@ -0,0 +1,18 @@ +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingPermissionItem + { + public ProfileEditorSharingPermissionItem() + { + PermissionId = -1; + NameFi = ""; + NameEn = ""; + NameSv = ""; + } + + public int PermissionId { get; set; } + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionToAddOrDelete.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionToAddOrDelete.cs new file mode 100644 index 00000000..414f2b5a --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionToAddOrDelete.cs @@ -0,0 +1,12 @@ +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingPermissionToAddOrDelete + { + public ProfileEditorSharingPermissionToAddOrDelete() + { + } + + public int PurposeId { get; set; } + public int PermissionId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionsResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionsResponse.cs new file mode 100644 index 00000000..e79f772c --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPermissionsResponse.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingPermissionsResponse { + public ProfileEditorSharingPermissionsResponse(List items) + { + permissions = items; + } + + public List permissions { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposeItem.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposeItem.cs new file mode 100644 index 00000000..1fa96a89 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposeItem.cs @@ -0,0 +1,24 @@ +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingPurposeItem + { + public ProfileEditorSharingPurposeItem() + { + PurposeId = -1; + NameFi = ""; + NameEn = ""; + NameSv = ""; + DescriptionFi = ""; + DescriptionEn = ""; + DescriptionSv = ""; + } + + public int PurposeId { get; set; } + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + public string DescriptionFi { get; set; } + public string DescriptionEn { get; set; } + public string DescriptionSv { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposesResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposesResponse.cs new file mode 100644 index 00000000..24792e15 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ManageSharing/ProfileEditorSharingPurposesResponse.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorSharingPurposesResponse { + public ProfileEditorSharingPurposesResponse(List items) + { + purposes = items; + } + + public List purposes { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs new file mode 100644 index 00000000..795ba5eb --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs @@ -0,0 +1,172 @@ +using System; +using System.Security.Cryptography; +using api.Models.Ttv; +using Nest; + +namespace api.Models.ProfileEditor +{ + public partial class ProfileDataFromSql + { + public ProfileDataFromSql() + { + } + + // FactFieldValues + public bool? FactFieldValues_Show { get; set; } + public bool? FactFieldValues_PrimaryValue { get; set; } + public int FactFieldValues_DimUserProfileId { get; set; } + public int FactFieldValues_DimNameId { get; set; } + public int FactFieldValues_DimWebLinkId { get; set; } + public int FactFieldValues_DimResearcherDescriptionId { get; set; } + public int FactFieldValues_DimEmailAddrressId { get; set; } + public int FactFieldValues_DimTelephoneNumberId { get; set; } + public int FactFieldValues_DimFieldOfScienceId { get; set; } + public int FactFieldValues_DimKeywordId { get; set; } + public int FactFieldValues_DimPidId { get; set; } + public int FactFieldValues_DimAffiliationId { get; set; } + public int FactFieldValues_DimIdentifierlessDataId { get; set; } + public int FactFieldValues_DimEducationId { get; set; } + public int FactFieldValues_DimPublicationId { get; set; } + public int FactFieldValues_DimOrcidPublicationId { get; set; } + public int FactFieldValues_DimResearchActivityId { get; set; } + public int FactFieldValues_DimFundingDecisionId { get; set; } + public int FactFieldValues_DimResearchDatasetId { get; set; } + //public int DimPublicationId { get; set; } + //public int DimPidIdOrcidPutCode { get; set; } + //public int DimEventId { get; set; } + //public int DimCompetenceId { get; set; } + //public int DimResearchCommunityId { get; set; } + //public int DimResearcherToResearchCommunityId { get; set; } + //public int DimRegisteredDataSourceId { get; set; } + // DimFieldDisplaySettings + public int DimFieldDisplaySettings_Id { get; set; } + public int DimFieldDisplaySettings_FieldIdentifier { get; set; } + public bool DimFieldDisplaySettings_Show { get; set; } + // DimRegisteredDataSource + public int DimRegisteredDataSource_Id { get; set; } + public string DimRegisteredDataSource_Name { get; set; } + // DimRegisteredDataSource => DimOrganization + public string DimRegisteredDataSource_DimOrganization_NameFi { get; set; } + public string DimRegisteredDataSource_DimOrganization_NameSv { get; set; } + public string DimRegisteredDataSource_DimOrganization_NameEn { get; set; } + // DimRegisteredDataSource => DimOrganization => DimSector + public string DimRegisteredDataSource_DimOrganization_DimSector_SectorId { get; set; } + // DimName + public string DimName_LastName { get; set; } + public string DimName_FirstNames { get; set; } + public string DimName_FullName { get; set; } + // DimWebLink + public string DimWebLink_Url { get; set; } + public string DimWebLink_LinkLabel { get; set; } + // DimResearcherDescription + public string DimResearcherDescription_ResearchDescriptionFi { get; set; } + public string DimResearcherDescription_ResearchDescriptionEn { get; set; } + public string DimResearcherDescription_ResearchDescriptionSv { get; set; } + // DimEmailAddrress + public string DimEmailAddrress_Email { get; set; } + // DimTelephoneNumber + public string DimTelephoneNumber_TelephoneNumber { get; set; } + // DimFieldOfScience + public string DimFieldOfScience_NameFi { get; set; } + public string DimFieldOfScience_NameEn { get; set; } + public string DimFieldOfScience_NameSv { get; set; } + // DimKeyword + public string DimKeyword_Keyword { get; set; } + // DimPid + public string DimPid_PidContent { get; set; } + public string DimPid_PidType { get; set; } + // DimAffiliation + public int DimAffiliation_DimOrganization_Id { get; set; } + public string DimAffiliation_DimOrganization_NameFi { get; set; } + public string DimAffiliation_DimOrganization_NameEn { get; set; } + public string DimAffiliation_DimOrganization_NameSv { get; set; } + public string DimAffiliation_PositionNameFi { get; set; } + public string DimAffiliation_PositionNameEn { get; set; } + public string DimAffiliation_PositionNameSv { get; set; } + public int DimAffiliation_StartDate_Year { get; set; } + public int DimAffiliation_StartDate_Month { get; set; } + public int DimAffiliation_StartDate_Day { get; set; } + public int DimAffiliation_EndDate_Year { get; set; } + public int DimAffiliation_EndDate_Month { get; set; } + public int DimAffiliation_EndDate_Day { get; set; } + public string DimAffiliation_DimReferenceData_NameFi { get; set; } + // DimIdentifierlessData + public string DimIdentifierlessData_Type { get; set; } + public string DimIdentifierlessData_ValueFi { get; set; } + public string DimIdentifierlessData_ValueEn { get; set; } + public string DimIdentifierlessData_ValueSv { get; set; } + public string DimIdentifierlessData_UnlinkedIdentifier { get; set; } + // DimIdentifierlessData (child) + public string DimIdentifierlessData_Child_Type { get; set; } + public string DimIdentifierlessData_Child_ValueFi { get; set; } + public string DimIdentifierlessData_Child_ValueEn { get; set; } + public string DimIdentifierlessData_Child_ValueSv { get; set; } + public string DimIdentifierlessData_Child_UnlinkedIdentifier { get; set; } + // DimEducation + public string DimEducation_NameFi { get; set; } + public string DimEducation_NameEn { get; set; } + public string DimEducation_NameSv { get; set; } + public string DimEducation_DegreeGrantingInstitutionName { get; set; } + public int DimEducation_StartDate_Year { get; set; } + public int DimEducation_StartDate_Month { get; set; } + public int DimEducation_StartDate_Day { get; set; } + public int DimEducation_EndDate_Year { get; set; } + public int DimEducation_EndDate_Month { get; set; } + public int DimEducation_EndDate_Day { get; set; } + // DimPublication + public string DimPublication_PublicationId { get; set; } + public string DimPublication_PublicationName { get; set; } + public int DimPublication_PublicationYear { get; set; } + public string DimPublication_Doi { get; set; } + public string DimPublication_PublicationTypeCode { get; set; } + // DimOrcidPublication + public string DimOrcidPublication_PublicationId { get; set; } + public string DimOrcidPublication_PublicationName { get; set; } + public int DimOrcidPublication_PublicationYear { get; set; } + public string DimOrcidPublication_Doi { get; set; } + // DimResearchActivity + public string DimResearchActivity_NameFi { get; set; } + public string DimResearchActivity_NameEn { get; set; } + public string DimResearchActivity_NameSv { get; set; } + public string DimResearchActivity_DescriptionFi { get; set; } + public string DimResearchActivity_DescriptionEn { get; set; } + public string DimResearchActivity_DescriptionSv { get; set; } + public bool DimResearchActivity_InternationalCollaboration { get; set; } + public int DimResearchActivity_StartDate_Year { get; set; } + public int DimResearchActivity_StartDate_Month { get; set; } + public int DimResearchActivity_StartDate_Day { get; set; } + public int DimResearchActivity_EndDate_Year { get; set; } + public int DimResearchActivity_EndDate_Month { get; set; } + public int DimResearchActivity_EndDate_Day { get; set; } + // DimFundingDecision + public string DimFundingDecision_Acronym { get; set; } + public string DimFundingDecision_FunderProjectNumber { get; set; } + public string DimFundingDecision_NameFi { get; set; } + public string DimFundingDecision_NameEn { get; set; } + public string DimFundingDecision_NameSv { get; set; } + public string DimFundingDecision_DescriptionFi { get; set; } + public string DimFundingDecision_DescriptionEn { get; set; } + public string DimFundingDecision_DescriptionSv { get; set; } + public decimal DimFundingDecision_amount_in_EUR { get; set; } + public int DimFundingDecision_StartDate_Year { get; set; } + public int DimFundingDecision_EndDate_Year { get; set; } + public string DimFundingDecision_DimTypeOfFunding_NameFi { get; set; } + public string DimFundingDecision_DimTypeOfFunding_NameEn { get; set; } + public string DimFundingDecision_DimTypeOfFunding_NameSv { get; set; } + public string DimFundingDecision_DimCallProgramme_NameFi { get; set; } + public string DimFundingDecision_DimCallProgramme_NameEn { get; set; } + public string DimFundingDecision_DimCallProgramme_NameSv { get; set; } + public string DimFundingDecision_Funder_NameFi { get; set; } + public string DimFundingDecision_Funder_NameEn { get; set; } + public string DimFundingDecision_Funder_NameSv { get; set; } + // DimResearchDataset + public string DimResearchDataset_LocalIdentifier { get; set; } + public string DimResearchDataset_NameFi { get; set; } + public string DimResearchDataset_NameEn { get; set; } + public string DimResearchDataset_NameSv { get; set; } + public string DimResearchDataset_DescriptionFi { get; set; } + public string DimResearchDataset_DescriptionEn { get; set; } + public string DimResearchDataset_DescriptionSv { get; set; } + public DateTime? DimResearchDataset_DatasetCreated {get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorCooperationItem.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorCooperationItem.cs new file mode 100644 index 00000000..1ec53aa8 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorCooperationItem.cs @@ -0,0 +1,15 @@ +namespace api.Models.ProfileEditor +{ + public partial class ProfileEditorCooperationItem + { + public ProfileEditorCooperationItem() + { + } + + public int Id { get; set; } + public string NameFi { get; set; } + public string NameEn { get; set; } + public string NameSv { get; set; } + public bool Selected { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataActivity.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataActivity.cs deleted file mode 100644 index 2c65b474..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataActivity.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorDataActivity { - public ProfileEditorDataActivity() - { - affiliationGroups = new List(); - educationGroups = new List(); - publicationGroups = new List(); - } - - public List affiliationGroups { get; set; } - public List educationGroups { get; set; } - public List publicationGroups { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationRequest.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationRequest.cs index 9e9ed8a3..4e44d0df 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationRequest.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationRequest.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; -using api.Models; +using System.Collections.Generic; +using api.Models.ProfileEditor.Items; -namespace api.Models +namespace api.Models.ProfileEditor { public partial class ProfileEditorDataModificationRequest { public ProfileEditorDataModificationRequest() diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationResponse.cs index 775bcf65..fae520e4 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationResponse.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataModificationResponse.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; -using api.Models; +using System.Collections.Generic; +using api.Models.ProfileEditor.Items; -namespace api.Models +namespace api.Models.ProfileEditor { public partial class ProfileEditorDataModificationResponse { public ProfileEditorDataModificationResponse() diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataPersonal.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataPersonal.cs deleted file mode 100644 index d48b3926..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataPersonal.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorDataPersonal - { - public ProfileEditorDataPersonal() - { - nameGroups = new List (); - otherNameGroups = new List(); - emailGroups = new List(); - telephoneNumberGroups = new List(); - webLinkGroups = new List (); - keywordGroups = new List(); - fieldOfScienceGroups = new List(); - researcherDescriptionGroups = new List(); - externalIdentifierGroups = new List(); - } - - public List nameGroups { get; set; } - public List otherNameGroups { get; set; } - public List emailGroups { get; set; } - public List telephoneNumberGroups { get; set; } - public List webLinkGroups { get; set; } - public List keywordGroups { get; set; } - public List fieldOfScienceGroups { get; set; } - public List researcherDescriptionGroups { get; set; } - public List externalIdentifierGroups { get; set; } - } -} - diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataResponse.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataResponse.cs deleted file mode 100644 index 1496cf53..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorDataResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorDataResponse { - public ProfileEditorDataResponse() - { - personal = new ProfileEditorDataPersonal(); - activity = new ProfileEditorDataActivity(); - } - - public ProfileEditorDataPersonal personal { get; set; } - public ProfileEditorDataActivity activity { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroup.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroup.cs deleted file mode 100644 index a94dd930..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroup.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace api.Models -{ - public partial class ProfileEditorGroup - { - public ProfileEditorGroup() - { - } - - public ProfileEditorGroupMeta groupMeta { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupAffiliation.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupAffiliation.cs deleted file mode 100644 index 8767456c..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupAffiliation.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupAffiliation : ProfileEditorGroup - { - public ProfileEditorGroupAffiliation() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEducation.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEducation.cs deleted file mode 100644 index 1e85d7bd..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEducation.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupEducation : ProfileEditorGroup - { - public ProfileEditorGroupEducation() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEmail.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEmail.cs deleted file mode 100644 index 2abafb6e..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupEmail.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupEmail : ProfileEditorGroup - { - public ProfileEditorGroupEmail() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupExternalIdentifier.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupExternalIdentifier.cs deleted file mode 100644 index 72275fa4..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupExternalIdentifier.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupExternalIdentifier : ProfileEditorGroup - { - public ProfileEditorGroupExternalIdentifier() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupFieldOfScience.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupFieldOfScience.cs deleted file mode 100644 index 7ddbe7ab..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupFieldOfScience.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupFieldOfScience : ProfileEditorGroup - { - public ProfileEditorGroupFieldOfScience() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupKeyword.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupKeyword.cs deleted file mode 100644 index 5750e6ad..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupKeyword.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupKeyword : ProfileEditorGroup - { - public ProfileEditorGroupKeyword() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupMeta.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupMeta.cs index 25166e54..8c2f9d7c 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupMeta.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupMeta.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace api.Models +namespace api.Models.ProfileEditor { public partial class ProfileEditorGroupMeta { diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupName.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupName.cs deleted file mode 100644 index 29b945df..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupName.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupName : ProfileEditorGroup - { - public ProfileEditorGroupName() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupOtherName.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupOtherName.cs deleted file mode 100644 index 18de1c7f..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupOtherName.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupOtherName : ProfileEditorGroup - { - public ProfileEditorGroupOtherName() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupPublication.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupPublication.cs deleted file mode 100644 index 56409acf..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupPublication.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupPublication : ProfileEditorGroup - { - public ProfileEditorGroupPublication() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupResearcherDescription.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupResearcherDescription.cs deleted file mode 100644 index e7f3acfc..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupResearcherDescription.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupResearcherDescription : ProfileEditorGroup - { - public ProfileEditorGroupResearcherDescription() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupTelephoneNumber.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupTelephoneNumber.cs deleted file mode 100644 index f113769b..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupTelephoneNumber.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupTelephoneNumber : ProfileEditorGroup - { - public ProfileEditorGroupTelephoneNumber() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupWebLink.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupWebLink.cs deleted file mode 100644 index 0238a5cb..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorGroupWebLink.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorGroupWebLink : ProfileEditorGroup - { - public ProfileEditorGroupWebLink() - { - } - - public ProfileEditorSource source { get; set; } - public List items { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEducation.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEducation.cs deleted file mode 100644 index 96d6052a..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEducation.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorItemEducation : ProfileEditorItem - { - public ProfileEditorItemEducation() - { - NameFi = ""; - NameEn = ""; - NameSv = ""; - DegreeGrantingInstitutionName = ""; - StartDate = new ProfileEditorItemDate(); - EndDate = new ProfileEditorItemDate(); - } - - public string NameFi { get; set; } - public string NameEn { get; set; } - public string NameSv { get; set; } - public string DegreeGrantingInstitutionName { get; set; } - public ProfileEditorItemDate StartDate { get; set; } - public ProfileEditorItemDate EndDate { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEmail.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEmail.cs deleted file mode 100644 index 16dc083a..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemEmail.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorItemEmail : ProfileEditorItem - { - public ProfileEditorItemEmail() - { - Value = ""; - } - - public string Value { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemExternalIdentifier.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemExternalIdentifier.cs deleted file mode 100644 index d63fdbb7..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemExternalIdentifier.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorItemExternalIdentifier : ProfileEditorItem - { - public ProfileEditorItemExternalIdentifier() - { - PidContent = ""; - PidType = ""; - } - - public string PidContent { get; set; } - public string PidType { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemKeyword.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemKeyword.cs deleted file mode 100644 index 793b9f76..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemKeyword.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorItemKeyword : ProfileEditorItem - { - public ProfileEditorItemKeyword() - { - Value = ""; - } - - public string Value { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemTelephoneNumber.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemTelephoneNumber.cs deleted file mode 100644 index 87e350b0..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemTelephoneNumber.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorItemTelephoneNumber : ProfileEditorItem - { - public ProfileEditorItemTelephoneNumber() - { - Value = ""; - } - - public string Value { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemWebLink.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemWebLink.cs deleted file mode 100644 index 13107016..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorItemWebLink.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using api.Models; - -namespace api.Models -{ - public partial class ProfileEditorItemWebLink : ProfileEditorItem - { - public ProfileEditorItemWebLink() - { - Url = ""; - LinkLabel = ""; - } - - public string Url { get; set; } - public string LinkLabel { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorPublicationId.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorPublicationId.cs deleted file mode 100644 index 4c747cd5..00000000 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileEditorPublicationId.cs +++ /dev/null @@ -1,12 +0,0 @@ - -namespace api.Models -{ - public partial class ProfileEditorPublicationId - { - public ProfileEditorPublicationId() - { - } - - public string PublicationId { get; set; } - } -} diff --git a/aspnetcore/src/api/Models/Ttv/BrArtpublicationTypecategory.cs b/aspnetcore/src/api/Models/Ttv/BrArtpublicationTypecategory.cs new file mode 100644 index 00000000..3ae58724 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/BrArtpublicationTypecategory.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class BrArtpublicationTypecategory + { + public int DimPublicationId { get; set; } + public int DimReferencedataid { get; set; } + + public virtual DimPublication DimPublication { get; set; } + public virtual DimReferencedatum DimReferencedata { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs b/aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs new file mode 100644 index 00000000..3852c7c6 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class BrDatasetDatasetRelationship + { + public int DimResearchDatasetId { get; set; } + public int DimResearchDatasetId2 { get; set; } + public string Type { get; set; } + + public virtual DimResearchDataset DimResearchDataset { get; set; } + public virtual DimResearchDataset DimResearchDatasetId2Navigation { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs b/aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs new file mode 100644 index 00000000..1d46ccf7 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class BrGrantedPermission + { + public int DimUserProfileId { get; set; } + public int DimExternalServiceId { get; set; } + public int DimPermittedFieldGroup { get; set; } + + public virtual DimPurpose DimExternalService { get; set; } + public virtual DimReferencedatum DimPermittedFieldGroupNavigation { get; set; } + public virtual DimUserProfile DimUserProfile { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrKeywordDimKeyword.cs b/aspnetcore/src/api/Models/Ttv/BrKeywordDimKeyword.cs new file mode 100644 index 00000000..6635e6e2 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/BrKeywordDimKeyword.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class BrKeywordDimKeyword + { + public int DimKeywordId { get; set; } + public int DimKeywordId2 { get; set; } + public int DimReferencedataIdRelationshipTypeCode { get; set; } + + public virtual DimKeyword DimKeyword { get; set; } + public virtual DimKeyword DimKeywordId2Navigation { get; set; } + public virtual DimReferencedatum DimReferencedataIdRelationshipTypeCodeNavigation { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrLanguageCodesForDataset.cs b/aspnetcore/src/api/Models/Ttv/BrLanguageCodesForDataset.cs index 4f6ed919..afb4d2a8 100644 --- a/aspnetcore/src/api/Models/Ttv/BrLanguageCodesForDataset.cs +++ b/aspnetcore/src/api/Models/Ttv/BrLanguageCodesForDataset.cs @@ -9,8 +9,5 @@ public partial class BrLanguageCodesForDataset { public int DimResearchDatasetId { get; set; } public int DimReferencedataId { get; set; } - - public virtual DimReferencedatum DimReferencedata { get; set; } - public virtual DimResearchDataset DimResearchDataset { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/BrLicensesForResearchDataset.cs b/aspnetcore/src/api/Models/Ttv/BrLicensesForResearchDataset.cs new file mode 100644 index 00000000..aabef2e9 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/BrLicensesForResearchDataset.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class BrLicensesForResearchDataset + { + public int DimResearchDatasetId { get; set; } + public int DimReferencedataId { get; set; } + + public virtual DimReferencedatum DimReferencedata { get; set; } + public virtual DimResearchDataset DimResearchDataset { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimFieldOfScience.cs b/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimFieldOfScience.cs index aee34a74..70752591 100644 --- a/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimFieldOfScience.cs +++ b/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimFieldOfScience.cs @@ -9,8 +9,5 @@ public partial class BrResearchDatasetDimFieldOfScience { public int DimResearchDatasetid { get; set; } public int DimFieldOfScienceid { get; set; } - - public virtual DimFieldOfScience DimFieldOfScience { get; set; } - public virtual DimResearchDataset DimResearchDataset { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimKeyword.cs b/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimKeyword.cs index d728752e..fa025d41 100644 --- a/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimKeyword.cs +++ b/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimKeyword.cs @@ -9,8 +9,5 @@ public partial class BrResearchDatasetDimKeyword { public int DimResearchDatasetId { get; set; } public int DimKeywordId { get; set; } - - public virtual DimKeyword DimKeyword { get; set; } - public virtual DimResearchDataset DimResearchDataset { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs b/aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs new file mode 100644 index 00000000..7f23e04f --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class BrWordClusterDimFundingDecision + { + public int DimWordClusterId { get; set; } + public int DimFundingDecisionId { get; set; } + public string SourceDescription { get; set; } + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + public string SourceId { get; set; } + + public virtual DimFundingDecision DimFundingDecision { get; set; } + public virtual DimWordCluster DimWordCluster { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs b/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs index fcff1f4b..52e24880 100644 --- a/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs +++ b/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs @@ -9,6 +9,10 @@ public partial class BrWordsDefineACluster { public int DimMinedWordsId { get; set; } public int DimWordClusterId { get; set; } + public string SourceDescription { get; set; } + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + public string SourceId { get; set; } public virtual DimMinedWord DimMinedWords { get; set; } public virtual DimWordCluster DimWordCluster { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs b/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs index ba8a5edd..7f998803 100644 --- a/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs +++ b/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs @@ -11,6 +11,7 @@ public DimCallProgramme() { BrCallProgrammeDimCallProgrammeDimCallProgrammeId2Navigations = new HashSet(); BrCallProgrammeDimCallProgrammeDimCallProgrammes = new HashSet(); + BrDimReferencedataDimCallProgrammes = new HashSet(); BrOrganizationsFundCallProgrammes = new HashSet(); DimFundingDecisions = new HashSet(); DimWebLinks = new HashSet(); @@ -25,19 +26,30 @@ public DimCallProgramme() public string NameSv { get; set; } public string NameEn { get; set; } public string NameUnd { get; set; } - public string SourceProgrammeId { get; set; } public string SourceId { get; set; } public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } public int? DimCallProgrammeId { get; set; } + public string SourceProgrammeId { get; set; } public int DimRegisteredDataSourceId { get; set; } + public string DescriptionFi { get; set; } + public string DescriptionSv { get; set; } + public string DescriptionEn { get; set; } + public string ApplicationTermsFi { get; set; } + public string ApplicationTermsSv { get; set; } + public string ApplicationTermsEn { get; set; } + public string ContactInformation { get; set; } + public bool? ContinuousApplicationPeriod { get; set; } + public bool IsOpenCall { get; set; } + public TimeSpan? DueDateDueTime { get; set; } public virtual DimDate DimDateIdDueNavigation { get; set; } public virtual DimDate DimDateIdOpenNavigation { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual ICollection BrCallProgrammeDimCallProgrammeDimCallProgrammeId2Navigations { get; set; } public virtual ICollection BrCallProgrammeDimCallProgrammeDimCallProgrammes { get; set; } + public virtual ICollection BrDimReferencedataDimCallProgrammes { get; set; } public virtual ICollection BrOrganizationsFundCallProgrammes { get; set; } public virtual ICollection DimFundingDecisions { get; set; } public virtual ICollection DimWebLinks { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs b/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs index 5f79157c..8df8a923 100644 --- a/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs +++ b/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs @@ -12,7 +12,6 @@ public DimFieldOfScience() BrFieldOfScienceDimFundingDecisions = new HashSet(); BrFieldOfScienceDimPublications = new HashSet(); BrInfrastructureDimFieldOfSciences = new HashSet(); - BrResearchDatasetDimFieldOfSciences = new HashSet(); DimFieldOfScienceDimResearchActivities = new HashSet(); DimKnownPersonDimFieldOfSciences = new HashSet(); FactFieldValues = new HashSet(); @@ -31,7 +30,6 @@ public DimFieldOfScience() public virtual ICollection BrFieldOfScienceDimFundingDecisions { get; set; } public virtual ICollection BrFieldOfScienceDimPublications { get; set; } public virtual ICollection BrInfrastructureDimFieldOfSciences { get; set; } - public virtual ICollection BrResearchDatasetDimFieldOfSciences { get; set; } public virtual ICollection DimFieldOfScienceDimResearchActivities { get; set; } public virtual ICollection DimKnownPersonDimFieldOfSciences { get; set; } public virtual ICollection FactFieldValues { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs b/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs index e78984fd..e9924d1e 100644 --- a/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs +++ b/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs @@ -18,6 +18,7 @@ public DimFundingDecision() BrPreviousFundingDecisionDimFundingDecisionTos = new HashSet(); BrRelatedFundingDecisionDimFundingDecisionFroms = new HashSet(); BrRelatedFundingDecisionDimFundingDecisionTos = new HashSet(); + BrWordClusterDimFundingDecisions = new HashSet(); DimPids = new HashSet(); DimWebLinks = new HashSet(); FactContributions = new HashSet(); @@ -75,6 +76,7 @@ public DimFundingDecision() public virtual ICollection BrPreviousFundingDecisionDimFundingDecisionTos { get; set; } public virtual ICollection BrRelatedFundingDecisionDimFundingDecisionFroms { get; set; } public virtual ICollection BrRelatedFundingDecisionDimFundingDecisionTos { get; set; } + public virtual ICollection BrWordClusterDimFundingDecisions { get; set; } public virtual ICollection DimPids { get; set; } public virtual ICollection DimWebLinks { get; set; } public virtual ICollection FactContributions { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs b/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs index 40049166..cddab7d9 100644 --- a/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs +++ b/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs @@ -16,12 +16,15 @@ public DimIdentifierlessDatum() public int Id { get; set; } public string Type { get; set; } - public string Value { get; set; } public int? DimIdentifierlessDataId { get; set; } public string SourceId { get; set; } public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } + public string ValueFi { get; set; } + public string ValueEn { get; set; } + public string ValueSv { get; set; } + public string UnlinkedIdentifier { get; set; } public virtual DimIdentifierlessDatum DimIdentifierlessData { get; set; } public virtual ICollection FactContributions { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs b/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs index 4eeb5a56..af86bd7f 100644 --- a/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs +++ b/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs @@ -27,18 +27,18 @@ public DimInfrastructure() public string DescriptionFi { get; set; } public string DescriptionSv { get; set; } public string DescriptionEn { get; set; } - public string ScientificDescriptionFi { get; set; } - public string ScientificDescriptionSv { get; set; } - public string ScientificDescriptionEn { get; set; } public int? StartYear { get; set; } public int? EndYear { get; set; } public string Acronym { get; set; } public bool FinlandRoadmap { get; set; } - public string Urn { get; set; } public string SourceId { get; set; } public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } + public string Urn { get; set; } + public string ScientificDescriptionFi { get; set; } + public string ScientificDescriptionSv { get; set; } + public string ScientificDescriptionEn { get; set; } public virtual DimInfrastructure NextInfastructure { get; set; } public virtual ICollection BrEsfriDimInfrastructures { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimKeyword.cs b/aspnetcore/src/api/Models/Ttv/DimKeyword.cs index 689ef73c..e08b8776 100644 --- a/aspnetcore/src/api/Models/Ttv/DimKeyword.cs +++ b/aspnetcore/src/api/Models/Ttv/DimKeyword.cs @@ -11,7 +11,6 @@ public DimKeyword() { BrKeywordDimFundingDecisions = new HashSet(); BrKeywordDimPublications = new HashSet(); - BrResearchDatasetDimKeywords = new HashSet(); FactFieldValues = new HashSet(); FactInfraKeywords = new HashSet(); InverseDimKeywordCloseMatchNavigation = new HashSet(); @@ -40,7 +39,6 @@ public DimKeyword() public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual ICollection BrKeywordDimFundingDecisions { get; set; } public virtual ICollection BrKeywordDimPublications { get; set; } - public virtual ICollection BrResearchDatasetDimKeywords { get; set; } public virtual ICollection FactFieldValues { get; set; } public virtual ICollection FactInfraKeywords { get; set; } public virtual ICollection InverseDimKeywordCloseMatchNavigation { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs b/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs index 7b09b23e..98cc8aa7 100644 --- a/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs +++ b/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs @@ -14,8 +14,7 @@ public DimKnownPerson() DimEducations = new HashSet(); DimEmailAddrresses = new HashSet(); DimKnownPersonDimFieldOfSciences = new HashSet(); - DimNameDimKnownPersonIdConfirmedIdentityNavigations = new HashSet(); - DimNameDimKnownPersonidFormerNamesNavigations = new HashSet(); + DimNames = new HashSet(); DimOrcidPublications = new HashSet(); DimPids = new HashSet(); DimResearcherDescriptions = new HashSet(); @@ -26,19 +25,19 @@ public DimKnownPerson() } public int Id { get; set; } - public string SourceProjectId { get; set; } public string SourceId { get; set; } public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } + public string SourceProjectId { get; set; } + public int? DimRegisteredDataSourceId { get; set; } public virtual ICollection DimAffiliations { get; set; } public virtual ICollection DimCompetences { get; set; } public virtual ICollection DimEducations { get; set; } public virtual ICollection DimEmailAddrresses { get; set; } public virtual ICollection DimKnownPersonDimFieldOfSciences { get; set; } - public virtual ICollection DimNameDimKnownPersonIdConfirmedIdentityNavigations { get; set; } - public virtual ICollection DimNameDimKnownPersonidFormerNamesNavigations { get; set; } + public virtual ICollection DimNames { get; set; } public virtual ICollection DimOrcidPublications { get; set; } public virtual ICollection DimPids { get; set; } public virtual ICollection DimResearcherDescriptions { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs b/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs index 9a4397be..12b82ec6 100644 --- a/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs +++ b/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs @@ -14,6 +14,10 @@ public DimMinedWord() public int Id { get; set; } public string Word { get; set; } + public string SourceDescription { get; set; } + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + public string SourceId { get; set; } public virtual ICollection BrWordsDefineAClusters { get; set; } } diff --git a/aspnetcore/src/api/Models/Ttv/DimName.cs b/aspnetcore/src/api/Models/Ttv/DimName.cs index b9e0b959..a14dca32 100644 --- a/aspnetcore/src/api/Models/Ttv/DimName.cs +++ b/aspnetcore/src/api/Models/Ttv/DimName.cs @@ -23,13 +23,11 @@ public DimName() public DateTime? Created { get; set; } public DateTime? Modified { get; set; } public int DimKnownPersonIdConfirmedIdentity { get; set; } + public string SourceProjectId { get; set; } public string FullName { get; set; } public int DimRegisteredDataSourceId { get; set; } - public string SourceProjectId { get; set; } - public int DimKnownPersonidFormerNames { get; set; } public virtual DimKnownPerson DimKnownPersonIdConfirmedIdentityNavigation { get; set; } - public virtual DimKnownPerson DimKnownPersonidFormerNamesNavigation { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual ICollection BrParticipatesInFundingGroups { get; set; } public virtual ICollection DimFundingDecisions { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimOrganization.cs b/aspnetcore/src/api/Models/Ttv/DimOrganization.cs index 75d39c1c..b8d461d6 100644 --- a/aspnetcore/src/api/Models/Ttv/DimOrganization.cs +++ b/aspnetcore/src/api/Models/Ttv/DimOrganization.cs @@ -20,6 +20,7 @@ public DimOrganization() DimExternalServices = new HashSet(); DimFundingDecisions = new HashSet(); DimPids = new HashSet(); + DimPurposes = new HashSet(); DimRegisteredDataSources = new HashSet(); DimResearchActivities = new HashSet(); DimWebLinks = new HashSet(); @@ -71,6 +72,7 @@ public DimOrganization() public virtual ICollection DimExternalServices { get; set; } public virtual ICollection DimFundingDecisions { get; set; } public virtual ICollection DimPids { get; set; } + public virtual ICollection DimPurposes { get; set; } public virtual ICollection DimRegisteredDataSources { get; set; } public virtual ICollection DimResearchActivities { get; set; } public virtual ICollection DimWebLinks { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimPublication.cs b/aspnetcore/src/api/Models/Ttv/DimPublication.cs index 6dfc8dd9..b132d467 100644 --- a/aspnetcore/src/api/Models/Ttv/DimPublication.cs +++ b/aspnetcore/src/api/Models/Ttv/DimPublication.cs @@ -9,6 +9,7 @@ public partial class DimPublication { public DimPublication() { + BrArtpublicationTypecategories = new HashSet(); BrFieldOfArtDimPublications = new HashSet(); BrFieldOfEducationDimPublications = new HashSet(); BrFieldOfScienceDimPublications = new HashSet(); @@ -47,7 +48,7 @@ public DimPublication() public string PublicationTypeCode { get; set; } public bool InternationalCollaboration { get; set; } public bool HospitalDistrictCollaboration { get; set; } - public bool InternationalPublication { get; set; } + public int InternationalPublication { get; set; } public bool GovermentCollaboration { get; set; } public bool OtherCollaboration { get; set; } public string LanguageCode { get; set; } @@ -69,18 +70,22 @@ public DimPublication() public bool? PeerReviewed { get; set; } public bool? Report { get; set; } public int? ThesisTypeCode { get; set; } + public string SelfArchivedCode { get; set; } public string SourceId { get; set; } public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - public string SelfArchivedCode { get; set; } public int DimRegisteredDataSourceId { get; set; } + public string OpenAccess { get; set; } + public string PublisherOpenAccessCode { get; set; } + public string Abstract { get; set; } public virtual DimReferencedatum ArticleTypeCodeNavigation { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimReferencedatum ParentPublicationTypeCodeNavigation { get; set; } public virtual DimReferencedatum PublicationTypeCode2Navigation { get; set; } public virtual DimReferencedatum TargetAudienceCodeNavigation { get; set; } + public virtual ICollection BrArtpublicationTypecategories { get; set; } public virtual ICollection BrFieldOfArtDimPublications { get; set; } public virtual ICollection BrFieldOfEducationDimPublications { get; set; } public virtual ICollection BrFieldOfScienceDimPublications { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimPurpose.cs b/aspnetcore/src/api/Models/Ttv/DimPurpose.cs new file mode 100644 index 00000000..54e82be6 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/DimPurpose.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class DimPurpose + { + public DimPurpose() + { + BrGrantedPermissions = new HashSet(); + } + + public int Id { get; set; } + public int DimOrganizationId { get; set; } + public string NameFi { get; set; } + public string NameSv { get; set; } + public string NameEn { get; set; } + public string NameUnd { get; set; } + public string DescriptionFi { get; set; } + public string DescriptionEn { get; set; } + public string DescriptionSv { get; set; } + public string SourceId { get; set; } + public string SourceDescription { get; set; } + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + + public virtual DimOrganization DimOrganization { get; set; } + public virtual ICollection BrGrantedPermissions { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs b/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs index 816da299..2cc30f40 100644 --- a/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs +++ b/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs @@ -9,7 +9,9 @@ public partial class DimReferencedatum { public DimReferencedatum() { - BrLanguageCodesForDatasets = new HashSet(); + BrArtpublicationTypecategories = new HashSet(); + BrDimReferencedataDimCallProgrammes = new HashSet(); + BrGrantedPermissions = new HashSet(); DimAffiliationAffiliationTypeNavigations = new HashSet(); DimAffiliationPositionCodeNavigations = new HashSet(); DimEducations = new HashSet(); @@ -24,8 +26,10 @@ public DimReferencedatum() DimPublicationTargetAudienceCodeNavigations = new HashSet(); DimResearchDatasetDimReferencedataAvailabilityNavigations = new HashSet(); DimResearchDatasetDimReferencedataLicenseNavigations = new HashSet(); + DimUserChoices = new HashSet(); FactContributions = new HashSet(); FactJufoClassCodesForPubChannels = new HashSet(); + InverseDimReferencedata = new HashSet(); } public int Id { get; set; } @@ -39,8 +43,12 @@ public DimReferencedatum() public DateTime? Created { get; set; } public DateTime? Modified { get; set; } public string State { get; set; } + public int DimReferencedataId { get; set; } - public virtual ICollection BrLanguageCodesForDatasets { get; set; } + public virtual DimReferencedatum DimReferencedata { get; set; } + public virtual ICollection BrArtpublicationTypecategories { get; set; } + public virtual ICollection BrDimReferencedataDimCallProgrammes { get; set; } + public virtual ICollection BrGrantedPermissions { get; set; } public virtual ICollection DimAffiliationAffiliationTypeNavigations { get; set; } public virtual ICollection DimAffiliationPositionCodeNavigations { get; set; } public virtual ICollection DimEducations { get; set; } @@ -55,7 +63,9 @@ public DimReferencedatum() public virtual ICollection DimPublicationTargetAudienceCodeNavigations { get; set; } public virtual ICollection DimResearchDatasetDimReferencedataAvailabilityNavigations { get; set; } public virtual ICollection DimResearchDatasetDimReferencedataLicenseNavigations { get; set; } + public virtual ICollection DimUserChoices { get; set; } public virtual ICollection FactContributions { get; set; } public virtual ICollection FactJufoClassCodesForPubChannels { get; set; } + public virtual ICollection InverseDimReferencedata { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs b/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs index 9d2d88e6..82af4162 100644 --- a/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs +++ b/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs @@ -28,6 +28,7 @@ public DimRegisteredDataSource() DimResearcherDescriptions = new HashSet(); DimResearcherToResearchCommunities = new HashSet(); DimTelephoneNumbers = new HashSet(); + FactFieldValues = new HashSet(); } public int Id { get; set; } @@ -58,5 +59,6 @@ public DimRegisteredDataSource() public virtual ICollection DimResearcherDescriptions { get; set; } public virtual ICollection DimResearcherToResearchCommunities { get; set; } public virtual ICollection DimTelephoneNumbers { get; set; } + public virtual ICollection FactFieldValues { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs b/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs index 261eda8b..b588b0d8 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs @@ -12,6 +12,7 @@ public DimResearchActivity() DimFieldOfScienceDimResearchActivities = new HashSet(); DimPids = new HashSet(); DimResearchActivityDimKeywords = new HashSet(); + DimWebLinks = new HashSet(); FactContributions = new HashSet(); FactFieldValues = new HashSet(); } @@ -49,6 +50,7 @@ public DimResearchActivity() public virtual ICollection DimFieldOfScienceDimResearchActivities { get; set; } public virtual ICollection DimPids { get; set; } public virtual ICollection DimResearchActivityDimKeywords { get; set; } + public virtual ICollection DimWebLinks { get; set; } public virtual ICollection FactContributions { get; set; } public virtual ICollection FactFieldValues { get; set; } } diff --git a/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs b/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs index efd71447..035c469a 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs @@ -9,19 +9,17 @@ public partial class DimResearchDataset { public DimResearchDataset() { - BrLanguageCodesForDatasets = new HashSet(); - BrResearchDatasetDimFieldOfSciences = new HashSet(); - BrResearchDatasetDimKeywords = new HashSet(); + BrDatasetDatasetRelationshipDimResearchDatasetId2Navigations = new HashSet(); + BrDatasetDatasetRelationshipDimResearchDatasets = new HashSet(); DimWebLinks = new HashSet(); FactContributions = new HashSet(); - InverseDimResearchDatasetNavigation = new HashSet(); + FactFieldValues = new HashSet(); } public int Id { get; set; } public int? DimResearchDataCatalogId { get; set; } public int? DimReferencedataLicense { get; set; } public int? DimReferencedataAvailability { get; set; } - public int? DimResearchDatasetId { get; set; } public string LocalIdentifier { get; set; } public string NameFi { get; set; } public string NameSv { get; set; } @@ -42,17 +40,16 @@ public DimResearchDataset() public string NameUnd { get; set; } public string DescriptionUnd { get; set; } public int DimRegisteredDataSourceId { get; set; } + public string VersionInfo { get; set; } public virtual DimReferencedatum DimReferencedataAvailabilityNavigation { get; set; } public virtual DimReferencedatum DimReferencedataLicenseNavigation { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimResearchDataCatalog DimResearchDataCatalog { get; set; } - public virtual DimResearchDataset DimResearchDatasetNavigation { get; set; } - public virtual ICollection BrLanguageCodesForDatasets { get; set; } - public virtual ICollection BrResearchDatasetDimFieldOfSciences { get; set; } - public virtual ICollection BrResearchDatasetDimKeywords { get; set; } + public virtual ICollection BrDatasetDatasetRelationshipDimResearchDatasetId2Navigations { get; set; } + public virtual ICollection BrDatasetDatasetRelationshipDimResearchDatasets { get; set; } public virtual ICollection DimWebLinks { get; set; } public virtual ICollection FactContributions { get; set; } - public virtual ICollection InverseDimResearchDatasetNavigation { get; set; } + public virtual ICollection FactFieldValues { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs b/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs index ba484b10..f7ec8811 100644 --- a/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs +++ b/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs @@ -23,8 +23,12 @@ public DimServicePoint() public string VisitingAddress { get; set; } public string Phone { get; set; } public string Email { get; set; } - public string LinkAdditionalInfo { get; set; } - public string LinkAccessPolicy { get; set; } + public string LinkAdditionalInfoFi { get; set; } + public string LinkAdditionalInfoSv { get; set; } + public string LinkAdditionalInfoEn { get; set; } + public string LinkAccessPolicyFi { get; set; } + public string LinkAccessPolicySv { get; set; } + public string LinkAccessPolicyEn { get; set; } public string LinkInternationalInfra { get; set; } public string SourceId { get; set; } public string SourceDescription { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimUserChoice.cs b/aspnetcore/src/api/Models/Ttv/DimUserChoice.cs new file mode 100644 index 00000000..0cbd97cd --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/DimUserChoice.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; + +#nullable disable + +namespace api.Models.Ttv +{ + public partial class DimUserChoice + { + public int Id { get; set; } + public bool UserChoiceValue { get; set; } + public int DimUserProfileId { get; set; } + public int DimReferencedataIdAsUserChoiceLabel { get; set; } + public string SourceId { get; set; } + public string SourceDescription { get; set; } + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + + public virtual DimReferencedatum DimReferencedataIdAsUserChoiceLabelNavigation { get; set; } + public virtual DimUserProfile DimUserProfile { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs b/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs index 7b217a1f..b1bd2dc0 100644 --- a/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs +++ b/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs @@ -9,7 +9,9 @@ public partial class DimUserProfile { public DimUserProfile() { + BrGrantedPermissions = new HashSet(); DimFieldDisplaySettings = new HashSet(); + DimUserChoices = new HashSet(); FactFieldValues = new HashSet(); } @@ -20,9 +22,17 @@ public DimUserProfile() public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } + public string OrcidAccessToken { get; set; } + public string OrcidRefreshToken { get; set; } + public string OrcidTokenScope { get; set; } + public DateTime? OrcidTokenExpires { get; set; } + public string OrcidId { get; set; } + public int? Statuscode { get; set; } public virtual DimKnownPerson DimKnownPerson { get; set; } + public virtual ICollection BrGrantedPermissions { get; set; } public virtual ICollection DimFieldDisplaySettings { get; set; } + public virtual ICollection DimUserChoices { get; set; } public virtual ICollection FactFieldValues { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimWebLink.cs b/aspnetcore/src/api/Models/Ttv/DimWebLink.cs index 4942f00b..a748d23a 100644 --- a/aspnetcore/src/api/Models/Ttv/DimWebLink.cs +++ b/aspnetcore/src/api/Models/Ttv/DimWebLink.cs @@ -23,16 +23,18 @@ public DimWebLink() public int? DimFundingDecisionId { get; set; } public int? DimResearchDataCatalogId { get; set; } public int? DimResearchDatasetId { get; set; } + public int? DimResearchCommunityId { get; set; } public string SourceDescription { get; set; } public string SourceId { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - public int? DimResearchCommunityId { get; set; } + public int? DimResearchActivityId { get; set; } public virtual DimCallProgramme DimCallProgramme { get; set; } public virtual DimFundingDecision DimFundingDecision { get; set; } public virtual DimKnownPerson DimKnownPerson { get; set; } public virtual DimOrganization DimOrganization { get; set; } + public virtual DimResearchActivity DimResearchActivity { get; set; } public virtual DimResearchCommunity DimResearchCommunity { get; set; } public virtual DimResearchDataCatalog DimResearchDataCatalog { get; set; } public virtual DimResearchDataset DimResearchDataset { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs b/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs index 159c2703..710f0c96 100644 --- a/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs +++ b/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs @@ -9,13 +9,17 @@ public partial class DimWordCluster { public DimWordCluster() { + BrWordClusterDimFundingDecisions = new HashSet(); BrWordsDefineAClusters = new HashSet(); - DimFundingDecisions = new HashSet(); } public int Id { get; set; } + public string SourceDescription { get; set; } + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + public string SourceId { get; set; } + public virtual ICollection BrWordClusterDimFundingDecisions { get; set; } public virtual ICollection BrWordsDefineAClusters { get; set; } - public virtual ICollection DimFundingDecisions { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs b/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs index f1e39e36..049d6903 100644 --- a/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs +++ b/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs @@ -35,6 +35,8 @@ public partial class FactFieldValue public DateTime? Created { get; set; } public DateTime? Modified { get; set; } public int DimFieldOfScienceId { get; set; } + public int DimResearchDatasetId { get; set; } + public int DimRegisteredDataSourceId { get; set; } public virtual DimAffiliation DimAffiliation { get; set; } public virtual DimCompetence DimCompetence { get; set; } @@ -51,8 +53,10 @@ public partial class FactFieldValue public virtual DimPid DimPid { get; set; } public virtual DimPid DimPidIdOrcidPutCodeNavigation { get; set; } public virtual DimPublication DimPublication { get; set; } + public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimResearchActivity DimResearchActivity { get; set; } public virtual DimResearchCommunity DimResearchCommunity { get; set; } + public virtual DimResearchDataset DimResearchDataset { get; set; } public virtual DimResearcherDescription DimResearcherDescription { get; set; } public virtual DimResearcherToResearchCommunity DimResearcherToResearchCommunity { get; set; } public virtual DimTelephoneNumber DimTelephoneNumber { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/TtvContext.cs b/aspnetcore/src/api/Models/Ttv/TtvContext.cs index 17375ac9..f555c944 100644 --- a/aspnetcore/src/api/Models/Ttv/TtvContext.cs +++ b/aspnetcore/src/api/Models/Ttv/TtvContext.cs @@ -17,7 +17,10 @@ public TtvContext(DbContextOptions options) { } + public virtual DbSet BrArtpublicationTypecategories { get; set; } public virtual DbSet BrCallProgrammeDimCallProgrammes { get; set; } + public virtual DbSet BrDatasetDatasetRelationships { get; set; } + public virtual DbSet BrDimReferencedataDimCallProgrammes { get; set; } public virtual DbSet BrEsfriDimInfrastructures { get; set; } public virtual DbSet BrFieldDisplaySettingsDimRegisteredDataSources { get; set; } public virtual DbSet BrFieldOfArtDimPublications { get; set; } @@ -26,6 +29,7 @@ public TtvContext(DbContextOptions options) public virtual DbSet BrFieldOfScienceDimPublications { get; set; } public virtual DbSet BrFundingConsortiumParticipations { get; set; } public virtual DbSet BrFundingDecisionDimFieldOfArts { get; set; } + public virtual DbSet BrGrantedPermissions { get; set; } public virtual DbSet BrInfrastructureDimFieldOfSciences { get; set; } public virtual DbSet BrKeywordDimFundingDecisions { get; set; } public virtual DbSet BrKeywordDimPublications { get; set; } @@ -40,6 +44,8 @@ public TtvContext(DbContextOptions options) public virtual DbSet BrResearchDatasetDimKeywords { get; set; } public virtual DbSet BrServiceSubscriptions { get; set; } public virtual DbSet BrSuccessorOrganizations { get; set; } + public virtual DbSet BrWordClusterDimFundingDecisions { get; set; } + public virtual DbSet BrWordsDefineAClusters { get; set; } public virtual DbSet DimAffiliations { get; set; } public virtual DbSet DimCallProgrammes { get; set; } public virtual DbSet DimCompetences { get; set; } @@ -63,6 +69,7 @@ public TtvContext(DbContextOptions options) public virtual DbSet DimKnownPersonDimFieldOfSciences { get; set; } public virtual DbSet DimLocallyReportedPubInfos { get; set; } public virtual DbSet DimMerils { get; set; } + public virtual DbSet DimMinedWords { get; set; } public virtual DbSet DimNames { get; set; } public virtual DbSet DimNewsFeeds { get; set; } public virtual DbSet DimNewsItems { get; set; } @@ -72,6 +79,7 @@ public TtvContext(DbContextOptions options) public virtual DbSet DimPids { get; set; } public virtual DbSet DimPublications { get; set; } public virtual DbSet DimPublicationChannels { get; set; } + public virtual DbSet DimPurposes { get; set; } public virtual DbSet DimReferencedata { get; set; } public virtual DbSet DimRegisteredDataSources { get; set; } public virtual DbSet DimResearchActivities { get; set; } @@ -86,8 +94,10 @@ public TtvContext(DbContextOptions options) public virtual DbSet DimServicePoints { get; set; } public virtual DbSet DimTelephoneNumbers { get; set; } public virtual DbSet DimTypeOfFundings { get; set; } + public virtual DbSet DimUserChoices { get; set; } public virtual DbSet DimUserProfiles { get; set; } public virtual DbSet DimWebLinks { get; set; } + public virtual DbSet DimWordClusters { get; set; } public virtual DbSet FactContributions { get; set; } public virtual DbSet FactFieldValues { get; set; } public virtual DbSet FactInfraKeywords { get; set; } @@ -98,8 +108,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { -#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263. - optionsBuilder.UseSqlServer("Server=localhost;User Id=sa;Password=Test1234;database=Ttv;"); + optionsBuilder.UseSqlServer("Name=ConnectionStrings:DefaultConnection"); } } @@ -107,10 +116,34 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS"); + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.DimPublicationId, e.DimReferencedataid }) + .HasName("PK__br_artpu__7AE5307A84CC324E"); + + entity.ToTable("br_artpublication_typecategory"); + + entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication id"); + + entity.Property(e => e.DimReferencedataid).HasColumnName("dim_referencedataid"); + + entity.HasOne(d => d.DimPublication) + .WithMany(p => p.BrArtpublicationTypecategories) + .HasForeignKey(d => d.DimPublicationId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_artpubl464312"); + + entity.HasOne(d => d.DimReferencedata) + .WithMany(p => p.BrArtpublicationTypecategories) + .HasForeignKey(d => d.DimReferencedataid) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_artpubl101187"); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimCallProgrammeId, e.DimCallProgrammeId2 }) - .HasName("PK__br_call___6F0CEDFB9005A738"); + .HasName("PK__br_call___6F0CEDFB46440361"); entity.ToTable("br_call_programme_dim_call_programme"); @@ -131,10 +164,63 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("FKbr_call_pr785575"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.DimResearchDatasetId, e.DimResearchDatasetId2 }) + .HasName("PK__br_datas__9FEA685AE8535A03"); + + entity.ToTable("br_dataset_dataset_relationship"); + + entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); + + entity.Property(e => e.DimResearchDatasetId2).HasColumnName("dim_research_dataset_id2"); + + entity.Property(e => e.Type) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("type"); + + entity.HasOne(d => d.DimResearchDataset) + .WithMany(p => p.BrDatasetDatasetRelationshipDimResearchDatasets) + .HasForeignKey(d => d.DimResearchDatasetId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_dataset173300"); + + entity.HasOne(d => d.DimResearchDatasetId2Navigation) + .WithMany(p => p.BrDatasetDatasetRelationshipDimResearchDatasetId2Navigations) + .HasForeignKey(d => d.DimResearchDatasetId2) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_dataset168991"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.DimCallProgrammeId, e.DimReferencedataId }) + .HasName("PK__br_dim_r__0A5B885D306ADB18"); + + entity.ToTable("br_dim_referencedata_dim_call_programme"); + + entity.Property(e => e.DimCallProgrammeId).HasColumnName("dim_call_programme_id"); + + entity.Property(e => e.DimReferencedataId).HasColumnName("dim_referencedata_id"); + + entity.HasOne(d => d.DimCallProgramme) + .WithMany(p => p.BrDimReferencedataDimCallProgrammes) + .HasForeignKey(d => d.DimCallProgrammeId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("has disciplines"); + + entity.HasOne(d => d.DimReferencedata) + .WithMany(p => p.BrDimReferencedataDimCallProgrammes) + .HasForeignKey(d => d.DimReferencedataId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_dim_ref172472"); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimEsfriId, e.DimInfrastructureId }) - .HasName("PK__br_esfri__A4A0FE10228343FE"); + .HasName("PK__br_esfri__A4A0FE10CAB3657A"); entity.ToTable("br_esfri_dim_infrastructure"); @@ -158,7 +244,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFieldDisplaySettingsId, e.DimRegisteredDataSourceId }) - .HasName("PK__br_field__6148A7723B475AB5"); + .HasName("PK__br_field__6148A772BBC33689"); entity.ToTable("br_field_display_settings_dim_registered_data_source"); @@ -182,7 +268,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFieldOfArtId, e.DimPublicationId }) - .HasName("PK__br_field__809A87CD5F66E394"); + .HasName("PK__br_field__809A87CD97415E08"); entity.ToTable("br_field_of_art_dim_publication"); @@ -206,7 +292,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFieldOfEducationId, e.DimPublicationId }) - .HasName("PK__br_field__6E377B2C93F2343F"); + .HasName("PK__br_field__6E377B2C0FF16E01"); entity.ToTable("br_field_of_education_dim_publication"); @@ -230,7 +316,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimFundingDecisionId }) - .HasName("PK__br_field__1A103AF75CAFC581"); + .HasName("PK__br_field__1A103AF730AFF80F"); entity.ToTable("br_field_of_science_dim_funding_decision"); @@ -254,7 +340,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimPublicationId }) - .HasName("PK__br_field__5088B7764BF4E923"); + .HasName("PK__br_field__5088B7766332C590"); entity.ToTable("br_field_of_science_dim_publication"); @@ -278,7 +364,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionId, e.DimOrganizationid }) - .HasName("PK__br_fundi__3DB567F8FBE9330E"); + .HasName("PK__br_fundi__3DB567F87ABBFFA9"); entity.ToTable("br_funding_consortium_participation"); @@ -312,7 +398,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionId, e.DimFieldOfArtId }) - .HasName("PK__br_fundi__07CB586DD1342EED"); + .HasName("PK__br_fundi__07CB586D20BFF86B"); entity.ToTable("br_funding_decision_dim_field_of_art"); @@ -333,10 +419,42 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("FKbr_funding281737"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.DimUserProfileId, e.DimExternalServiceId, e.DimPermittedFieldGroup }) + .HasName("PK__br_grant__F51F7BCB9EEFE4B6"); + + entity.ToTable("br_granted_permissions"); + + entity.Property(e => e.DimUserProfileId).HasColumnName("dim_user_profile_id"); + + entity.Property(e => e.DimExternalServiceId).HasColumnName("dim_external_service_id"); + + entity.Property(e => e.DimPermittedFieldGroup).HasColumnName("dim_permitted_field_group"); + + entity.HasOne(d => d.DimExternalService) + .WithMany(p => p.BrGrantedPermissions) + .HasForeignKey(d => d.DimExternalServiceId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_granted953402"); + + entity.HasOne(d => d.DimPermittedFieldGroupNavigation) + .WithMany(p => p.BrGrantedPermissions) + .HasForeignKey(d => d.DimPermittedFieldGroup) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("field group"); + + entity.HasOne(d => d.DimUserProfile) + .WithMany(p => p.BrGrantedPermissions) + .HasForeignKey(d => d.DimUserProfileId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("permitted_services"); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimInfrastructureId, e.DimFieldOfScienceId }) - .HasName("PK__br_infra__17B77C16BC82C13B"); + .HasName("PK__br_infra__17B77C16825186C1"); entity.ToTable("br_infrastructure_dim_field_of_science"); @@ -360,7 +478,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimKeywordId, e.DimFundingDecisionId }) - .HasName("PK__br_keywo__8C7B929B2049F4FB"); + .HasName("PK__br_keywo__8C7B929B21F5028A"); entity.ToTable("br_keyword_dim_funding_decision"); @@ -384,7 +502,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimKeywordId, e.DimPublicationId }) - .HasName("PK__br_keywo__C6E31F1A3BF7304E"); + .HasName("PK__br_keywo__C6E31F1ABABE5C19"); entity.ToTable("br_keyword_dim_publication"); @@ -408,31 +526,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimResearchDatasetId, e.DimReferencedataId }) - .HasName("PK__br_langu__576647BF68B20345"); + .HasName("PK__br_langu__576647BF54CFAC65"); entity.ToTable("br_language_codes_for_datasets"); entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); entity.Property(e => e.DimReferencedataId).HasColumnName("dim_referencedata_id"); - - entity.HasOne(d => d.DimReferencedata) - .WithMany(p => p.BrLanguageCodesForDatasets) - .HasForeignKey(d => d.DimReferencedataId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_languag480770"); - - entity.HasOne(d => d.DimResearchDataset) - .WithMany(p => p.BrLanguageCodesForDatasets) - .HasForeignKey(d => d.DimResearchDatasetId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_languag34243"); }); modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimMerilId, e.DimInfrastructureId }) - .HasName("PK__br_meril__A30C54DA625F830D"); + .HasName("PK__br_meril__A30C54DA7582FE32"); entity.ToTable("br_meril_dim_infrastructure"); @@ -456,7 +562,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimOrganizationid, e.DimCallProgrammeid }) - .HasName("PK__br_organ__10F219BCDAF3E03A"); + .HasName("PK__br_organ__10F219BC6E5C0983"); entity.ToTable("br_organizations_fund_call_programmes"); @@ -480,7 +586,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionid, e.DimNameId }) - .HasName("PK__br_parti__5EC9BC64243B857E"); + .HasName("PK__br_parti__5EC9BC64C369D361"); entity.ToTable("br_participates_in_funding_group"); @@ -526,7 +632,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimOrganizationid, e.DimOrganizationid2 }) - .HasName("PK__br_prede__A7CAD2F488E9A5D7"); + .HasName("PK__br_prede__A7CAD2F4CB0E1A8E"); entity.ToTable("br_predecessor_organization"); @@ -550,7 +656,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionFromId, e.DimFundingDecisionToId }) - .HasName("PK__br_previ__90966491B197976E"); + .HasName("PK__br_previ__90966491D4F0C457"); entity.ToTable("br_previous_funding_decision"); @@ -574,7 +680,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionFromId, e.DimFundingDecisionToId }) - .HasName("PK__br_relat__909664916F5CA1D0"); + .HasName("PK__br_relat__909664919623E695"); entity.ToTable("br_related_funding_decision"); @@ -598,49 +704,25 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimResearchDatasetid, e.DimFieldOfScienceid }) - .HasName("PK__br_resea__ADD3384B91A34EE9"); + .HasName("PK__br_resea__ADD3384BB46E4B15"); entity.ToTable("br_research_dataset_dim_field_of_science"); entity.Property(e => e.DimResearchDatasetid).HasColumnName("dim_research_datasetid"); entity.Property(e => e.DimFieldOfScienceid).HasColumnName("dim_field_of_scienceid"); - - entity.HasOne(d => d.DimFieldOfScience) - .WithMany(p => p.BrResearchDatasetDimFieldOfSciences) - .HasForeignKey(d => d.DimFieldOfScienceid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_researc385994"); - - entity.HasOne(d => d.DimResearchDataset) - .WithMany(p => p.BrResearchDatasetDimFieldOfSciences) - .HasForeignKey(d => d.DimResearchDatasetid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_researc927326"); }); modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimResearchDatasetId, e.DimKeywordId }) - .HasName("PK__br_resea__4D226DF2BE09C5C5"); + .HasName("PK__br_resea__4D226DF2A00AACE3"); entity.ToTable("br_research_dataset_dim_keyword"); entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); entity.Property(e => e.DimKeywordId).HasColumnName("dim_keyword_id"); - - entity.HasOne(d => d.DimKeyword) - .WithMany(p => p.BrResearchDatasetDimKeywords) - .HasForeignKey(d => d.DimKeywordId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbt_researc329080"); - - entity.HasOne(d => d.DimResearchDataset) - .WithMany(p => p.BrResearchDatasetDimKeywords) - .HasForeignKey(d => d.DimResearchDatasetId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("dataset-keywords"); }); modelBuilder.Entity(entity => @@ -669,7 +751,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimOrganizationid, e.DimOrganizationid2 }) - .HasName("PK__br_succe__A7CAD2F46F847B1F"); + .HasName("PK__br_succe__A7CAD2F436CC1A1E"); entity.ToTable("br_successor organization"); @@ -690,6 +772,88 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("FKbr_success902531"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.DimWordClusterId, e.DimFundingDecisionId }) + .HasName("PK__br_word___7D640B5A3F85B915"); + + entity.ToTable("br_word_cluster_dim_funding_decision"); + + entity.Property(e => e.DimWordClusterId).HasColumnName("dim_word_cluster_id"); + + entity.Property(e => e.DimFundingDecisionId).HasColumnName("dim_funding_decision_id"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + + entity.HasOne(d => d.DimFundingDecision) + .WithMany(p => p.BrWordClusterDimFundingDecisions) + .HasForeignKey(d => d.DimFundingDecisionId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_word_cl350721"); + + entity.HasOne(d => d.DimWordCluster) + .WithMany(p => p.BrWordClusterDimFundingDecisions) + .HasForeignKey(d => d.DimWordClusterId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_word_cl424955"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.DimMinedWordsId, e.DimWordClusterId }) + .HasName("PK__br_words__0602FA370C173B6A"); + + entity.ToTable("br_words_define_a_cluster"); + + entity.Property(e => e.DimMinedWordsId).HasColumnName("dim_mined_words_id"); + + entity.Property(e => e.DimWordClusterId).HasColumnName("dim_word_cluster_id"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + + entity.HasOne(d => d.DimMinedWords) + .WithMany(p => p.BrWordsDefineAClusters) + .HasForeignKey(d => d.DimMinedWordsId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_words_d537149"); + + entity.HasOne(d => d.DimWordCluster) + .WithMany(p => p.BrWordsDefineAClusters) + .HasForeignKey(d => d.DimWordClusterId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKbr_words_d714819"); + }); + modelBuilder.Entity(entity => { entity.ToTable("dim_affiliation"); @@ -790,10 +954,26 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasMaxLength(511) .HasColumnName("abbreviation"); + entity.Property(e => e.ApplicationTermsEn).HasColumnName("application_terms_en"); + + entity.Property(e => e.ApplicationTermsFi).HasColumnName("application_terms_fi"); + + entity.Property(e => e.ApplicationTermsSv).HasColumnName("application_terms_sv"); + + entity.Property(e => e.ContactInformation).HasColumnName("contact_information"); + + entity.Property(e => e.ContinuousApplicationPeriod).HasColumnName("continuous_application_period"); + entity.Property(e => e.Created) .HasColumnType("datetime") .HasColumnName("created"); + entity.Property(e => e.DescriptionEn).HasColumnName("description_en"); + + entity.Property(e => e.DescriptionFi).HasColumnName("description_fi"); + + entity.Property(e => e.DescriptionSv).HasColumnName("description_sv"); + entity.Property(e => e.DimCallProgrammeId).HasColumnName("dim_call_programme_id"); entity.Property(e => e.DimDateIdDue).HasColumnName("dim_date_id_due"); @@ -802,10 +982,16 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); + entity.Property(e => e.DueDateDueTime) + .HasColumnType("time(0)") + .HasColumnName("due_date_due_time"); + entity.Property(e => e.EuCallId) .HasMaxLength(511) .HasColumnName("eu_call_id"); + entity.Property(e => e.IsOpenCall).HasColumnName("is_open_call"); + entity.Property(e => e.Modified) .HasColumnType("datetime") .HasColumnName("modified"); @@ -1422,7 +1608,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimResearchActivityId }) - .HasName("PK__dim_fiel__D251A58212473C45"); + .HasName("PK__dim_fiel__D251A5825374B163"); entity.ToTable("dim_field_of_science_dim_research_activity"); @@ -1697,9 +1883,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsUnicode(false) .HasColumnName("type"); - entity.Property(e => e.Value) + entity.Property(e => e.UnlinkedIdentifier) + .HasMaxLength(4000) + .HasColumnName("unlinked_identifier"); + + entity.Property(e => e.ValueEn) + .HasMaxLength(4000) + .HasColumnName("value_en"); + + entity.Property(e => e.ValueFi) + .HasMaxLength(4000) + .HasColumnName("value_fi"); + + entity.Property(e => e.ValueSv) .HasMaxLength(4000) - .HasColumnName("value"); + .HasColumnName("value_sv"); entity.HasOne(d => d.DimIdentifierlessData) .WithMany(p => p.InverseDimIdentifierlessData) @@ -1884,6 +2082,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnType("datetime") .HasColumnName("created"); + entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); + entity.Property(e => e.Modified) .HasColumnType("datetime") .HasColumnName("modified"); @@ -1905,7 +2105,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimKnownPersonId }) - .HasName("PK__dim_know__493EE076B9D35CA2"); + .HasName("PK__dim_know__493EE0763377A11B"); entity.ToTable("dim_known_person_dim_field_of_science"); @@ -2016,6 +2216,35 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("source_id"); }); + modelBuilder.Entity(entity => + { + entity.ToTable("dim_mined_words"); + + entity.Property(e => e.Id).HasColumnName("id"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + + entity.Property(e => e.Word) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("word"); + }); + modelBuilder.Entity(entity => { entity.ToTable("dim_name"); @@ -2028,10 +2257,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimKnownPersonIdConfirmedIdentity).HasColumnName("dim_known_person_id_confirmed_identity"); - entity.Property(e => e.DimKnownPersonidFormerNames) - .HasColumnName("dim_known_personid_former_names") - .HasDefaultValueSql("((-1))"); - entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); entity.Property(e => e.FirstNames) @@ -2064,17 +2289,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("source_project_id"); entity.HasOne(d => d.DimKnownPersonIdConfirmedIdentityNavigation) - .WithMany(p => p.DimNameDimKnownPersonIdConfirmedIdentityNavigations) + .WithMany(p => p.DimNames) .HasForeignKey(d => d.DimKnownPersonIdConfirmedIdentity) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("confirmed identity"); - entity.HasOne(d => d.DimKnownPersonidFormerNamesNavigation) - .WithMany(p => p.DimNameDimKnownPersonidFormerNamesNavigations) - .HasForeignKey(d => d.DimKnownPersonidFormerNames) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("former names"); - entity.HasOne(d => d.DimRegisteredDataSource) .WithMany(p => p.DimNames) .HasForeignKey(d => d.DimRegisteredDataSourceId) @@ -2117,7 +2336,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.Id, e.DimNewsFeedid }) - .HasName("PK__dim_news__B87E6703099306E5"); + .HasName("PK__dim_news__B87E6703D18BE696"); entity.ToTable("dim_news_item"); @@ -2623,6 +2842,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Abstract).HasColumnName("abstract"); + entity.Property(e => e.ApcFeeEur) .HasColumnType("decimal(18, 2)") .HasColumnName("apc_fee_EUR"); @@ -2715,6 +2936,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.NumberOfAuthors).HasColumnName("number_of_authors"); + entity.Property(e => e.OpenAccess) + .HasMaxLength(255) + .HasColumnName("open_access"); + entity.Property(e => e.OpenAccessCode) .HasMaxLength(255) .HasColumnName("open_access_code"); @@ -2781,6 +3006,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasMaxLength(4000) .HasColumnName("publisher_name"); + entity.Property(e => e.PublisherOpenAccessCode) + .HasMaxLength(255) + .HasColumnName("publisher_open_access_code"); + entity.Property(e => e.Report).HasColumnName("report"); entity.Property(e => e.ReportingYear).HasColumnName("reporting_year"); @@ -2854,6 +3083,60 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("publisher_name_text"); }); + modelBuilder.Entity(entity => + { + entity.ToTable("dim_purpose"); + + entity.Property(e => e.Id).HasColumnName("id"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.DescriptionEn).HasColumnName("description_en"); + + entity.Property(e => e.DescriptionFi).HasColumnName("description_fi"); + + entity.Property(e => e.DescriptionSv).HasColumnName("description_sv"); + + entity.Property(e => e.DimOrganizationId).HasColumnName("dim_organization_id"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.NameEn) + .HasMaxLength(255) + .HasColumnName("name_en"); + + entity.Property(e => e.NameFi) + .HasMaxLength(255) + .HasColumnName("name_fi"); + + entity.Property(e => e.NameSv) + .HasMaxLength(255) + .HasColumnName("name_sv"); + + entity.Property(e => e.NameUnd) + .HasMaxLength(255) + .HasColumnName("name_und"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + + entity.HasOne(d => d.DimOrganization) + .WithMany(p => p.DimPurposes) + .HasForeignKey(d => d.DimOrganizationId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKdim_purpos656541"); + }); + modelBuilder.Entity(entity => { entity.ToTable("dim_referencedata"); @@ -2874,6 +3157,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnType("datetime") .HasColumnName("created"); + entity.Property(e => e.DimReferencedataId).HasColumnName("dim_referencedata_id"); + entity.Property(e => e.Modified) .HasColumnType("datetime") .HasColumnName("modified"); @@ -2903,6 +3188,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.State) .HasMaxLength(255) .HasColumnName("state"); + + entity.HasOne(d => d.DimReferencedata) + .WithMany(p => p.InverseDimReferencedata) + .HasForeignKey(d => d.DimReferencedataId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_dim_referencedata_dim_referencedata"); }); modelBuilder.Entity(entity => @@ -3055,7 +3346,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimResearchActivityId, e.DimKeywordId }) - .HasName("PK__dim_rese__F7B536BC194A32D5"); + .HasName("PK__dim_rese__F7B536BC499552FD"); entity.ToTable("dim_research_activity_dim_keyword"); @@ -3139,11 +3430,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnType("datetime") .HasColumnName("created"); - entity.Property(e => e.DescriptionEn).HasColumnName("description_en"); + entity.Property(e => e.DescriptionEn) + .HasMaxLength(4000) + .HasColumnName("description_en"); - entity.Property(e => e.DescriptionFi).HasColumnName("description_fi"); + entity.Property(e => e.DescriptionFi) + .HasMaxLength(4000) + .HasColumnName("description_fi"); - entity.Property(e => e.DescriptionSv).HasColumnName("description_sv"); + entity.Property(e => e.DescriptionSv) + .HasMaxLength(4000) + .HasColumnName("description_sv"); entity.Property(e => e.Modified) .HasColumnType("datetime") @@ -3205,8 +3502,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimResearchDataCatalogId).HasColumnName("dim_research_data_catalog_id"); - entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); - entity.Property(e => e.GeographicCoverage) .HasMaxLength(4000) .HasColumnName("geographic_coverage"); @@ -3254,6 +3549,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnType("datetime") .HasColumnName("temporal_coverage_start"); + entity.Property(e => e.VersionInfo) + .HasMaxLength(255) + .HasColumnName("version_info"); + entity.HasOne(d => d.DimReferencedataAvailabilityNavigation) .WithMany(p => p.DimResearchDatasetDimReferencedataAvailabilityNavigations) .HasForeignKey(d => d.DimReferencedataAvailability) @@ -3274,11 +3573,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(p => p.DimResearchDatasets) .HasForeignKey(d => d.DimResearchDataCatalogId) .HasConstraintName("FKdim_resear753411"); - - entity.HasOne(d => d.DimResearchDatasetNavigation) - .WithMany(p => p.InverseDimResearchDatasetNavigation) - .HasForeignKey(d => d.DimResearchDatasetId) - .HasConstraintName("part of dataset"); }); modelBuilder.Entity(entity => @@ -3549,15 +3843,35 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsUnicode(false) .HasColumnName("email"); - entity.Property(e => e.LinkAccessPolicy) + entity.Property(e => e.LinkAccessPolicyEn) + .HasMaxLength(4000) + .IsUnicode(false) + .HasColumnName("link_access_policy_en"); + + entity.Property(e => e.LinkAccessPolicyFi) + .HasMaxLength(4000) + .IsUnicode(false) + .HasColumnName("link_access_policy_fi"); + + entity.Property(e => e.LinkAccessPolicySv) + .HasMaxLength(4000) + .IsUnicode(false) + .HasColumnName("link_access_policy_sv"); + + entity.Property(e => e.LinkAdditionalInfoEn) + .HasMaxLength(4000) + .IsUnicode(false) + .HasColumnName("link_additional_info_en"); + + entity.Property(e => e.LinkAdditionalInfoFi) .HasMaxLength(4000) .IsUnicode(false) - .HasColumnName("link_access_policy"); + .HasColumnName("link_additional_info_fi"); - entity.Property(e => e.LinkAdditionalInfo) + entity.Property(e => e.LinkAdditionalInfoSv) .HasMaxLength(4000) .IsUnicode(false) - .HasColumnName("link_additional_info"); + .HasColumnName("link_additional_info_sv"); entity.Property(e => e.LinkInternationalInfra) .HasMaxLength(4000) @@ -3646,7 +3960,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { entity.ToTable("dim_type_of_funding"); - entity.HasIndex(e => e.TypeId, "UQ__dim_type__2C000599B555DD80") + entity.HasIndex(e => e.TypeId, "UQ__dim_type__2C00059919EB8A6B") .IsUnique(); entity.Property(e => e.Id).HasColumnName("id"); @@ -3695,6 +4009,48 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("part of "); }); + modelBuilder.Entity(entity => + { + entity.ToTable("dim_user_choices"); + + entity.Property(e => e.Id).HasColumnName("id"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.DimReferencedataIdAsUserChoiceLabel).HasColumnName("dim_referencedata_id_as_user_choice_label"); + + entity.Property(e => e.DimUserProfileId).HasColumnName("dim_user_profile_id"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + + entity.Property(e => e.UserChoiceValue).HasColumnName("user_choice_value"); + + entity.HasOne(d => d.DimReferencedataIdAsUserChoiceLabelNavigation) + .WithMany(p => p.DimUserChoices) + .HasForeignKey(d => d.DimReferencedataIdAsUserChoiceLabel) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("user_selection_label"); + + entity.HasOne(d => d.DimUserProfile) + .WithMany(p => p.DimUserChoices) + .HasForeignKey(d => d.DimUserProfileId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKdim_user_c733293"); + }); + modelBuilder.Entity(entity => { entity.ToTable("dim_user_profile"); @@ -3713,6 +4069,26 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnType("datetime") .HasColumnName("modified"); + entity.Property(e => e.OrcidAccessToken) + .HasMaxLength(255) + .HasColumnName("orcid_access_token"); + + entity.Property(e => e.OrcidId) + .HasMaxLength(20) + .HasColumnName("orcid_id"); + + entity.Property(e => e.OrcidRefreshToken) + .HasMaxLength(255) + .HasColumnName("orcid_refresh_token"); + + entity.Property(e => e.OrcidTokenExpires) + .HasColumnType("datetime") + .HasColumnName("orcid_token_expires"); + + entity.Property(e => e.OrcidTokenScope) + .HasMaxLength(255) + .HasColumnName("orcid_token_scope"); + entity.Property(e => e.SourceDescription) .HasMaxLength(255) .HasColumnName("source_description"); @@ -3722,6 +4098,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasMaxLength(255) .HasColumnName("source_id"); + entity.Property(e => e.Statuscode).HasColumnName("statuscode"); + entity.HasOne(d => d.DimKnownPerson) .WithMany(p => p.DimUserProfiles) .HasForeignKey(d => d.DimKnownPersonId) @@ -3747,6 +4125,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimOrganizationId).HasColumnName("dim_organization_id"); + entity.Property(e => e.DimResearchActivityId).HasColumnName("dim_research_activity_id"); + entity.Property(e => e.DimResearchCommunityId).HasColumnName("dim_research_community_id"); entity.Property(e => e.DimResearchDataCatalogId).HasColumnName("dim_research_data_catalog_id"); @@ -3802,6 +4182,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(d => d.DimOrganizationId) .HasConstraintName("language specific homepage"); + entity.HasOne(d => d.DimResearchActivity) + .WithMany(p => p.DimWebLinks) + .HasForeignKey(d => d.DimResearchActivityId) + .HasConstraintName("FKdim_web_li272158"); + entity.HasOne(d => d.DimResearchCommunity) .WithMany(p => p.DimWebLinks) .HasForeignKey(d => d.DimResearchCommunityId) @@ -3818,10 +4203,36 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("fairdata_weblink"); }); + modelBuilder.Entity(entity => + { + entity.ToTable("dim_word_cluster"); + + entity.Property(e => e.Id) + .ValueGeneratedNever() + .HasColumnName("id"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionId, e.DimOrganizationId, e.DimDateId, e.DimNameId, e.DimPublicationId, e.DimGeoId, e.DimInfrastructureId, e.DimNewsFeedId, e.DimResearchDatasetId, e.DimResearchDataCatalogId, e.DimIdentifierlessDataId, e.DimResearchActivityId, e.DimResearchCommunityId, e.DimReferencedataActorRoleId }) - .HasName("PK__fact_con__B7D7E1B5CCE1D2AA"); + .HasName("PK__fact_con__B7D7E1B5CFA48C5A"); entity.ToTable("fact_contribution"); @@ -3962,7 +4373,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { - entity.HasKey(e => new { e.DimUserProfileId, e.DimFieldDisplaySettingsId, e.DimNameId, e.DimWebLinkId, e.DimFundingDecisionId, e.DimPublicationId, e.DimPidId, e.DimPidIdOrcidPutCode, e.DimResearchActivityId, e.DimEventId, e.DimEducationId, e.DimCompetenceId, e.DimResearchCommunityId, e.DimTelephoneNumberId, e.DimEmailAddrressId, e.DimResearcherDescriptionId, e.DimIdentifierlessDataId, e.DimOrcidPublicationId, e.DimKeywordId, e.DimAffiliationId, e.DimResearcherToResearchCommunityId, e.DimFieldOfScienceId }); + entity.HasKey(e => new { e.DimUserProfileId, e.DimFieldDisplaySettingsId, e.DimNameId, e.DimWebLinkId, e.DimFundingDecisionId, e.DimPublicationId, e.DimPidId, e.DimPidIdOrcidPutCode, e.DimResearchActivityId, e.DimEventId, e.DimEducationId, e.DimCompetenceId, e.DimResearchCommunityId, e.DimTelephoneNumberId, e.DimEmailAddrressId, e.DimResearcherDescriptionId, e.DimIdentifierlessDataId, e.DimOrcidPublicationId, e.DimKeywordId, e.DimAffiliationId, e.DimResearcherToResearchCommunityId, e.DimFieldOfScienceId, e.DimResearchDatasetId, e.DimRegisteredDataSourceId }) + .HasName("PK__fact_fie__A1BF31AA946882DF"); entity.ToTable("fact_field_values"); @@ -4008,9 +4420,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimResearcherToResearchCommunityId).HasColumnName("dim_researcher_to_research_community_id"); - entity.Property(e => e.DimFieldOfScienceId) - .HasColumnName("dim_field_of_science_id") - .HasDefaultValueSql("((-1))"); + entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); + + entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); + + entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); entity.Property(e => e.Created) .HasColumnType("datetime") @@ -4037,7 +4451,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(p => p.FactFieldValues) .HasForeignKey(d => d.DimAffiliationId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKfact_field236121"); + .HasConstraintName("FKfact_field816276"); entity.HasOne(d => d.DimCompetence) .WithMany(p => p.FactFieldValues) @@ -4073,7 +4487,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(p => p.FactFieldValues) .HasForeignKey(d => d.DimFieldOfScienceId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKfact_field1234567"); + .HasConstraintName("FKfact_field339072"); entity.HasOne(d => d.DimFundingDecision) .WithMany(p => p.FactFieldValues) @@ -4123,6 +4537,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("displayed publications"); + entity.HasOne(d => d.DimRegisteredDataSource) + .WithMany(p => p.FactFieldValues) + .HasForeignKey(d => d.DimRegisteredDataSourceId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_field346220"); + entity.HasOne(d => d.DimResearchActivity) .WithMany(p => p.FactFieldValues) .HasForeignKey(d => d.DimResearchActivityId) @@ -4135,6 +4555,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FKfact_field750435"); + entity.HasOne(d => d.DimResearchDataset) + .WithMany(p => p.FactFieldValues) + .HasForeignKey(d => d.DimResearchDatasetId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_field998843"); + entity.HasOne(d => d.DimResearcherDescription) .WithMany(p => p.FactFieldValues) .HasForeignKey(d => d.DimResearcherDescriptionId) @@ -4169,7 +4595,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimKeywordId, e.DimServiceId, e.DimServicePointId, e.DimInfrastructureId }) - .HasName("PK__fact_inf__3C29B680CB1233FF"); + .HasName("PK__fact_inf__3C29B680609CD02A"); entity.ToTable("fact_infra_keywords"); @@ -4226,7 +4652,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimPublicationChannelId, e.DimReferencedataId, e.Year }) - .HasName("PK__fact_juf__0E099E4BEF49D978"); + .HasName("PK__fact_juf__0E099E4B753B21B6"); entity.ToTable("fact_jufo_class_codes_for_pub_channels"); @@ -4252,7 +4678,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimOrganizationId, e.DimGeoId, e.DimInfrastructureId, e.DimServiceId, e.DimServicePointId, e.DimDateIdStart, e.DimDateIdEnd }) - .HasName("PK__fact_upk__850A8E304254F728"); + .HasName("PK__fact_upk__850A8E30D5F346F4"); entity.ToTable("fact_upkeep"); diff --git a/aspnetcore/src/api/Program.cs b/aspnetcore/src/api/Program.cs index 61d533f8..98038a28 100644 --- a/aspnetcore/src/api/Program.cs +++ b/aspnetcore/src/api/Program.cs @@ -1,23 +1,41 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - +using Serilog; + namespace api { public class Program { public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .CreateLogger(); + + try + { + Log.Information("Application starting up"); + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "The application start failed."); + } + finally + { + Log.CloseAndFlush(); + } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .UseSerilog() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/aspnetcore/src/api/Properties/launchSettings.json b/aspnetcore/src/api/Properties/launchSettings.json index b37fae77..7a447f75 100644 --- a/aspnetcore/src/api/Properties/launchSettings.json +++ b/aspnetcore/src/api/Properties/launchSettings.json @@ -20,7 +20,6 @@ "applicationUrl": "http://localhost:6001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - //"OAUTH__AUTHORITY": "https://" } }, "api": { diff --git a/aspnetcore/src/api/README.md b/aspnetcore/src/api/README.md new file mode 100644 index 00000000..c0c86a63 --- /dev/null +++ b/aspnetcore/src/api/README.md @@ -0,0 +1,19 @@ +# Mydata API application +This application implements API functionality for Researchfi Mydata. + +# Environment variables +- ASPNETCORE_ENVIRONMENT +- ASPNETCORE_URLS +- ASPNETCORE_FORWARDEDHEADERS_ENABLED +- ELASTICSEARCH__ENABLED +- ELASTICSEARCH__URL +- ELASTICSEARCH__USERNAME +- ELASTICSEARCH__PASSWORD +- CONNECTIONSTRINGS__DEFAULTCONNECTION +- ORCID__API +- KEYCLOAK__REALM +- KEYCLOAK__TOKENENDPOINT +- KEYCLOAK__ORCIDTOKENENDPOINT +- KEYCLOAK__ADMIN__CLIENTID +- KEYCLOAK__ADMIN__CLIENTSECRET +- KEYCLOAK__ADMIN__REALMUSERSENDPOINT diff --git a/aspnetcore/src/api/Services/DataSourceHelperService.cs b/aspnetcore/src/api/Services/DataSourceHelperService.cs new file mode 100644 index 00000000..fd527b36 --- /dev/null +++ b/aspnetcore/src/api/Services/DataSourceHelperService.cs @@ -0,0 +1,107 @@ +namespace api.Services +{ + /* + * DataSourceHelperService contains often needed values + * from DimRegisteredDataSource and DimOrganization. + * - ORCID + * - TTV + * + * Idea is to collect the values into this service during application startup. + * In code the values can be used from this service instead of querying the DB again. + */ + public class DataSourceHelperService : IDataSourceHelperService + { + private int dimRegisteredDataSourceId_ORCID; + private string dimRegisteredDataSourceName_ORCID; + private int dimOrganizationId_ORCID; + private string dimOrganizationNameFi_ORCID; + private string dimOrganizationNameEn_ORCID; + private string dimOrganizationNameSv_ORCID; + private int dimRegisteredDataSourceId_TTV; + private string dimRegisteredDataSourceName_TTV; + private int dimOrganizationId_TTV; + private string dimOrganizationNameFi_TTV; + private string dimOrganizationNameEn_TTV; + private string dimOrganizationNameSv_TTV; + private int dimPurposeId_TTV; + + public int DimRegisteredDataSourceId_ORCID + { + get { return this.dimRegisteredDataSourceId_ORCID; } + set { this.dimRegisteredDataSourceId_ORCID = value; } + } + + public string DimRegisteredDataSourceName_ORCID + { + get { return this.dimRegisteredDataSourceName_ORCID; } + set { this.dimRegisteredDataSourceName_ORCID = value; } + } + + public int DimOrganizationId_ORCID + { + get { return this.dimOrganizationId_ORCID; } + set { this.dimOrganizationId_ORCID = value; } + } + + public string DimOrganizationNameFi_ORCID + { + get { return this.dimOrganizationNameFi_ORCID; } + set { this.dimOrganizationNameFi_ORCID = value; } + } + + public string DimOrganizationNameEn_ORCID + { + get { return this.dimOrganizationNameEn_ORCID; } + set { this.dimOrganizationNameEn_ORCID = value; } + } + + public string DimOrganizationNameSv_ORCID + { + get { return this.dimOrganizationNameSv_ORCID; } + set { this.dimOrganizationNameSv_ORCID = value; } + } + + + public int DimRegisteredDataSourceId_TTV + { + get { return this.dimRegisteredDataSourceId_TTV; } + set { this.dimRegisteredDataSourceId_TTV = value; } + } + + public string DimRegisteredDataSourceName_TTV + { + get { return this.dimRegisteredDataSourceName_TTV; } + set { this.dimRegisteredDataSourceName_TTV = value; } + } + + public int DimOrganizationId_TTV + { + get { return this.dimOrganizationId_TTV; } + set { this.dimOrganizationId_TTV = value; } + } + + public string DimOrganizationNameFi_TTV + { + get { return this.dimOrganizationNameFi_TTV; } + set { this.dimOrganizationNameFi_TTV = value; } + } + + public string DimOrganizationNameEn_TTV + { + get { return this.dimOrganizationNameEn_TTV; } + set { this.dimOrganizationNameEn_TTV = value; } + } + + public string DimOrganizationNameSv_TTV + { + get { return this.dimOrganizationNameSv_TTV; } + set { this.dimOrganizationNameSv_TTV = value; } + } + + public int DimPurposeId_TTV + { + get { return this.dimPurposeId_TTV; } + set { this.dimPurposeId_TTV = value; } + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/DemoDataService.cs b/aspnetcore/src/api/Services/DemoDataService.cs deleted file mode 100644 index 35572f9c..00000000 --- a/aspnetcore/src/api/Services/DemoDataService.cs +++ /dev/null @@ -1,880 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Models; -using api.Models.Ttv; -using Microsoft.EntityFrameworkCore; - -namespace api.Services -{ - /* - * DemoDataService adds demo data to each profile. - * This service is used in summer 2021 demo and must be disabled after that. - */ - public class DemoDataService - { - private readonly TtvContext _ttvContext; - private readonly UserProfileService _userProfileService; - private readonly string DemoOrganization1Name = "Yliopisto A"; - private readonly string DemoOrganization2Name = "Tutkimuslaitos X"; - private readonly string DemoOrganization3Name = Constants.SourceIdentifiers.TIEDEJATUTKIMUS; - private readonly string DemoOrganization1DataSourceName = "Testidata"; - private readonly string DemoOrganization2DataSourceName = "Testidata"; - private readonly string DemoOrganization3DataSourceName = Constants.SourceIdentifiers.TIEDEJATUTKIMUS; - private readonly string DemoOrganization1FieldOfScience1 = "Fysiikka"; - private readonly string DemoOrganization1FieldOfScience2 = "Historia"; - private readonly string DemoOrganization2FieldOfScience1 = "Yleislääketiede"; - private readonly string DemoOrganization2FieldOfScience2 = "Sisätaudit ja muut kliiniset lääketieteet"; - - public DemoDataService(TtvContext ttvContext, UserProfileService userProfileService) - { - _ttvContext = ttvContext; - _userProfileService = userProfileService; - } - - public string GetDemoOrganization1Name() - { - return this.DemoOrganization1Name; - } - - public string GetDemoOrganization2Name() - { - return this.DemoOrganization2Name; - } - - public string GetDemoOrganization3Name() - { - return this.DemoOrganization3Name; - } - - public DimOrganization GetOrganization1() - { - return _ttvContext.DimOrganizations.FirstOrDefault(org => org.SourceId == Constants.SourceIdentifiers.DEMO && org.NameFi == this.DemoOrganization1Name); - } - - public DimOrganization GetOrganization2() - { - return _ttvContext.DimOrganizations.FirstOrDefault(org => org.SourceId == Constants.SourceIdentifiers.DEMO && org.NameFi == this.DemoOrganization2Name); - } - - public DimOrganization GetOrganization3() - { - return _ttvContext.DimOrganizations.FirstOrDefault(org => org.SourceId == Constants.SourceIdentifiers.DEMO && org.NameFi == this.DemoOrganization3Name); - } - - public DimRegisteredDataSource GetOrganization1RegisteredDataSource() - { - var organization1 = this.GetOrganization1(); - return _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefault(drds => drds.DimOrganizationId == organization1.Id && drds.Name == this.DemoOrganization1DataSourceName && drds.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public DimRegisteredDataSource GetOrganization2RegisteredDataSource() - { - var organization2 = this.GetOrganization2(); - return _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefault(drds => drds.DimOrganizationId == organization2.Id && drds.Name == this.DemoOrganization2DataSourceName && drds.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public DimRegisteredDataSource GetOrganization3RegisteredDataSource() - { - var organization3 = this.GetOrganization3(); - return _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefault(drds => drds.DimOrganizationId == organization3.Id && drds.Name == this.DemoOrganization3DataSourceName && drds.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public void AddOrganizations() - { - // Organization 1 - var organization1 = this.GetOrganization1(); - if (organization1 == null) - { - organization1 = new DimOrganization() - { - DimSectorid = -1, - NameFi = this.DemoOrganization1Name, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = -1 - }; - _ttvContext.DimOrganizations.Add(organization1); - } - - // Organization 2 - var organization2 = this.GetOrganization2(); - if (organization2 == null) - { - organization2 = new DimOrganization() - { - DimSectorid = -1, - NameFi = this.DemoOrganization2Name, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = -1 - }; - _ttvContext.DimOrganizations.Add(organization2); - } - - // Organization 3 - var organization3 = this.GetOrganization3(); - if (organization3 == null) - { - organization3 = new DimOrganization() - { - DimSectorid = -1, - NameFi = this.DemoOrganization3Name, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = -1 - }; - _ttvContext.DimOrganizations.Add(organization3); - } - - _ttvContext.SaveChanges(); - } - - - public void AddRegisteredDatasources() - { - // Registered data source 1 - var organization1 = this.GetOrganization1(); - var registeredDatasourceOrg1 = this.GetOrganization1RegisteredDataSource(); - if (registeredDatasourceOrg1 == null) - { - registeredDatasourceOrg1 = new DimRegisteredDataSource() - { - Name = this.DemoOrganization1DataSourceName, - DimOrganizationId = organization1.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimRegisteredDataSources.Add(registeredDatasourceOrg1); - } - - // Registered data source 2 - var organization2 = this.GetOrganization2(); - var registeredDatasourceOrg2 = this.GetOrganization2RegisteredDataSource(); - if (registeredDatasourceOrg2 == null) - { - registeredDatasourceOrg2 = new DimRegisteredDataSource() - { - Name = this.DemoOrganization2DataSourceName, - DimOrganizationId = organization2.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimRegisteredDataSources.Add(registeredDatasourceOrg2); - } - - // Registered data source 3 - var organization3 = this.GetOrganization3(); - var registeredDatasourceOrg3 = this.GetOrganization3RegisteredDataSource(); - if (registeredDatasourceOrg3 == null) - { - registeredDatasourceOrg3 = new DimRegisteredDataSource() - { - Name = this.DemoOrganization3DataSourceName, - DimOrganizationId = organization3.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimRegisteredDataSources.Add(registeredDatasourceOrg3); - } - - _ttvContext.SaveChanges(); - } - - - public void AddReferenceData() - { - var referenceData = _ttvContext.DimReferencedata.FirstOrDefault(dr => dr.SourceId == Constants.SourceIdentifiers.DEMO && dr.NameFi == "Työsuhde"); - if (referenceData == null) - { - referenceData = new DimReferencedatum() - { - CodeScheme = "", - CodeValue = "", - NameFi = "Työsuhde", - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimReferencedata.Add(referenceData); - _ttvContext.SaveChanges(); - } - } - - - //public void AddResearchCommunities() - //{ - // var registeredDatasourceOrg1 = this.GetOrganization1RegisteredDataSource(); - // var registeredDatasourceOrg2 = this.GetOrganization2RegisteredDataSource(); - - // _ttvContext.DimResearchCommunities.Add( - // new DimResearchCommunity() - // { - // NameFi = "Tutkimuskeskus A", - // SourceId = Constants.SourceIdentifiers.DEMO, - // SourceDescription = Constants.SourceDescriptions.PROFILE_API, - // Created = DateTime.Now, - // DimRegisteredDataSourceId = registeredDatasourceOrg1.Id - // } - // ); - // _ttvContext.DimResearchCommunities.Add( - // new DimResearchCommunity() - // { - // NameFi = "Tutkimuslaitos X", - // SourceId = Constants.SourceIdentifiers.DEMO, - // SourceDescription = Constants.SourceDescriptions.PROFILE_API, - // Created = DateTime.Now, - // DimRegisteredDataSourceId = registeredDatasourceOrg2.Id - // } - // ); - // _ttvContext.SaveChanges(); - //} - - - public void AddFieldsOfScience() - { - var fieldsOfScienceNames = new List { - this.DemoOrganization1FieldOfScience1, - this.DemoOrganization1FieldOfScience2, - this.DemoOrganization2FieldOfScience1, - this.DemoOrganization2FieldOfScience2 - }; - foreach (string fieldOfScienceName in fieldsOfScienceNames) - { - var dimFieldOfScience = _ttvContext.DimFieldOfSciences.FirstOrDefault(dfos => dfos.NameFi == fieldOfScienceName && dfos.SourceId == Constants.SourceIdentifiers.DEMO); - - if (dimFieldOfScience == null) - { - dimFieldOfScience = new DimFieldOfScience() - { - FieldId = " ", - NameFi = fieldOfScienceName, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimFieldOfSciences.Add(dimFieldOfScience); - } - } - _ttvContext.SaveChanges(); - } - - - public void InitDemo() - { - this.AddOrganizations(); - this.AddRegisteredDatasources(); - this.AddReferenceData(); // DimAffiliation.PositionCode => DimReferenceData - //this.AddResearchCommunities(); - this.AddFieldsOfScience(); - } - - - public async Task GetOrganization1Async() - { - return await _ttvContext.DimOrganizations.AsNoTracking().FirstOrDefaultAsync(org => org.NameFi == this.DemoOrganization1Name && org.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public async Task GetOrganization2Async() - { - return await _ttvContext.DimOrganizations.AsNoTracking().FirstOrDefaultAsync(org => org.NameFi == this.DemoOrganization2Name && org.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public async Task GetOrganization3Async() - { - return await _ttvContext.DimOrganizations.AsNoTracking().FirstOrDefaultAsync(org => org.NameFi == this.DemoOrganization3Name && org.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public async Task GetOrganization1RegisteredDataSourceAsync() - { - var organization1 = await this.GetOrganization1Async(); - return await _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefaultAsync(drds => drds.DimOrganizationId == organization1.Id && drds.Name == this.DemoOrganization1DataSourceName && drds.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public async Task GetOrganization2RegisteredDataSourceAsync() - { - var organization2 = await this.GetOrganization2Async(); - return await _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefaultAsync(drds => drds.DimOrganizationId == organization2.Id && drds.Name == this.DemoOrganization2DataSourceName && drds.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public async Task GetOrganization3RegisteredDataSourceAsync() - { - var organization3 = await this.GetOrganization3Async(); - return await _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefaultAsync(drds => drds.DimOrganizationId == organization3.Id && drds.Name == this.DemoOrganization3DataSourceName && drds.SourceId == Constants.SourceIdentifiers.DEMO); - } - - public async Task AddDemoDataToUserProfile(DimUserProfile dimUserProfile) - { - var organization1 = await this.GetOrganization1Async(); - var organization2 = await this.GetOrganization2Async(); - var organization1RegisteredDataSource = await this.GetOrganization1RegisteredDataSourceAsync(); - var organization2RegisteredDataSource = await this.GetOrganization2RegisteredDataSourceAsync(); - - // Name - var dimFieldDisplaySettings_name_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_NAME); - var dimNameOrganization1 = new DimName() - { - FirstNames = "Tuisku", - LastName = "Tutkija", - DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, - DimKnownPersonidFormerNames = -1, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id - }; - _ttvContext.DimNames.Add(dimNameOrganization1); - - var dimFieldDisplaySettings_name_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_NAME); - var dimNameOrganization2 = new DimName() - { - FirstNames = "Ami", - LastName = "Asiantuntija", - DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, - DimKnownPersonidFormerNames = -1, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id - }; - _ttvContext.DimNames.Add(dimNameOrganization2); - await _ttvContext.SaveChangesAsync(); - - var factFieldValue_name_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_name_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_name_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_name_Organization1.Id; - factFieldValue_name_Organization1.DimNameId = dimNameOrganization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_name_Organization1); - var factFieldValue_name_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_name_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_name_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_name_Organization2.Id; - factFieldValue_name_Organization2.DimNameId = dimNameOrganization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_name_Organization2); - await _ttvContext.SaveChangesAsync(); - - - // Other names - var dimFieldDisplaySettings_othername_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_OTHER_NAMES); - var dimOtherNameOrganization1_1 = new DimName() - { - FullName = "T. Tutkija", - DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, - DimKnownPersonidFormerNames = -1, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id - }; - _ttvContext.DimNames.Add(dimOtherNameOrganization1_1); - var dimOtherNameOrganization1_2 = new DimName() - { - FullName = "T.A. Tutkija", - DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, - DimKnownPersonidFormerNames = -1, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id - }; - _ttvContext.DimNames.Add(dimOtherNameOrganization1_2); - var dimFieldDisplaySettings_othername_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_OTHER_NAMES); - var dimOtherNameOrganization2 = new DimName() - { - FullName = "Tuisku Tutkija", - DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, - DimKnownPersonidFormerNames = -1, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id - }; - _ttvContext.DimNames.Add(dimOtherNameOrganization2); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_othername_Organization1_1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_othername_Organization1_1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_othername_Organization1_1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_othername_Organization1.Id; - factFieldValue_othername_Organization1_1.DimNameId = dimOtherNameOrganization1_1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_othername_Organization1_1); - var factFieldValue_othername_Organization1_2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_othername_Organization1_2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_othername_Organization1_2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_othername_Organization1.Id; - factFieldValue_othername_Organization1_2.DimNameId = dimOtherNameOrganization1_2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_othername_Organization1_2); - var factFieldValue_othername_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_othername_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_othername_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_othername_Organization2.Id; - factFieldValue_othername_Organization2.DimNameId = dimOtherNameOrganization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_othername_Organization2); - await _ttvContext.SaveChangesAsync(); - - - // External identifiers (DimPid) - var dimFieldDisplaySettings_externalIdentifier_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER); - var dimPid_Organization1 = _userProfileService.GetEmptyDimPid(); - dimPid_Organization1.PidContent = "O-2000-1000"; - dimPid_Organization1.PidType = "ResearcherID"; - dimPid_Organization1.DimKnownPersonId = dimUserProfile.DimKnownPersonId; - dimPid_Organization1.SourceId = Constants.SourceIdentifiers.DEMO; - _ttvContext.DimPids.Add(dimPid_Organization1); - var dimFieldDisplaySettings_externalIdentifier_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER); - var dimPid_Organization2 = _userProfileService.GetEmptyDimPid(); - dimPid_Organization2.PidContent = "https://isni.org/isni/0000000100020003"; - dimPid_Organization2.PidType = "ISNI"; - dimPid_Organization2.DimKnownPersonId = dimUserProfile.DimKnownPersonId; - dimPid_Organization2.SourceId = Constants.SourceIdentifiers.DEMO; - dimPid_Organization2.SourceDescription = Constants.SourceDescriptions.PROFILE_API; - dimPid_Organization2.Created = DateTime.Now; - _ttvContext.DimPids.Add(dimPid_Organization2); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_externalIdentifier_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_externalIdentifier_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_externalIdentifier_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_externalIdentifier_Organization1.Id; - factFieldValue_externalIdentifier_Organization1.DimPidId = dimPid_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_externalIdentifier_Organization1); - var factFieldValue_externalIdentifier_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_externalIdentifier_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_externalIdentifier_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_externalIdentifier_Organization2.Id; - factFieldValue_externalIdentifier_Organization2.DimPidId = dimPid_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_externalIdentifier_Organization2); - await _ttvContext.SaveChangesAsync(); - - - // Researcher description - var dimFieldDisplaySettings_researcherDescription_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION); - var dimResearcherDescription_Organization1 = new DimResearcherDescription() - { - ResearchDescriptionFi = "Tutkimuksen kuvausta suomeksi. Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - ResearchDescriptionEn = "Description of my research in English. Duis ullamcorper sem in sapien pretium bibendum. Vestibulum ex dui, volutpat commodo condimentum sed, lobortis at justo.", - ResearchDescriptionSv = "Beskrivning av forskningen på svenska. Fusce in lorem tempor, feugiat nunc ut, consectetur erat. Integer purus sem, hendrerit at bibendum vel, laoreet nec tellus.", - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id, - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimResearcherDescriptions.Add(dimResearcherDescription_Organization1); - var dimFieldDisplaySettings_researcherDescription_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION); - var dimResearcherDescription_Organization2 = new DimResearcherDescription() - { - ResearchDescriptionFi = "Tutkimuksen kuvausta suomeksi. Duis finibus velit rutrum euismod scelerisque. Praesent sit amet fermentum ex. Donec vitae tellus eu nisl dignissim laoreet.", - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id, - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimResearcherDescriptions.Add(dimResearcherDescription_Organization2); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_researcherDescription_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_researcherDescription_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_researcherDescription_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_researcherDescription_Organization1.Id; - factFieldValue_researcherDescription_Organization1.DimResearcherDescriptionId = dimResearcherDescription_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_researcherDescription_Organization1); - var factFieldValue_researcherDescription_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_researcherDescription_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_researcherDescription_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_researcherDescription_Organization2.Id; - factFieldValue_researcherDescription_Organization2.DimResearcherDescriptionId = dimResearcherDescription_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_researcherDescription_Organization2); - await _ttvContext.SaveChangesAsync(); - - - // Keywords - var dimFieldDisplaySettings_keyword_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_KEYWORD); - var dimKeyword1_Organization1 = new DimKeyword() - { - Keyword = "digitalisaatio", - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - var dimKeyword2_Organization1 = new DimKeyword() - { - Keyword = "aerosolit", - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - var dimKeyword3_Organization1 = new DimKeyword() - { - Keyword = "sisätaudit", - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - var dimKeyword4_Organization1 = new DimKeyword() - { - Keyword = "Suomen historia", - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimKeywords.Add(dimKeyword1_Organization1); - _ttvContext.DimKeywords.Add(dimKeyword2_Organization1); - _ttvContext.DimKeywords.Add(dimKeyword3_Organization1); - _ttvContext.DimKeywords.Add(dimKeyword4_Organization1); - var dimFieldDisplaySettings_keyword_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_KEYWORD); - var dimKeyword1_Organization2 = new DimKeyword() - { - Keyword = "digitalization", - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - var dimKeyword2_Organization2 = new DimKeyword() - { - Keyword = "aerosols", - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - var dimKeyword3_Organization2 = new DimKeyword() - { - Keyword = "internal medicine", - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - var dimKeyword4_Organization2 = new DimKeyword() - { - Keyword = "history of Finland", - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimKeywords.Add(dimKeyword1_Organization2); - _ttvContext.DimKeywords.Add(dimKeyword2_Organization2); - _ttvContext.DimKeywords.Add(dimKeyword3_Organization2); - _ttvContext.DimKeywords.Add(dimKeyword4_Organization2); - await _ttvContext.SaveChangesAsync(); - - var factFieldValue_keyword1_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword1_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword1_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization1.Id; - factFieldValue_keyword1_Organization1.DimKeywordId = dimKeyword1_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword1_Organization1); - var factFieldValue_keyword2_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword2_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword2_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization1.Id; - factFieldValue_keyword2_Organization1.DimKeywordId = dimKeyword2_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword2_Organization1); - var factFieldValue_keyword3_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword3_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword3_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization1.Id; - factFieldValue_keyword3_Organization1.DimKeywordId = dimKeyword3_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword3_Organization1); - var factFieldValue_keyword4_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword4_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword4_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization1.Id; - factFieldValue_keyword4_Organization1.DimKeywordId = dimKeyword4_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword4_Organization1); - var factFieldValue_keyword1_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword1_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword1_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization2.Id; - factFieldValue_keyword1_Organization2.DimKeywordId = dimKeyword1_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword1_Organization2); - var factFieldValue_keyword2_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword2_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword2_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization2.Id; - factFieldValue_keyword2_Organization2.DimKeywordId = dimKeyword2_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword2_Organization2); - var factFieldValue_keyword3_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword3_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword3_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization2.Id; - factFieldValue_keyword3_Organization2.DimKeywordId = dimKeyword3_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword3_Organization2); - var factFieldValue_keyword4_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_keyword4_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_keyword4_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_keyword_Organization2.Id; - factFieldValue_keyword4_Organization2.DimKeywordId = dimKeyword4_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_keyword4_Organization2); - await _ttvContext.SaveChangesAsync(); - - - // Fields of science org1 - var dimFieldDisplaySettings_fieldOfScience_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE); - var dimFieldOfScience1_Organization1 = _ttvContext.DimFieldOfSciences.FirstOrDefault(dfos => dfos.NameFi == this.DemoOrganization1FieldOfScience1 && dfos.SourceId == Constants.SourceIdentifiers.DEMO); - var dimFieldOfScience2_Organization1 = _ttvContext.DimFieldOfSciences.FirstOrDefault(dfos => dfos.NameFi == this.DemoOrganization1FieldOfScience2 && dfos.SourceId == Constants.SourceIdentifiers.DEMO); - var factFieldValue_fieldOfScience1_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_fieldOfScience1_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_fieldOfScience1_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_fieldOfScience_Organization1.Id; - factFieldValue_fieldOfScience1_Organization1.DimFieldOfScienceId = dimFieldOfScience1_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_fieldOfScience1_Organization1); - var factFieldValue_fieldOfScience2_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_fieldOfScience2_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_fieldOfScience2_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_fieldOfScience_Organization1.Id; - factFieldValue_fieldOfScience2_Organization1.DimFieldOfScienceId = dimFieldOfScience2_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_fieldOfScience2_Organization1); - - // Fields of science org2 - var dimFieldDisplaySettings_fieldOfScience_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE); - var dimFieldOfScience1_Organization2 = _ttvContext.DimFieldOfSciences.FirstOrDefault(dfos => dfos.NameFi == this.DemoOrganization2FieldOfScience1 && dfos.SourceId == Constants.SourceIdentifiers.DEMO); - var dimFieldOfScience2_Organization2 = _ttvContext.DimFieldOfSciences.FirstOrDefault(dfos => dfos.NameFi == this.DemoOrganization2FieldOfScience2 && dfos.SourceId == Constants.SourceIdentifiers.DEMO); - var factFieldValue_fieldOfScience1_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_fieldOfScience1_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_fieldOfScience1_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_fieldOfScience_Organization2.Id; - factFieldValue_fieldOfScience1_Organization2.DimFieldOfScienceId = dimFieldOfScience1_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_fieldOfScience1_Organization2); - var factFieldValue_fieldOfScience2_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_fieldOfScience2_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_fieldOfScience2_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_fieldOfScience_Organization2.Id; - factFieldValue_fieldOfScience2_Organization2.DimFieldOfScienceId = dimFieldOfScience2_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_fieldOfScience2_Organization2); - - await _ttvContext.SaveChangesAsync(); - - - - //var dimFieldDisplaySettings_fieldOfScience_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE); - //var dimFieldOfScience1_Organization2 = new DimFieldOfScience() - //{ - // NameFi = "Yleislääketiede", - // FieldId = "", - // SourceId = Constants.SourceIdentifiers.DEMO, - // Created = DateTime.Now - //}; - //var dimFieldOfScience2_Organization2 = new DimFieldOfScience() - //{ - // NameFi = "sisätaudit ja muut kliiniset lääketieteet", - // FieldId = "", - // SourceId = Constants.SourceIdentifiers.DEMO, - // Created = DateTime.Now - //}; - //_ttvContext.DimFieldOfSciences.Add(dimFieldOfScience1_Organization2); - //_ttvContext.DimFieldOfSciences.Add(dimFieldOfScience2_Organization2); - //await _ttvContext.SaveChangesAsync(); - - //var factFieldValue_fieldOfScience1_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - //factFieldValue_fieldOfScience1_Organization1.DimUserProfileId = dimUserProfile.Id; - //factFieldValue_fieldOfScience1_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_fieldOfScience_Organization1.Id; - //factFieldValue_fieldOfScience1_Organization1.DimFieldOfScienceId = dimFieldOfScience1_Organization1.Id; - //_ttvContext.FactFieldValues.Add(factFieldValue_fieldOfScience1_Organization1); - - - // Email - var dimFieldDisplaySettings_email_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS); - var dimEmail_Organization1 = new DimEmailAddrress() - { - Email = "tuisku.tutkija@yliopisto.fi", - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimEmailAddrresses.Add(dimEmail_Organization1); - var dimFieldDisplaySettings_email_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS); - var dimEmail_Organization2 = new DimEmailAddrress() - { - Email = "ami.asiantuntija@tutkimuslaitos.fi", - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimEmailAddrresses.Add(dimEmail_Organization2); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_emails_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_emails_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_emails_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_email_Organization1.Id; - factFieldValue_emails_Organization1.DimEmailAddrressId = dimEmail_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_emails_Organization1); - var factFieldValue_emails_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_emails_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_emails_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_email_Organization2.Id; - factFieldValue_emails_Organization2.DimEmailAddrressId = dimEmail_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_emails_Organization2); - await _ttvContext.SaveChangesAsync(); - - - // Telephone number - var dimFieldDisplaySettings_telephone_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER); - var dimTelephone_Organization1 = new DimTelephoneNumber() - { - TelephoneNumber = "+35899999999", - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimTelephoneNumbers.Add(dimTelephone_Organization1); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_telephone_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_telephone_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_telephone_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_telephone_Organization1.Id; - factFieldValue_telephone_Organization1.DimTelephoneNumberId = dimTelephone_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_telephone_Organization1); - await _ttvContext.SaveChangesAsync(); - - - // Web link - var dimFieldDisplaySettings_weblink_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_WEB_LINK); - var dimWeblink_Organization1 = new DimWebLink() - { - Url = "https://tutkijanomaverkkosivu.yliopisto.fii", - LinkLabel = "Tutkijan oma verkkosivu", - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimWebLinks.Add(dimWeblink_Organization1); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_weblink_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_weblink_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_weblink_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_weblink_Organization1.Id; - factFieldValue_weblink_Organization1.DimWebLinkId = dimWeblink_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_weblink_Organization1); - await _ttvContext.SaveChangesAsync(); - - - // Role in research community - - - - // Affiliation - var affiliationType = await _ttvContext.DimReferencedata.AsNoTracking().FirstOrDefaultAsync(drd => drd.SourceId == Constants.SourceIdentifiers.DEMO && drd.NameFi == "Työsuhde"); - var dimFieldDisplaySettings_affiliation_Organization1 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization1Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION); - var dimStartDate_affiliation_organization1 = await _ttvContext.DimDates.AsNoTracking().FirstOrDefaultAsync(dd => dd.Year == 2020 && dd.Month == 1 && dd.Day == 1); - if (dimStartDate_affiliation_organization1 == null) - { - dimStartDate_affiliation_organization1 = new DimDate() - { - Year = 2020, - Month = 1, - Day = 1, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(dimStartDate_affiliation_organization1); - await _ttvContext.SaveChangesAsync(); - } - var dimAffiliation_Organization1 = new DimAffiliation() - { - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - DimOrganizationId = organization1.Id, - StartDate = dimStartDate_affiliation_organization1.Id, - AffiliationType = affiliationType.Id, - PositionNameFi = "Akatemiatutkija", - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization1RegisteredDataSource.Id - }; - _ttvContext.DimAffiliations.Add(dimAffiliation_Organization1); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_affiliation_Organization1 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_affiliation_Organization1.DimUserProfileId = dimUserProfile.Id; - factFieldValue_affiliation_Organization1.DimFieldDisplaySettingsId = dimFieldDisplaySettings_affiliation_Organization1.Id; - factFieldValue_affiliation_Organization1.DimAffiliationId = dimAffiliation_Organization1.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_affiliation_Organization1); - await _ttvContext.SaveChangesAsync(); - var dimFieldDisplaySettings_affiliation_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION); - var dimStartDate_affiliation_organization2 = await _ttvContext.DimDates.AsNoTracking().FirstOrDefaultAsync(dd => dd.Year == 2016 && dd.Month == 1 && dd.Day == 1); - if (dimStartDate_affiliation_organization2 == null) - { - dimStartDate_affiliation_organization2 = new DimDate() - { - Year = 2016, - Month = 1, - Day = 1, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(dimStartDate_affiliation_organization2); - await _ttvContext.SaveChangesAsync(); - } - var dimEndDate_affiliation_organization2 = await _ttvContext.DimDates.AsNoTracking().FirstOrDefaultAsync(dd => dd.Year == 2019 && dd.Month == 12 && dd.Day == 31); - if (dimEndDate_affiliation_organization2 == null) - { - dimEndDate_affiliation_organization2 = new DimDate() - { - Year = 2019, - Month = 12, - Day = 31, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(dimEndDate_affiliation_organization2); - await _ttvContext.SaveChangesAsync(); - } - var dimAffiliation_Organization2 = new DimAffiliation() - { - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - DimOrganizationId = organization2.Id, - StartDate = dimStartDate_affiliation_organization2.Id, - EndDate = dimEndDate_affiliation_organization2.Id, - AffiliationType = affiliationType.Id, - PositionNameFi = "Erikoistutkija", - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id - }; - _ttvContext.DimAffiliations.Add(dimAffiliation_Organization2); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_affiliation_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_affiliation_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_affiliation_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_affiliation_Organization2.Id; - factFieldValue_affiliation_Organization2.DimAffiliationId = dimAffiliation_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_affiliation_Organization2); - await _ttvContext.SaveChangesAsync(); - - - // Education - var dimFieldDisplaySettings_education_Organization2 = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.SourceId == Constants.SourceIdentifiers.DEMO && dfds.SourceDescription == this.DemoOrganization2Name && dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION); - var dimEndDate_education_organization2 = await _ttvContext.DimDates.AsNoTracking().FirstOrDefaultAsync(dd => dd.Year == 2011 && dd.Month == 0 && dd.Day == 0); - if (dimEndDate_education_organization2 == null) - { - dimEndDate_education_organization2 = new DimDate() - { - Year = 2011, - Month = 0, - Day = 0, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimDates.Add(dimEndDate_education_organization2); - await _ttvContext.SaveChangesAsync(); - } - var dimEducation_Organization2 = new DimEducation() - { - DimKnownPersonId = dimUserProfile.DimKnownPersonId, - NameFi = "Filosofian tohtori, luonnontieteellinen ala", - DegreeGrantingInstitutionName = "Yliopisto Y", - DimEndDate = dimEndDate_education_organization2.Id, - SourceId = Constants.SourceIdentifiers.DEMO, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = organization2RegisteredDataSource.Id - }; - _ttvContext.DimEducations.Add(dimEducation_Organization2); - await _ttvContext.SaveChangesAsync(); - var factFieldValue_education_Organization2 = _userProfileService.GetEmptyFactFieldValueDemo(); - factFieldValue_education_Organization2.DimUserProfileId = dimUserProfile.Id; - factFieldValue_education_Organization2.DimFieldDisplaySettingsId = dimFieldDisplaySettings_education_Organization2.Id; - factFieldValue_education_Organization2.DimEducationId = dimEducation_Organization2.Id; - _ttvContext.FactFieldValues.Add(factFieldValue_education_Organization2); - await _ttvContext.SaveChangesAsync(); - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/DuplicateHandlerService.cs b/aspnetcore/src/api/Services/DuplicateHandlerService.cs new file mode 100644 index 00000000..070a1e02 --- /dev/null +++ b/aspnetcore/src/api/Services/DuplicateHandlerService.cs @@ -0,0 +1,132 @@ + +using System.Collections.Generic; +using api.Models.Common; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; +using api.Models.Ttv; + +namespace api.Services +{ + /* + * DuplicateHandlerService + */ + public class DuplicateHandlerService : IDuplicateHandlerService + { + private readonly List typeCodes = new() { "A3", "A4", "B2", "B3", "D2", "D3", "E1" }; + + public DuplicateHandlerService() + { + } + + /* + * Check if ProfileDataRaw contains ORCID publication. + */ + public bool IsOrcidPublication(ProfileDataFromSql profileData) + { + return profileData.FactFieldValues_DimOrcidPublicationId != -1 && profileData.DimFieldDisplaySettings_FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID; + } + + /* + * When two publications have the same DOI, they will be considered as DIFFERENT publications if: + * - Virta publication code is A3, A4, B2, B3, D2, D3 or E1 + * - AND the publication names differ + */ + public bool HasSameDoiButIsDifferentPublication(string orcidPublicationName, ProfileEditorPublication publication) + { + return this.typeCodes.Contains(publication.TypeCode) && orcidPublicationName != publication.PublicationName; + } + + /* + * Add publication data source. + */ + public List AddDataSource(ProfileEditorPublication publication, ProfileEditorSource dataSource) + { + if (!publication.DataSources.Contains(dataSource)) + { + publication.DataSources.Add(dataSource); + } + return publication.DataSources; + } + + /* + * Add publication to publication list. + * Handle duplicates by matching DOI. Handle special case in DOI matching regarding Virta publication type codes. + */ + public List AddPublicationToProfileEditorData(ProfileEditorSource dataSource, ProfileDataFromSql profileData, List publications) + { + // Loop existing publications and check for duplicates. + foreach (ProfileEditorPublication publication in publications) + { + // Check duplicate publicationId. + if ( + (!IsOrcidPublication(profileData) && profileData.DimPublication_PublicationId != "" && profileData.DimPublication_PublicationId == publication.PublicationId) || + (IsOrcidPublication(profileData) && profileData.DimOrcidPublication_PublicationId != "" && profileData.DimOrcidPublication_PublicationId == publication.PublicationId) + ) + { + AddDataSource(publication, dataSource); + return publications; + } + + // Check duplicate DOI. + if ( + IsOrcidPublication(profileData) && + profileData.DimOrcidPublication_Doi != "" && + profileData.DimOrcidPublication_Doi == publication.Doi && + !HasSameDoiButIsDifferentPublication(profileData.DimOrcidPublication_PublicationName, publication) + ) + { + AddDataSource(publication, dataSource); + return publications; + } + } + + // Duplication not detected. Add publication to list of publications. + if (!this.IsOrcidPublication(profileData)) + { + // Add Virta publication + publications.Add( + new ProfileEditorPublication() + { + PublicationId = profileData.DimPublication_PublicationId, + PublicationName = profileData.DimPublication_PublicationName, + PublicationYear = profileData.DimPublication_PublicationYear, + Doi = profileData.DimPublication_Doi, + TypeCode = profileData.DimPublication_PublicationTypeCode, + itemMeta = new ProfileEditorItemMeta() + { + Id = profileData.FactFieldValues_DimPublicationId, + Type = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, + Show = profileData.FactFieldValues_Show, + PrimaryValue = profileData.FactFieldValues_PrimaryValue + }, + DataSources = new List { dataSource } + } + ); + } + else + { + // Add ORCID publication + publications.Add( + new ProfileEditorPublication() + { + PublicationId = profileData.DimOrcidPublication_PublicationId, + PublicationName = profileData.DimOrcidPublication_PublicationName, + PublicationYear = profileData.DimOrcidPublication_PublicationYear, + Doi = profileData.DimOrcidPublication_Doi, + TypeCode = "", + itemMeta = new ProfileEditorItemMeta() + { + Id = profileData.FactFieldValues_DimOrcidPublicationId, + Type = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID, + Show = profileData.FactFieldValues_Show, + PrimaryValue = profileData.FactFieldValues_PrimaryValue + }, + DataSources = new List { dataSource } + } + ); + } + + return publications; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/ElasticsearchService.cs b/aspnetcore/src/api/Services/ElasticsearchService.cs index 5e9d118a..a30ea378 100644 --- a/aspnetcore/src/api/Services/ElasticsearchService.cs +++ b/aspnetcore/src/api/Services/ElasticsearchService.cs @@ -1,62 +1,111 @@ -using System; -using System.Net.Http; -using System.Text; +using System; using System.Threading.Tasks; +using api.Models.Elasticsearch; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Nest; namespace api.Services { /* - * ElasticsearchService handles person index update. + * ElasticsearchService handles person index modifications. */ - public class ElasticsearchService + public class ElasticsearchService : IElasticsearchService { - public HttpClient Client { get; } + public ElasticClient ESclient; public IConfiguration Configuration { get; } private readonly ILogger _logger; - public ElasticsearchService(IConfiguration configuration, HttpClient client, ILogger logger) + // Check if Elasticsearch synchronization is enabled and related configuration is valid. + public bool IsElasticsearchSyncEnabled() { - Configuration = configuration; - - client.BaseAddress = new Uri(Configuration["ELASTICSEARCH:URL"]); - Client = client; - _logger = logger; + return Configuration["ELASTICSEARCH:ENABLED"] != null + && Configuration["ELASTICSEARCH:ENABLED"] == "true" + && Configuration["ELASTICSEARCH:URL"] != null + && Uri.IsWellFormedUriString( + Configuration["ELASTICSEARCH:URL"], + UriKind.Absolute + ); } - // Check if Elasticsearch synchronization is enabled - public Boolean IsElasticsearchSyncEnabled() + // Constructor. + // Do not setup Elasticsearch client unless configuration values are ok. + public ElasticsearchService(IConfiguration configuration, ILogger logger) { - return Configuration["ELASTICSEARCH:URL"] != null && Configuration["ELASTICSEARCH:URL"] == "1"; - } + Configuration = configuration; + _logger = logger; - // Get Elasticsearch person index url - public String GetUrlElasticsearchPersonIndex(String orcidId) - { - return Client.BaseAddress + "/portalapi/person" + orcidId; + if (IsElasticsearchSyncEnabled()) + { + ConnectionSettings settings = new ConnectionSettings(new Uri(Configuration["ELASTICSEARCH:URL"])) + .DefaultIndex("person") + .BasicAuthentication(Configuration["ELASTICSEARCH:USERNAME"], Configuration["ELASTICSEARCH:PASSWORD"]); + ESclient = new ElasticClient(settings); + } } + // Update entry in Elasticsearch person index // TODO: When 3rd party sharing feature is implemented, check that TTV share is enabled in user profile. - public async Task UpdateEntryInElasticsearchPersonIndex(String orcidId, String personData) + public async Task UpdateEntryInElasticsearchPersonIndex(string orcidId, ElasticsearchPerson person) { - string result = string.Empty; - var url = GetUrlElasticsearchPersonIndex(orcidId); - HttpResponseMessage response = await Client.PostAsync( - url, - new StringContent(personData, Encoding.UTF8, "application/json") - ); - response.EnsureSuccessStatusCode(); + if (!IsElasticsearchSyncEnabled()) + { + return false; + } + + //_logger.LogInformation("ElasticsearchService: updating entry: " + orcidId); + + IndexResponse asyncIndexResponse = await ESclient.IndexDocumentAsync(person); + + if (asyncIndexResponse.IsValid) + { + return true; + } + { + string errormessage = ""; + if (asyncIndexResponse.OriginalException != null && asyncIndexResponse.OriginalException.Message != null) + { + errormessage = asyncIndexResponse.OriginalException.Message; + } + else if (asyncIndexResponse.ServerError != null && asyncIndexResponse.ServerError != null && asyncIndexResponse.ServerError.Error != null && asyncIndexResponse.ServerError.Error.Reason != null) + { + errormessage = asyncIndexResponse.ServerError.Error.Reason; + } + _logger.LogError("ElasticsearchService: ERROR updating: " + orcidId + ": " + errormessage); + return false; + } } // Delete entry from Elasticsearch person index - public async Task DeleteEntryFromElasticsearchPersonIndex(String orcidId) + public async Task DeleteEntryFromElasticsearchPersonIndex(string orcidId) { - string result = string.Empty; - var url = GetUrlElasticsearchPersonIndex(orcidId); - HttpResponseMessage response = await Client.DeleteAsync(url); - response.EnsureSuccessStatusCode(); + if (!IsElasticsearchSyncEnabled()) + { + return false; + } + + //_logger.LogInformation("ElasticsearchService: deleting entry: " + orcidId); + + DeleteResponse asyncDeleteResponse = await ESclient.DeleteAsync(orcidId); + + if (asyncDeleteResponse.IsValid) + { + return true; + } + { + string errormessage = ""; + if (asyncDeleteResponse.OriginalException != null && asyncDeleteResponse.OriginalException.Message != null) + { + errormessage = asyncDeleteResponse.OriginalException.Message; + } + else if (asyncDeleteResponse.ServerError != null && asyncDeleteResponse.ServerError != null && asyncDeleteResponse.ServerError.Error != null && asyncDeleteResponse.ServerError.Error.Reason != null) + { + errormessage = asyncDeleteResponse.ServerError.Error.Reason; + } + _logger.LogError("ElasticsearchService: ERROR deleting: " + orcidId + ": " + errormessage); + return false; + } } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IDataSourceHelperService.cs b/aspnetcore/src/api/Services/IDataSourceHelperService.cs new file mode 100644 index 00000000..0d5fd734 --- /dev/null +++ b/aspnetcore/src/api/Services/IDataSourceHelperService.cs @@ -0,0 +1,19 @@ +namespace api.Services +{ + public interface IDataSourceHelperService + { + int DimRegisteredDataSourceId_ORCID { get; set; } + string DimRegisteredDataSourceName_ORCID { get; set; } + int DimOrganizationId_ORCID { get; set; } + string DimOrganizationNameFi_ORCID { get; set; } + string DimOrganizationNameEn_ORCID { get; set; } + string DimOrganizationNameSv_ORCID { get; set; } + int DimRegisteredDataSourceId_TTV { get; set; } + string DimRegisteredDataSourceName_TTV { get; set; } + int DimOrganizationId_TTV { get; set; } + string DimOrganizationNameFi_TTV { get; set; } + string DimOrganizationNameEn_TTV { get; set; } + string DimOrganizationNameSv_TTV { get; set; } + int DimPurposeId_TTV { get; set; } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IDuplicateHandlerService.cs b/aspnetcore/src/api/Services/IDuplicateHandlerService.cs new file mode 100644 index 00000000..b66a54fa --- /dev/null +++ b/aspnetcore/src/api/Services/IDuplicateHandlerService.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; + +namespace api.Services +{ + public interface IDuplicateHandlerService + { + List AddDataSource(ProfileEditorPublication publication, ProfileEditorSource dataSource); + List AddPublicationToProfileEditorData(ProfileEditorSource dataSource, ProfileDataFromSql profileData, List publications); + bool HasSameDoiButIsDifferentPublication(string orcidPublicationName, ProfileEditorPublication publication); + bool IsOrcidPublication(ProfileDataFromSql profileData); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IElasticsearchService.cs b/aspnetcore/src/api/Services/IElasticsearchService.cs new file mode 100644 index 00000000..6f7dac77 --- /dev/null +++ b/aspnetcore/src/api/Services/IElasticsearchService.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using api.Models.Elasticsearch; +using Microsoft.Extensions.Configuration; + +namespace api.Services +{ + public interface IElasticsearchService + { + IConfiguration Configuration { get; } + + Task DeleteEntryFromElasticsearchPersonIndex(string orcidId); + bool IsElasticsearchSyncEnabled(); + Task UpdateEntryInElasticsearchPersonIndex(string orcidId, ElasticsearchPerson person); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IKeycloakAdminApiService.cs b/aspnetcore/src/api/Services/IKeycloakAdminApiService.cs new file mode 100644 index 00000000..92139d78 --- /dev/null +++ b/aspnetcore/src/api/Services/IKeycloakAdminApiService.cs @@ -0,0 +1,21 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; + +namespace api.Services +{ + public interface IKeycloakAdminApiService + { + IConfiguration Configuration { get; } + + string GetAccessTokenFromHttpRequest(HttpRequest httpRequest); + string GetKeycloakUserIdFromAccessToken(HttpRequest httpRequest); + string GetOrcidIdFromRawKeycloakUserData(string keycloakUserDataRaw); + Task GetRawUserDataFromKeycloakAdminApi(string keycloakUserId); + Task LogoutUser(string tokenStr); + Task RemoveUser(string tokenStr); + Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUser); + Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, string orcidId); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/ILanguageService.cs b/aspnetcore/src/api/Services/ILanguageService.cs new file mode 100644 index 00000000..d817cb66 --- /dev/null +++ b/aspnetcore/src/api/Services/ILanguageService.cs @@ -0,0 +1,9 @@ +using api.Models.Common; + +namespace api.Services +{ + public interface ILanguageService + { + NameTranslation GetNameTranslation(string nameFi, string nameEn, string nameSv); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IOrcidApiService.cs b/aspnetcore/src/api/Services/IOrcidApiService.cs new file mode 100644 index 00000000..d7218fe6 --- /dev/null +++ b/aspnetcore/src/api/Services/IOrcidApiService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace api.Services +{ + public interface IOrcidApiService + { + string GetOrcidRecordPath(string orcidId); + Task GetRecordFromPublicApi(string orcidId); + Task GetRecordFromMemberApi(string orcidId, string orcidAccessToken); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IOrcidImportService.cs b/aspnetcore/src/api/Services/IOrcidImportService.cs new file mode 100644 index 00000000..2a04500a --- /dev/null +++ b/aspnetcore/src/api/Services/IOrcidImportService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace api.Services +{ + public interface IOrcidImportService + { + Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, string json); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IOrcidJsonParserService.cs b/aspnetcore/src/api/Services/IOrcidJsonParserService.cs new file mode 100644 index 00000000..455c019d --- /dev/null +++ b/aspnetcore/src/api/Services/IOrcidJsonParserService.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using api.Models.Orcid; + +namespace api.Services +{ + public interface IOrcidJsonParserService + { + OrcidBiography GetBiography(string json); + OrcidCreditName GetCreditName(string json); + List GetEducations(string json); + List GetEmails(string json); + List GetEmployments(string json); + List GetExternalIdentifiers(string json); + OrcidFamilyName GetFamilyName(string json); + OrcidGivenNames GetGivenNames(string json); + List GetKeywords(string json); + List GetOtherNames(string json); + List GetPublications(string json); + List GetResearcherUrls(string json); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IOrganizationHandlerService.cs b/aspnetcore/src/api/Services/IOrganizationHandlerService.cs new file mode 100644 index 00000000..8fb4a612 --- /dev/null +++ b/aspnetcore/src/api/Services/IOrganizationHandlerService.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using api.Models.Ttv; + +namespace api.Services +{ + public interface IOrganizationHandlerService + { + DimIdentifierlessDatum CreateIdentifierlessData_OrganizationName(string nameFi, string nameEn, string nameSv); + DimIdentifierlessDatum CreateIdentifierlessData_OrganizationUnit(DimIdentifierlessDatum parentDimIdentifierlessData, string nameFi, string nameEn, string nameSv); + Task FindOrganizationIdByOrcidDisambiguationIdentifier(string orcidDisambiguationSource, string orcidDisambiguatedOrganizationIdentifier); + string GetAffiliationDepartmentNameFromFactFieldValue(FactFieldValue factFieldValue); + string MapOrcidDisambiguationSourceToPidType(string orcidDisambiguationSource); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/ISharingService.cs b/aspnetcore/src/api/Services/ISharingService.cs new file mode 100644 index 00000000..600b635f --- /dev/null +++ b/aspnetcore/src/api/Services/ISharingService.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using api.Models.ProfileEditor; +using api.Models.Ttv; + +namespace api.Services +{ + public interface ISharingService + { + Task AddPermissions(int userprofileId, List permissionsToAdd); + Task DeleteAllGrantedPermissionsFromUserprofile(int userprofileId); + Task DeletePermissions(int userprofileId, List permissionsToDelete); + Task> GetDefaultSharingPermissionsListForUserProfile(DimUserProfile dimUserProfile); + string GetDimReferenceDataCodeScheme(); + List GetDimReferenceDataCodeValues(); + Task GetProfileEditorSharingPermissionsResponse(); + Task GetProfileEditorSharingPurposesResponse(); + Task GetProfileEditorSharingResponse(int userprofileId); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IStartupHelperService.cs b/aspnetcore/src/api/Services/IStartupHelperService.cs new file mode 100644 index 00000000..d8b020ad --- /dev/null +++ b/aspnetcore/src/api/Services/IStartupHelperService.cs @@ -0,0 +1,11 @@ +using api.Models.Ttv; + +namespace api.Services +{ + public interface IStartupHelperService + { + DimPurpose GetDimPurposeId_OnStartup_TTV(); + DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_ORCID(); + DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_TTV(); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/ITokenService.cs b/aspnetcore/src/api/Services/ITokenService.cs new file mode 100644 index 00000000..cbc19d82 --- /dev/null +++ b/aspnetcore/src/api/Services/ITokenService.cs @@ -0,0 +1,17 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Threading.Tasks; +using api.Models.Orcid; +using Microsoft.Extensions.Configuration; + +namespace api.Services +{ + public interface ITokenService + { + IConfiguration Configuration { get; } + + JwtSecurityToken GetJwtFromString(string tokenStr); + string GetKeycloakUserIdFromJwt(JwtSecurityToken jwt); + Task GetOrcidTokensJsonFromKeycloak(string usersKeycloakAccessToken); + OrcidTokens ParseOrcidTokensJson(string orcidTokensJson); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/ITtvSqlService.cs b/aspnetcore/src/api/Services/ITtvSqlService.cs new file mode 100644 index 00000000..bfc62d46 --- /dev/null +++ b/aspnetcore/src/api/Services/ITtvSqlService.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using api.Models.ProfileEditor.Items; + +namespace api.Services +{ + public interface ITtvSqlService + { + string ConvertListOfIntsToCommaSeparatedString(List listOfInts); + string GetFactFieldValuesFKColumnNameFromFieldIdentifier(int fieldIdentifier); + string GetSqlQuery_Delete_BrGrantedPermissions(int userprofileId); + string GetSqlQuery_Delete_DimAffiliations(List dimAffiliationIds); + string GetSqlQuery_Delete_DimCompetences(List dimCompetenceIds); + string GetSqlQuery_Delete_DimEducations(List dimEducationIds); + string GetSqlQuery_Delete_DimEmailAddrresses(List dimEmailAddrressIds); + string GetSqlQuery_Delete_DimEvents(List dimEventIds); + string GetSqlQuery_Delete_DimFieldDisplaySettings(int userprofileId); + string GetSqlQuery_Delete_DimFieldsOfScience(List dimFieldOfScienceIds); + string GetSqlQuery_Delete_DimFundingDecisions(List dimFundingDecisionIds); + string GetSqlQuery_Delete_DimIdentifierlessData_Children(int dimIdentifierlessDataId); + string GetSqlQuery_Delete_DimIdentifierlessData_Parent(int id); + string GetSqlQuery_Delete_DimKeyword(List dimKeywordIds); + string GetSqlQuery_Delete_DimNames(List dimNameIds); + string GetSqlQuery_Delete_DimOrcidPublications(List dimOrcidPublicationIds); + string GetSqlQuery_Delete_DimPids(List dimPidIds); + string GetSqlQuery_Delete_DimResearchActivities(List dimResearchActivityIds); + string GetSqlQuery_Delete_DimResearchCommunities(List dimResearchCommunityIds); + string GetSqlQuery_Delete_DimResearchDatasets(List dimResearchDatasetIds); + string GetSqlQuery_Delete_DimResearchDescriptions(List dimResearcherDescriptionIds); + string GetSqlQuery_Delete_DimResearcherToResearchCommunities(List dimResearcherToResearchCommunityIds); + string GetSqlQuery_Delete_DimTelephoneNumbers(List dimTelephoneNumberIds); + string GetSqlQuery_Delete_DimUserChoices(int userprofileId); + string GetSqlQuery_Delete_DimUserProfile(int userprofileId); + string GetSqlQuery_Delete_DimWebLinks(List dimWebLinkIds); + string GetSqlQuery_Delete_FactFieldValues(int userprofileId); + string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = false); + string GetSqlQuery_Select_FactFieldValues(int userprofileId); + string GetSqlQuery_Update_FactFieldValues(int dimUserProfileId, ProfileEditorItemMeta profileEditorItemMeta); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IUserProfileService.cs b/aspnetcore/src/api/Services/IUserProfileService.cs new file mode 100644 index 00000000..86fad021 --- /dev/null +++ b/aspnetcore/src/api/Services/IUserProfileService.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using api.Models.Orcid; +using api.Models.ProfileEditor.Items; +using api.Models.Ttv; + +namespace api.Services +{ + public interface IUserProfileService + { + void AddDimAffiliationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + void AddDimEducationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + void AddDimEmailAddressItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + void AddDimResearcherDescriptionToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + void AddDimTelephoneItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + void AddFactContributionItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + Task AddOrUpdateDimEmailAddress(string emailAddress, int dimKnownPersonId, int dimRegisteredDataSourceId); + Task AddOrUpdateDimName(string lastName, string firstNames, int dimKnownPersonId, int dimRegisteredDataSourceId); + Task AddOrUpdateDimResearcherDescription(string description_fi, string description_en, string description_sv, int dimKnownPersonId, int dimRegisteredDataSourceId); + void AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + bool CanDeleteFactFieldValueRelatedData(FactFieldValue ffv); + Task CreateProfile(string orcidId); + Task DeleteProfileDataAsync(int userprofileId); + Task ExecuteRawSql(string sql); + DimOrcidPublication GetEmptyDimOrcidPublication(); + DimPid GetEmptyDimPid(); + FactFieldValue GetEmptyFactFieldValue(); + FactFieldValue GetEmptyFactFieldValueDemo(); + List GetFieldIdentifiers(); + Task GetProfileDataAsync2(int userprofileId, bool forElasticsearch = false); + Task GetUserprofileId(string orcidId); + Task UpdateOrcidTokensInDimUserProfile(int dimUserProfileId, OrcidTokens orcidTokens); + Task UserprofileExistsForOrcidId(string orcidId); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IUtilityService.cs b/aspnetcore/src/api/Services/IUtilityService.cs new file mode 100644 index 00000000..23ef5efe --- /dev/null +++ b/aspnetcore/src/api/Services/IUtilityService.cs @@ -0,0 +1,9 @@ +using System; + +namespace api.Services +{ + public interface IUtilityService + { + DateTime GetCurrentDateTime(); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/KeycloakAdminApiService.cs b/aspnetcore/src/api/Services/KeycloakAdminApiService.cs new file mode 100644 index 00000000..4c7a777a --- /dev/null +++ b/aspnetcore/src/api/Services/KeycloakAdminApiService.cs @@ -0,0 +1,218 @@ +using System; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; +using Microsoft.Extensions.Configuration; +using System.IdentityModel.Tokens.Jwt; +using System.Text; +using Microsoft.Extensions.Logging; + +namespace api.Services +{ + /* + * KeycloakAdminApiService implements Keycloak Admin API calls. + * https://www.keycloak.org/docs-api/15.0/rest-api/index.html + */ + public class KeycloakAdminApiService : IKeycloakAdminApiService + { + private readonly ITokenService _tokenService; + public IConfiguration Configuration { get; } + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + + public KeycloakAdminApiService(ITokenService tokenService, IConfiguration configuration, + IHttpClientFactory httpClientFactory, ILogger logger) + { + _tokenService = tokenService; + Configuration = configuration; + _httpClientFactory = httpClientFactory; + _logger = logger; + } + + /* + * Get access token from HttpRequest header Authorization. + */ + public String GetAccessTokenFromHttpRequest(HttpRequest httpRequest) + { + return httpRequest.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", ""); + } + + /* + * Get Keycloak user ID from access token. + */ + public String GetKeycloakUserIdFromAccessToken(HttpRequest httpRequest) + { + return httpRequest.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", ""); + } + + /* + * Get user data from Keycloak Admin API. + * Use name HttpClient "keycloakClient". + */ + public async Task GetRawUserDataFromKeycloakAdminApi(string keycloakUserId) + { + //_logger.LogInformation("Get userdata for user id: " + keycloakUserId); + HttpClient keycloakAdminApiHttpClient = _httpClientFactory.CreateClient("keycloakClient"); + HttpRequestMessage request = new(method: HttpMethod.Get, requestUri: keycloakUserId); + HttpResponseMessage response = await keycloakAdminApiHttpClient.SendAsync(request); + try + { + response.EnsureSuccessStatusCode(); + return await response.Content.ReadAsStringAsync(); + } + catch (HttpRequestException) + { + _logger.LogError("Keycloak get userdata failed: " + keycloakUserId); + return ""; + } + } + + /* + * Get orcid id from raw Keycloak user data. + */ + public string? GetOrcidIdFromRawKeycloakUserData(string keycloakUserDataRaw) + { + using (JsonDocument document = JsonDocument.Parse(keycloakUserDataRaw)) + { + document.RootElement.TryGetProperty("federatedIdentities", out JsonElement federatedIdentitiesElement); + foreach (JsonElement identityElement in federatedIdentitiesElement.EnumerateArray()) + { + identityElement.TryGetProperty("identityProvider", out JsonElement identityProviderElement); + identityElement.TryGetProperty("userId", out JsonElement userIdElement); + if (identityProviderElement.GetString() == "orcid") + { + return userIdElement.GetString(); + } + } + } + return null; + } + + /* + * Set ORCID ID as Keycloak user attribute using Keycloak Admin API. + * Use name HttpClient "keycloakClient". + */ + public async Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, string orcidId) + { + //_logger.LogInformation("Set ORCID ID " + orcidId + " as attribute to Keycloak user: " + keycloakUserId); + HttpClient keycloakAdminApiHttpClient = _httpClientFactory.CreateClient("keycloakClient"); + HttpRequestMessage request = new(method: HttpMethod.Put, requestUri: keycloakUserId); + string stringPayload = "{\"attributes\": {\"orcid\": [\"" + orcidId + "\"]}}"; + request.Content = new StringContent(stringPayload, Encoding.UTF8, "application/json"); + HttpResponseMessage response = await keycloakAdminApiHttpClient.SendAsync(request); + try + { + response.EnsureSuccessStatusCode(); + return true; + } + catch (HttpRequestException) + { + _logger.LogError("Keycloak set ORCID ID " + orcidId + " as attribute to Keycloak user failed: " + keycloakUserId); + return false; + } + } + + /* + * Set attribute "orcid" for Keycloak user. + */ + public async Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUser) + { + /* + * check is orcid already in token + * yes: + * return + * no: + * get sub (=kc user id) + * get userdata from kc + * update user attribute in kc + * return + */ + if (!TokenService.JwtContainsOrcid(jwtFromUser)) + { + // Get Keycloak user id. + string keycloakUserId = _tokenService.GetKeycloakUserIdFromJwt(jwtFromUser); + + // Get Keycloak user data. + string keycloakUserDataRaw = await this.GetRawUserDataFromKeycloakAdminApi(keycloakUserId); + + // Stop if Keycloak user data was not received. + if (keycloakUserDataRaw == "") + { + return false; + } + + // Get orcid id from user data. + string orcidId = this.GetOrcidIdFromRawKeycloakUserData(keycloakUserDataRaw); + + // Stop if ORCID ID was not found. + if (orcidId == null) + { + return false; + } + + // Set orcid id as Keycloak user attribute. + return await this.SetOrcidIdAsKeycloakUserAttribute(keycloakUserId, orcidId); + } + return true; + } + + /* + * Logout Keycloak user. + * Remove all user sessions associated with the user by sending Keycloak admin api message: + * POST /{realm}/users/{id}/logout + */ + public async Task LogoutUser(String tokenStr) + { + // Get jwt from string + JwtSecurityToken jwtFromUser = _tokenService.GetJwtFromString(tokenStr); + + // Get Keycloak user id. + string keycloakUserId = _tokenService.GetKeycloakUserIdFromJwt(jwtFromUser); + + HttpClient keycloakAdminApiHttpClient = _httpClientFactory.CreateClient("keycloakClient"); + HttpRequestMessage request = new(method: HttpMethod.Post, requestUri: keycloakUserId + "/logout"); + HttpResponseMessage response = await keycloakAdminApiHttpClient.SendAsync(request); + try + { + response.EnsureSuccessStatusCode(); + _logger.LogInformation("Keycloak user logout OK: " + keycloakUserId); + return true; + } + catch (HttpRequestException) + { + _logger.LogError("Keycloak user logout failed: " + keycloakUserId); + return false; + } + } + + /* + * Remove Keycloak user. + * DELETE /{realm}/users/{id} + */ + public async Task RemoveUser(String tokenStr) + { + // Get jwt from string + JwtSecurityToken jwtFromUser = _tokenService.GetJwtFromString(tokenStr); + + // Get Keycloak user id. + string keycloakUserId = _tokenService.GetKeycloakUserIdFromJwt(jwtFromUser); + + HttpClient keycloakAdminApiHttpClient = _httpClientFactory.CreateClient("keycloakClient"); + HttpRequestMessage request = new(method: HttpMethod.Delete, requestUri: keycloakUserId); + HttpResponseMessage response = await keycloakAdminApiHttpClient.SendAsync(request); + try + { + response.EnsureSuccessStatusCode(); + _logger.LogInformation("Keycloak user delete OK: " + keycloakUserId); + return true; + } + catch (HttpRequestException) + { + _logger.LogError("Keycloak user delete failed: " + keycloakUserId); + return false; + } + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/LanguageService.cs b/aspnetcore/src/api/Services/LanguageService.cs new file mode 100644 index 00000000..7a3d7661 --- /dev/null +++ b/aspnetcore/src/api/Services/LanguageService.cs @@ -0,0 +1,84 @@ +using api.Models.Common; + +namespace api.Services +{ + /* + * LanguageService implements language and translation related functionality. + */ + public class LanguageService : ILanguageService + { + public LanguageService() + { + } + + /* + * Fill empty name properties. + * Logic: If only one language field contains value, copy it to all other fields. + */ + + public NameTranslation GetNameTranslation(string nameFi, string nameEn, string nameSv) + { + // Convert null to "" + if (nameFi == null) + { + nameFi = ""; + } + + if (nameEn == null) + { + nameEn = ""; + } + + if (nameSv == null) + { + nameSv = ""; + } + + // Only FI contains value => copy to EN and SV + if (nameFi != "" && nameEn == "" && nameSv == "") + { + nameEn = nameFi; + nameSv = nameFi; + } + + // Only EN contains value => copy to FI and SV + if (nameEn != "" && nameFi == "" && nameSv == "") + { + nameFi = nameEn; + nameSv = nameEn; + } + + // Only SV contains value => copy to FI and EN + if (nameSv != "" && nameFi == "" && nameEn == "") + { + nameFi = nameSv; + nameEn = nameSv; + } + + // FI and EN contain values => copy to FI to SV + if (nameFi != "" && nameEn != "" && nameSv == "") + { + nameSv = nameFi; + } + + // FI and SV contain values => copy to FI to EN + if (nameFi != "" && nameSv != "" && nameEn == "") + { + nameEn = nameFi; + } + + // EN and SV contain values => copy to SV to FI + if (nameFi == "" && nameSv != "" && nameEn != "") + { + nameFi = nameSv; + } + + return new NameTranslation() + { + NameFi = nameFi, + NameEn = nameEn, + NameSv = nameSv + }; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/OrcidApiService.cs b/aspnetcore/src/api/Services/OrcidApiService.cs index b6cb9824..577f975e 100644 --- a/aspnetcore/src/api/Services/OrcidApiService.cs +++ b/aspnetcore/src/api/Services/OrcidApiService.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading.Tasks; namespace api.Services @@ -7,43 +8,56 @@ namespace api.Services /* * OrcidApiService handles ORCID api http requests. */ - public class OrcidApiService + public class OrcidApiService : IOrcidApiService { - public HttpClient Client { get; } + private readonly IHttpClientFactory _httpClientFactory; - public OrcidApiService(HttpClient client) + public OrcidApiService(IHttpClientFactory httpClientFactory) { - client.BaseAddress = new Uri("https://pub.orcid.org/v3.0/"); - client.DefaultRequestHeaders.Add("Accept", "application/json"); - Client = client; + _httpClientFactory = httpClientFactory; } - public String GetUrlRecord(String orcidId) + /* + * Get path for retrieving ORCID record. + */ + public String GetOrcidRecordPath(String orcidId) { - return Client.BaseAddress + orcidId + "/record"; + return orcidId + "/record"; } - public String GetUrlPersonalDetails(String orcidId) + /* + * Get ORCID record from ORCID public API. + */ + public async Task GetRecordFromPublicApi(String orcidId) { - return Client.BaseAddress + orcidId + "/personal-details"; - } - - // Get ORCID record - public async Task GetRecord(String orcidId) - { - string result = string.Empty; - var url = GetUrlRecord(orcidId); - HttpResponseMessage response = await Client.GetAsync(url); + // Get ORCID public API specific http client. + HttpClient orcidPublicApiHttpClient = _httpClientFactory.CreateClient("ORCID_PUBLIC_API"); + // Get path for retrieving ORCID record. API base address is already set in http client. + string orcidRecordPath = GetOrcidRecordPath(orcidId); + // GET request + HttpRequestMessage request = new(method: HttpMethod.Get, requestUri: orcidRecordPath); + HttpResponseMessage response = await orcidPublicApiHttpClient.SendAsync(request); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } - // Get ORCID personal details - public async Task GetPersonalDetails(String orcidId) + /* + * Get ORCID record from ORCID member API. + * ORCID API call MUST include user's ORCID access token in the authorization header. + */ + public async Task GetRecordFromMemberApi(String orcidId, String orcidAccessToken) { - string result = string.Empty; - var url = GetUrlPersonalDetails(orcidId); - HttpResponseMessage response = await Client.GetAsync(url); + // TODO: check orcidId and orcidAccessToken + + // Get ORCID member API specific http client. + HttpClient orcidMemberApiHttpClient = _httpClientFactory.CreateClient("ORCID_MEMBER_API"); + // Get path for retrieving ORCID record. API base address is already set in http client. + string orcidRecordPath = GetOrcidRecordPath(orcidId); + // GET request + HttpRequestMessage request = new(method: HttpMethod.Get, requestUri: orcidRecordPath); + // Insert ORCID access token into authorization header for each request. + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", orcidAccessToken); + HttpResponseMessage response = await orcidMemberApiHttpClient.SendAsync(request); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } diff --git a/aspnetcore/src/api/Services/OrcidImportService.cs b/aspnetcore/src/api/Services/OrcidImportService.cs new file mode 100644 index 00000000..9eb72c79 --- /dev/null +++ b/aspnetcore/src/api/Services/OrcidImportService.cs @@ -0,0 +1,833 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using api.Models.Common; +using api.Models.Orcid; +using api.Models.Ttv; +using Microsoft.EntityFrameworkCore; + +namespace api.Services +{ + /* + * OrcidImportService imports ORCID data into user profile. + */ + public class OrcidImportService : IOrcidImportService + { + private readonly TtvContext _ttvContext; + private readonly IUserProfileService _userProfileService; + private readonly IOrcidJsonParserService _orcidJsonParserService; + private readonly IOrganizationHandlerService _organizationHandlerService; + private readonly IDataSourceHelperService _dataSourceHelperService; + private readonly IUtilityService _utilityService; + + public OrcidImportService(TtvContext ttvContext, IUserProfileService userProfileService, IOrcidJsonParserService orcidJsonParserService, + IOrganizationHandlerService organizationHandlerService, IUtilityService utilityService, IDataSourceHelperService dataSourceHelperService) + { + _ttvContext = ttvContext; + _userProfileService = userProfileService; + _orcidJsonParserService = orcidJsonParserService; + _organizationHandlerService = organizationHandlerService; + _utilityService = utilityService; + _dataSourceHelperService = dataSourceHelperService; + } + + /* + * Import ORCID record json into user profile. + */ + public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, string json) + { + // Get ORCID registered data source id. + int orcidRegisteredDataSourceId = _dataSourceHelperService.DimRegisteredDataSourceId_ORCID; + + // Get DimUserProfile and related entities + DimUserProfile dimUserProfile = await _ttvContext.DimUserProfiles.TagWith("Insert ORCID data").Where(dup => dup.Id == userprofileId) + .Include(dup => dup.DimFieldDisplaySettings) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimRegisteredDataSource) + .ThenInclude(drds => drds.DimOrganization) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimName) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimWebLink) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimFundingDecision) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimPublication) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimOrcidPublication) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimPid) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimPidIdOrcidPutCodeNavigation) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimResearchActivity) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimEvent) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimEducation) + .ThenInclude(de => de.DimStartDateNavigation) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimEducation) + .ThenInclude(de => de.DimEndDateNavigation) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimAffiliation) + .ThenInclude(da => da.DimOrganization) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimAffiliation) + .ThenInclude(da => da.StartDateNavigation) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimAffiliation) + .ThenInclude(da => da.EndDateNavigation) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimCompetence) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimResearchCommunity) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimTelephoneNumber) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimEmailAddrress) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimResearcherDescription) + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimIdentifierlessData) + .ThenInclude(did => did.InverseDimIdentifierlessData) // DimIdentifierlessData can have a child entity. + .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + .ThenInclude(ffv => ffv.DimKeyword).FirstOrDefaultAsync(); + + // Get current DateTime + DateTime currentDateTime = _utilityService.GetCurrentDateTime(); + + // Must use "Constants.SourceIdentifiers.ORCID" as value for "FactFieldValue.SourceId". It is used to identify what data can be deleted when userprofile is deleted. + + // Name + DimFieldDisplaySetting dimFieldDisplaySettingsName = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaysettingsName => dimFieldDisplaysettingsName.FieldIdentifier == Constants.FieldIdentifiers.PERSON_NAME); + // FactFieldValues + FactFieldValue factFieldValuesName = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimFieldDisplaySettings.Id == dimFieldDisplaySettingsName.Id && ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId); + if (factFieldValuesName != null) + { + // Update existing DimName + DimName dimName = factFieldValuesName.DimName; + dimName.LastName = _orcidJsonParserService.GetFamilyName(json).Value; + dimName.FirstNames = _orcidJsonParserService.GetGivenNames(json).Value; + dimName.Modified = _utilityService.GetCurrentDateTime(); + // Update existing FactFieldValue + factFieldValuesName.Show = true; // ORCID name is selected by default. + factFieldValuesName.Modified = currentDateTime; + } + else + { + // Create new DimName + DimName dimName = new() + { + LastName = _orcidJsonParserService.GetFamilyName(json).Value, + FirstNames = _orcidJsonParserService.GetGivenNames(json).Value, + DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, + DimRegisteredDataSourceId = orcidRegisteredDataSourceId, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimNames.Add(dimName); + // Create FactFieldValues for name + factFieldValuesName = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesName.DimUserProfile = dimUserProfile; + factFieldValuesName.DimFieldDisplaySettings = dimFieldDisplaySettingsName; + factFieldValuesName.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesName.DimName = dimName; + factFieldValuesName.Show = true; + _ttvContext.FactFieldValues.Add(factFieldValuesName); + } + + + // Other names + List otherNames = _orcidJsonParserService.GetOtherNames(json); + foreach (OrcidOtherName otherName in otherNames) + { + // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid + FactFieldValue factFieldValuesOtherName = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == otherName.PutCode.Value.ToString()); + + if (factFieldValuesOtherName != null) + { + // Update existing DimName + DimName dimName_otherName = factFieldValuesOtherName.DimName; + dimName_otherName.FullName = otherName.Value; + dimName_otherName.Modified = currentDateTime; + // Update existing FactFieldValue + factFieldValuesOtherName.Modified = currentDateTime; + } + else + { + // Create new DimName for other name + DimName dimName_otherName = new() + { + FullName = otherName.Value, + DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, + DimRegisteredDataSourceId = orcidRegisteredDataSourceId, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimNames.Add(dimName_otherName); + + // Add other name ORCID put code into DimPid + DimPid dimPidOrcidPutCodeOtherName = _userProfileService.GetEmptyDimPid(); + dimPidOrcidPutCodeOtherName.PidContent = otherName.PutCode.GetDbValue(); + dimPidOrcidPutCodeOtherName.PidType = Constants.PidTypes.ORCID_PUT_CODE; + dimPidOrcidPutCodeOtherName.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPidOrcidPutCodeOtherName.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPidOrcidPutCodeOtherName); + + // Get DimFieldDisplaySettings for other name + DimFieldDisplaySetting dimFieldDisplaySettingsOtherName = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_OTHER_NAMES); + + // Create FactFieldValues for other name + factFieldValuesOtherName = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesOtherName.DimUserProfile = dimUserProfile; + factFieldValuesOtherName.DimFieldDisplaySettings = dimFieldDisplaySettingsOtherName; + factFieldValuesOtherName.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesOtherName.DimName = dimName_otherName; + factFieldValuesOtherName.DimPidIdOrcidPutCodeNavigation = dimPidOrcidPutCodeOtherName; + _ttvContext.FactFieldValues.Add(factFieldValuesOtherName); + } + } + + + // Researcher urls + List researcherUrls = _orcidJsonParserService.GetResearcherUrls(json); + foreach (OrcidResearcherUrl researchUrl in researcherUrls) + { + // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid + FactFieldValue factFieldValuesWebLink = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == researchUrl.PutCode.Value.ToString()); + + if (factFieldValuesWebLink != null) + { + // Update existing DimWebLink + DimWebLink dimWebLink = factFieldValuesWebLink.DimWebLink; + dimWebLink.Url = researchUrl.Url; + dimWebLink.LinkLabel = researchUrl.UrlName; + dimWebLink.Modified = currentDateTime; + + // Update existing FactFieldValue + factFieldValuesWebLink.Modified = currentDateTime; + } + else + { + // Create new DimWebLink + DimWebLink dimWebLink = new() + { + Url = researchUrl.Url, + LinkLabel = researchUrl.UrlName, + DimOrganizationId = -1, + DimKnownPersonId = dimUserProfile.DimKnownPersonId, + DimCallProgrammeId = -1, + DimFundingDecisionId = -1, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimWebLinks.Add(dimWebLink); + + // Add web link ORCID put code into DimPid + DimPid dimPidOrcidPutCodeWebLink = _userProfileService.GetEmptyDimPid(); + dimPidOrcidPutCodeWebLink.PidContent = researchUrl.PutCode.GetDbValue(); + dimPidOrcidPutCodeWebLink.PidType = Constants.PidTypes.ORCID_PUT_CODE; + dimPidOrcidPutCodeWebLink.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPidOrcidPutCodeWebLink.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPidOrcidPutCodeWebLink); + + // Get DimFieldDisplaySettings for weblink + DimFieldDisplaySetting dimFieldDisplaySettingsWebLink = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_WEB_LINK); + + // Create FactFieldValues for weblink + factFieldValuesWebLink = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesWebLink.DimUserProfile = dimUserProfile; + factFieldValuesWebLink.DimFieldDisplaySettings = dimFieldDisplaySettingsWebLink; + factFieldValuesWebLink.DimWebLink = dimWebLink; + factFieldValuesWebLink.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesWebLink.DimPidIdOrcidPutCodeNavigation = dimPidOrcidPutCodeWebLink; + _ttvContext.FactFieldValues.Add(factFieldValuesWebLink); + } + } + + + // Researcher description + OrcidBiography biography = _orcidJsonParserService.GetBiography(json); + if (biography != null) + { + DimResearcherDescription dimResearcherDescription = await _userProfileService.AddOrUpdateDimResearcherDescription( + "", + _orcidJsonParserService.GetBiography(json).Value, + "", + dimUserProfile.DimKnownPersonId, + orcidRegisteredDataSourceId + ); + + // Researcher description: DimFieldDisplaySettings + DimFieldDisplaySetting dimFieldDisplaySettingsResearcherDescription = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaySettingsResearcherDescription => dimFieldDisplaySettingsResearcherDescription.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION); + + // Researcher description: FactFieldValues + FactFieldValue factFieldValuesResearcherDescription = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimResearcherDescriptionId == dimResearcherDescription.Id); + if (factFieldValuesResearcherDescription == null) + { + factFieldValuesResearcherDescription = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesResearcherDescription.DimUserProfile = dimUserProfile; + factFieldValuesResearcherDescription.DimFieldDisplaySettings = dimFieldDisplaySettingsResearcherDescription; + factFieldValuesResearcherDescription.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesResearcherDescription.DimResearcherDescription = dimResearcherDescription; + _ttvContext.FactFieldValues.Add(factFieldValuesResearcherDescription); + } + else + { + factFieldValuesResearcherDescription.Modified = currentDateTime; + } + } + + + // Email + List emails = _orcidJsonParserService.GetEmails(json); + foreach (OrcidEmail email in emails) + { + // Email: DimEmailAddrressess + DimEmailAddrress dimEmailAddress = await _userProfileService.AddOrUpdateDimEmailAddress( + email.Value, + dimUserProfile.DimKnownPersonId, + orcidRegisteredDataSourceId + ); + + // Email: DimFieldDisplaySettings + DimFieldDisplaySetting dimFieldDisplaySettingsEmailAddress = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaySettingsEmailAddress => dimFieldDisplaySettingsEmailAddress.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS); + + // Email: FactFieldValues + FactFieldValue factFieldValuesEmailAddress = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimEmailAddrressId == dimEmailAddress.Id); + if (factFieldValuesEmailAddress == null) + { + factFieldValuesEmailAddress = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesEmailAddress.DimUserProfile = dimUserProfile; + factFieldValuesEmailAddress.DimFieldDisplaySettings = dimFieldDisplaySettingsEmailAddress; + factFieldValuesEmailAddress.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesEmailAddress.DimEmailAddrress = dimEmailAddress; + _ttvContext.FactFieldValues.Add(factFieldValuesEmailAddress); + } + else + { + factFieldValuesEmailAddress.Modified = currentDateTime; + } + } + + + // Keyword + List keywords = _orcidJsonParserService.GetKeywords(json); + // Get DimFieldDisplaySettings for keyword + DimFieldDisplaySetting dimFieldDisplaySettingsKeyword = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_KEYWORD); + // Collect list of processed FactFieldValues related to keyword. Needed when deleting keywords. + List processedKeywordFactFieldValues = new(); + foreach (OrcidKeyword keyword in keywords) + { + // Check if FactFieldValues contains entry, which points to ORCID put code value in DimKeyword + FactFieldValue factFieldValuesKeyword = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == keyword.PutCode.Value.ToString()); + + if (factFieldValuesKeyword != null) + { + // Update existing DimKeyword + DimKeyword dimKeyword = factFieldValuesKeyword.DimKeyword; + dimKeyword.Keyword = keyword.Value; + dimKeyword.Modified = currentDateTime; + // Update existing FactFieldValue + factFieldValuesKeyword.Modified = currentDateTime; + } + else + { + // Create new DimKeyword + DimKeyword dimKeyword = new() + { + Keyword = keyword.Value, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + DimRegisteredDataSourceId = orcidRegisteredDataSourceId, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimKeywords.Add(dimKeyword); + + // Add keyword ORCID put code into DimPid + DimPid dimPidOrcidPutCodeKeyword = _userProfileService.GetEmptyDimPid(); + dimPidOrcidPutCodeKeyword.PidContent = keyword.PutCode.GetDbValue(); + dimPidOrcidPutCodeKeyword.PidType = Constants.PidTypes.ORCID_PUT_CODE; + dimPidOrcidPutCodeKeyword.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPidOrcidPutCodeKeyword.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPidOrcidPutCodeKeyword); + + // Create FactFieldValues for keyword + factFieldValuesKeyword = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesKeyword.DimUserProfile = dimUserProfile; + factFieldValuesKeyword.DimFieldDisplaySettings = dimFieldDisplaySettingsKeyword; + factFieldValuesKeyword.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesKeyword.DimKeyword = dimKeyword; + factFieldValuesKeyword.DimPidIdOrcidPutCodeNavigation = dimPidOrcidPutCodeKeyword; + _ttvContext.FactFieldValues.Add(factFieldValuesKeyword); + } + processedKeywordFactFieldValues.Add(factFieldValuesKeyword); + } + // Remove existing keywords which were not in ORCID data. + foreach (FactFieldValue ffvKeyword in dimFieldDisplaySettingsKeyword.FactFieldValues) + { + if (!processedKeywordFactFieldValues.Contains(ffvKeyword)) + { + _ttvContext.FactFieldValues.Remove(ffvKeyword); + _ttvContext.DimKeywords.Remove(ffvKeyword.DimKeyword); + } + } + + + // External identifier (=DimPid) + List externalIdentifiers = _orcidJsonParserService.GetExternalIdentifiers(json); + // Get DimFieldDisplaySettings for keyword + DimFieldDisplaySetting dimFieldDisplaySettingsExternalIdentifier = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER); + foreach (OrcidExternalIdentifier externalIdentifier in externalIdentifiers) + { + // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid + FactFieldValue factFieldValuesExternalIdentifier = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == externalIdentifier.PutCode.Value.ToString()); + + if (factFieldValuesExternalIdentifier != null) + { + // Update existing DimPid + DimPid dimPid = factFieldValuesExternalIdentifier.DimPid; + dimPid.PidContent = externalIdentifier.ExternalIdValue; + dimPid.PidType = externalIdentifier.ExternalIdType; + dimPid.Modified = currentDateTime; + // Update existing FactFieldValue + factFieldValuesExternalIdentifier.Modified = currentDateTime; + } + else + { + // Create new DimPid (external identifier is stored into DimPid) + DimPid dimPid = _userProfileService.GetEmptyDimPid(); + dimPid.PidContent = externalIdentifier.ExternalIdValue; + dimPid.PidType = externalIdentifier.ExternalIdType; + dimPid.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPid.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPid); + + // Add ORCID put code into DimPid + DimPid dimPidOrcidPutCodeExternalIdentifier = _userProfileService.GetEmptyDimPid(); + dimPidOrcidPutCodeExternalIdentifier.PidContent = externalIdentifier.PutCode.GetDbValue(); + dimPidOrcidPutCodeExternalIdentifier.PidType = Constants.PidTypes.ORCID_PUT_CODE; + dimPidOrcidPutCodeExternalIdentifier.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPidOrcidPutCodeExternalIdentifier.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPidOrcidPutCodeExternalIdentifier); + + // Create FactFieldValues for external identifier + factFieldValuesExternalIdentifier = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesExternalIdentifier.DimUserProfile = dimUserProfile; + factFieldValuesExternalIdentifier.DimFieldDisplaySettings = dimFieldDisplaySettingsExternalIdentifier; + factFieldValuesExternalIdentifier.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesExternalIdentifier.DimPid = dimPid; + factFieldValuesExternalIdentifier.DimPidIdOrcidPutCodeNavigation = dimPidOrcidPutCodeExternalIdentifier; + _ttvContext.FactFieldValues.Add(factFieldValuesExternalIdentifier); + } + } + + + // Education + List educations = _orcidJsonParserService.GetEducations(json); + foreach (OrcidEducation education in educations) + { + // Check if FactFieldValues contains entry, which points to ORCID put code value in DimEducation + FactFieldValue factFieldValuesEducation = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == education.PutCode.Value.ToString()); + + // Start date + DimDate startDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == education.StartDate.Year && dd.Month == education.StartDate.Month && dd.Day == education.StartDate.Day); + if (startDate == null) + { + startDate = new DimDate() + { + Year = education.StartDate.Year, + Month = education.StartDate.Month, + Day = education.StartDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(startDate); + } + + // End date + DimDate endDate = await _ttvContext.DimDates.FirstOrDefaultAsync(ed => ed.Year == education.EndDate.Year && ed.Month == education.EndDate.Month && ed.Day == education.EndDate.Day); + if (endDate == null) + { + endDate = new DimDate() + { + Year = education.EndDate.Year, + Month = education.EndDate.Month, + Day = education.EndDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(endDate); + } + + if (factFieldValuesEducation != null) + { + // Update existing DimEducation + DimEducation dimEducation = factFieldValuesEducation.DimEducation; + dimEducation.NameEn = education.RoleTitle; + dimEducation.DegreeGrantingInstitutionName = education.OrganizationName; + dimEducation.DimStartDateNavigation = startDate; + dimEducation.DimEndDateNavigation = endDate; + dimEducation.Modified = currentDateTime; + + // Update existing FactFieldValue + factFieldValuesEducation.Modified = currentDateTime; + } + else + { + // Create new DimEducation + DimEducation dimEducation = new() + { + NameEn = education.RoleTitle, + DegreeGrantingInstitutionName = education.OrganizationName, + DimStartDateNavigation = startDate, + DimEndDateNavigation = endDate, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + DimKnownPersonId = dimUserProfile.DimKnownPersonId, + DimRegisteredDataSourceId = orcidRegisteredDataSourceId, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimEducations.Add(dimEducation); + + // Add education ORCID put code into DimPid + DimPid dimPidOrcidPutCodeEducation = _userProfileService.GetEmptyDimPid(); + dimPidOrcidPutCodeEducation.PidContent = education.PutCode.GetDbValue(); + dimPidOrcidPutCodeEducation.PidType = Constants.PidTypes.ORCID_PUT_CODE; + dimPidOrcidPutCodeEducation.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPidOrcidPutCodeEducation.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPidOrcidPutCodeEducation); + + // Get DimFieldDisplaySettings for education + DimFieldDisplaySetting dimFieldDisplaySettingsEducation = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsEducation => dfdsEducation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION); + + // Create FactFieldValues for education + factFieldValuesEducation = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesEducation.DimUserProfile = dimUserProfile; + factFieldValuesEducation.DimFieldDisplaySettings = dimFieldDisplaySettingsEducation; + factFieldValuesEducation.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesEducation.DimEducation = dimEducation; + factFieldValuesEducation.DimPidIdOrcidPutCodeNavigation = dimPidOrcidPutCodeEducation; + _ttvContext.FactFieldValues.Add(factFieldValuesEducation); + } + } + + + + // Employment (Affiliation in Ttv database) + List employments = _orcidJsonParserService.GetEmployments(json); + foreach (OrcidEmployment employment in employments) + { + /* + * Organization handling. + * Search organization identifier from DimPid based on ORCID's disambiguated-organization-identifier data. + * If organization identifier is found in DimPid, use the linked DimOrganization. + * If organization identifier is not found, add organization name into DimIdentifierlessData as type 'organization_name'. + * + * Department name handling. + * ORCID employment may contain 'department-name'. Add deparment-name into DimIdentifierlessData as type 'organization_unit'. + */ + + // Check if FactFieldValues contains entry, which points to ORCID put code value in DimAffiliation + FactFieldValue factFieldValuesAffiliation = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == employment.PutCode.Value.ToString()); + + // Search organization identifier from DimPid based on ORCID's disambiguated-organization-identifier data. + int? dimOrganization_id_affiliation = await _organizationHandlerService.FindOrganizationIdByOrcidDisambiguationIdentifier( + orcidDisambiguatedOrganizationIdentifier: employment.DisambiguatedOrganizationIdentifier, + orcidDisambiguationSource: employment.DisambiguationSource + ); + + // Start date + DimDate startDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.StartDate.Year && dd.Month == employment.StartDate.Month && dd.Day == employment.StartDate.Day); + if (startDate == null) + { + startDate = new DimDate() + { + Year = employment.StartDate.Year, + Month = employment.StartDate.Month, + Day = employment.StartDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(startDate); + } + + // End date + DimDate endDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.EndDate.Year && dd.Month == employment.EndDate.Month && dd.Day == employment.EndDate.Day); + if (endDate == null) + { + endDate = new DimDate() + { + Year = employment.EndDate.Year, + Month = employment.EndDate.Month, + Day = employment.EndDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(endDate); + } + + /* + * Check if affiliation already exists in profile. + */ + if (factFieldValuesAffiliation != null) + { + /* + * Affiliation already exists in profile, update. + */ + DimAffiliation dimAffiliation_existing = factFieldValuesAffiliation.DimAffiliation; + dimAffiliation_existing.PositionNameEn = employment.RoleTitle; + dimAffiliation_existing.StartDateNavigation = startDate; + dimAffiliation_existing.EndDateNavigation = endDate; + dimAffiliation_existing.Modified = currentDateTime; + + /* + * Update organization relation or identifierless data for existing affiliation. + */ + if (dimOrganization_id_affiliation != null && dimOrganization_id_affiliation > 0) + { + /* + * Affiliation relates directly to DimOrganization. + */ + dimAffiliation_existing.DimOrganizationId = (int)dimOrganization_id_affiliation; + + /* + * When affiliation has related DimOrganization, possibly existing DimIdentifierlessData of type organization_name must be removed. + */ + if (factFieldValuesAffiliation.DimIdentifierlessDataId != -1 && factFieldValuesAffiliation.DimIdentifierlessData.Type == Constants.IdentifierlessDataTypes.ORGANIZATION_NAME) + { + _ttvContext.DimIdentifierlessData.Remove(factFieldValuesAffiliation.DimIdentifierlessData); + } + } + else + { + /* + * Affiliation does to relate directly to any DimOrganization. + * Update or create relation to DimIdentifierlessData via FactFieldValues. + */ + if (factFieldValuesAffiliation.DimIdentifierlessDataId != -1 && factFieldValuesAffiliation.DimIdentifierlessData.Type == Constants.IdentifierlessDataTypes.ORGANIZATION_NAME) + { + /* + * Update organization name in existing DimIdentifierlessData. + */ + factFieldValuesAffiliation.DimIdentifierlessData.ValueEn = employment.OrganizationName; + } + else + { + /* + * Create new DimIdentifierlessData for organization name. + */ + DimIdentifierlessDatum dimIdentifierlessDatum_organization_name = _organizationHandlerService.CreateIdentifierlessData_OrganizationName(nameFi: "", nameEn: employment.OrganizationName, nameSv: ""); + _ttvContext.DimIdentifierlessData.Add(dimIdentifierlessDatum_organization_name); + factFieldValuesAffiliation.DimIdentifierlessData = dimIdentifierlessDatum_organization_name; + } + } + + // Update modified timestamp in FactFieldValue + factFieldValuesAffiliation.Modified = currentDateTime; + } + else + { + /* + * Affiliation does not yet exists in profile. Create new. + * TODO: AffiliationType handling + */ + DimAffiliation dimAffiliation_new = new() + { + DimOrganizationId = -1, + StartDateNavigation = startDate, + EndDateNavigation = endDate, + PositionNameEn = employment.RoleTitle, + AffiliationType = -1, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + DimKnownPersonId = dimUserProfile.DimKnownPersonId, + DimRegisteredDataSourceId = orcidRegisteredDataSourceId, + Created = currentDateTime, + Modified = currentDateTime + }; + + // If organization was found, add relation + if (dimOrganization_id_affiliation != null && dimOrganization_id_affiliation > 0) + { + dimAffiliation_new.DimOrganizationId = (int)dimOrganization_id_affiliation; + } + _ttvContext.DimAffiliations.Add(dimAffiliation_new); + + // Add employment (=affiliation) ORCID put code into DimPid + DimPid dimPidOrcidPutCodeEmployment = _userProfileService.GetEmptyDimPid(); + dimPidOrcidPutCodeEmployment.PidContent = employment.PutCode.GetDbValue(); + dimPidOrcidPutCodeEmployment.PidType = Constants.PidTypes.ORCID_PUT_CODE; + dimPidOrcidPutCodeEmployment.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPidOrcidPutCodeEmployment.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPidOrcidPutCodeEmployment); + + // Get DimFieldDisplaySettings for affiliation + DimFieldDisplaySetting dimFieldDisplaySettingsAffiliation = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsAffiliation => dfdsAffiliation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION); + + // Create FactFieldValues for affiliation + factFieldValuesAffiliation = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesAffiliation.DimUserProfile = dimUserProfile; + factFieldValuesAffiliation.DimFieldDisplaySettings = dimFieldDisplaySettingsAffiliation; + factFieldValuesAffiliation.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesAffiliation.DimAffiliation = dimAffiliation_new; + factFieldValuesAffiliation.DimPidIdOrcidPutCodeNavigation = dimPidOrcidPutCodeEmployment; + + // If organization was not found, add organization_name into DimIdentifierlessData + if (dimOrganization_id_affiliation == null || dimOrganization_id_affiliation == -1) + { + DimIdentifierlessDatum dimIdentifierlessData_oganizationName = _organizationHandlerService.CreateIdentifierlessData_OrganizationName(nameFi: "", nameEn: employment.OrganizationName, nameSv: ""); + _ttvContext.DimIdentifierlessData.Add(dimIdentifierlessData_oganizationName); + factFieldValuesAffiliation.DimIdentifierlessData = dimIdentifierlessData_oganizationName; + } + + _ttvContext.FactFieldValues.Add(factFieldValuesAffiliation); + } + + /* + * Affiliation department name handling + */ + if (employment.DepartmentName != "") + { + // ORCID employment contains 'department-name' + + // Check if FactFieldValue has related DimIdentifierlessData. + // If exists, check if type is 'organization_name' or 'organization_unit' + // If type is 'organization_name', check if it has related DimIdentifierlessData of type 'organization_unit'. If exists, update value. If does not exist, create new. + // If type is 'organization_unit, update value. + // If does not exist, create new using type 'organization_unit'. + if (factFieldValuesAffiliation.DimIdentifierlessData != null) + { + // DimIdentifierlessData exists + // Check type. + if (factFieldValuesAffiliation.DimIdentifierlessData.Type == Constants.IdentifierlessDataTypes.ORGANIZATION_NAME) + { + // Type is 'organization_name'. Check if it has related DimIdentifierlessData of type 'organization_unit' + if ( + factFieldValuesAffiliation.DimIdentifierlessData.InverseDimIdentifierlessData.Count > 0 && + factFieldValuesAffiliation.DimIdentifierlessData.InverseDimIdentifierlessData.First().Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT + ) + { + // Has related DimIdentifierlessData of type 'organization_unit'. Update. + factFieldValuesAffiliation.DimIdentifierlessData.InverseDimIdentifierlessData.First().ValueEn = employment.DepartmentName; + factFieldValuesAffiliation.DimIdentifierlessData.InverseDimIdentifierlessData.First().ValueFi = ""; + factFieldValuesAffiliation.DimIdentifierlessData.InverseDimIdentifierlessData.First().ValueSv = ""; + factFieldValuesAffiliation.DimIdentifierlessData.InverseDimIdentifierlessData.First().Modified = currentDateTime; + } + else + { + // Does not have related DimIdentifierlessData of type 'organization_unit'. Add new. Set as child of DimIdentifierlessData of type 'organization_name' + DimIdentifierlessDatum dimIdentifierlessData_organizationUnit = + _organizationHandlerService.CreateIdentifierlessData_OrganizationUnit( + parentDimIdentifierlessData: factFieldValuesAffiliation.DimIdentifierlessData, + nameFi: "", + nameEn: employment.DepartmentName, + nameSv: "" + ); + _ttvContext.DimIdentifierlessData.Add(dimIdentifierlessData_organizationUnit); + } + } + else if (factFieldValuesAffiliation.DimIdentifierlessData.Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT) + { + // Type is 'organization_unit'. Update + factFieldValuesAffiliation.DimIdentifierlessData.ValueEn = employment.DepartmentName; + factFieldValuesAffiliation.DimIdentifierlessData.ValueFi = ""; + factFieldValuesAffiliation.DimIdentifierlessData.ValueSv = ""; + factFieldValuesAffiliation.DimIdentifierlessData.Modified = currentDateTime; + } + } + else + { + // DimIdentifierlessData does not exist. Create new. Do not set parent DimIdentifierlessData, instead link to FactFieldValue + DimIdentifierlessDatum dimIdentifierlessData_organizationUnit = + _organizationHandlerService.CreateIdentifierlessData_OrganizationUnit( + parentDimIdentifierlessData: null, + nameFi: "", + nameEn: employment.DepartmentName, + nameSv: "" + ); + _ttvContext.DimIdentifierlessData.Add(dimIdentifierlessData_organizationUnit); + factFieldValuesAffiliation.DimIdentifierlessData = dimIdentifierlessData_organizationUnit; + } + } + } + + + + // Publication + List orcidPublications = _orcidJsonParserService.GetPublications(json); + foreach (OrcidPublication orcidPublication in orcidPublications) + { + // Check if FactFieldValues contains entry, which points to ORCID put code value in DimOrcidPublication + FactFieldValue factFieldValuesPublication = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == orcidPublication.PutCode.Value.ToString()); + + if (factFieldValuesPublication != null) + { + // Update existing DimOrcidPublication + DimOrcidPublication dimOrcidPublication = factFieldValuesPublication.DimOrcidPublication; + dimOrcidPublication.OrcidWorkType = orcidPublication.Type; + dimOrcidPublication.PublicationName = orcidPublication.PublicationName; + dimOrcidPublication.PublicationYear = orcidPublication.PublicationYear; + dimOrcidPublication.DoiHandle = orcidPublication.Doi; + dimOrcidPublication.Modified = currentDateTime; + // Update existing FactFieldValue + factFieldValuesPublication.Modified = currentDateTime; + } + else + { + // Create new DimOrcidPublication + DimOrcidPublication dimOrcidPublication = _userProfileService.GetEmptyDimOrcidPublication(); + dimOrcidPublication.OrcidWorkType = orcidPublication.Type; + dimOrcidPublication.PublicationName = orcidPublication.PublicationName; + dimOrcidPublication.PublicationYear = orcidPublication.PublicationYear; + dimOrcidPublication.DoiHandle = orcidPublication.Doi; + dimOrcidPublication.SourceId = Constants.SourceIdentifiers.PROFILE_API; + dimOrcidPublication.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + dimOrcidPublication.Created = currentDateTime; + _ttvContext.DimOrcidPublications.Add(dimOrcidPublication); + + // Add publication's ORCID put code into DimPid + DimPid dimPidOrcidPutCodePublication = _userProfileService.GetEmptyDimPid(); + dimPidOrcidPutCodePublication.PidContent = orcidPublication.PutCode.GetDbValue(); + dimPidOrcidPutCodePublication.PidType = Constants.PidTypes.ORCID_PUT_CODE; + dimPidOrcidPutCodePublication.DimKnownPersonId = dimUserProfile.DimKnownPersonId; + dimPidOrcidPutCodePublication.SourceId = Constants.SourceIdentifiers.PROFILE_API; + _ttvContext.DimPids.Add(dimPidOrcidPutCodePublication); + + // Get DimFieldDisplaySettings for orcid publication + DimFieldDisplaySetting dimFieldDisplaySettingsOrcidPublication = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsPublication => dfdsPublication.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID); + + // Create FactFieldValues for orcid publication + factFieldValuesPublication = _userProfileService.GetEmptyFactFieldValue(); + factFieldValuesPublication.DimUserProfile = dimUserProfile; + factFieldValuesPublication.DimFieldDisplaySettings = dimFieldDisplaySettingsOrcidPublication; + factFieldValuesPublication.DimRegisteredDataSourceId = orcidRegisteredDataSourceId; + factFieldValuesPublication.DimOrcidPublication = dimOrcidPublication; + factFieldValuesPublication.DimPidIdOrcidPutCodeNavigation = dimPidOrcidPutCodePublication; + _ttvContext.FactFieldValues.Add(factFieldValuesPublication); + } + } + + await _ttvContext.SaveChangesAsync(); + return true; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/OrcidJsonParserService.cs b/aspnetcore/src/api/Services/OrcidJsonParserService.cs index 642155b8..8f4a59b4 100644 --- a/aspnetcore/src/api/Services/OrcidJsonParserService.cs +++ b/aspnetcore/src/api/Services/OrcidJsonParserService.cs @@ -11,14 +11,14 @@ namespace api.Services * ORCID record schema * https://info.orcid.org/documentation/integration-guide/orcid-record/ */ - public class OrcidJsonParserService + public class OrcidJsonParserService : IOrcidJsonParserService { /* * Put code */ - private OrcidPutCode getOrcidPutCode(JsonElement orcidJsonElement) + private OrcidPutCode GetOrcidPutCode(JsonElement orcidJsonElement) { - var putCode = new OrcidPutCode(null); + OrcidPutCode putCode = new(null); // put code if (orcidJsonElement.TryGetProperty("put-code", out JsonElement putCodeElement)) @@ -36,28 +36,28 @@ private OrcidPutCode getOrcidPutCode(JsonElement orcidJsonElement) /* * Date */ - private OrcidDate getOrcidDate(JsonElement orcidJsonDateElement) + private OrcidDate GetOrcidDate(JsonElement orcidJsonDateElement) { - var orcidDate = new OrcidDate(); + OrcidDate orcidDate = new(); if (orcidJsonDateElement.ValueKind != JsonValueKind.Null) { // Year - orcidJsonDateElement.TryGetProperty("year", out var yearElement); + orcidJsonDateElement.TryGetProperty("year", out JsonElement yearElement); if (yearElement.ValueKind != JsonValueKind.Null) { orcidDate.Year = int.Parse(yearElement.GetProperty("value").GetString()); } // Month - orcidJsonDateElement.TryGetProperty("month", out var monthElement); + orcidJsonDateElement.TryGetProperty("month", out JsonElement monthElement); if (monthElement.ValueKind != JsonValueKind.Null) { orcidDate.Month = int.Parse(monthElement.GetProperty("value").GetString()); } // Day - orcidJsonDateElement.TryGetProperty("day", out var dayElement); + orcidJsonDateElement.TryGetProperty("day", out JsonElement dayElement); if (dayElement.ValueKind != JsonValueKind.Null) { orcidDate.Day = int.Parse(dayElement.GetProperty("value").GetString()); @@ -70,21 +70,17 @@ private OrcidDate getOrcidDate(JsonElement orcidJsonDateElement) /* * Get DOI from ORCID publication */ - private string getPublicationDoi(JsonElement workElement) + private string GetPublicationDoi(JsonElement externalIdsElement) { string doi = ""; - JsonElement externalIdsElement; - if (workElement.TryGetProperty("external-ids", out externalIdsElement)) + if (externalIdsElement.ValueKind != JsonValueKind.Null && externalIdsElement.TryGetProperty("external-id", out JsonElement externalIdElement)) { - JsonElement externalIdElement; - if (externalIdsElement.ValueKind != JsonValueKind.Null && externalIdsElement.TryGetProperty("external-id", out externalIdElement)) + foreach (JsonElement idElement in externalIdElement.EnumerateArray()) { - foreach (JsonElement idElement in externalIdElement.EnumerateArray()) + if (idElement.GetProperty("external-id-type").GetString() == "doi") { - if (idElement.GetProperty("external-id-type").GetString() == "doi") - { - doi = idElement.GetProperty("external-id-value").GetString(); - } + doi = idElement.GetProperty("external-id-value").GetString(); + break; } } } @@ -94,17 +90,14 @@ private string getPublicationDoi(JsonElement workElement) /* * Get publication year from ORCID publication */ - private int? getPublicationYear(JsonElement workElement) + private int? GetPublicationYear(JsonElement workElement) { int? publicationYear = null; - JsonElement publicationDateElement; - if (workElement.TryGetProperty("publication-date", out publicationDateElement)) + if (workElement.TryGetProperty("publication-date", out JsonElement publicationDateElement)) { - JsonElement yearElement; - if (publicationDateElement.ValueKind != JsonValueKind.Null && publicationDateElement.TryGetProperty("year", out yearElement)) + if (publicationDateElement.ValueKind != JsonValueKind.Null && publicationDateElement.TryGetProperty("year", out JsonElement yearElement)) { - JsonElement valueElement; - if (yearElement.ValueKind != JsonValueKind.Null && yearElement.TryGetProperty("value", out valueElement)) + if (yearElement.ValueKind != JsonValueKind.Null && yearElement.TryGetProperty("value", out JsonElement valueElement)) { publicationYear = Int32.Parse(valueElement.GetString()); } @@ -116,10 +109,12 @@ private string getPublicationDoi(JsonElement workElement) /* * Check if Json document is full ORCID record */ - private Boolean isFullRecord(JsonDocument orcidJsonDocument) + private Boolean IsFullRecord(JsonDocument orcidJsonDocument) { - var myValue = new System.Text.Json.JsonElement(); - return orcidJsonDocument.RootElement.TryGetProperty("person", out myValue); + //var myValue = new System.Text.Json.JsonElement(); + //return orcidJsonDocument.RootElement.TryGetProperty("person", out myValue); + + return orcidJsonDocument.RootElement.TryGetProperty("person", out _); } /* @@ -127,28 +122,34 @@ private Boolean isFullRecord(JsonDocument orcidJsonDocument) */ public OrcidGivenNames GetGivenNames(String json) { - using (JsonDocument document = JsonDocument.Parse(json)) + using JsonDocument document = JsonDocument.Parse(json); + + // Get name element + JsonElement nameElement = IsFullRecord(document) + ? document.RootElement.GetProperty("person").GetProperty("name") + : document.RootElement.GetProperty("name"); + + JsonElement givenNamesElement; + // Check if name element is null + if (nameElement.ValueKind == JsonValueKind.Null) { - JsonElement givenNamesElement; - if (this.isFullRecord(document)) - { - givenNamesElement = document.RootElement.GetProperty("person").GetProperty("name").GetProperty("given-names"); - } - else - { - givenNamesElement = document.RootElement.GetProperty("name").GetProperty("given-names"); - } + return new OrcidGivenNames(""); + } + else + { + givenNamesElement = nameElement.GetProperty("given-names"); + } - JsonElement valueElement; - if (givenNamesElement.ValueKind != JsonValueKind.Null && givenNamesElement.TryGetProperty("value", out valueElement)) - { - return new OrcidGivenNames( - valueElement.GetString() - ); - } else - { - return new OrcidGivenNames(""); - } + // Get value + if (givenNamesElement.ValueKind != JsonValueKind.Null && givenNamesElement.TryGetProperty("value", out JsonElement valueElement)) + { + return new OrcidGivenNames( + valueElement.GetString() + ); + } + else + { + return new OrcidGivenNames(""); } } @@ -157,29 +158,34 @@ public OrcidGivenNames GetGivenNames(String json) */ public OrcidFamilyName GetFamilyName(String json) { - using (JsonDocument document = JsonDocument.Parse(json)) + using JsonDocument document = JsonDocument.Parse(json); + + // Get name element + JsonElement nameElement = IsFullRecord(document) + ? document.RootElement.GetProperty("person").GetProperty("name") + : document.RootElement.GetProperty("name"); + + JsonElement familyNameElement; + // Check if name element is null + if (nameElement.ValueKind == JsonValueKind.Null) { - JsonElement familyNameElement; - if (this.isFullRecord(document)) - { - familyNameElement = document.RootElement.GetProperty("person").GetProperty("name").GetProperty("family-name"); - } - else - { - familyNameElement = document.RootElement.GetProperty("name").GetProperty("family-name"); - } + return new OrcidFamilyName(""); + } + else + { + familyNameElement = nameElement.GetProperty("family-name"); + } - JsonElement valueElement; - if (familyNameElement.ValueKind != JsonValueKind.Null && familyNameElement.TryGetProperty("value", out valueElement)) - { - return new OrcidFamilyName( - valueElement.GetString() - ); - } - else - { - return new OrcidFamilyName(""); - } + // Get value + if (familyNameElement.ValueKind != JsonValueKind.Null && familyNameElement.TryGetProperty("value", out JsonElement valueElement)) + { + return new OrcidFamilyName( + valueElement.GetString() + ); + } + else + { + return new OrcidFamilyName(""); } } @@ -188,29 +194,34 @@ public OrcidFamilyName GetFamilyName(String json) */ public OrcidCreditName GetCreditName(String json) { - using (JsonDocument document = JsonDocument.Parse(json)) + using JsonDocument document = JsonDocument.Parse(json); + + // Get name element + JsonElement nameElement = IsFullRecord(document) + ? document.RootElement.GetProperty("person").GetProperty("name") + : document.RootElement.GetProperty("name"); + + JsonElement creditNameElement; + // Check if name element is null + if (nameElement.ValueKind == JsonValueKind.Null) { - JsonElement creditNameElement; - if (this.isFullRecord(document)) - { - creditNameElement = document.RootElement.GetProperty("person").GetProperty("name").GetProperty("credit-name"); - } - else - { - creditNameElement = document.RootElement.GetProperty("name").GetProperty("credit-name"); - } + return new OrcidCreditName(""); + } + else + { + creditNameElement = nameElement.GetProperty("credit-name"); + } - JsonElement valueElement; - if (creditNameElement.ValueKind != JsonValueKind.Null && creditNameElement.TryGetProperty("value", out valueElement)) - { - return new OrcidCreditName( - valueElement.GetString() - ); - } - else - { - return new OrcidCreditName(""); - } + // Get value + if (creditNameElement.ValueKind != JsonValueKind.Null && creditNameElement.TryGetProperty("value", out JsonElement valueElement)) + { + return new OrcidCreditName( + valueElement.GetString() + ); + } + else + { + return new OrcidCreditName(""); } } @@ -219,23 +230,17 @@ public OrcidCreditName GetCreditName(String json) */ public List GetOtherNames(String json) { - var otherNamesElement = new JsonElement(); - var otherNames = new List { }; + List otherNames = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { - if (this.isFullRecord(document)) - { - otherNamesElement = document.RootElement.GetProperty("person").GetProperty("other-names"); - } - else - { - otherNamesElement = document.RootElement.GetProperty("other-names"); - } + JsonElement otherNamesElement = IsFullRecord(document) + ? document.RootElement.GetProperty("person").GetProperty("other-names") + : document.RootElement.GetProperty("other-names"); foreach (JsonElement element in otherNamesElement.GetProperty("other-name").EnumerateArray()) { - var value = element.GetProperty("content").GetString(); - var putCode = this.getOrcidPutCode(element); + string value = element.GetProperty("content").GetString(); + OrcidPutCode putCode = this.GetOrcidPutCode(element); otherNames.Add( new OrcidOtherName(value, putCode) ); @@ -249,38 +254,29 @@ public List GetOtherNames(String json) */ public OrcidBiography GetBiography(String json) { - using (JsonDocument document = JsonDocument.Parse(json)) - { - JsonElement biographyElement; - JsonElement contentElement; + using JsonDocument document = JsonDocument.Parse(json); - if (this.isFullRecord(document)) - { - biographyElement = document.RootElement.GetProperty("person").GetProperty("biography"); - } - else - { - biographyElement = document.RootElement.GetProperty("biography"); - } + JsonElement biographyElement = IsFullRecord(document) + ? document.RootElement.GetProperty("person").GetProperty("biography") + : document.RootElement.GetProperty("biography"); - if (biographyElement.ValueKind == JsonValueKind.Null) + if (biographyElement.ValueKind == JsonValueKind.Null) + { + return null; + } + else + { + biographyElement.TryGetProperty("content", out JsonElement contentElement); + + if (contentElement.ValueKind.Equals(null)) { return null; } else { - biographyElement.TryGetProperty("content", out contentElement); - - if (contentElement.ValueKind.Equals(null)) - { - return null; - } - else - { - return new OrcidBiography( - contentElement.GetString() - ); - } + return new OrcidBiography( + value: contentElement.GetString() + ); } } } @@ -290,16 +286,16 @@ public OrcidBiography GetBiography(String json) */ public List GetResearcherUrls(String json) { - var urls = new List { }; + List urls = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { foreach (JsonElement element in document.RootElement.GetProperty("person").GetProperty("researcher-urls").GetProperty("researcher-url").EnumerateArray()) { urls.Add( new OrcidResearcherUrl( - element.GetProperty("url-name").GetString(), - element.GetProperty("url").GetProperty("value").GetString(), - this.getOrcidPutCode(element) + urlName: element.GetProperty("url-name").GetString(), + url: element.GetProperty("url").GetProperty("value").GetString(), + putCode: this.GetOrcidPutCode(element) ) ); } @@ -312,15 +308,15 @@ public List GetResearcherUrls(String json) */ public List GetEmails(String json) { - var emails = new List { }; + List emails = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { foreach (JsonElement element in document.RootElement.GetProperty("person").GetProperty("emails").GetProperty("email").EnumerateArray()) { emails.Add( new OrcidEmail( - element.GetProperty("email").GetString(), - this.getOrcidPutCode(element) + value: element.GetProperty("email").GetString(), + putCode: this.GetOrcidPutCode(element) ) ); } @@ -333,15 +329,15 @@ public List GetEmails(String json) */ public List GetKeywords(String json) { - var keywords = new List { }; + List keywords = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { foreach (JsonElement element in document.RootElement.GetProperty("person").GetProperty("keywords").GetProperty("keyword").EnumerateArray()) { keywords.Add( new OrcidKeyword( - element.GetProperty("content").GetString(), - this.getOrcidPutCode(element) + value: element.GetProperty("content").GetString(), + putCode: this.GetOrcidPutCode(element) ) ); } @@ -354,17 +350,17 @@ public List GetKeywords(String json) */ public List GetExternalIdentifiers(String json) { - var externalIdentifiers = new List { }; + List externalIdentifiers = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { foreach (JsonElement element in document.RootElement.GetProperty("person").GetProperty("external-identifiers").GetProperty("external-identifier").EnumerateArray()) { externalIdentifiers.Add( new OrcidExternalIdentifier( - element.GetProperty("external-id-type").GetString(), - element.GetProperty("external-id-value").GetString(), - element.GetProperty("external-id-url").GetProperty("value").GetString(), - this.getOrcidPutCode(element) + externalIdType: element.GetProperty("external-id-type").GetString(), + externalIdValue: element.GetProperty("external-id-value").GetString(), + externalIdUrl: element.GetProperty("external-id-url").GetProperty("value").GetString(), + putCode: this.GetOrcidPutCode(element) ) ); } @@ -377,31 +373,41 @@ public List GetExternalIdentifiers(String json) */ public List GetEducations(String json) { - var educations = new List { }; + List educations = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { JsonElement educationsElement = document.RootElement.GetProperty("activities-summary").GetProperty("educations"); - JsonElement affiliationGroupsElement; - if (educationsElement.TryGetProperty("affiliation-group", out affiliationGroupsElement)) - { - foreach(JsonElement affiliationGroupElement in affiliationGroupsElement.EnumerateArray()) { - JsonElement summariesElement; + if (educationsElement.TryGetProperty("affiliation-group", out JsonElement affiliationGroupsElement)) + { + foreach (JsonElement affiliationGroupElement in affiliationGroupsElement.EnumerateArray()) + { - if (affiliationGroupElement.TryGetProperty("summaries", out summariesElement)) + if (affiliationGroupElement.TryGetProperty("summaries", out JsonElement summariesElement)) { foreach (JsonElement summaryElement in summariesElement.EnumerateArray()) { - JsonElement educationSummaryElement; - if (summaryElement.TryGetProperty("education-summary", out educationSummaryElement)) { + if (summaryElement.TryGetProperty("education-summary", out JsonElement educationSummaryElement)) + { + string disambiguatedOrganizationIdentifier = ""; + string disambiguationSource = ""; + if (educationSummaryElement.GetProperty("organization").TryGetProperty("disambiguated-organization", out JsonElement disambiguatedOrganizationElement)) + { + if (disambiguatedOrganizationElement.ValueKind != JsonValueKind.Null) + { + disambiguatedOrganizationIdentifier = disambiguatedOrganizationElement.GetProperty("disambiguated-organization-identifier").GetString(); + disambiguationSource = disambiguatedOrganizationElement.GetProperty("disambiguation-source").GetString(); + } + } + educations.Add( new OrcidEducation( - educationSummaryElement.GetProperty("organization").GetProperty("name").GetString(), - educationSummaryElement.GetProperty("department-name").GetString(), - educationSummaryElement.GetProperty("role-title").GetString(), - getOrcidDate(educationSummaryElement.GetProperty("start-date")), - getOrcidDate(educationSummaryElement.GetProperty("end-date")), - this.getOrcidPutCode(educationSummaryElement) + organizationName: educationSummaryElement.GetProperty("organization").GetProperty("name").GetString(), + departmentName: educationSummaryElement.GetProperty("department-name").GetString(), + roleTitle: educationSummaryElement.GetProperty("role-title").GetString(), + startDate: GetOrcidDate(educationSummaryElement.GetProperty("start-date")), + endDate: GetOrcidDate(educationSummaryElement.GetProperty("end-date")), + putCode: this.GetOrcidPutCode(educationSummaryElement) ) ); } @@ -418,33 +424,43 @@ public List GetEducations(String json) */ public List GetEmployments(String json) { - var employments = new List { }; + List employments = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { JsonElement employmentsElement = document.RootElement.GetProperty("activities-summary").GetProperty("employments"); - JsonElement affiliationGroupsElement; - if (employmentsElement.TryGetProperty("affiliation-group", out affiliationGroupsElement)) + if (employmentsElement.TryGetProperty("affiliation-group", out JsonElement affiliationGroupsElement)) { foreach (JsonElement affiliationGroupElement in affiliationGroupsElement.EnumerateArray()) { - JsonElement summariesElement; - if (affiliationGroupElement.TryGetProperty("summaries", out summariesElement)) + if (affiliationGroupElement.TryGetProperty("summaries", out JsonElement summariesElement)) { foreach (JsonElement summaryElement in summariesElement.EnumerateArray()) { - JsonElement employmentSummaryElement; - if (summaryElement.TryGetProperty("employment-summary", out employmentSummaryElement)) + if (summaryElement.TryGetProperty("employment-summary", out JsonElement employmentSummaryElement)) { + string disambiguatedOrganizationIdentifier = ""; + string disambiguationSource = ""; + if (employmentSummaryElement.GetProperty("organization").TryGetProperty("disambiguated-organization", out JsonElement disambiguatedOrganizationElement)) + { + if (disambiguatedOrganizationElement.ValueKind != JsonValueKind.Null) + { + disambiguatedOrganizationIdentifier = disambiguatedOrganizationElement.GetProperty("disambiguated-organization-identifier").GetString(); + disambiguationSource = disambiguatedOrganizationElement.GetProperty("disambiguation-source").GetString(); + } + } + employments.Add( new OrcidEmployment( - employmentSummaryElement.GetProperty("organization").GetProperty("name").GetString(), - employmentSummaryElement.GetProperty("department-name").GetString(), - employmentSummaryElement.GetProperty("role-title").GetString(), - getOrcidDate(employmentSummaryElement.GetProperty("start-date")), - getOrcidDate(employmentSummaryElement.GetProperty("end-date")), - this.getOrcidPutCode(employmentSummaryElement) + organizationName: employmentSummaryElement.GetProperty("organization").GetProperty("name").GetString(), + disambiguatedOrganizationIdentifier: disambiguatedOrganizationIdentifier, + disambiguationSource: disambiguationSource, + departmentName: employmentSummaryElement.GetProperty("department-name").GetString(), + roleTitle: employmentSummaryElement.GetProperty("role-title").GetString(), + startDate: GetOrcidDate(employmentSummaryElement.GetProperty("start-date")), + endDate: GetOrcidDate(employmentSummaryElement.GetProperty("end-date")), + putCode: this.GetOrcidPutCode(employmentSummaryElement) ) ); } @@ -461,18 +477,30 @@ public List GetEmployments(String json) */ public List GetPublications(String json) { - var publications = new List { }; + List publications = new() { }; using (JsonDocument document = JsonDocument.Parse(json)) { JsonElement publicationsElement = document.RootElement.GetProperty("activities-summary").GetProperty("works"); - JsonElement groupsElement; - if (publicationsElement.TryGetProperty("group", out groupsElement)) + if (publicationsElement.TryGetProperty("group", out JsonElement groupsElement)) { foreach (JsonElement groupElement in groupsElement.EnumerateArray()) { - JsonElement workSummariesElement; - if (groupElement.TryGetProperty("work-summary", out workSummariesElement)) + /* + * Elements in "group" can contain "external-ids" and "work-summary". + * "work-summary" can contain multiple entries of the same publication. + * Get DOI from "external-ids" and the other properties from the first element in "work-summary". + */ + string DOI = ""; + + // Get publication DOI from "external-ids" array. + if (groupElement.TryGetProperty("external-ids", out JsonElement externalIdsElement)) + { + DOI = GetPublicationDoi(externalIdsElement); + } + + // Get publication properties from "work-summary" array. + if (groupElement.TryGetProperty("work-summary", out JsonElement workSummariesElement)) { foreach (JsonElement workElement in workSummariesElement.EnumerateArray()) { @@ -480,12 +508,15 @@ public List GetPublications(String json) new OrcidPublication() { PublicationName = workElement.GetProperty("title").GetProperty("title").GetProperty("value").GetString(), - DoiHandle = this.getPublicationDoi(workElement), - PublicationYear = this.getPublicationYear(workElement), + Doi = DOI, + PublicationYear = this.GetPublicationYear(workElement), Type = workElement.GetProperty("type").GetString(), - PutCode = this.getOrcidPutCode(workElement) + PutCode = this.GetOrcidPutCode(workElement) } ); + + // Import only one element from "work-summary" array. + break; } } } diff --git a/aspnetcore/src/api/Services/OrganizationHandlerService.cs b/aspnetcore/src/api/Services/OrganizationHandlerService.cs new file mode 100644 index 00000000..4d8016ac --- /dev/null +++ b/aspnetcore/src/api/Services/OrganizationHandlerService.cs @@ -0,0 +1,140 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using api.Models.Common; +using api.Models.ProfileEditor; +using api.Models.Ttv; +using Microsoft.EntityFrameworkCore; + +namespace api.Services +{ + /* + * OrganizationHandlerService implements logic related to DimOrganization. + */ + public class OrganizationHandlerService : IOrganizationHandlerService + { + private readonly TtvContext _ttvContext; + private readonly IUtilityService _utilityService; + + public OrganizationHandlerService(TtvContext ttvContext, IUtilityService utilityService) + { + _ttvContext = ttvContext; + _utilityService = utilityService; + } + + // For unit test + public OrganizationHandlerService() { } + + /* + * Map ORCID field disambiguation-source to pid_type in DimPid + */ + public string MapOrcidDisambiguationSourceToPidType(string orcidDisambiguationSource) + { + // TODO: add more mappings, when both ORCID disambiguation-source and DimPid.PidType values are known + switch (orcidDisambiguationSource) + { + case "GRID": + return "GRIDID"; + case "RINGGOLD": + return "RinggoldID"; + case "ROR": + return "RORID"; + } + // By default return empty string, do not return original disambiguation-source. + // This prevents unneccessary searches for DimPid. + return ""; + } + + /* + * Find organization by ORCID disambiguation identifier. + */ + public async Task FindOrganizationIdByOrcidDisambiguationIdentifier(string orcidDisambiguationSource, string orcidDisambiguatedOrganizationIdentifier) + { + // Map ORCID field disambiguation-source to pid_type in DimPid. + string pidType = MapOrcidDisambiguationSourceToPidType(orcidDisambiguationSource); + + // Exit immediately, if either of the search conditions is empty. + if (pidType == "" || orcidDisambiguatedOrganizationIdentifier == "") + { + return null; + } + + // Find matching DimPid. + DimPid dimPid = await _ttvContext.DimPids.Include(dp => dp.DimOrganization).Where(dp => dp.PidType == pidType && dp.PidContent == orcidDisambiguatedOrganizationIdentifier).AsNoTracking().FirstOrDefaultAsync(); + + // Check object validity and return related DimOrganization. + if (dimPid == null || dimPid.Id == -1 || dimPid.DimOrganizationId == -1) + { + return null; + } + else + { + return dimPid.DimOrganization.Id; + } + } + + /* + * Create entity DimIdentifierlessData for organization name. + */ + public DimIdentifierlessDatum CreateIdentifierlessData_OrganizationName(string nameFi, string nameEn, string nameSv) + { + DateTime currentDateTime = _utilityService.GetCurrentDateTime(); + return new DimIdentifierlessDatum() + { + Type = "organization_name", + DimIdentifierlessDataId = -1, + ValueFi = nameFi, + ValueEn = nameEn, + ValueSv = nameSv, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.ORCID, + Created = currentDateTime, + Modified = currentDateTime, + UnlinkedIdentifier = null + }; + } + + /* + * Create entity DimIdentifierlessData for organization unit. + */ + public DimIdentifierlessDatum CreateIdentifierlessData_OrganizationUnit(DimIdentifierlessDatum parentDimIdentifierlessData, string nameFi, string nameEn, string nameSv) + { + DateTime currentDateTime = _utilityService.GetCurrentDateTime(); + return new DimIdentifierlessDatum() + { + Type = "organization_unit", + DimIdentifierlessData = parentDimIdentifierlessData, + ValueFi = nameFi, + ValueEn = nameEn, + ValueSv = nameSv, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.ORCID, + Created = currentDateTime, + Modified = currentDateTime, + UnlinkedIdentifier = null + }; + } + + /* + * Get affiliation department name from FactFieldValue related DimIdentifierlessData. + */ + + // TODO: Remove this version? + public string GetAffiliationDepartmentNameFromFactFieldValue(FactFieldValue factFieldValue) + { + if (factFieldValue.DimIdentifierlessDataId > 0 && factFieldValue.DimIdentifierlessData.Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT) + { + return factFieldValue.DimIdentifierlessData.ValueEn; + } + else if ( + factFieldValue.DimIdentifierlessDataId > 0 && factFieldValue.DimIdentifierlessData.Type == Constants.IdentifierlessDataTypes.ORGANIZATION_NAME && + factFieldValue.DimIdentifierlessData.InverseDimIdentifierlessData.Count > 0 && factFieldValue.DimIdentifierlessData.InverseDimIdentifierlessData.First().Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT + ) + { + return factFieldValue.DimIdentifierlessData.InverseDimIdentifierlessData.First().ValueEn; + } + + return ""; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/SharingService.cs b/aspnetcore/src/api/Services/SharingService.cs new file mode 100644 index 00000000..2987af9f --- /dev/null +++ b/aspnetcore/src/api/Services/SharingService.cs @@ -0,0 +1,302 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using api.Models.Common; +using api.Models.ProfileEditor; +using api.Models.Ttv; +using Microsoft.EntityFrameworkCore; + +namespace api.Services +{ + /* + * SharingService implements user profile sharing related functionality. + */ + public class SharingService : ISharingService + { + private readonly TtvContext _ttvContext; + private readonly IDataSourceHelperService _dataSourceHelperService; + private readonly ILanguageService _languageService; + + public SharingService(TtvContext ttvContext, + IDataSourceHelperService dataSourceHelperService, ILanguageService languageService) + { + _ttvContext = ttvContext; + _dataSourceHelperService = dataSourceHelperService; + _languageService = languageService; + } + + // For unit test + public SharingService() { } + + /* + * Get DimReferenceData code_scheme for sharing. + */ + public string GetDimReferenceDataCodeScheme() + { + return Constants.ReferenceDataCodeSchemes.PROFILE_SHARING; + } + + /*s + * Get DimReferenceData code_values for sharing. + */ + public List GetDimReferenceDataCodeValues() + { + return new List() + { + Constants.ReferenceDataCodeValues.PROFILE_SHARING_PROFILE_INFORMATION, + Constants.ReferenceDataCodeValues.PROFILE_SHARING_EMAIL_ADDRESS, + Constants.ReferenceDataCodeValues.PROFILE_SHARING_PHONE_NUMBER, + Constants.ReferenceDataCodeValues.PROFILE_SHARING_AFFILIATION_AND_EDUCATION, + Constants.ReferenceDataCodeValues.PROFILE_SHARING_PUBLICATIONS, + Constants.ReferenceDataCodeValues.PROFILE_SHARING_DATASETS, + Constants.ReferenceDataCodeValues.PROFILE_SHARING_GRANTS, + Constants.ReferenceDataCodeValues.PROFILE_SHARING_ACTIVITIES_AND_DISTINCTIONS + }; + } + + /* + * Get a list of default user profile sharing permissions. + */ + public async Task> GetDefaultSharingPermissionsListForUserProfile(DimUserProfile dimUserProfile) + { + List defaultSharingPermissions = new(); + foreach (string sharingGroupIdentifier in GetDimReferenceDataCodeValues()) + { + DimReferencedatum dimReferencedata = await _ttvContext.DimReferencedata.AsNoTracking().FirstOrDefaultAsync(dr => dr.CodeScheme == GetDimReferenceDataCodeScheme() && dr.CodeValue == sharingGroupIdentifier); + + if (dimReferencedata != null) + { + defaultSharingPermissions.Add( + new BrGrantedPermission() + { + DimUserProfile = dimUserProfile, + DimExternalServiceId = _dataSourceHelperService.DimPurposeId_TTV, + DimPermittedFieldGroup = dimReferencedata.Id + } + ); + } + } + return defaultSharingPermissions; + } + + /* + * Delete all granted permissions. + */ + public async Task DeleteAllGrantedPermissionsFromUserprofile(int userprofileId) + { + List grantedPermissions = await _ttvContext.BrGrantedPermissions.Where(bgp => bgp.DimUserProfileId == userprofileId).ToListAsync(); + _ttvContext.BrGrantedPermissions.RemoveRange(grantedPermissions); + } + + /* + * Get list of sharing purposes. + */ + public async Task GetProfileEditorSharingPurposesResponse() + { + List profileEditorSharingPurposeItems = new(); + + foreach (DimPurpose dimPurpose in await _ttvContext.DimPurposes.AsNoTracking().ToListAsync()) + { + // Translate purpose name + NameTranslation nameTranslation = _languageService.GetNameTranslation( + nameFi: dimPurpose.NameFi, + nameEn: dimPurpose.NameEn, + nameSv: dimPurpose.NameSv + ); + + // Translate purpose description + NameTranslation descriptionTranslation = _languageService.GetNameTranslation( + nameFi: dimPurpose.DescriptionFi, + nameEn: dimPurpose.DescriptionEn, + nameSv: dimPurpose.DescriptionSv + ); + + profileEditorSharingPurposeItems.Add( + new ProfileEditorSharingPurposeItem() + { + PurposeId = dimPurpose.Id, + NameFi = nameTranslation.NameFi, + NameEn = nameTranslation.NameEn, + NameSv = nameTranslation.NameSv, + DescriptionFi = descriptionTranslation.NameFi, + DescriptionEn = descriptionTranslation.NameEn, + DescriptionSv = descriptionTranslation.NameSv + } + ); + } + + return new ProfileEditorSharingPurposesResponse(items: profileEditorSharingPurposeItems); + } + + /* + * Get list of sharing permissions. + */ + public async Task GetProfileEditorSharingPermissionsResponse() + { + List profileEditorSharingPermissionItems = new(); + + foreach (DimReferencedatum dimReferencedata in await _ttvContext.DimReferencedata.Where(dr => dr.CodeScheme == GetDimReferenceDataCodeScheme()).AsNoTracking().ToListAsync()) + { + // Translate permission name + NameTranslation nameTranslation = _languageService.GetNameTranslation( + nameFi: dimReferencedata.NameFi, + nameEn: dimReferencedata.NameEn, + nameSv: dimReferencedata.NameSv + ); + + profileEditorSharingPermissionItems.Add( + new ProfileEditorSharingPermissionItem() + { + PermissionId = dimReferencedata.Id, + NameFi = nameTranslation.NameFi, + NameEn = nameTranslation.NameEn, + NameSv = nameTranslation.NameSv, + } + ); + } + + return new ProfileEditorSharingPermissionsResponse(items: profileEditorSharingPermissionItems); + } + + /* + * Get list of given permissions. + */ + public async Task GetProfileEditorSharingResponse(int userprofileId) + { + List profileEditorSharingItems = new(); + + // Get all BrGrantedPermissions related to the user profile + List grantedPermissions = await _ttvContext.BrGrantedPermissions + .Include(bgp => bgp.DimExternalService).AsNoTracking() + .Include(bgp => bgp.DimPermittedFieldGroupNavigation).AsNoTracking() + .Where(bgp => bgp.DimUserProfileId == userprofileId).AsNoTracking().ToListAsync(); + + // Group BrGrantedPermissions by DimExternalServiceId + IEnumerable> groupedBrGrantedPermissions = grantedPermissions.GroupBy(bgp => bgp.DimExternalServiceId); + + foreach (IGrouping permissionGroup in groupedBrGrantedPermissions) + { + // Since grouping is done based on DimExternalServiceId, all items in the group + // are related to the same DimExternalService. Therefore the properties of + // DimExternalService can be taken from any item, here First() is used. + + // Translate purpose name + NameTranslation purposeNameTranslation = _languageService.GetNameTranslation( + nameFi: permissionGroup.First().DimExternalService.NameFi, + nameEn: permissionGroup.First().DimExternalService.NameEn, + nameSv: permissionGroup.First().DimExternalService.NameSv + ); + + // Translate purpose description + NameTranslation purposeDescriptionTranslation = _languageService.GetNameTranslation( + nameFi: permissionGroup.First().DimExternalService.DescriptionFi, + nameEn: permissionGroup.First().DimExternalService.DescriptionEn, + nameSv: permissionGroup.First().DimExternalService.DescriptionSv + ); + + ProfileEditorSharingItem profileEditorSharingItem = new() + { + Purpose = new ProfileEditorSharingPurposeItem() + { + PurposeId = permissionGroup.First().DimExternalServiceId, + NameFi = purposeNameTranslation.NameFi, + NameEn = purposeNameTranslation.NameEn, + NameSv = purposeNameTranslation.NameSv, + DescriptionFi = purposeDescriptionTranslation.NameFi, + DescriptionEn = purposeDescriptionTranslation.NameEn, + DescriptionSv = purposeDescriptionTranslation.NameSv + }, + Permissions = new List() + }; + + // Loop group to collect related permission items. + foreach (BrGrantedPermission permission in permissionGroup) + { + // Translate permission name + NameTranslation permissionNameTranslation = _languageService.GetNameTranslation( + nameFi: permission.DimPermittedFieldGroupNavigation.NameFi, + nameEn: permission.DimPermittedFieldGroupNavigation.NameEn, + nameSv: permission.DimPermittedFieldGroupNavigation.NameSv + ); + + profileEditorSharingItem.Permissions.Add( + new ProfileEditorSharingPermissionItem() + { + PermissionId = permission.DimPermittedFieldGroup, + NameFi = permissionNameTranslation.NameFi, + NameEn = permissionNameTranslation.NameEn, + NameSv = permissionNameTranslation.NameSv + } + ); + } + profileEditorSharingItems.Add(profileEditorSharingItem); + } + + return new ProfileEditorSharingGivenPermissionsResponse(items: profileEditorSharingItems); + } + + + /* + * Add permissions. + */ + public async Task AddPermissions(int userprofileId, List permissionsToAdd) + { + foreach (ProfileEditorSharingPermissionToAddOrDelete permission in permissionsToAdd) + { + // Validate purpose ID + DimPurpose dimPurpose = await _ttvContext.DimPurposes.Where(dp => dp.Id == permission.PurposeId).AsNoTracking().FirstOrDefaultAsync(); + if (dimPurpose == null) + { + break; + } + + // Validate permission ID + DimReferencedatum dimReferencedata = await _ttvContext.DimReferencedata.Where(dr => dr.Id == permission.PermissionId).AsNoTracking().FirstOrDefaultAsync(); + if (dimReferencedata == null) + { + break; + } + + // Check that permission is not already added + BrGrantedPermission brGrantedPermissionExisting = await _ttvContext.BrGrantedPermissions.Where( + bgp => bgp.DimUserProfileId == userprofileId && + bgp.DimExternalServiceId == permission.PurposeId && + bgp.DimPermittedFieldGroup == permission.PermissionId + ).FirstOrDefaultAsync(); + + // Add permission + if (brGrantedPermissionExisting == null) + { + _ttvContext.BrGrantedPermissions.Add( + new BrGrantedPermission() + { + DimUserProfileId = userprofileId, + DimExternalServiceId = permission.PurposeId, + DimPermittedFieldGroup = permission.PermissionId + } + ); + } + } + } + + /* + * Delete permissions. + */ + public async Task DeletePermissions(int userprofileId, List permissionsToDelete) + { + foreach (ProfileEditorSharingPermissionToAddOrDelete permission in permissionsToDelete) + { + BrGrantedPermission brGrantedPermission = await _ttvContext.BrGrantedPermissions.Where( + bgp => bgp.DimUserProfileId == userprofileId && + bgp.DimExternalServiceId == permission.PurposeId && + bgp.DimPermittedFieldGroup == permission.PermissionId + ).FirstOrDefaultAsync(); + + if (brGrantedPermission != null) + { + _ttvContext.BrGrantedPermissions.Remove(brGrantedPermission); + } + } + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/StartupHelperService.cs b/aspnetcore/src/api/Services/StartupHelperService.cs new file mode 100644 index 00000000..82451bc4 --- /dev/null +++ b/aspnetcore/src/api/Services/StartupHelperService.cs @@ -0,0 +1,93 @@ +using api.Models.Common; +using api.Models.Ttv; +using Microsoft.Extensions.Logging; +using System.Linq; +using Microsoft.EntityFrameworkCore; + +namespace api.Services +{ + /* + * StartupHelperService contains methods using application startup. + */ + public class StartupHelperService : IStartupHelperService + { + private readonly TtvContext _ttvContext; + private readonly ILogger _logger; + + public StartupHelperService(TtvContext ttvContext, ILogger logger) + { + _ttvContext = ttvContext; + _logger = logger; + } + + /* + * Get ORCID registered data source and related organization. + * + * Note! This method is synchronous, because it should be run in the application startup phase. + */ + public DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_ORCID() + { + _logger.LogInformation("Get data source for ORCID in DimRegisteredDataSource"); + // Get data source and related organization. + DimRegisteredDataSource orcidRegisteredDataSource = _ttvContext.DimRegisteredDataSources + .Include(drds => drds.DimOrganization) + .Where(drds => drds.DimOrganization.NameFi == Constants.OrganizationNames.ORCID).AsNoTracking().FirstOrDefault(); + + // Log error and raise exception on missing ORCID registered data source. The application does not function without this. + if (orcidRegisteredDataSource == null) + { + string errorMessage = "Registered data source was not found from dim_registered_data_source for organization: " + Constants.OrganizationNames.ORCID; + _logger.LogError(errorMessage); + throw new System.Exception(errorMessage); + } + + return orcidRegisteredDataSource; + } + + /* + * Get TTV registered data source and related organization. + * + * Note! This method is synchronous, because it should be run in the application startup phase. + */ + public DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_TTV() + { + _logger.LogInformation("Get data source for TTV in DimRegisteredDataSource"); + // Get data source and related organization. + DimRegisteredDataSource ttvRegisteredDataSource = _ttvContext.DimRegisteredDataSources + .Include(drds => drds.DimOrganization) + .Where(drds => drds.DimOrganization.NameFi == Constants.OrganizationNames.TTV).AsNoTracking().FirstOrDefault(); + + // Log error and raise exception on missing TTV registered data source. The application does not function without this. + if (ttvRegisteredDataSource == null) + { + string errorMessage = "Registered data source was not found from dim_registered_data_source for organization: " + Constants.OrganizationNames.TTV; + _logger.LogError(errorMessage); + throw new System.Exception(errorMessage); + } + + return ttvRegisteredDataSource; + } + + /* + * Get TTV purpose. + * + * Note! This method is synchronous, because it should be run in the application startup phase. + */ + public DimPurpose GetDimPurposeId_OnStartup_TTV() + { + DimPurpose dimPurpose = _ttvContext.DimPurposes + .Include(dp => dp.DimOrganization) + .Where(dp => dp.DimOrganization.OrganizationId == Constants.OrganizationIds.OKM).AsNoTracking().FirstOrDefault(); + + // Log error and raise exception on missing TTV purpose. The application does not function without this. + if (dimPurpose == null) + { + string errorMessage = "Purpose was not found from dim_purpose for organization_id: " + Constants.OrganizationIds.OKM; + _logger.LogError(errorMessage); + throw new System.Exception(errorMessage); + } + + return dimPurpose; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/TokenService.cs b/aspnetcore/src/api/Services/TokenService.cs new file mode 100644 index 00000000..71e477d0 --- /dev/null +++ b/aspnetcore/src/api/Services/TokenService.cs @@ -0,0 +1,96 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading.Tasks; +using api.Models.Orcid; +using Microsoft.Extensions.Configuration; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; + +namespace api.Services +{ + /* + * TokenService implements functionality related to access tokens. + */ + public class TokenService : ITokenService + { + private readonly IHttpClientFactory _httpClientFactory; + public IConfiguration Configuration { get; } + + public TokenService(IConfiguration configuration, IHttpClientFactory httpClientFactory) + { + Configuration = configuration; + _httpClientFactory = httpClientFactory; + } + + /* + * Get JWT from string. + */ + public JwtSecurityToken GetJwtFromString(String tokenStr) + { + JwtSecurityTokenHandler handler = new(); + return handler.ReadJwtToken(tokenStr); + } + + /* + * Check if JSON Web Token contains claim "orcid". + */ + public static bool JwtContainsOrcid(JwtSecurityToken jwt) + { + return jwt.Claims.FirstOrDefault(x => x.Type == "orcid") != null; + } + + /* + * Get Keycloak user id from JSON Web Token. + */ + public string GetKeycloakUserIdFromJwt(JwtSecurityToken jwt) + { + return jwt.Subject; + } + + /* + * Get user's ORCID tokens json from Keycloak. + * Keycloak can store access tokens from external IDPs, such as ORCID. + * Those can be requested from Keycloak using IDP specific endpoint. + * https://www.keycloak.org/docs/latest/server_development/#retrieving-external-idp-tokens + */ + public async Task GetOrcidTokensJsonFromKeycloak(String usersKeycloakAccessToken) + { + // Get http client. + HttpClient keycloakHttpClient = _httpClientFactory.CreateClient("keycloakUserOrcidTokens"); + // GET request + HttpRequestMessage request = new(method: HttpMethod.Get, requestUri: ""); + // Insert ORCID access token into authorization header for each request. + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", usersKeycloakAccessToken); + HttpResponseMessage response = await keycloakHttpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadAsStringAsync(); + } + + /* + * Parse ORCID tokens json into OrcidTokens object + */ + public OrcidTokens ParseOrcidTokensJson(String orcidTokensJson) + { + using JsonDocument document = JsonDocument.Parse(orcidTokensJson); + // Access token + document.RootElement.TryGetProperty("access_token", out JsonElement accessTokenElement); + // Refresh token + document.RootElement.TryGetProperty("refresh_token", out JsonElement refreshTokenElement); + // Scope + document.RootElement.TryGetProperty("scope", out JsonElement scopeElement); + // Access token expires seconds + document.RootElement.TryGetProperty("accessTokenExpiration", out JsonElement expiresSecondsElement); + + return new OrcidTokens( + accessToken: accessTokenElement.ToString(), + refreshToken: refreshTokenElement.ToString(), + scope: scopeElement.ToString(), + expiresSeconds: expiresSecondsElement.GetInt64(), + expiresDatetime: null + ) + { }; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/TtvSqlService.cs b/aspnetcore/src/api/Services/TtvSqlService.cs new file mode 100644 index 00000000..a106f954 --- /dev/null +++ b/aspnetcore/src/api/Services/TtvSqlService.cs @@ -0,0 +1,445 @@ +using api.Models.ProfileEditor.Items; +using api.Models.Common; +using api.Models.Ttv; +using System.Collections.Generic; + +namespace api.Services +{ + /* + * TtvSqlService implements utilities for constructing TTV database SQL queries. + */ + public class TtvSqlService : ITtvSqlService + { + public TtvSqlService() + { + } + + // Based on field identifier, return FactFieldValues foreign key column name. + public string GetFactFieldValuesFKColumnNameFromFieldIdentifier(int fieldIdentifier) + { + string fk_column_name = ""; + switch (fieldIdentifier) + { + case Constants.FieldIdentifiers.PERSON_NAME: + fk_column_name = "dim_name_id"; + break; + case Constants.FieldIdentifiers.PERSON_OTHER_NAMES: + fk_column_name = "dim_name_id"; + break; + case Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION: + fk_column_name = "dim_researcher_description_id"; + break; + case Constants.FieldIdentifiers.PERSON_WEB_LINK: + fk_column_name = "dim_web_link_id"; + break; + case Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS: + fk_column_name = "dim_email_addrress_id"; + break; + case Constants.FieldIdentifiers.PERSON_KEYWORD: + fk_column_name = "dim_keyword_id"; + break; + case Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER: + fk_column_name = "dim_telephone_number_id"; + break; + case Constants.FieldIdentifiers.ACTIVITY_AFFILIATION: + fk_column_name = "dim_affiliation_id"; + break; + case Constants.FieldIdentifiers.ACTIVITY_EDUCATION: + fk_column_name = "dim_education_id"; + break; + case Constants.FieldIdentifiers.ACTIVITY_PUBLICATION: + fk_column_name = "dim_publication_id"; + break; + case Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID: + fk_column_name = "dim_orcid_publication_id"; + break; + case Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION: + fk_column_name = "dim_funding_decision_id"; + break; + case Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET: + fk_column_name = "dim_research_dataset_id"; + break; + default: + break; + } + return fk_column_name; + } + + // Return SQL update statement for updating FactFieldValues. + public string GetSqlQuery_Update_FactFieldValues(int dimUserProfileId, ProfileEditorItemMeta profileEditorItemMeta) + { + string fk_column_name = GetFactFieldValuesFKColumnNameFromFieldIdentifier(profileEditorItemMeta.Type); + string showToSql = profileEditorItemMeta.Show == true ? "1" : "0"; + string primaryValueToSql = profileEditorItemMeta.PrimaryValue == true ? "1" : "0"; + + return $@"UPDATE fact_field_values + SET + show={showToSql}, + primary_value={primaryValueToSql}, + modified=GETDATE() + WHERE + dim_user_profile_id={dimUserProfileId} AND + {fk_column_name}={profileEditorItemMeta.Id}"; + } + + // Convert list of integers into a comma separated string + public string ConvertListOfIntsToCommaSeparatedString(List listOfInts) + { + return string.Join(",", listOfInts); + } + + /* + * Return SQL statement for getting profile data. + * Parameter forElasticsearch controls if query should be limited to + * contain only user's published items. + */ + public string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = false) + { + return $@" + SELECT + dfds.id AS 'DimFieldDisplaySettings_Id', + dfds.field_identifier AS 'DimFieldDisplaySettings_FieldIdentifier', + dfds.show AS 'DimFieldDisplaySettings_Show', + ffv.show AS 'FactFieldValues_Show', + ffv.primary_value AS 'FactFieldValues_PrimaryValue', + drds.id AS 'DimRegisteredDataSource_Id', + drds.name AS 'DimRegisteredDataSource_Name', + drds_organization.name_fi AS 'DimRegisteredDataSource_DimOrganization_NameFi', + drds_organization.name_en AS 'DimRegisteredDataSource_DimOrganization_NameEn', + drds_organization.name_sv AS 'DimRegisteredDataSource_DimOrganization_NameSv', + drds_organization_sector.sector_id AS 'DimRegisteredDataSource_DimOrganization_DimSector_SectorId', + ffv.dim_user_profile_id AS 'FactFieldValues_DimUserProfileId', + ffv.dim_name_id AS 'FactFieldValues_DimNameId', + ffv.dim_web_link_id AS 'FactFieldValues_DimWebLinkId', + ffv.dim_researcher_description_id AS 'FactFieldValues_DimResearcherDescriptionId', + ffv.dim_email_addrress_id AS 'FactFieldValues_DimEmailAddrressId', + ffv.dim_telephone_number_id AS 'FactFieldValues_DimTelephoneNumberId', + ffv.dim_field_of_science_id AS ' FactFieldValues_DimFieldOfScienceId', + ffv.dim_keyword_id AS 'FactFieldValues_DimKeywordId', + ffv.dim_pid_id AS 'FactFieldValues_DimPidId', + ffv.dim_affiliation_id AS 'FactFieldValues_DimAffiliationId', + ffv.dim_identifierless_data_id AS 'FactFieldValues_DimIdentifierlessDataId', + ffv.dim_education_id AS 'FactFieldValues_DimEducationId', + ffv.dim_publication_id AS 'FactFieldValues_DimPublicationId', + ffv.dim_orcid_publication_id AS 'FactFieldValues_DimOrcidPublicationId', + ffv.dim_research_activity_id AS 'FactFieldValues_DimResearchActivityId', + ffv.dim_funding_decision_id AS 'FactFieldValues_DimFundingDecisionId', + ffv.dim_research_dataset_id AS 'FactFieldValues_DimResearchDatasetId', + dim_name.lASt_name AS 'DimName_LastName', + dim_name.first_names AS 'DimName_FirstNames', + dim_name.full_name AS 'DimName_FullName', + dim_web_link.url AS 'DimWebLink_Url', + dim_web_link.link_label AS 'DimWebLink_LinkLabel', + dim_researcher_description.research_description_fi AS 'DimResearcherDescription_ResearchDescriptionFi', + dim_researcher_description.research_description_en AS 'DimResearcherDescription_ResearchDescriptionEn', + dim_researcher_description.research_description_sv AS 'DimResearcherDescription_ResearchDescriptionSv', + dim_email_addrress.email AS 'DimEmailAddrress_Email', + dim_telephone_number.telephone_number AS 'DimTelephoneNumber_TelephoneNumber', + dim_field_of_science.name_fi AS 'DimFieldOfScience_NameFi', + dim_field_of_science.name_en AS 'DimFieldOfScience_NameEn', + dim_field_of_science.name_sv AS 'DimFieldOfScience_NameSv', + dim_keyword.keyword AS 'DimKeyword_Keyword', + dim_pid.pid_type AS 'DimPid_PidType', + dim_pid.pid_content AS 'DimPid_PidContent', + affiliation_organization.id AS 'DimAffiliation_DimOrganization_Id', + affiliation_organization.name_fi AS 'DimAffiliation_DimOrganization_NameFi', + affiliation_organization.name_en AS 'DimAffiliation_DimOrganization_NameEn', + affiliation_organization.name_sv AS 'DimAffiliation_DimOrganization_NameSv', + dim_affiliation.position_name_fi AS 'DimAffiliation_PositionNameFi', + dim_affiliation.position_name_en AS 'DimAffiliation_PositionNameEn', + dim_affiliation.position_name_sv AS 'DimAffiliation_PositionNameSv', + affiliation_start_date.year AS 'DimAffiliation_StartDate_Year', + affiliation_start_date.month AS 'DimAffiliation_StartDate_Month', + affiliation_start_date.day AS 'DimAffiliation_StartDate_Day', + affiliation_end_date.year AS 'DimAffiliation_EndDate_Year', + affiliation_end_date.month AS 'DimAffiliation_EndDate_Month', + affiliation_end_date.day AS 'DimAffiliation_EndDate_Day', + affiliation_type.name_fi AS 'DimAffiliation_DimReferenceData_NameFi', + did.type AS 'DimIdentifierlessData_Type', + did.value_fi AS 'DimIdentifierlessData_ValueFi', + did.value_en AS 'DimIdentifierlessData_ValueEn', + did.value_sv AS 'DimIdentifierlessData_ValueSv', + did.unlinked_identifier AS 'DimIdentifierlessData_UnlinkedIdentifier', + did_child.type AS 'DimIdentifierlessData_Child_Type', + did_child.value_fi AS 'DimIdentifierlessData_Child_ValueFi', + did_child.value_en AS 'DimIdentifierlessData_Child_ValueEn', + did_child.value_sv AS 'DimIdentifierlessData_Child_ValueSv', + did_child.unlinked_identifier AS 'DimIdentifierlessData_Child_UnlinkedIdentifier', + dim_education.name_fi AS 'DimEducation_NameFi', + dim_education.name_en AS 'DimEducation_NameEn', + dim_education.name_sv AS 'DimEducation_NameSv', + dim_education.degree_granting_institution_name AS 'DimEducation_DegreeGrantingInstitutionName', + education_start_date.year AS 'DimEducation_StartDate_Year', + education_start_date.month AS 'DimEducation_StartDate_Month', + education_start_date.day AS 'DimEducation_StartDate_Day', + education_end_date.year AS 'DimEducation_EndDate_Year', + education_end_date.month AS 'DimEducation_EndDate_Month', + education_end_date.day AS 'DimEducation_EndDate_Day', + dim_publication.publication_id AS 'DimPublication_PublicationId', + dim_publication.publication_name AS 'DimPublication_PublicationName', + dim_publication.publication_year AS 'DimPublication_PublicationYear', + dim_publication.doi AS 'DimPublication_Doi', + dim_publication.publication_type_code AS 'DimPublication_PublicationTypeCode', + dim_orcid_publication.publication_id AS 'DimOrcidPublication_PublicationId', + dim_orcid_publication.publication_name AS 'DimOrcidPublication_PublicationName', + dim_orcid_publication.publication_year AS 'DimOrcidPublication_PublicationYear', + dim_orcid_publication.doi_handle AS 'DimOrcidPublication_Doi', + dim_research_activity.name_fi AS 'DimResearchActivity_NameFi', + dim_research_activity.name_en AS 'DimResearchActivity_NameEn', + dim_research_activity.name_sv AS 'DimResearchActivity_NameSv', + dim_research_activity.description_fi AS 'DimResearchActivity_DescriptionFi', + dim_research_activity.description_en AS 'DimResearchActivity_DescriptionEn', + dim_research_activity.description_sv AS 'DimResearchActivity_DescriptionSv', + dim_research_activity.international_collaboration AS 'DimResearchActivity_InternationalCollaboration', + research_activity_start_date.year AS 'DimResearchActivity_StartDate_Year', + research_activity_start_date.month AS 'DimResearchActivity_StartDate_Month', + research_activity_start_date.day AS 'DimResearchActivity_StartDate_Day', + research_activity_end_date.year AS 'DimResearchActivity_EndDate_Year', + research_activity_end_date.month AS 'DimResearchActivity_EndDate_Month', + research_activity_end_date.day AS 'DimResearchActivity_EndDate_Day', + dfd.acronym AS 'DimFundingDecision_Acronym', + dfd.funder_project_number AS 'DimFundingDecision_FunderProjectNumber', + dfd.name_fi AS 'DimFundingDecision_NameFi', + dfd.name_en AS 'DimFundingDecision_NameEn', + dfd.name_sv AS 'DimFundingDecision_NameSv', + dfd.description_fi AS 'DimFundingDecision_DescriptionFi', + dfd.description_en AS 'DimFundingDecision_DescriptionEn', + dfd.description_sv AS 'DimFundingDecision_DescriptionSv', + dfd.amount_in_EUR AS 'DimFundingDecision_AmountInEur', + funding_decision_start_date.year AS 'DimFundingDecision_StartDate_Year', + funding_decision_end_date.year AS 'DimFundingDecision_EndDate_Year', + dim_type_of_funding.name_fi AS 'DimFundingDecision_DimTypeOfFunding_NameFi', + dim_type_of_funding.name_en AS 'DimFundingDecision_DimTypeOfFunding_NameEn', + dim_type_of_funding.name_sv AS 'DimFundingDecision_DimTypeOfFunding_NameSv', + dim_call_programme.name_fi AS 'DimFundingDecision_DimCallProgramme_NameFi', + dim_call_programme.name_en AS 'DimFundingDecision_DimCallProgramme_NameEn', + dim_call_programme.name_sv AS 'DimFundingDecision_DimCallProgramme_NameSv', + dfd_organization.name_fi AS 'DimFundingDecision_Funder_NameFi', + dfd_organization.name_en AS 'DimFundingDecision_Funder_NameEn', + dfd_organization.name_sv AS 'DimFundingDecision_Funder_NameSv', + dim_research_dataset.local_identifier AS 'DimResearchDataset_LocalIdentifier', + dim_research_dataset.name_fi AS 'DimResearchDataset_NameFi', + dim_research_dataset.name_en AS 'DimResearchDataset_NameEn', + dim_research_dataset.name_sv AS 'DimResearchDataset_NameSv', + dim_research_dataset.description_fi AS 'DimResearchDataset_DescriptionFi', + dim_research_dataset.description_en AS 'DimResearchDataset_DescriptionEn', + dim_research_dataset.description_sv AS 'DimResearchDataset_DescriptionSv', + dim_research_dataset.dataset_created AS 'DimResearchDataset_DatasetCreated' + + FROM fact_field_values AS ffv + + JOIN dim_field_display_settings AS dfds ON ffv.dim_field_display_settings_id=dfds.id + JOIN dim_registered_data_source AS drds ON ffv.dim_registered_data_source_id=drds.id + JOIN dim_organization AS drds_organization ON drds.dim_organization_id=drds_organization.id + JOIN dim_sector AS drds_organization_sector ON drds_organization.dim_sectorid=drds_organization_sector.id + JOIN dim_name ON ffv.dim_name_id=dim_name.id + JOIN dim_web_link ON ffv.dim_web_link_id=dim_web_link.id + JOIN dim_researcher_description ON ffv.dim_researcher_description_id=dim_researcher_description.id + JOIN dim_email_addrress ON ffv.dim_email_addrress_id=dim_email_addrress.id + JOIN dim_telephone_number ON ffv.dim_telephone_number_id=dim_telephone_number.id + JOIN dim_field_of_science ON ffv.dim_field_of_science_id=dim_field_of_science.id + JOIN dim_keyword ON ffv.dim_keyword_id=dim_keyword.id + JOIN dim_pid ON ffv.dim_pid_id=dim_pid.id + JOIN dim_affiliation ON ffv.dim_affiliation_id=dim_affiliation.id + JOIN dim_organization AS affiliation_organization ON dim_affiliation.dim_organization_id=affiliation_organization.id + LEFT JOIN dim_date AS affiliation_start_date ON dim_affiliation.start_date=affiliation_start_date.id AND affiliation_start_date.id!=-1 + LEFT JOIN dim_date AS affiliation_end_date ON dim_affiliation.end_date=affiliation_end_date.id AND affiliation_end_date.id!=-1 + JOIN dim_referencedata AS affiliation_type ON dim_affiliation.affiliation_type=affiliation_type.id + JOIN dim_identifierless_data AS did ON ffv.dim_identifierless_data_id=did.id + LEFT JOIN dim_identifierless_data AS did_child ON did_child.dim_identifierless_data_id=did.id AND did_child.dim_identifierless_data_id!=-1 + JOIN dim_education ON ffv.dim_education_id=dim_education.id + LEFT JOIN dim_date AS education_start_date ON dim_education.dim_start_date=education_start_date.id AND education_start_date.id!=-1 + LEFT JOIN dim_date AS education_end_date ON dim_education.dim_end_date=education_end_date.id AND education_end_date.id!=-1 + JOIN dim_publication ON ffv.dim_publication_id=dim_publication.id + JOIN dim_orcid_publication ON ffv.dim_orcid_publication_id=dim_orcid_publication.id + JOIN dim_research_activity ON ffv.dim_research_activity_id=dim_research_activity.id + LEFT JOIN dim_date AS research_activity_start_date ON dim_research_activity.dim_start_date=research_activity_start_date.id AND research_activity_start_date.id!=-1 + LEFT JOIN dim_date AS research_activity_end_date ON dim_research_activity.dim_end_date=research_activity_end_date.id AND research_activity_end_date.id!=-1 + JOIN dim_funding_decision AS dfd ON ffv.dim_funding_decision_id=dfd.id + LEFT JOIN dim_date AS funding_decision_start_date ON dfd.dim_date_id_start=funding_decision_start_date.id AND funding_decision_start_date.id!=-1 + LEFT JOIN dim_date AS funding_decision_end_date ON dfd.dim_date_id_end=funding_decision_end_date.id AND funding_decision_end_date.id!=-1 + LEFT JOIN dim_call_programme ON dim_call_programme.id=dfd.dim_call_programme_id + LEFT JOIN dim_type_of_funding ON dim_type_of_funding.id=dfd.dim_type_of_funding_id + LEFT JOIN dim_organization AS dfd_organization ON dfd_organization.id=dfd.dim_organization_id_funder + JOIN dim_research_dataset ON ffv.dim_research_dataset_id=dim_research_dataset.id + + WHERE + ffv.dim_user_profile_id={userprofileId} AND {(forElasticsearch ? " ffv.show=1 AND " : "")} + ( + ffv.dim_name_id != -1 OR + ffv.dim_web_link_id != -1 OR + ffv.dim_researcher_description_id != -1 OR + ffv.dim_email_addrress_id != -1 OR + ffv.dim_telephone_number_id != -1 OR + ffv.dim_field_of_science_id != -1 OR + ffv.dim_keyword_id != -1 OR + ffv.dim_pid_id != -1 OR + ffv.dim_affiliation_id != -1 OR + ffv.dim_identifierless_data_id != -1 OR + ffv.dim_education_id != -1 OR + ffv.dim_publication_id != -1 OR + ffv.dim_orcid_publication_id != -1 OR + ffv.dim_research_activity_id != -1 OR + ffv.dim_funding_decision_id != -1 OR + ffv.dim_research_dataset_id != -1 + ) + "; + } + + // Return SQL SELECT statement for fact_field_values. + public string GetSqlQuery_Select_FactFieldValues(int userprofileId) + { + return $"SELECT * FROM fact_field_values WHERE dim_user_profile_id={userprofileId}"; + } + + // Return SQL DELETE statement for fact_field_values. + public string GetSqlQuery_Delete_FactFieldValues(int userprofileId) + { + return $"DELETE FROM fact_field_values WHERE dim_user_profile_id={userprofileId}"; + } + + // Return SQL DELETE statement for dim_identifierless_data children. + public string GetSqlQuery_Delete_DimIdentifierlessData_Children(int dimIdentifierlessDataId) + { + return $"DELETE FROM dim_identifierless_data where dim_identifierless_data_id={dimIdentifierlessDataId}"; + } + + // Return SQL DELETE statement for dim_identifierless_data parent. + public string GetSqlQuery_Delete_DimIdentifierlessData_Parent(int id) + { + return $"DELETE FROM dim_identifierless_data where id={id}"; + } + + // Return SQL DELETE statement for dim_affiliation + public string GetSqlQuery_Delete_DimAffiliations(List dimAffiliationIds) + { + return $"DELETE FROM dim_affiliation WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimAffiliationIds)})"; + } + + // Return SQL DELETE statement for dim_competence + public string GetSqlQuery_Delete_DimCompetences(List dimCompetenceIds) + { + return $"DELETE FROM dim_competence WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimCompetenceIds)})"; + } + + // Return SQL DELETE statement for dim_education + public string GetSqlQuery_Delete_DimEducations(List dimEducationIds) + { + return $"DELETE FROM dim_education WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimEducationIds)})"; + } + + // Return SQL DELETE statement for dim_email_addrress + public string GetSqlQuery_Delete_DimEmailAddrresses(List dimEmailAddrressIds) + { + return $"DELETE FROM dim_email_addrress WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimEmailAddrressIds)})"; + } + + // Return SQL DELETE statement for dim_event + public string GetSqlQuery_Delete_DimEvents(List dimEventIds) + { + return $"DELETE FROM dim_event WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimEventIds)})"; + } + + // Return SQL DELETE statement for dim_field_of_science + public string GetSqlQuery_Delete_DimFieldsOfScience(List dimFieldOfScienceIds) + { + return $"DELETE FROM dim_field_of_science WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimFieldOfScienceIds)})"; + } + + // Return SQL DELETE statement for dim_funding_decision + public string GetSqlQuery_Delete_DimFundingDecisions(List dimFundingDecisionIds) + { + return $"DELETE FROM dim_funding_decision WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimFundingDecisionIds)})"; + } + + // Return SQL DELETE statement for dim_keyword + public string GetSqlQuery_Delete_DimKeyword(List dimKeywordIds) + { + return $"DELETE FROM dim_keyword WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimKeywordIds)})"; + } + + // Return SQL DELETE statement for dim_name + public string GetSqlQuery_Delete_DimNames(List dimNameIds) + { + return $"DELETE FROM dim_name WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimNameIds)})"; + } + + // Return SQL DELETE statement for dim_orcid_publication + public string GetSqlQuery_Delete_DimOrcidPublications(List dimOrcidPublicationIds) + { + return $"DELETE FROM dim_orcid_publication WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimOrcidPublicationIds)})"; + } + + // Return SQL DELETE statement for dim_pid + public string GetSqlQuery_Delete_DimPids(List dimPidIds) + { + return $"DELETE FROM dim_pid WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimPidIds)})"; + } + + // Return SQL DELETE statement for dim_research_activity + public string GetSqlQuery_Delete_DimResearchActivities(List dimResearchActivityIds) + { + return $"DELETE FROM dim_research_activity WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimResearchActivityIds)})"; + } + + // Return SQL DELETE statement for dim_research_community + public string GetSqlQuery_Delete_DimResearchCommunities(List dimResearchCommunityIds) + { + return $"DELETE FROM dim_research_community WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimResearchCommunityIds)})"; + } + + // Return SQL DELETE statement for dim_research_dataset + public string GetSqlQuery_Delete_DimResearchDatasets(List dimResearchDatasetIds) + { + return $"DELETE FROM dim_research_dataset WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimResearchDatasetIds)})"; + } + + // Return SQL DELETE statement for dim_researcher_description + public string GetSqlQuery_Delete_DimResearchDescriptions(List dimResearcherDescriptionIds) + { + return $"DELETE FROM dim_researcher_description WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimResearcherDescriptionIds)})"; + } + + // Return SQL DELETE statement for dim_researcher_to_research_community + public string GetSqlQuery_Delete_DimResearcherToResearchCommunities(List dimResearcherToResearchCommunityIds) + { + return $"DELETE FROM dim_researcher_to_research_community WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimResearcherToResearchCommunityIds)})"; + } + + // Return SQL DELETE statement for dim_telephone_number + public string GetSqlQuery_Delete_DimTelephoneNumbers(List dimTelephoneNumberIds) + { + return $"DELETE FROM dim_telephone_number WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimTelephoneNumberIds)})"; + } + + // Return SQL DELETE statement for dim_web_link + public string GetSqlQuery_Delete_DimWebLinks(List dimWebLinkIds) + { + return $"DELETE FROM dim_web_link WHERE id IN ({ConvertListOfIntsToCommaSeparatedString(dimWebLinkIds)})"; + } + + // Return SQL DELETE statement for dim_field_display_settings. + public string GetSqlQuery_Delete_DimFieldDisplaySettings(int userprofileId) + { + return $"DELETE FROM dim_field_display_settings WHERE dim_user_profile_id={userprofileId}"; + } + + // Return SQL DELETE statement for br_granted_permissions. + public string GetSqlQuery_Delete_BrGrantedPermissions(int userprofileId) + { + return $"DELETE FROM br_granted_permissions WHERE dim_user_profile_id={userprofileId}"; + } + + // Return SQL DELETE statement for dim_user_choices. + public string GetSqlQuery_Delete_DimUserChoices(int userprofileId) + { + return $"DELETE FROM dim_user_choices WHERE dim_user_profile_id={userprofileId}"; + } + + // Return SQL DELETE statement for dim_user_profile. + public string GetSqlQuery_Delete_DimUserProfile(int userprofileId) + { + return $"DELETE FROM dim_user_profile WHERE id={userprofileId}"; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/UserProfileService.cs b/aspnetcore/src/api/Services/UserProfileService.cs index 33e99b46..37cea94a 100644 --- a/aspnetcore/src/api/Services/UserProfileService.cs +++ b/aspnetcore/src/api/Services/UserProfileService.cs @@ -1,130 +1,131 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -using api.Models; +using System.Threading.Tasks; using api.Models.Ttv; +using api.Models.ProfileEditor; +using api.Models.ProfileEditor.Items; using Microsoft.EntityFrameworkCore; +using api.Models.Common; +using api.Models.Orcid; +using Dapper; +using System.Transactions; +using api.Controllers; +using Microsoft.Extensions.Logging; +using api.Models.Elasticsearch; namespace api.Services { /* * UserProfileService implements utilities, which simplify handling of userprofile and related data. */ - public class UserProfileService + public class UserProfileService : IUserProfileService { private readonly TtvContext _ttvContext; + private readonly IDataSourceHelperService _dataSourceHelperService; + private readonly IUtilityService _utilityService; + private readonly ILanguageService _languageService; + private readonly IDuplicateHandlerService _duplicateHandlerService; + private readonly IOrganizationHandlerService _organizationHandlerService; + private readonly ISharingService _sharingService; + private readonly ITtvSqlService _ttvSqlService; + private readonly ILogger _logger; - public UserProfileService(TtvContext ttvContext) + public UserProfileService(TtvContext ttvContext, + IDataSourceHelperService dataSourceHelperService, + IUtilityService utilityService, + ILanguageService languageService, + IDuplicateHandlerService duplicateHandlerService, + IOrganizationHandlerService organizationHandlerService, + ISharingService sharingService, + ITtvSqlService ttvSqlService, + ILogger logger) { _ttvContext = ttvContext; + _dataSourceHelperService = dataSourceHelperService; + _utilityService = utilityService; + _languageService = languageService; + _duplicateHandlerService = duplicateHandlerService; + _organizationHandlerService = organizationHandlerService; + _sharingService = sharingService; + _ttvSqlService = ttvSqlService; + _logger = logger; } + // For unit test + public UserProfileService() { } + /* - * Check if data related to FactFieldValue can be removed. + * Get FieldIdentifiers. */ - public bool CanDeleteFactFieldValueRelatedData(FactFieldValue ffv) + public List GetFieldIdentifiers() { - // ORCID and demo data can be removed. - return ffv.SourceId == Constants.SourceIdentifiers.ORCID || ffv.SourceId == Constants.SourceIdentifiers.DEMO; + return new List() + { + Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS, + Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER, + Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE, + Constants.FieldIdentifiers.PERSON_KEYWORD, + Constants.FieldIdentifiers.PERSON_NAME, + Constants.FieldIdentifiers.PERSON_OTHER_NAMES, + Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION, + Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER, + Constants.FieldIdentifiers.PERSON_WEB_LINK, + Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, + Constants.FieldIdentifiers.ACTIVITY_EDUCATION, + Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, + Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID, + Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION, + Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET, + Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY + }; } /* - * Get id of DimUserProfile. + * Update ORCID tokens in DimUserProfile */ - public async Task GetUserprofileId(String orcidId) + public async Task UpdateOrcidTokensInDimUserProfile(int dimUserProfileId, OrcidTokens orcidTokens) { - var dimPid = await _ttvContext.DimPids - .Include(i => i.DimKnownPerson) - .ThenInclude(kp => kp.DimUserProfiles).AsNoTracking().AsSplitQuery().FirstOrDefaultAsync(p => p.PidContent == orcidId && p.PidType == Constants.PidTypes.ORCID); - - if (dimPid == null || dimPid.DimKnownPerson == null || dimPid.DimKnownPerson.DimUserProfiles.Count() == 0) - { - return -1; - } - else - { - return dimPid.DimKnownPerson.DimUserProfiles.FirstOrDefault().Id; - } + DimUserProfile dimUserProfile = await _ttvContext.DimUserProfiles.FindAsync(dimUserProfileId); + dimUserProfile.OrcidAccessToken = orcidTokens.AccessToken; + dimUserProfile.OrcidRefreshToken = orcidTokens.RefreshToken; + dimUserProfile.OrcidTokenScope = orcidTokens.Scope; + dimUserProfile.OrcidTokenExpires = orcidTokens.ExpiresDatetime; + await _ttvContext.SaveChangesAsync(); } - /* - * Get id of ORCID DimOrganization. - * Create organization if it does not exist. + /* + * Check if data related to FactFieldValue can be removed. + * Data from registered data source ORCID can be removed. */ - public async Task GetOrcidOrganizationId() + public bool CanDeleteFactFieldValueRelatedData(FactFieldValue ffv) { - var orcidOrganization = await _ttvContext.DimOrganizations.AsNoTracking().FirstOrDefaultAsync(org => org.NameEn == "ORCID"); - if (orcidOrganization == null) - { - orcidOrganization = new DimOrganization() - { - DimSectorid = -1, - OrganizationId = "ORCID", - OrganizationActive = true, - NameFi = "ORCID", - NameEn = "ORCID", - NameSv = "ORCID", - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - DimRegisteredDataSourceId = -1 - }; - _ttvContext.DimOrganizations.Add(orcidOrganization); - await _ttvContext.SaveChangesAsync(); - return orcidOrganization.Id; - } - else - { - return orcidOrganization.Id; - } + return ffv.DimRegisteredDataSourceId == _dataSourceHelperService.DimRegisteredDataSourceId_ORCID; } /* - * Get id of ORCID DimRegisteredDataSource. - * Create data source if it does not exist. + * Get Id of DimUserProfile based on ORCID Id. */ - public async Task GetOrcidRegisteredDataSourceId() + public async Task GetUserprofileId(string orcidId) { - var orcidDatasourceName = "ORCID"; - var orcidRegisteredDataSource = await _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefaultAsync(p => p.Name == orcidDatasourceName); - if (orcidRegisteredDataSource == null) + DimUserProfile dimUserProfile = await _ttvContext.DimUserProfiles.Where(dup => dup.OrcidId == orcidId).AsNoTracking().FirstOrDefaultAsync(); + if (dimUserProfile == null) { - // Get ORCID organization - var orcidOrganizationId = await this.GetOrcidOrganizationId(); - - orcidRegisteredDataSource = new DimRegisteredDataSource() - { - DimOrganizationId = orcidOrganizationId, - Name = orcidDatasourceName, - SourceId = Constants.SourceIdentifiers.ORCID, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - _ttvContext.DimRegisteredDataSources.Add(orcidRegisteredDataSource); - await _ttvContext.SaveChangesAsync(); - return orcidRegisteredDataSource.Id; + return -1; } else { - return orcidRegisteredDataSource.Id; + return dimUserProfile.Id; } } /* - * Get id of Tiedejatutkimus.fi DimRegisteredDataSource. + * Check if user profile exists for ORCID Id. */ - public async Task GetTiedejatutkimusFiRegisteredDataSourceId() + public async Task UserprofileExistsForOrcidId(string orcidId) { - var tiedejatutkimusfiRegisteredDataSource = await _ttvContext.DimRegisteredDataSources.AsNoTracking().FirstOrDefaultAsync(p => p.Name == Constants.SourceIdentifiers.TIEDEJATUTKIMUS); - if (tiedejatutkimusfiRegisteredDataSource == null) - { - return -1; - } - else - { - return tiedejatutkimusfiRegisteredDataSource.Id; - } + int userProfileId = await GetUserprofileId(orcidId: orcidId); + return userProfileId > -1; } /* @@ -132,7 +133,7 @@ public async Task GetTiedejatutkimusFiRegisteredDataSourceId() */ public async Task AddOrUpdateDimName(String lastName, String firstNames, int dimKnownPersonId, int dimRegisteredDataSourceId) { - var dimName = await _ttvContext.DimNames.FirstOrDefaultAsync(dn => dn.DimKnownPersonIdConfirmedIdentityNavigation.Id == dimKnownPersonId && dn.DimRegisteredDataSourceId == dimRegisteredDataSourceId); + DimName dimName = await _ttvContext.DimNames.FirstOrDefaultAsync(dn => dn.DimKnownPersonIdConfirmedIdentityNavigation.Id == dimKnownPersonId && dn.DimRegisteredDataSourceId == dimRegisteredDataSourceId); if (dimName == null) { dimName = new DimName() @@ -140,10 +141,10 @@ public async Task AddOrUpdateDimName(String lastName, String firstNames LastName = lastName, FirstNames = firstNames, DimKnownPersonIdConfirmedIdentity = dimKnownPersonId, - DimKnownPersonidFormerNames = -1, - SourceId = "", + SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, + Created = _utilityService.GetCurrentDateTime(), + Modified = _utilityService.GetCurrentDateTime(), DimRegisteredDataSourceId = dimRegisteredDataSourceId }; _ttvContext.DimNames.Add(dimName); @@ -152,7 +153,7 @@ public async Task AddOrUpdateDimName(String lastName, String firstNames { dimName.LastName = lastName; dimName.FirstNames = firstNames; - dimName.Modified = DateTime.Now; + dimName.Modified = _utilityService.GetCurrentDateTime(); } await _ttvContext.SaveChangesAsync(); return dimName; @@ -163,7 +164,7 @@ public async Task AddOrUpdateDimName(String lastName, String firstNames */ public async Task AddOrUpdateDimResearcherDescription(String description_fi, String description_en, String description_sv, int dimKnownPersonId, int dimRegisteredDataSourceId) { - var dimResearcherDescription = await _ttvContext.DimResearcherDescriptions.FirstOrDefaultAsync(dr => dr.DimKnownPersonId == dimKnownPersonId && dr.DimRegisteredDataSourceId == dimRegisteredDataSourceId); + DimResearcherDescription dimResearcherDescription = await _ttvContext.DimResearcherDescriptions.FirstOrDefaultAsync(dr => dr.DimKnownPersonId == dimKnownPersonId && dr.DimRegisteredDataSourceId == dimRegisteredDataSourceId); if (dimResearcherDescription == null) { dimResearcherDescription = new DimResearcherDescription() @@ -171,9 +172,10 @@ public async Task AddOrUpdateDimResearcherDescription( ResearchDescriptionFi = description_fi, ResearchDescriptionEn = description_en, ResearchDescriptionSv = description_sv, - SourceId = "", + SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, + Created = _utilityService.GetCurrentDateTime(), + Modified = _utilityService.GetCurrentDateTime(), DimKnownPersonId = dimKnownPersonId, DimRegisteredDataSourceId = dimRegisteredDataSourceId }; @@ -184,7 +186,7 @@ public async Task AddOrUpdateDimResearcherDescription( dimResearcherDescription.ResearchDescriptionFi = description_fi; dimResearcherDescription.ResearchDescriptionEn = description_en; dimResearcherDescription.ResearchDescriptionSv = description_sv; - dimResearcherDescription.Modified = DateTime.Now; + dimResearcherDescription.Modified = _utilityService.GetCurrentDateTime(); } await _ttvContext.SaveChangesAsync(); return dimResearcherDescription; @@ -195,15 +197,16 @@ public async Task AddOrUpdateDimResearcherDescription( */ public async Task AddOrUpdateDimEmailAddress(string emailAddress, int dimKnownPersonId, int dimRegisteredDataSourceId) { - var dimEmailAddress = await _ttvContext.DimEmailAddrresses.FirstOrDefaultAsync(dr => dr.Email == emailAddress && dr.DimKnownPersonId == dimKnownPersonId && dr.DimRegisteredDataSourceId == dimRegisteredDataSourceId); + DimEmailAddrress dimEmailAddress = await _ttvContext.DimEmailAddrresses.FirstOrDefaultAsync(dr => dr.Email == emailAddress && dr.DimKnownPersonId == dimKnownPersonId && dr.DimRegisteredDataSourceId == dimRegisteredDataSourceId); if (dimEmailAddress == null) { dimEmailAddress = new DimEmailAddrress() { Email = emailAddress, - SourceId = "", + SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, + Created = _utilityService.GetCurrentDateTime(), + Modified = _utilityService.GetCurrentDateTime(), DimKnownPersonId = dimKnownPersonId, DimRegisteredDataSourceId = dimRegisteredDataSourceId }; @@ -211,7 +214,7 @@ public async Task AddOrUpdateDimEmailAddress(string emailAddre } else { - dimEmailAddress.Modified = DateTime.Now; + dimEmailAddress.Modified = _utilityService.GetCurrentDateTime(); } await _ttvContext.SaveChangesAsync(); return dimEmailAddress; @@ -247,31 +250,22 @@ public FactFieldValue GetEmptyFactFieldValue() DimAffiliationId = -1, DimResearcherToResearchCommunityId = -1, DimFieldOfScienceId = -1, + DimResearchDatasetId = -1, Show = false, PrimaryValue = false, - SourceId = " ", + SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now, - Modified = null + Created = _utilityService.GetCurrentDateTime(), + Modified = _utilityService.GetCurrentDateTime() }; } - /* - * Get empty FactFieldValue. Set SourceID to ORCID. - */ - public FactFieldValue GetEmptyFactFieldValueOrcid() - { - var factFieldValue = this.GetEmptyFactFieldValue(); - factFieldValue.SourceId = Constants.SourceIdentifiers.ORCID; - return factFieldValue; - } - /* * Get empty FactFieldValue. Set SourceID to DEMO. */ public FactFieldValue GetEmptyFactFieldValueDemo() { - var factFieldValue = this.GetEmptyFactFieldValue(); + FactFieldValue factFieldValue = this.GetEmptyFactFieldValue(); factFieldValue.SourceId = Constants.SourceIdentifiers.DEMO; return factFieldValue; } @@ -315,7 +309,7 @@ public DimOrcidPublication GetEmptyDimOrcidPublication() Report = null, ThesisTypeCode = null, DoiHandle = null, - SourceId = "", + SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, Created = null, Modified = null, @@ -333,8 +327,8 @@ public DimPid GetEmptyDimPid() { return new DimPid() { - PidContent = " ", - PidType = " ", + PidContent = "", + PidType = "", DimOrganizationId = -1, DimKnownPersonId = -1, DimPublicationId = -1, @@ -347,135 +341,1243 @@ public DimPid GetEmptyDimPid() DimResearchActivityId = -1, DimEventId = -1, DimOrcidPublicationId = -1, - SourceId = " ", + SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now + Created = _utilityService.GetCurrentDateTime(), + Modified = _utilityService.GetCurrentDateTime() }; } + /* + * Search DimAffiliation items from TTV database and link them to user profile. + */ + public void AddDimAffiliationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + { + DimFieldDisplaySetting dimFieldDisplaySetting_affiliation = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION).First(); + + foreach (DimAffiliation dimAffiliation in dimKnownPerson.DimAffiliations.Where(dimAffiliation => dimAffiliation.DimRegisteredDataSourceId != -1)) + { + FactFieldValue factFieldValueAffiliation = this.GetEmptyFactFieldValue(); + factFieldValueAffiliation.DimUserProfileId = dimUserProfile.Id; + factFieldValueAffiliation.DimFieldDisplaySettingsId = dimFieldDisplaySetting_affiliation.Id; + factFieldValueAffiliation.DimAffiliationId = dimAffiliation.Id; + factFieldValueAffiliation.DimRegisteredDataSourceId = dimAffiliation.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueAffiliation); + } + } + + /* + * Search DimEducation items from TTV database and link them to user profile. + */ + public void AddDimEducationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + { + DimFieldDisplaySetting dimFieldDisplaySetting_education = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION).First(); + + foreach (DimEducation dimEducation in dimKnownPerson.DimEducations.Where(dimEducation => dimEducation.DimRegisteredDataSourceId != -1)) + { + FactFieldValue factFieldValueEducation = this.GetEmptyFactFieldValue(); + factFieldValueEducation.DimUserProfileId = dimUserProfile.Id; + factFieldValueEducation.DimFieldDisplaySettingsId = dimFieldDisplaySetting_education.Id; + factFieldValueEducation.DimEducationId = dimEducation.Id; + factFieldValueEducation.DimRegisteredDataSourceId = dimEducation.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueEducation); + } + } - //public async Task AddTtvTelephoneNumbers(DimKnownPerson dimKnownPerson) - //{ - // var dimUserProfile = dimKnownPerson.DimUserProfiles.FirstOrDefault(); - // if (dimUserProfile != null) - // { - // foreach (DimTelephoneNumber dimTelephoneNumber in dimKnownPerson.DimTelephoneNumbers) - // { - // // Find DimFieldDisplaySettings for registered data source - // var dimFieldDisplaySettingsTelephoneNumber = - // await _ttvContext.DimFieldDisplaySettings.FirstOrDefaultAsync( - // dfds => - // dfds.DimUserProfileId == dimUserProfile.Id && - // dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER && - // dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSourceId == dimTelephoneNumber.DimRegisteredDataSourceId - // ); - - // if (dimFieldDisplaySettingsTelephoneNumber == null) - // { - // // Add new DimFieldDisplaySettings for DimTelephoneNumber - // dimFieldDisplaySettingsTelephoneNumber = new DimFieldDisplaySetting() - // { - // DimUserProfileId = dimUserProfile.Id, - // } - - // dimFieldDisplaySettingsTelephoneNumber.BrFieldDisplaySettingsDimRegisteredDataSources.Add( - // new BrFieldDisplaySettingsDimRegisteredDataSource() - // { - // DimFieldDisplaySettingsId = dimFieldDisplaySettingsTelephoneNumber.Id, - // DimRegisteredDataSourceId = orcidRegisteredDataSourceId - // } - // ); - // } - // } - // await _ttvContext.SaveChangesAsync(); - // } - // return false; - //} + /* + * Search DimResearcherDescription items from TTV database and link them to user profile. + */ + public void AddDimResearcherDescriptionToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + { + DimFieldDisplaySetting dimFieldDisplaySetting_researcher_description = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION).First(); + + foreach (DimResearcherDescription dimResearcherDescription in dimKnownPerson.DimResearcherDescriptions.Where(dimResearcherDescription => dimResearcherDescription.DimRegisteredDataSourceId != -1)) + { + FactFieldValue factFieldValueResearcherDescription = this.GetEmptyFactFieldValue(); + factFieldValueResearcherDescription.DimUserProfileId = dimUserProfile.Id; + factFieldValueResearcherDescription.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researcher_description.Id; + factFieldValueResearcherDescription.DimResearcherDescriptionId = dimResearcherDescription.Id; + factFieldValueResearcherDescription.DimRegisteredDataSourceId = dimResearcherDescription.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueResearcherDescription); + } + } /* - * Add publications from DimPublication into userprofile. + * Search DimEmailAddress items from TTV database and link them to user profile. */ - public async Task AddTtvPublications(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + public void AddDimEmailAddressItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + { + DimFieldDisplaySetting dimFieldDisplaySetting_emailAddress = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS).First(); + + foreach (DimEmailAddrress dimEmailAddress in dimKnownPerson.DimEmailAddrresses.Where(dimEmailAddress => dimEmailAddress.DimRegisteredDataSourceId != -1)) + { + FactFieldValue factFieldValueEmailAddress = this.GetEmptyFactFieldValue(); + factFieldValueEmailAddress.DimUserProfileId = dimUserProfile.Id; + factFieldValueEmailAddress.DimFieldDisplaySettingsId = dimFieldDisplaySetting_emailAddress.Id; + factFieldValueEmailAddress.DimEmailAddrressId = dimEmailAddress.Id; + factFieldValueEmailAddress.DimRegisteredDataSourceId = dimEmailAddress.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueEmailAddress); + } + } + + /* + * Search DimTelephoneNumber items from TTV database and link them to user profile. + */ + public void AddDimTelephoneItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + { + DimFieldDisplaySetting dimFieldDisplaySetting_telephoneNumber = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER).First(); + + foreach (DimTelephoneNumber dimTelephoneNumber in dimKnownPerson.DimTelephoneNumbers.Where(dimTelephoneNumber => dimTelephoneNumber.DimRegisteredDataSourceId != -1)) + { + FactFieldValue factFieldValueTelephoneNumber = this.GetEmptyFactFieldValue(); + factFieldValueTelephoneNumber.DimUserProfileId = dimUserProfile.Id; + factFieldValueTelephoneNumber.DimFieldDisplaySettingsId = dimFieldDisplaySetting_telephoneNumber.Id; + factFieldValueTelephoneNumber.DimTelephoneNumberId = dimTelephoneNumber.Id; + factFieldValueTelephoneNumber.DimRegisteredDataSourceId = dimTelephoneNumber.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueTelephoneNumber); + } + } + + /* + * Search FactContribution related items from TTV database and link them to user profile. + */ + public void AddFactContributionItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) { /* - * Loop DimNames, then related FactContributions. FactContribution may have relation to DimPublication (DimPublicationId != -1). + * Loop DimNames, then related FactContributions. * - * DimKnownPerson => DimName => FactContribution => DimPublication + * DimKnownPerson + * => DimName + * => FactContribution + * => DimPublication + * => DimFundingDecision + * => DimResearchDataset + * => DimResearchActivity * - * NOTE! Data source for DimPublication must be taken from DimName, not from DimPublication. - * Skip item if DimName does not have data source set. + * NOTE! Registered data source must be taken from DimName. + * Skip item if DimName does not have registered data source. */ - foreach (DimName dimName in dimKnownPerson.DimNameDimKnownPersonIdConfirmedIdentityNavigations) + + // DimFieldDisplaySetting for publications + DimFieldDisplaySetting dimFieldDisplaySetting_publication = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION).First(); + + // DimFieldDisplaySetting for funding decisions + DimFieldDisplaySetting dimFieldDisplaySetting_fundingDecision = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION).First(); + + // DimFieldDisplaySetting for research datasets + DimFieldDisplaySetting dimFieldDisplaySetting_researchDataset = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET).First(); + + // DimFieldDisplaySetting for research activity + DimFieldDisplaySetting dimFieldDisplaySetting_researchActivity = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY).First(); + + // Loop DimNames, which have valid registered data source + foreach (DimName dimName in dimKnownPerson.DimNames.Where(dimName => dimName.DimRegisteredDataSourceId != -1)) { - // Collect publication ids into a list. - var publicationsIds = new List(); - // Registered data source from DimName must be used as a publication data source. - var dimNameRegisteredDataSource = dimName.DimRegisteredDataSource; + // Collect entity IDs into lists. + List publicationsIds = new(); + List fundingDecisionIds = new(); + List researchDatasetIds = new(); + List researchActivityIds = new(); - // Skip if DimName does not have registered data source. - if (dimNameRegisteredDataSource != null) + // Loop FactContributions. Collect "non -1" values only. + foreach ( + FactContribution factContribution in dimName.FactContributions.Where( + fc => fc.DimPublicationId != -1 || fc.DimFundingDecisionId != -1 || fc.DimResearchDatasetId != -1 || fc.DimResearchActivityId != -1 + ) + ) { - // DimName can relate to many FactContributions. Collect valid publication ids (not -1). - foreach (FactContribution factContribution in dimName.FactContributions.Where(fc => fc.DimPublicationId != -1)) + // FactContribution is linked to DimPublication + if (factContribution.DimPublicationId != -1) { publicationsIds.Add(factContribution.DimPublicationId); } - // Get DimFieldDisplaySetting for the registered data source. - var dimFieldDisplaySetting = await _ttvContext.DimFieldDisplaySettings - .Include(dfds => dfds.BrFieldDisplaySettingsDimRegisteredDataSources).AsNoTracking() - .FirstOrDefaultAsync( - dfds => - dfds.DimUserProfileId == dimUserProfile.Id && - dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION && - dfds.BrFieldDisplaySettingsDimRegisteredDataSources.First().DimRegisteredDataSourceId == dimNameRegisteredDataSource.Id - ); + // FactContribution is linked to DimFundingDecision + if (factContribution.DimFundingDecisionId != -1) + { + fundingDecisionIds.Add(factContribution.DimFundingDecisionId); + } + + // FactContribution is linked to DimResearchDataset + if (factContribution.DimResearchDatasetId != -1) + { + researchDatasetIds.Add(factContribution.DimResearchDatasetId); + } + + // FactContribution is linked to DimResearchActivity + if (factContribution.DimResearchActivityId != -1) + { + researchActivityIds.Add(factContribution.DimResearchActivityId); + } + } + + // Add FactFieldValues for DimPublication. Remove duplicate IDs. + foreach (int publicationId in publicationsIds.Distinct()) + { + FactFieldValue factFieldValuePublication = this.GetEmptyFactFieldValue(); + factFieldValuePublication.DimUserProfileId = dimUserProfile.Id; + factFieldValuePublication.DimFieldDisplaySettingsId = dimFieldDisplaySetting_publication.Id; + factFieldValuePublication.DimPublicationId = publicationId; + factFieldValuePublication.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValuePublication); + } + + // Add FactFieldValues for DimFundingDecision. Remove duplicate IDs. + foreach (int fundingDecisionId in fundingDecisionIds.Distinct()) + { + FactFieldValue factFieldValueFundingDecision = this.GetEmptyFactFieldValue(); + factFieldValueFundingDecision.DimUserProfileId = dimUserProfile.Id; + factFieldValueFundingDecision.DimFieldDisplaySettingsId = dimFieldDisplaySetting_fundingDecision.Id; + factFieldValueFundingDecision.DimFundingDecisionId = fundingDecisionId; + factFieldValueFundingDecision.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueFundingDecision); + } + + // Add FactFieldValues for DimResearchDataset. Remove duplicate IDs. + foreach (int researchDatasetId in researchDatasetIds.Distinct()) + { + FactFieldValue factFieldValueResearchDataset = this.GetEmptyFactFieldValue(); + factFieldValueResearchDataset.DimUserProfileId = dimUserProfile.Id; + factFieldValueResearchDataset.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researchDataset.Id; + factFieldValueResearchDataset.DimResearchDatasetId = researchDatasetId; + factFieldValueResearchDataset.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueResearchDataset); + } + + // Add FactFieldValues for DimResearchActivity. Remove duplicate IDs. + foreach (int researchActivityId in researchActivityIds.Distinct()) + { + FactFieldValue factFieldValueResearchActivity = this.GetEmptyFactFieldValue(); + factFieldValueResearchActivity.DimUserProfileId = dimUserProfile.Id; + factFieldValueResearchActivity.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researchActivity.Id; + factFieldValueResearchActivity.DimResearchActivityId = researchActivityId; + factFieldValueResearchActivity.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueResearchActivity); + } + } + } + + /* + * Search and add data from TTV database. + * This is data that is already linked to the ORCID id in DimPid and it's related DimKnownPerson. + */ + public void AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + { + // DimEmailAddress + AddDimEmailAddressItemsToUserProfile(dimKnownPerson, dimUserProfile); + + // DimTelephoneNumber + AddDimTelephoneItemsToUserProfile(dimKnownPerson, dimUserProfile); + + // DimAffiliation + AddDimAffiliationToUserProfile(dimKnownPerson, dimUserProfile); + + // DimEducation + AddDimEducationToUserProfile(dimKnownPerson, dimUserProfile); + + // DimResearcherDescription + AddDimResearcherDescriptionToUserProfile(dimKnownPerson, dimUserProfile); + + // FactContribution + AddFactContributionItemsToUserProfile(dimKnownPerson, dimUserProfile); + } + + /* + * Create user profile. + * The following entities will be created in the database: + * - DimPid + * - DimKnownPerson + * - DimUserProfile + * - DimFieldDisplaySettings + * - FactFieldValues + * - BrGrantedPermissions + */ + public async Task CreateProfile(string orcidId) + { + // Get DimPid by ORCID ID. + // Also get all related entities, that should be automatically included in profile. + DimPid dimPid = await _ttvContext.DimPids + // FactContribution + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimNames) + .ThenInclude(dn => dn.FactContributions).AsNoTracking() + // DimName + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimNames).AsNoTracking() + // DimAffiliation + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimAffiliations).AsNoTracking() + // DimEducation + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimEducations).AsNoTracking() + // DimReseacherDescription + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimResearcherDescriptions).AsNoTracking() + // DimEmailAddress + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimEmailAddrresses).AsNoTracking() + // DimTelephoneNumber + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimTelephoneNumbers).AsNoTracking() + // DimUserProfile + .Include(dp => dp.DimKnownPerson) + .ThenInclude(dkp => dkp.DimUserProfiles) + .ThenInclude(dup => dup.DimFieldDisplaySettings).AsNoTracking() + .FirstOrDefaultAsync(dimPid => dimPid.PidContent == orcidId && dimPid.PidType == Constants.PidTypes.ORCID); + + // Get current DateTime + DateTime currentDateTime = _utilityService.GetCurrentDateTime(); + + // DimPid and DimKnownPerson + if (dimPid == null) + { + // DimPid was not found, add new. + dimPid = GetEmptyDimPid(); + dimPid.PidContent = orcidId; + dimPid.PidType = Constants.PidTypes.ORCID; + dimPid.SourceId = Constants.SourceIdentifiers.PROFILE_API; + + // Since new DimPid is added, then new DimKnownPerson must be added. + dimPid.DimKnownPerson = new DimKnownPerson() + { + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + + _ttvContext.DimPids.Add(dimPid); + } + else if (dimPid.DimKnownPerson == null || dimPid.DimKnownPersonId == -1) + { + // DimPid was found but it does not have related DimKnownPerson, add new. + DimKnownPerson kp = new() + { + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimKnownPeople.Add(kp); + dimPid.DimKnownPerson = kp; + } + + // Save DimPid and DimKnownPerson changes. + await _ttvContext.SaveChangesAsync(); - // If it was not found, then create DimFieldDisplaySetting for the registered data source. - if (dimFieldDisplaySetting == null) + // DimUserProfile + DimUserProfile dimUserProfile = dimPid.DimKnownPerson.DimUserProfiles.FirstOrDefault(); + if (dimUserProfile == null) + { + // DimUserProfile was not found, add new. + dimUserProfile = new DimUserProfile() + { + DimKnownPersonId = dimPid.DimKnownPerson.Id, + OrcidId = orcidId, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + AllowAllSubscriptions = false + }; + _ttvContext.DimUserProfiles.Add(dimUserProfile); + } + + // DimFieldDisplaySettings + if (dimUserProfile.DimFieldDisplaySettings.Count == 0) + { + // DimUserProfile does not have DimFieldDisplaySettings, add new. + foreach (int fieldIdentifier in GetFieldIdentifiers()) + { + DimFieldDisplaySetting dimFieldDisplaySetting = new() { - dimFieldDisplaySetting = new DimFieldDisplaySetting() + FieldIdentifier = fieldIdentifier, + DimUserProfile = dimUserProfile, + Show = false, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimFieldDisplaySettings.Add(dimFieldDisplaySetting); + } + } + + // BrGrantedPermissions - Add default sharing permissions + _ttvContext.BrGrantedPermissions.AddRange( + await _sharingService.GetDefaultSharingPermissionsListForUserProfile(dimUserProfile) + ); + + // Save DimUserProfile, DimFieldDisplaySettings and BrGrantedPermissions changes. + await _ttvContext.SaveChangesAsync(); + + // FactFieldValues - Search TTV database and add related entries into user profile. + AddTtvDataToUserProfile(dimPid.DimKnownPerson, dimUserProfile); + + // Save FactFieldValues changes. + await _ttvContext.SaveChangesAsync(); + } + + + + /* + * Get profile data. New version using data structure, + * where each item contains a list of data sources. + */ + public async Task GetProfileDataAsync2(int userprofileId, bool forElasticsearch = false) + { + // Response data + ProfileEditorDataResponse profileDataResponse = new() { }; + + // Get SQL statement for profile data query + string profileDataSql = _ttvSqlService.GetSqlQuery_ProfileData(userprofileId, forElasticsearch); + + // Execute SQL statement using Dapper + var connection = _ttvContext.Database.GetDbConnection(); + List profileDataList = (await connection.QueryAsync(profileDataSql)).ToList(); + + // Helper list, which is used in collecting list of unique data sources and detecting whether the item is already in the list. + List uniqueDataSourceIds = new(); + + foreach (ProfileDataFromSql p in profileDataList) + { + // Organization name translation + NameTranslation nameTranslationSourceOrganization = _languageService.GetNameTranslation( + nameFi: p.DimRegisteredDataSource_DimOrganization_NameFi, + nameEn: p.DimRegisteredDataSource_DimOrganization_NameEn, + nameSv: p.DimRegisteredDataSource_DimOrganization_NameSv + ); + + // Source object containing registered data source and organization name. + ProfileEditorSource profileEditorSource = new() + { + Id = p.DimRegisteredDataSource_Id, + RegisteredDataSource = p.DimRegisteredDataSource_Name, + Organization = new Organization() + { + NameFi = nameTranslationSourceOrganization.NameFi, + NameEn = nameTranslationSourceOrganization.NameEn, + NameSv = nameTranslationSourceOrganization.NameSv, + SectorId = p.DimRegisteredDataSource_DimOrganization_DimSector_SectorId + } + }; + + // Add data source into list of unique data sources. + if (!uniqueDataSourceIds.Contains(profileEditorSource.Id)) + { + profileDataResponse.uniqueDataSources.Add(profileEditorSource); + uniqueDataSourceIds.Add(profileEditorSource.Id); + } + + switch (p.DimFieldDisplaySettings_FieldIdentifier) + { + // Name + case Constants.FieldIdentifiers.PERSON_NAME: + profileDataResponse.personal.names.Add( + new ProfileEditorName() + { + FirstNames = p.DimName_FirstNames, + LastName = p.DimName_LastName, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimNameId, + Type = Constants.FieldIdentifiers.PERSON_NAME, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Other name + case Constants.FieldIdentifiers.PERSON_OTHER_NAMES: + profileDataResponse.personal.otherNames.Add( + new ProfileEditorName() + { + FullName = p.DimName_FullName, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimNameId, + Type = Constants.FieldIdentifiers.PERSON_OTHER_NAMES, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Web link + case Constants.FieldIdentifiers.PERSON_WEB_LINK: + profileDataResponse.personal.webLinks.Add( + new ProfileEditorWebLink() + { + Url = p.DimWebLink_Url, + LinkLabel = p.DimWebLink_LinkLabel, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimWebLinkId, + Type = Constants.FieldIdentifiers.PERSON_WEB_LINK, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Researcher description + case Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION: + // Researcher description name translation + NameTranslation nameTranslationResearcherDescription = _languageService.GetNameTranslation( + nameFi: p.DimResearcherDescription_ResearchDescriptionFi, + nameEn: p.DimResearcherDescription_ResearchDescriptionEn, + nameSv: p.DimResearcherDescription_ResearchDescriptionSv + ); + profileDataResponse.personal.researcherDescriptions.Add( + new ProfileEditorResearcherDescription() + { + ResearchDescriptionFi = nameTranslationResearcherDescription.NameFi, + ResearchDescriptionEn = nameTranslationResearcherDescription.NameEn, + ResearchDescriptionSv = nameTranslationResearcherDescription.NameSv, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimResearcherDescriptionId, + Type = Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Email + case Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS: + profileDataResponse.personal.emails.Add( + new ProfileEditorEmail() + { + Value = p.DimEmailAddrress_Email, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimEmailAddrressId, + Type = Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Telephone number + case Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER: + profileDataResponse.personal.telephoneNumbers.Add( + new ProfileEditorTelephoneNumber() + { + Value = p.DimTelephoneNumber_TelephoneNumber, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimTelephoneNumberId, + Type = Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Field of science + case Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE: + // Field of science name translation + NameTranslation nameTranslationFieldOfScience = _languageService.GetNameTranslation( + nameFi: p.DimFieldOfScience_NameFi, + nameEn: p.DimFieldOfScience_NameEn, + nameSv: p.DimFieldOfScience_NameSv + ); + + profileDataResponse.personal.fieldOfSciences.Add( + new ProfileEditorFieldOfScience() + { + NameFi = nameTranslationFieldOfScience.NameFi, + NameEn = nameTranslationFieldOfScience.NameEn, + NameSv = nameTranslationFieldOfScience.NameSv, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimFieldOfScienceId, + Type = Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Keyword + case Constants.FieldIdentifiers.PERSON_KEYWORD: + profileDataResponse.personal.keywords.Add( + new ProfileEditorKeyword() + { + Value = p.DimKeyword_Keyword, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimKeywordId, + Type = Constants.FieldIdentifiers.PERSON_KEYWORD, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // External identifier + case Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER: + profileDataResponse.personal.externalIdentifiers.Add( + new ProfileEditorExternalIdentifier() + { + PidContent = p.DimPid_PidContent, + PidType = p.DimPid_PidType, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimPidId, + Type = Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Affiliation + case Constants.FieldIdentifiers.ACTIVITY_AFFILIATION: + // Get affiliation organization name from related DimOrganization (ffv.DimAffiliation.DimOrganization), if exists. + // Otherwise from DimIdentifierlessData (ffv.DimIdentifierlessData). + // Name translation service ensures that none of the language fields is empty. + NameTranslation nameTranslationAffiliationOrganization = new(); + if (p.DimAffiliation_DimOrganization_Id > 0) + { + nameTranslationAffiliationOrganization = _languageService.GetNameTranslation( + nameFi: p.DimAffiliation_DimOrganization_NameFi, + nameEn: p.DimAffiliation_DimOrganization_NameEn, + nameSv: p.DimAffiliation_DimOrganization_NameSv + ); + } + else if (p.FactFieldValues_DimIdentifierlessDataId > -1 && + p.DimIdentifierlessData_Type == Constants.IdentifierlessDataTypes.ORGANIZATION_NAME) { - DimUserProfileId = dimUserProfile.Id, - FieldIdentifier = Constants.FieldIdentifiers.ACTIVITY_PUBLICATION, - Show = false, - SourceId = " ", - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = DateTime.Now - }; - dimFieldDisplaySetting.BrFieldDisplaySettingsDimRegisteredDataSources.Add( - new BrFieldDisplaySettingsDimRegisteredDataSource() + nameTranslationAffiliationOrganization = _languageService.GetNameTranslation( + nameFi: p.DimIdentifierlessData_ValueFi, + nameEn: p.DimIdentifierlessData_ValueEn, + nameSv: p.DimIdentifierlessData_ValueSv + ); + } + + // Name translation for position name + NameTranslation nameTranslationPositionName = _languageService.GetNameTranslation( + nameFi: p.DimAffiliation_PositionNameFi, + nameEn: p.DimAffiliation_PositionNameEn, + nameSv: p.DimAffiliation_PositionNameSv + ); + + // Name translation for department name + NameTranslation nameTranslationAffiliationDepartment = new(); + if (p.DimIdentifierlessData_Type != null && p.DimIdentifierlessData_Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT) + { + nameTranslationAffiliationDepartment = _languageService.GetNameTranslation( + nameFi: p.DimIdentifierlessData_ValueFi, + nameEn: p.DimIdentifierlessData_ValueEn, + nameSv: p.DimIdentifierlessData_ValueSv + ); + } + else if (p.DimIdentifierlessData_Child_Type != null && p.DimIdentifierlessData_Child_Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT) + { + nameTranslationAffiliationDepartment = _languageService.GetNameTranslation( + nameFi: p.DimIdentifierlessData_Child_ValueFi, + nameEn: p.DimIdentifierlessData_Child_ValueEn, + nameSv: p.DimIdentifierlessData_Child_ValueSv + ); + } + + profileDataResponse.activity.affiliations.Add( + new() { - DimFieldDisplaySettingsId = dimFieldDisplaySetting.Id, - DimRegisteredDataSourceId = dimNameRegisteredDataSource.Id + OrganizationNameFi = nameTranslationAffiliationOrganization.NameFi, + OrganizationNameEn = nameTranslationAffiliationOrganization.NameEn, + OrganizationNameSv = nameTranslationAffiliationOrganization.NameSv, + DepartmentNameFi = nameTranslationAffiliationDepartment.NameFi, + DepartmentNameEn = nameTranslationAffiliationDepartment.NameSv, + DepartmentNameSv = nameTranslationAffiliationDepartment.NameEn, + PositionNameFi = nameTranslationPositionName.NameFi, + PositionNameEn = nameTranslationPositionName.NameEn, + PositionNameSv = nameTranslationPositionName.NameSv, + Type = p.DimAffiliation_DimReferenceData_NameFi, + StartDate = new ProfileEditorDate() + { + Year = p.DimAffiliation_StartDate_Year, + Month = p.DimAffiliation_StartDate_Month, + Day = p.DimAffiliation_StartDate_Day + }, + EndDate = new ProfileEditorDate() + { + Year = p.DimAffiliation_EndDate_Year, + Month = p.DimAffiliation_EndDate_Month, + Day = p.DimAffiliation_EndDate_Day + }, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimAffiliationId, + Type = Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } } ); - _ttvContext.DimFieldDisplaySettings.Add(dimFieldDisplaySetting); + break; + + // Education + case Constants.FieldIdentifiers.ACTIVITY_EDUCATION: + // Name translation service ensures that none of the language fields is empty. + NameTranslation nameTraslationEducation = _languageService.GetNameTranslation( + nameFi: p.DimEducation_NameFi, + nameEn: p.DimEducation_NameEn, + nameSv: p.DimEducation_NameSv + ); + + profileDataResponse.activity.educations.Add( + new() + { + NameFi = nameTraslationEducation.NameFi, + NameEn = nameTraslationEducation.NameEn, + NameSv = nameTraslationEducation.NameSv, + DegreeGrantingInstitutionName = p.DimEducation_DegreeGrantingInstitutionName, + StartDate = new ProfileEditorDate() + { + Year = p.DimEducation_StartDate_Year, + Month = p.DimEducation_StartDate_Month, + Day = p.DimEducation_StartDate_Day + }, + EndDate = new ProfileEditorDate() + { + Year = p.DimEducation_EndDate_Year, + Month = p.DimEducation_EndDate_Month, + Day = p.DimEducation_EndDate_Day + }, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimEducationId, + Type = Constants.FieldIdentifiers.ACTIVITY_EDUCATION, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Publication + case Constants.FieldIdentifiers.ACTIVITY_PUBLICATION: + profileDataResponse.activity.publications = + _duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSource, + profileData: p, + publications: profileDataResponse.activity.publications + ); + break; + + // Publication (ORCID) + case Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID: + profileDataResponse.activity.publications = + _duplicateHandlerService.AddPublicationToProfileEditorData( + dataSource: profileEditorSource, + profileData: p, + publications: profileDataResponse.activity.publications + ); + break; + // Research activity + case Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY: + NameTranslation nameTraslationResearchActivityName = _languageService.GetNameTranslation( + nameFi: p.DimResearchActivity_NameFi, + nameEn: p.DimResearchActivity_NameEn, + nameSv: p.DimResearchActivity_NameSv + ); + NameTranslation nameTraslationResearchActivityDescription = _languageService.GetNameTranslation( + nameFi: p.DimResearchActivity_DescriptionFi, + nameEn: p.DimResearchActivity_DescriptionEn, + nameSv: p.DimResearchActivity_DescriptionSv + ); + profileDataResponse.activity.activitiesAndRewards.Add( + new ProfileEditorActivityAndReward() + { + NameFi = nameTraslationResearchActivityName.NameFi, + NameEn = nameTraslationResearchActivityName.NameEn, + NameSv = nameTraslationResearchActivityName.NameSv, + DescriptionFi = nameTraslationResearchActivityDescription.NameFi, + DescriptionEn = nameTraslationResearchActivityDescription.NameEn, + DescriptionSv = nameTraslationResearchActivityDescription.NameSv, + InternationalCollaboration = p.DimResearchActivity_InternationalCollaboration, + StartDate = new ProfileEditorDate() + { + Year = p.DimResearchActivity_StartDate_Year, + Month = p.DimResearchActivity_StartDate_Month, + Day = p.DimResearchActivity_StartDate_Day + }, + EndDate = new ProfileEditorDate() + { + Year = p.DimResearchActivity_EndDate_Year, + Month = p.DimResearchActivity_EndDate_Month, + Day = p.DimResearchActivity_EndDate_Day + }, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimResearchActivityId, + Type = Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Funding decision + case Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION: + // Name translation: funding decision name + NameTranslation nameTranslationFundingDecisionName = _languageService.GetNameTranslation( + nameFi: p.DimFundingDecision_NameFi, + nameEn: p.DimFundingDecision_NameEn, + nameSv: p.DimFundingDecision_NameSv + ); + // Name translation: funding decision description + NameTranslation nameTranslationFundingDecisionDescription = _languageService.GetNameTranslation( + nameFi: p.DimFundingDecision_DescriptionFi, + nameEn: p.DimFundingDecision_DescriptionEn, + nameSv: p.DimFundingDecision_DescriptionSv + ); + // Name translation: funder name + NameTranslation nameTranslationFunderName = _languageService.GetNameTranslation( + nameFi: p.DimFundingDecision_Funder_NameFi, + nameEn: p.DimFundingDecision_Funder_NameEn, + nameSv: p.DimFundingDecision_Funder_NameSv + ); + // Name translation: call programme + NameTranslation nameTranslationCallProgramme = _languageService.GetNameTranslation( + nameFi: p.DimFundingDecision_DimCallProgramme_NameFi, + nameEn: p.DimFundingDecision_DimCallProgramme_NameEn, + nameSv: p.DimFundingDecision_DimCallProgramme_NameSv + ); + // Name translation: type of funding name + NameTranslation nameTranslationTypeOfFundingName = _languageService.GetNameTranslation( + nameFi: p.DimFundingDecision_DimTypeOfFunding_NameFi, + nameEn: p.DimFundingDecision_DimTypeOfFunding_NameEn, + nameSv: p.DimFundingDecision_DimTypeOfFunding_NameSv + ); + profileDataResponse.activity.fundingDecisions.Add( + new ProfileEditorFundingDecision() + { + ProjectId = p.FactFieldValues_DimFundingDecisionId, + ProjectAcronym = p.DimFundingDecision_Acronym, + ProjectNameFi = nameTranslationFundingDecisionName.NameFi, + ProjectNameEn = nameTranslationFundingDecisionName.NameEn, + ProjectNameSv = nameTranslationFundingDecisionName.NameSv, + ProjectDescriptionFi = nameTranslationFundingDecisionDescription.NameFi, + ProjectDescriptionEn = nameTranslationFundingDecisionDescription.NameEn, + ProjectDescriptionSv = nameTranslationFundingDecisionDescription.NameSv, + FunderNameFi = nameTranslationFunderName.NameFi, + FunderNameEn = nameTranslationFunderName.NameEn, + FunderNameSv = nameTranslationFunderName.NameSv, + FunderProjectNumber = p.DimFundingDecision_FunderProjectNumber, + TypeOfFundingNameFi = nameTranslationTypeOfFundingName.NameFi, + TypeOfFundingNameEn = nameTranslationTypeOfFundingName.NameEn, + TypeOfFundingNameSv = nameTranslationTypeOfFundingName.NameSv, + CallProgrammeNameFi = nameTranslationCallProgramme.NameFi, + CallProgrammeNameEn = nameTranslationCallProgramme.NameEn, + CallProgrammeNameSv = nameTranslationCallProgramme.NameSv, + FundingStartYear = p.DimFundingDecision_StartDate_Year, + FundingEndYear = p.DimFundingDecision_EndDate_Year, + AmountInEur = p.DimFundingDecision_amount_in_EUR, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimFundingDecisionId, + Type = Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + // Research dataset + case Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET: + // Name translation: research dataset name + NameTranslation nameTranslationResearchDatasetName = _languageService.GetNameTranslation( + nameFi: p.DimResearchDataset_NameFi, + nameEn: p.DimResearchDataset_NameEn, + nameSv: p.DimResearchDataset_NameSv + ); + // Name translation: research dataset description + NameTranslation nameTranslationResearchDatasetDescription = _languageService.GetNameTranslation( + nameFi: p.DimResearchDataset_DescriptionFi, + nameEn: p.DimResearchDataset_DescriptionEn, + nameSv: p.DimResearchDataset_DescriptionSv + ); + profileDataResponse.activity.researchDatasets.Add( + new ProfileEditorResearchDataset() + { + // List Actor + Identifier = p.DimResearchDataset_LocalIdentifier, + NameFi = nameTranslationResearchDatasetName.NameFi, + NameEn = nameTranslationResearchDatasetName.NameEn, + NameSv = nameTranslationResearchDatasetName.NameSv, + DescriptionFi = nameTranslationResearchDatasetDescription.NameFi, + DescriptionSv = nameTranslationResearchDatasetDescription.NameFi, + DescriptionEn = nameTranslationResearchDatasetDescription.NameFi, + // Only year part of datetime is set in DatasetCreated + DatasetCreated = + (p.DimResearchDataset_DatasetCreated != null) ? p.DimResearchDataset_DatasetCreated.Value.Year : null, + PreferredIdentifiers = + (await connection.QueryAsync( + $"SELECT pid_type AS 'PidType', pid_content AS 'PidContent' FROM dim_pid WHERE dim_research_dataset_id={p.FactFieldValues_DimResearchDatasetId}" + )).ToList(), + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimResearchDatasetId, + Type = Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + } + ); + break; + + default: + break; + } + } + + return profileDataResponse; + } + + /* + * Delete profile data using Dapper + * - get FactFieldValues + * - delete FactFieldValues + * - delete DimIdentifierlessData (children and parent) + * - deöete ORCID put codes from DimPid + * - delete FactFieldValues related items + * - delete DimFieldDisplaySettings + * - delete BrGrantedPermissions (sharing permission) + * - delete DimUserChoices (co-operation selection) + * - delete DimUserProfile + */ + public async Task DeleteProfileDataAsync(int userprofileId) + { + _logger.LogInformation($"Deleting user profile (dim_user_profile.id={userprofileId})"); + + using (var connection = _ttvContext.Database.GetDbConnection()) + { + // Get list of FactFieldValues using Entity Framework, which ensures that model FactFieldValue populates correctly. + // After that delete database items using Dapper. + List factFieldValues = + await _ttvContext.FactFieldValues.Where(ffv => ffv.DimUserProfileId == userprofileId) + .AsNoTracking().ToListAsync(); + + // Open database connection + await connection.OpenAsync(); + // Begin database transaction + var transaction = connection.BeginTransaction(); + + // For efficiency, rows from a table are deleted in on SQL statement. + // Collect deletable IDs into lists. + List dimAffiliationIds = new(); + List dimCompetenceIds = new(); + List dimEducationIds = new(); + List dimEmailAddrressIds = new(); + List dimEventIds = new(); + List dimFieldOfScienceIds = new(); + List dimFundingDecisionIds = new(); + List dimKeywordIds = new(); + List dimNameIds = new(); + List dimOrcidPublicationIds = new(); + List dimPidIds = new(); + List dimResearchActivityIds = new(); + List dimResearchCommunityIds = new(); + List dimResearchDatasetIds = new(); + List dimResearcherDescriptionIds = new(); + List dimResearcherToResearchCommunityIds = new(); + List dimTelephoneNumberIds = new(); + List dimWebLinkIds = new(); + + try + { + // Delete fact_field_values + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_FactFieldValues(userprofileId), + transaction: transaction + ); + + // Delete fact_field_values related items + foreach (FactFieldValue factFieldValue in factFieldValues) + { + // Not all related data should be automatically deleted + if (CanDeleteFactFieldValueRelatedData(factFieldValue)) + { + // dim_identifierless_data needs special handling, since it can have nested items + if (factFieldValue.DimIdentifierlessDataId != -1) + { + // First delete possible child items from dim_identifierless_data + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimIdentifierlessData_Children(factFieldValue.DimIdentifierlessDataId), + transaction: transaction + ); + + // Then delete parent from dim_identifierless_data + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimIdentifierlessData_Parent(factFieldValue.DimIdentifierlessDataId), + transaction: transaction + ); + } + + // Collect IDs + if (factFieldValue.DimAffiliationId != -1) dimAffiliationIds.Add(factFieldValue.DimAffiliationId); + if (factFieldValue.DimCompetenceId != -1) dimCompetenceIds.Add(factFieldValue.DimCompetenceId); + if (factFieldValue.DimEducationId != -1) dimEducationIds.Add(factFieldValue.DimEducationId); + if (factFieldValue.DimEmailAddrressId != -1) dimEmailAddrressIds.Add(factFieldValue.DimEmailAddrressId); + if (factFieldValue.DimEventId != -1) dimEventIds.Add(factFieldValue.DimEventId); + if (factFieldValue.DimFieldOfScienceId != -1) dimFieldOfScienceIds.Add(factFieldValue.DimFieldOfScienceId); + if (factFieldValue.DimFundingDecisionId != -1) dimFundingDecisionIds.Add(factFieldValue.DimFundingDecisionId); + if (factFieldValue.DimKeywordId != -1) dimKeywordIds.Add(factFieldValue.DimKeywordId); + if (factFieldValue.DimNameId != -1) dimNameIds.Add(factFieldValue.DimNameId); + if (factFieldValue.DimOrcidPublicationId != -1) dimOrcidPublicationIds.Add(factFieldValue.DimOrcidPublicationId); + if (factFieldValue.DimPidId != -1) dimPidIds.Add(factFieldValue.DimPidId); + if (factFieldValue.DimPidIdOrcidPutCode != -1) dimPidIds.Add(factFieldValue.DimPidIdOrcidPutCode); + if (factFieldValue.DimResearchActivityId != -1) dimResearchActivityIds.Add(factFieldValue.DimResearchActivityId); + if (factFieldValue.DimResearchCommunityId != -1) dimResearchCommunityIds.Add(factFieldValue.DimResearchCommunityId); + if (factFieldValue.DimResearchDatasetId != -1) dimResearchDatasetIds.Add(factFieldValue.DimResearchDatasetId); + if (factFieldValue.DimResearcherDescriptionId != -1) dimResearcherDescriptionIds.Add(factFieldValue.DimResearcherDescriptionId); + if (factFieldValue.DimResearcherToResearchCommunityId != -1) dimResearcherToResearchCommunityIds.Add(factFieldValue.DimResearcherToResearchCommunityId); + if (factFieldValue.DimTelephoneNumberId != -1) dimTelephoneNumberIds.Add(factFieldValue.DimTelephoneNumberId); + if (factFieldValue.DimWebLinkId != -1) dimWebLinkIds.Add(factFieldValue.DimWebLinkId); + } + } + + // Delete affiliations + if (dimAffiliationIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimAffiliations(dimAffiliationIds), + transaction: transaction + ); + } + // Delete competences + if (dimCompetenceIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimCompetences(dimCompetenceIds), + transaction: transaction + ); + } + // Delete educations + if (dimEducationIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimEducations(dimEducationIds), + transaction: transaction + ); + } + // Delete email addresses + if (dimEmailAddrressIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimEmailAddrresses(dimEmailAddrressIds), + transaction: transaction + ); + } + // Delete events + if (dimEventIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimEvents(dimEventIds), + transaction: transaction + ); + } + // Delete fields of science + if (dimFieldOfScienceIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimFieldsOfScience(dimFieldOfScienceIds), + transaction: transaction + ); + } + // Delete funding decisions + if (dimFundingDecisionIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimFundingDecisions(dimFundingDecisionIds), + transaction: transaction + ); + } + // Delete Keywords + if (dimKeywordIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimKeyword(dimKeywordIds), + transaction: transaction + ); + } + // Delete names + if (dimNameIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimNames(dimNameIds), + transaction: transaction + ); + } + // Delete ORCID publications + if (dimOrcidPublicationIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimOrcidPublications(dimOrcidPublicationIds), + transaction: transaction + ); + } + // Delete PIDs + if (dimPidIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimPids(dimPidIds), + transaction: transaction + ); } - await _ttvContext.SaveChangesAsync(); + // Delete research activities + if (dimResearchActivityIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimResearchActivities(dimResearchActivityIds), + transaction: transaction + ); + } + // Delete research communities + if (dimResearchCommunityIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimResearchCommunities(dimResearchCommunityIds), + transaction: transaction + ); + } + // Delete research datasets + if (dimResearchDatasetIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimResearchDatasets(dimResearchDatasetIds), + transaction: transaction + ); + } + // Delete researcher descriptions + if (dimResearcherDescriptionIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimResearchDescriptions(dimResearcherDescriptionIds), + transaction: transaction + ); + } + // Delete researcher to research communities + if (dimResearcherToResearchCommunityIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimResearcherToResearchCommunities(dimResearcherToResearchCommunityIds), + transaction: transaction + ); + } + // Delete telephone numbers + if (dimTelephoneNumberIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimTelephoneNumbers(dimTelephoneNumberIds), + transaction: transaction + ); + } + // Delete web links + if (dimWebLinkIds.Count > 0) + { + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimWebLinks(dimWebLinkIds), + transaction: transaction + ); + } + + // Delete dim_field_display_settings + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimFieldDisplaySettings(userprofileId), + transaction: transaction + ); - // Add FactFieldValues for DimPublications - foreach (int publicationId in publicationsIds.Distinct()) + // Delete br_granted_permissions + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_BrGrantedPermissions(userprofileId), + transaction: transaction + ); + + // Delete dim_user_choices + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimUserChoices(userprofileId), + transaction: transaction + ); + + // Delete dim_user_profile + await connection.ExecuteAsync( + sql: _ttvSqlService.GetSqlQuery_Delete_DimUserProfile(userprofileId), + transaction: transaction + ); + + // Commit transaction + transaction.Commit(); + } + catch (Exception exceptionFromProfileDelete) + { + // Log error from profile deletion + _logger.LogError($"Error deleting user profile (dim_user_profile.id={userprofileId}): " + exceptionFromProfileDelete.ToString()); + + // Rollback + _logger.LogInformation($"Try to rollback user profile deletion (dim_user_profile.id={userprofileId})"); + try + { + transaction.Rollback(); + _logger.LogInformation($"Rollback success (dim_user_profile.id={userprofileId})"); + } + catch (Exception exceptionFromRollback) { - var factFieldValuePublication = this.GetEmptyFactFieldValue(); - factFieldValuePublication.DimUserProfileId = dimUserProfile.Id; - factFieldValuePublication.DimFieldDisplaySettingsId = dimFieldDisplaySetting.Id; - factFieldValuePublication.DimPublicationId = publicationId; - _ttvContext.FactFieldValues.Add(factFieldValuePublication); + // Log error from rollback + _logger.LogError($"Error in rollback of user profile deletion (dim_user_profile.id={userprofileId}): " + exceptionFromRollback.ToString()); } - await _ttvContext.SaveChangesAsync(); + return false; } } + return true; } /* - * Search and add data from TTV database. - * This is data that is already linked to the ORCID id in DimPid and it's related DimKnownPerson. + * Execute raw sql. */ - public async Task AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + public async Task ExecuteRawSql(string sql) { - await this.AddTtvPublications(dimKnownPerson, dimUserProfile); + await _ttvContext.Database.ExecuteSqlRawAsync(sql); } } -} \ No newline at end of file +} diff --git a/aspnetcore/src/api/Services/UtilityService.cs b/aspnetcore/src/api/Services/UtilityService.cs new file mode 100644 index 00000000..8540f29c --- /dev/null +++ b/aspnetcore/src/api/Services/UtilityService.cs @@ -0,0 +1,20 @@ +using System; + +namespace api.Services +{ + /* + * UtilityService implements miscellaneous utilities, which simplify code. + */ + public class UtilityService : IUtilityService + { + public UtilityService() + { + } + + // Return current timestamp for database entries. + public DateTime GetCurrentDateTime() + { + return DateTime.UtcNow; + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Startup.cs b/aspnetcore/src/api/Startup.cs index 30806684..9b869ce7 100644 --- a/aspnetcore/src/api/Startup.cs +++ b/aspnetcore/src/api/Startup.cs @@ -1,68 +1,142 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using api.Services; using api.Models.Ttv; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; +using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Logging; using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.OpenApi.Models; +using System.Reflection; +using System.IO; +using System; +using IdentityModel.Client; +using Microsoft.Net.Http.Headers; +using System.Linq; +using Serilog; +using AutoMapper; namespace api { public class Startup { - public Startup(IConfiguration configuration) + public Startup(IConfiguration configuration, IWebHostEnvironment environment) { Configuration = configuration; + Environment = environment; } public IConfiguration Configuration { get; } + public IWebHostEnvironment Environment { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - var connectionString = Configuration.GetConnectionString("DefaultConnection"); - + services.AddResponseCompression(); + string connectionString = Configuration.GetConnectionString("DefaultConnection"); + // SQL Server options. + // Enable retry on failure. + // Split LINQ queriers into multiple SQL queries. services.AddDbContext(options => - options.UseSqlServer(connectionString)); + options.UseSqlServer( + connectionString, + options => + { + options.EnableRetryOnFailure(); + options.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); + } + ) + ); + + if (Environment.IsDevelopment()) + { + // Capture database-related exceptions that can be resolved by using Entity Framework migrations + services.AddDatabaseDeveloperPageExceptionFilter(); + } services.AddControllers(); + // Swagger documentation + if (Environment.IsDevelopment()) + { + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo + { + Title = "Mydata API", + Description = "An API for Mydata frontend.", + Version = "v1" + }); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + In = ParameterLocation.Header, + Description = "Please insert JWT with Bearer into field", + Name = "Authorization", + Type = SecuritySchemeType.ApiKey + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] { } + } + }); + + string xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); + }); + } + services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { - options.Authority = Configuration["OAUTH:AUTHORITY"]; + options.Authority = Configuration["KEYCLOAK:REALM"]; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; }); + // Authorization services.AddAuthorization(options => { - options.AddPolicy("ApiScope", policy => + /* + * Policy RequireScopeApi1AndClaimOrcid requires + * - Authenticated user + * - scope "api1" + * - claim "orcid" + */ + options.AddPolicy("RequireScopeApi1AndClaimOrcid", policy => { policy.RequireAuthenticatedUser(); - policy.RequireClaim("scope", "api1"); + // Required claim "orcid" + policy.RequireClaim("orcid"); + // Required scope "api1". The following allows presence of other scopes. + string[] scopes = new[] { "api1" }; + policy.RequireAssertion(context => { + System.Security.Claims.Claim claim = context.User.FindFirst("scope"); + if (claim == null) { return false; } + return claim.Value.Split(' ').Any(s => + scopes.Contains(s, StringComparer.Ordinal) + ); + }); }); }); // CORS policies services.AddCors(options => { - // Development and testing + // Development options.AddPolicy("development", builder => { builder.WithOrigins( @@ -75,38 +149,119 @@ public void ConfigureServices(IServiceCollection services) .AllowAnyMethod(); }); - // Production - TODO remove localhost + // Production options.AddPolicy("production", builder => { builder.WithOrigins( "https://*.csc.fi", - "https://*.rahtiapp.fi", - "https://localhost:5003" + "https://*.rahtiapp.fi" ) .SetIsOriginAllowedToAllowWildcardSubdomains() - .AllowAnyHeader() - .AllowAnyMethod(); + .AllowAnyHeader() // TODO: check if AllowAnyHeader() should be removed + .AllowAnyMethod(); // TODO: check if AllowAnyMethod() should be removed }); }); - services.AddHttpClient(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + /* + * HTTP client: ORCID MEMBER API + */ + services.AddHttpClient("ORCID_MEMBER_API", httpClient => + { + httpClient.BaseAddress = new Uri(Configuration["ORCID:MEMBERAPI"]); + httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); + }); + + /* + * HTTP client: ORCID PUBLIC API + */ + services.AddHttpClient("ORCID_PUBLIC_API", httpClient => + { + httpClient.BaseAddress = new Uri(Configuration["ORCID:PUBLICAPI"]); + httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); + }); + + /* + * HTTP client: Keycloak Admin user token management (client credentials flow). + * https://identitymodel.readthedocs.io/en/latest/aspnetcore/worker.html + */ + services.AddClientAccessTokenManagement(options => + { + options.Clients.Add("keycloakAdminTokenClient", new ClientCredentialsTokenRequest + { + Address = Configuration["KEYCLOAK:TOKENENDPOINT"], + ClientId = Configuration["KEYCLOAK:ADMIN:CLIENTID"], + ClientSecret = Configuration["KEYCLOAK:ADMIN:CLIENTSECRET"] + }); + }); + + /* + * HTTP client: Keycloak Admin API + * https://www.keycloak.org/docs-api/15.0/rest-api/index.html + * Access token management is provided by "keycloakAdminTokenClient". + */ + services.AddClientAccessTokenHttpClient(clientName: "keycloakClient", tokenClientName: "keycloakAdminTokenClient", configureClient: client => + { + client.BaseAddress = new Uri(Configuration["KEYCLOAK:ADMIN:REALMUSERSENDPOINT"]); + }); + + /* + * HTTP client: Keycloak user's external IDP token retrieval. + * https://wjw465150.gitbooks.io/keycloak-documentation/content/server_admin/topics/identity-broker/tokens.html + * For retrieving user's external IDP (ORCID) access tokens from Keycloak. + */ + services.AddHttpClient("keycloakUserOrcidTokens", httpClient => + { + httpClient.BaseAddress = new Uri(Configuration["KEYCLOAK:ORCIDTOKENENDPOINT"]); + httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); + }); + + // Automapper + // Auto Mapper Configurations + var mapperConfig = new MapperConfiguration(mc => + { + mc.AddProfile(new MappingProfile()); + }); + + IMapper mapper = mapperConfig.CreateMapper(); + services.AddSingleton(mapper); + + + services.AddResponseCompression(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddMemoryCache(); - /* - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); + // Background processing related services. + services.AddTransient(); + services.AddHostedService(); + services.AddSingleton(ctx => + { + return new BackgroundTaskQueue(); }); - */ } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DemoDataService demoDataService) + public void Configure(IApplicationBuilder app, IStartupHelperService startupHelperService, IDataSourceHelperService dataSourceHelperService) { + // Init services, which depend on database values + SetServiceValuesFromDatabase(startupHelperService, dataSourceHelperService); + + // Response compression. + app.UseResponseCompression(); + // Use Forwarded Headers Middleware to enable client ip address detection behind load balancer. // Must run this before other middleware. app.UseForwardedHeaders(new ForwardedHeadersOptions @@ -114,30 +269,24 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DemoData ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); - // Add registered data sources, organizations etc. needed in demo. - // Most of demo data is added to each user, who creates a profile. - demoDataService.InitDemo(); - - if (env.IsDevelopment()) + // Development environment settings + if (Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); - IdentityModelEventSource.ShowPII = true; - //app.UseSwagger(); - //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "api v1")); + //IdentityModelEventSource.ShowPII = true; + app.UseSwagger(); + app.UseSwaggerUI(); + app.UseCors("development"); } - - app.UseRouting(); - // CORS policy depends on the environment - if (env.IsDevelopment()) - { - app.UseCors("development"); - } - if (env.IsProduction()) + // Production environment settings + if (Environment.IsProduction()) { app.UseCors("production"); } + app.UseSerilogRequestLogging(); + app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); @@ -146,5 +295,37 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DemoData endpoints.MapControllers(); }); } + + /* + * Initialize services, which depend on database specific database entries. + * + * The services store common values, which are used for all users. + * Values are queried once on application startup and later used via services when processing user requests. + * The aim is to reduce overall database load, since the same values are no longer queried for every user. + * Also this should function as a requirement check on application startup. + * The application will not function properly, if the database is not populated correctly. + */ + public void SetServiceValuesFromDatabase(IStartupHelperService startupHelperService, IDataSourceHelperService dataSourceHelperService) + { + DimRegisteredDataSource dimRegisteredDataSource_ORCID = startupHelperService.GetDimRegisteredDataSourceId_OnStartup_ORCID(); + DimRegisteredDataSource dimRegisteredDataSource_TTV = startupHelperService.GetDimRegisteredDataSourceId_OnStartup_TTV(); + DimPurpose dimPurpose_TTV = startupHelperService.GetDimPurposeId_OnStartup_TTV(); + + dataSourceHelperService.DimRegisteredDataSourceId_ORCID = dimRegisteredDataSource_ORCID.Id; + dataSourceHelperService.DimRegisteredDataSourceName_ORCID = dimRegisteredDataSource_ORCID.Name; + dataSourceHelperService.DimOrganizationId_ORCID = dimRegisteredDataSource_ORCID.DimOrganization.Id; + dataSourceHelperService.DimOrganizationNameFi_ORCID = dimRegisteredDataSource_ORCID.DimOrganization.NameFi; + dataSourceHelperService.DimOrganizationNameEn_ORCID = dimRegisteredDataSource_ORCID.DimOrganization.NameEn; + dataSourceHelperService.DimOrganizationNameSv_ORCID = dimRegisteredDataSource_ORCID.DimOrganization.NameSv; + + dataSourceHelperService.DimRegisteredDataSourceId_TTV = dimRegisteredDataSource_TTV.Id; + dataSourceHelperService.DimRegisteredDataSourceName_TTV = dimRegisteredDataSource_TTV.Name; + dataSourceHelperService.DimOrganizationId_TTV = dimRegisteredDataSource_TTV.DimOrganization.Id; + dataSourceHelperService.DimOrganizationNameFi_TTV = dimRegisteredDataSource_TTV.DimOrganization.NameFi; + dataSourceHelperService.DimOrganizationNameEn_TTV = dimRegisteredDataSource_TTV.DimOrganization.NameEn; + dataSourceHelperService.DimOrganizationNameSv_TTV = dimRegisteredDataSource_TTV.DimOrganization.NameSv; + + dataSourceHelperService.DimPurposeId_TTV = dimPurpose_TTV.Id; + } } } diff --git a/aspnetcore/src/api/api.csproj b/aspnetcore/src/api/api.csproj index 90297043..7a55c351 100644 --- a/aspnetcore/src/api/api.csproj +++ b/aspnetcore/src/api/api.csproj @@ -1,24 +1,78 @@  - net5.0 + net6.0 b8da778e-0826-4f4f-86a7-9d6fb376c3ec + + 4 + bin\Debug\net5.0\api.xml + 1701;1702;1591;IDE0058;IDE0050 + + + 4 + bin\Release\net5.0\api.xml + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aspnetcore/src/api/appsettings.Development.json b/aspnetcore/src/api/appsettings.Development.json index dba68eb1..2c63c085 100644 --- a/aspnetcore/src/api/appsettings.Development.json +++ b/aspnetcore/src/api/appsettings.Development.json @@ -1,9 +1,2 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } } diff --git a/aspnetcore/src/api/appsettings.json b/aspnetcore/src/api/appsettings.json index 5e2133f8..97caed27 100644 --- a/aspnetcore/src/api/appsettings.json +++ b/aspnetcore/src/api/appsettings.json @@ -1,13 +1,23 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, "AllowedHosts": "*", - "ConnectionStrings": { - "DefaultConnection": "Server=localhost;User Id=sa;Password=Test1234;database=;" + "Serilog": { + "Using": [], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning", + //"Microsoft.EntityFrameworkCore.Database.Command": "Information" + } + }, + "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ], + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}" + } + } + ] } } \ No newline at end of file diff --git a/aspnetcore/src/identityserver/AspIdUsers.db b/aspnetcore/src/identityserver/AspIdUsers.db deleted file mode 100644 index 4e9d05dcf71e60f77c45538dc7f1c47598e0b890..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102400 zcmeI5O>7&UEHrF7R>@AoCNDes%$aS$;>>_*E!|Wjl0xWU~um%QLY*l}d z-Bfc(%|O6#@NZahcU8StuYT{n>S8rX6?ZdAM~>Fjc2n#`S3FldQ&XP1(WuAc@sgjn z$d7e|$;l<_g8a|e&WD|PJ+~53A8j1|jJA0*{8{K{v+Uf*fnNo_=l|6AH~-JQfAaly zYS#M;=10t1o}cQ9PMO#I5sqa}-%(nU{8ZShaN1E$?vypToo)NZK^iQ+mM-vI ziH{1o4Sp{g8!i}&=5x{5pm;2LBW5U$#T!@sk?Zr!>D#&q1+^jP#HK7zdkisJq`hW# zSk4K%4WQl5x#ZQ?lt|O)b=7{O3HR68p)gdG9x2PC`gJu|7 zsV!!G{6!?>kFYHBL$M=P8*(2`NqsE0w887J*>j1zm*MR!jpjsiFiH*!Xtc!dm7=*k z`6*{IH;uZC`bcT*wABw4Np8nno9KL8YaWP=u51bQbf)vUVyVChxl%N?vuz6;%dDM( zkmU;d(QSUe@9vw?n9~+gV0gx`)UI{&tX5k$R%pheoB0AS+|M~Xibo6lCSTxlX})NO zrcqA_QY=mbbA!+DB#P5qG0ko8!T6oLZ6|vh<^=n(-#tpR5(zk@|^to~=FXTvfSw2@X?+1Fj(#g@;X+5S_xlj4y283hcHcYx;@9oS? z(`k2KM{iEtn*5|Iw`%ghWxxS`pz=nGtP*Y#$@ueU{E+haA*H|#-Y_*@bs&D zxjVi)?ZOx2PTB=E$YsS~kFcIQ`zBrJ#2<|R;C-JzGC$A!_@jYbvdx*=v>S5$PT-*J z7~Js8A?pGgWiw5<8VMh5Q&bwVlvcZ~&e>eF5=w2=y6rZJx07N=Y#v!q`;#f(Bx;5f zr`1+nX*Xq;TsQS+)Y@aU+u>Vu#W+T@o>%(@^q0azOzAhE6y0wLJEEo?t8HmZ)DF)M zX6I0C$+>Q`+FxY$`?pn&9&b@5IF-&Yn5AQNQ>=B=_9!4OBMHWn)6)@Qe)Mf!v#;

qiRkuNMDz>a zX_@gyQYq%SpcAQ7_|2^Hs4aFBwWVz-8gbBxYvGiWl53kDub#>?bJbuv57BcLAz6M% z-CE}XSvC?=tw1)HNWr);H68gNH7r0}d#NnB7Sq@E6M#PdCvW=4 z0RkWZ0w4eaAOHd&00JNY0w4eaAn-LNK%f8P_5at{!5A6{fB*=900@8p2!H?xfB*=9 z006&pSY~No{7i-()#M7g-?tYakrTD$4Jo$RXhda~ zv%1KV<~4Rru2?_nmjOeE+dfd{dSICFFP~-9qH)FL3xRPw^i3x zwA{+QNBfVnslE3eKHFYcET`q;PC6@=8~KCI{k7xu>S6NvBD067o<0T2KI5C8!X009sH0T2KI5CDO%4uRJh zFUxS+QBLlZHMyN`h)Pon(AWQgxj%cte+YjZ-VKLC{}TGA(EFiplOh}-00JNY0w4ea zAOHd&00JNY0uxB!y8)l)o8Kxo)SB2))Rv|;<>N!SEsyhN^3@ri=Z$ZbTVhitALozi zpJ^XU&hPWQbFKVH`A}|EOzCmt)~9`*`AE4gHkHPSE;x3VfA8~o-j0`#<*KH1#O8~X3i??OKb|1tbY z_+|Km2^0#|PfTjSP!IqC5C8!X009sH0T2KI5C8!X_`(F{ z!mm65|EHeNzlVQ0_xsS#X4$!q1HTG<&;P0KZ~mWq|K$7a)U5Xx%#WD2JU`VtIb~k+ zM>v)_eMf0Y@>5~2!f8i2xl`8UcDf-dO|3#t1!=JOTDrh1%VNBEUL zbAf#j<6_Z#E*cv&F&4cMGmMYL8(002>+{U%+xjpIYD3P6Oejm1xyE3QWC5VBlh zKf2BD_uYLn8gtr03JlK}mfE#$p4DpW#tQ#fbTePzh5I>YNAYNZ-{cE?F3lJ1&@}1^ z5|?orm>YbCCz+DwifL|x5616=rz1s{@#vwbJ!vQ%xgvHus(x+j+ZbYHY0y3H4R^;m44`w#Ma0AHx#`I6!svJ~0NBDoF9?NSG5Rv!N6Ufc?F)P)#Y&h7_mO zR$XZ~WtUtx^=H)DW3}7iTXe-ZMzfw*`v&xv!b42yH=q>VZwWi1rX8zoX-m`&&kkni zP;JS%ZnN57WcK^FRgNBSQ6@N*&M=szV|7!kb=3AKATA>b#*@?25n+DxZC$gk@HGGF zo>z~`=f}3Fq&Zu@}HkU}jxG*&x`5-kcKwNvNEV&kY$?NPH`dXX*{lB^C|MG-Np=*~nF8hLy zgMSzJ=fK*`Z)O(#zxL-z6%G&p0T2KI5ST0iM{gQ?xbyl(nYHG!?cwUn^0&;JzH<-P z(b~Rws>_f?fNc-gY8#9H!yEp{J7m{5p!cXRXa~FdhS{CD58iJxq7!3v9RJ7H{gFEh z%;`7v9vr*q#!86(w)TmsVR~uA+38TRK^t^oI#@$1-Kf6ix=~GgGWQ_psoemh`1Q}R zz#%wjf>B9rFcec8F7y!u9uM@k6YQS<(wopPA|Yez0?!xPv9Hwgg)h0E{33JNkEaXv zhdtJ8Igw8nzVw#!i+E5^bUa_s?_Jh21)B5L`B&!o0^a{WSxZ`23j!bj0w4eaAOHd& z00JNY0w4eaUxWaC{XZ4{S5NrQ3!()sLE#G4Fo8G(H2*jZt`{{O2d{J-J9PR0OXB?y232!H?xfB*=9 d00@8p2!H?xfWQa>e(yE9J1{lljTqYj{}2ALd#V5c diff --git a/aspnetcore/src/identityserver/Data/ApplicationDbContext.cs b/aspnetcore/src/identityserver/Data/ApplicationDbContext.cs deleted file mode 100644 index 64d49949..00000000 --- a/aspnetcore/src/identityserver/Data/ApplicationDbContext.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using identityserver.Models; - -namespace identityserver.Data -{ - public class ApplicationDbContext : IdentityDbContext - { - public ApplicationDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - // Customize the ASP.NET Identity model and override the defaults if needed. - // For example, you can rename the ASP.NET Identity table names and more. - // Add your customizations after calling base.OnModelCreating(builder); - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.Designer.cs b/aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.Designer.cs deleted file mode 100644 index 8221ff52..00000000 --- a/aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.Designer.cs +++ /dev/null @@ -1,276 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using identityserver.Data; - -namespace identityserver.Migrations.ApplicationDb -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20200930122959_InitialMigration")] - partial class InitialMigration - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("identityserver.Models.ApplicationUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("OrcidIdentifier") - .HasColumnType("nvarchar(max)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.cs b/aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.cs deleted file mode 100644 index a556208d..00000000 --- a/aspnetcore/src/identityserver/Migrations/ApplicationDb/20200930122959_InitialMigration.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace identityserver.Migrations.ApplicationDb -{ - public partial class InitialMigration : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AspNetRoles", - columns: table => new - { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetUsers", - columns: table => new - { - Id = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - PasswordHash = table.Column(nullable: true), - SecurityStamp = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - TwoFactorEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false), - OrcidIdentifier = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetRoleClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(nullable: false), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetUserClaims_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserLogins", - columns: table => new - { - LoginProvider = table.Column(nullable: false), - ProviderKey = table.Column(nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_AspNetUserLogins_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserRoles", - columns: table => new - { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserTokens", - columns: table => new - { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(nullable: false), - Name = table.Column(nullable: false), - Value = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AspNetUserTokens_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AspNetRoleClaims_RoleId", - table: "AspNetRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "AspNetRoles", - column: "NormalizedName", - unique: true, - filter: "[NormalizedName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserClaims_UserId", - table: "AspNetUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserLogins_UserId", - table: "AspNetUserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserRoles_RoleId", - table: "AspNetUserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "AspNetUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "AspNetUsers", - column: "NormalizedUserName", - unique: true, - filter: "[NormalizedUserName] IS NOT NULL"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AspNetRoleClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserLogins"); - - migrationBuilder.DropTable( - name: "AspNetUserRoles"); - - migrationBuilder.DropTable( - name: "AspNetUserTokens"); - - migrationBuilder.DropTable( - name: "AspNetRoles"); - - migrationBuilder.DropTable( - name: "AspNetUsers"); - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs b/aspnetcore/src/identityserver/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs deleted file mode 100644 index 34e4922e..00000000 --- a/aspnetcore/src/identityserver/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs +++ /dev/null @@ -1,274 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using identityserver.Data; - -namespace identityserver.Migrations.ApplicationDb -{ - [DbContext(typeof(ApplicationDbContext))] - partial class ApplicationDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("identityserver.Models.ApplicationUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.Property("OrcidIdentifier") - .HasColumnType("nvarchar(max)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("identityserver.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.Designer.cs b/aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.Designer.cs deleted file mode 100644 index 506b7824..00000000 --- a/aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.Designer.cs +++ /dev/null @@ -1,905 +0,0 @@ -// -using System; -using IdentityServer4.EntityFramework.DbContexts; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace identityserver.Migrations.ConfigurationDb -{ - [DbContext(typeof(ConfigurationDbContext))] - [Migration("20200925100712_InitialIdentityServerConfigurationDbMigration")] - partial class InitialIdentityServerConfigurationDbMigration - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("AllowedAccessTokenSigningAlgorithms") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DisplayName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("LastAccessed") - .HasColumnType("datetime2"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("NonEditable") - .HasColumnType("bit"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("bit"); - - b.Property("Updated") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("ApiResources"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Scope") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceScopes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("Expiration") - .HasColumnType("datetime2"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(4000)") - .HasMaxLength(4000); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceSecrets"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DisplayName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Emphasize") - .HasColumnType("bit"); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Required") - .HasColumnType("bit"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("bit"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("ApiScopes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ScopeId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.ToTable("ApiScopeClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("ScopeId") - .HasColumnType("int"); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.ToTable("ApiScopeProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("AbsoluteRefreshTokenLifetime") - .HasColumnType("int"); - - b.Property("AccessTokenLifetime") - .HasColumnType("int"); - - b.Property("AccessTokenType") - .HasColumnType("int"); - - b.Property("AllowAccessTokensViaBrowser") - .HasColumnType("bit"); - - b.Property("AllowOfflineAccess") - .HasColumnType("bit"); - - b.Property("AllowPlainTextPkce") - .HasColumnType("bit"); - - b.Property("AllowRememberConsent") - .HasColumnType("bit"); - - b.Property("AllowedIdentityTokenSigningAlgorithms") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("AlwaysIncludeUserClaimsInIdToken") - .HasColumnType("bit"); - - b.Property("AlwaysSendClientClaims") - .HasColumnType("bit"); - - b.Property("AuthorizationCodeLifetime") - .HasColumnType("int"); - - b.Property("BackChannelLogoutSessionRequired") - .HasColumnType("bit"); - - b.Property("BackChannelLogoutUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("ClientClaimsPrefix") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("ConsentLifetime") - .HasColumnType("int"); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DeviceCodeLifetime") - .HasColumnType("int"); - - b.Property("EnableLocalLogin") - .HasColumnType("bit"); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("FrontChannelLogoutSessionRequired") - .HasColumnType("bit"); - - b.Property("FrontChannelLogoutUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("IdentityTokenLifetime") - .HasColumnType("int"); - - b.Property("IncludeJwtId") - .HasColumnType("bit"); - - b.Property("LastAccessed") - .HasColumnType("datetime2"); - - b.Property("LogoUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("NonEditable") - .HasColumnType("bit"); - - b.Property("PairWiseSubjectSalt") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ProtocolType") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("RefreshTokenExpiration") - .HasColumnType("int"); - - b.Property("RefreshTokenUsage") - .HasColumnType("int"); - - b.Property("RequireClientSecret") - .HasColumnType("bit"); - - b.Property("RequireConsent") - .HasColumnType("bit"); - - b.Property("RequirePkce") - .HasColumnType("bit"); - - b.Property("RequireRequestObject") - .HasColumnType("bit"); - - b.Property("SlidingRefreshTokenLifetime") - .HasColumnType("int"); - - b.Property("UpdateAccessTokenClaimsOnRefresh") - .HasColumnType("bit"); - - b.Property("Updated") - .HasColumnType("datetime2"); - - b.Property("UserCodeType") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("UserSsoLifetime") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("ClientId") - .IsUnique(); - - b.ToTable("Clients"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Origin") - .IsRequired() - .HasColumnType("nvarchar(150)") - .HasMaxLength(150); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientCorsOrigins"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("GrantType") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientGrantTypes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Provider") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientIdPRestrictions"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("PostLogoutRedirectUri") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientPostLogoutRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("RedirectUri") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Scope") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientScopes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("Expiration") - .HasColumnType("datetime2"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(4000)") - .HasMaxLength(4000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientSecrets"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DisplayName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Emphasize") - .HasColumnType("bit"); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("NonEditable") - .HasColumnType("bit"); - - b.Property("Required") - .HasColumnType("bit"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("bit"); - - b.Property("Updated") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("IdentityResources"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("IdentityResourceId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("IdentityResourceId") - .HasColumnType("int"); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("UserClaims") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Properties") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Scopes") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Secrets") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") - .WithMany("UserClaims") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") - .WithMany("Properties") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("Claims") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedCorsOrigins") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedGrantTypes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("IdentityProviderRestrictions") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("PostLogoutRedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("Properties") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("RedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedScopes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("ClientSecrets") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("UserClaims") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("Properties") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.cs b/aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.cs deleted file mode 100644 index feeea353..00000000 --- a/aspnetcore/src/identityserver/Migrations/ConfigurationDb/20200925100712_InitialIdentityServerConfigurationDbMigration.cs +++ /dev/null @@ -1,657 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace identityserver.Migrations.ConfigurationDb -{ - public partial class InitialIdentityServerConfigurationDbMigration : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ApiResources", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Enabled = table.Column(nullable: false), - Name = table.Column(maxLength: 200, nullable: false), - DisplayName = table.Column(maxLength: 200, nullable: true), - Description = table.Column(maxLength: 1000, nullable: true), - AllowedAccessTokenSigningAlgorithms = table.Column(maxLength: 100, nullable: true), - ShowInDiscoveryDocument = table.Column(nullable: false), - Created = table.Column(nullable: false), - Updated = table.Column(nullable: true), - LastAccessed = table.Column(nullable: true), - NonEditable = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiResources", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ApiScopes", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Enabled = table.Column(nullable: false), - Name = table.Column(maxLength: 200, nullable: false), - DisplayName = table.Column(maxLength: 200, nullable: true), - Description = table.Column(maxLength: 1000, nullable: true), - Required = table.Column(nullable: false), - Emphasize = table.Column(nullable: false), - ShowInDiscoveryDocument = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiScopes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Clients", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Enabled = table.Column(nullable: false), - ClientId = table.Column(maxLength: 200, nullable: false), - ProtocolType = table.Column(maxLength: 200, nullable: false), - RequireClientSecret = table.Column(nullable: false), - ClientName = table.Column(maxLength: 200, nullable: true), - Description = table.Column(maxLength: 1000, nullable: true), - ClientUri = table.Column(maxLength: 2000, nullable: true), - LogoUri = table.Column(maxLength: 2000, nullable: true), - RequireConsent = table.Column(nullable: false), - AllowRememberConsent = table.Column(nullable: false), - AlwaysIncludeUserClaimsInIdToken = table.Column(nullable: false), - RequirePkce = table.Column(nullable: false), - AllowPlainTextPkce = table.Column(nullable: false), - RequireRequestObject = table.Column(nullable: false), - AllowAccessTokensViaBrowser = table.Column(nullable: false), - FrontChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), - FrontChannelLogoutSessionRequired = table.Column(nullable: false), - BackChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), - BackChannelLogoutSessionRequired = table.Column(nullable: false), - AllowOfflineAccess = table.Column(nullable: false), - IdentityTokenLifetime = table.Column(nullable: false), - AllowedIdentityTokenSigningAlgorithms = table.Column(maxLength: 100, nullable: true), - AccessTokenLifetime = table.Column(nullable: false), - AuthorizationCodeLifetime = table.Column(nullable: false), - ConsentLifetime = table.Column(nullable: true), - AbsoluteRefreshTokenLifetime = table.Column(nullable: false), - SlidingRefreshTokenLifetime = table.Column(nullable: false), - RefreshTokenUsage = table.Column(nullable: false), - UpdateAccessTokenClaimsOnRefresh = table.Column(nullable: false), - RefreshTokenExpiration = table.Column(nullable: false), - AccessTokenType = table.Column(nullable: false), - EnableLocalLogin = table.Column(nullable: false), - IncludeJwtId = table.Column(nullable: false), - AlwaysSendClientClaims = table.Column(nullable: false), - ClientClaimsPrefix = table.Column(maxLength: 200, nullable: true), - PairWiseSubjectSalt = table.Column(maxLength: 200, nullable: true), - Created = table.Column(nullable: false), - Updated = table.Column(nullable: true), - LastAccessed = table.Column(nullable: true), - UserSsoLifetime = table.Column(nullable: true), - UserCodeType = table.Column(maxLength: 100, nullable: true), - DeviceCodeLifetime = table.Column(nullable: false), - NonEditable = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Clients", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "IdentityResources", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Enabled = table.Column(nullable: false), - Name = table.Column(maxLength: 200, nullable: false), - DisplayName = table.Column(maxLength: 200, nullable: true), - Description = table.Column(maxLength: 1000, nullable: true), - Required = table.Column(nullable: false), - Emphasize = table.Column(nullable: false), - ShowInDiscoveryDocument = table.Column(nullable: false), - Created = table.Column(nullable: false), - Updated = table.Column(nullable: true), - NonEditable = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_IdentityResources", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ApiResourceClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Type = table.Column(maxLength: 200, nullable: false), - ApiResourceId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiResourceClaims", x => x.Id); - table.ForeignKey( - name: "FK_ApiResourceClaims_ApiResources_ApiResourceId", - column: x => x.ApiResourceId, - principalTable: "ApiResources", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ApiResourceProperties", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Key = table.Column(maxLength: 250, nullable: false), - Value = table.Column(maxLength: 2000, nullable: false), - ApiResourceId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiResourceProperties", x => x.Id); - table.ForeignKey( - name: "FK_ApiResourceProperties_ApiResources_ApiResourceId", - column: x => x.ApiResourceId, - principalTable: "ApiResources", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ApiResourceScopes", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Scope = table.Column(maxLength: 200, nullable: false), - ApiResourceId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiResourceScopes", x => x.Id); - table.ForeignKey( - name: "FK_ApiResourceScopes_ApiResources_ApiResourceId", - column: x => x.ApiResourceId, - principalTable: "ApiResources", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ApiResourceSecrets", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Description = table.Column(maxLength: 1000, nullable: true), - Value = table.Column(maxLength: 4000, nullable: false), - Expiration = table.Column(nullable: true), - Type = table.Column(maxLength: 250, nullable: false), - Created = table.Column(nullable: false), - ApiResourceId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiResourceSecrets", x => x.Id); - table.ForeignKey( - name: "FK_ApiResourceSecrets_ApiResources_ApiResourceId", - column: x => x.ApiResourceId, - principalTable: "ApiResources", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ApiScopeClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Type = table.Column(maxLength: 200, nullable: false), - ScopeId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiScopeClaims", x => x.Id); - table.ForeignKey( - name: "FK_ApiScopeClaims_ApiScopes_ScopeId", - column: x => x.ScopeId, - principalTable: "ApiScopes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ApiScopeProperties", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Key = table.Column(maxLength: 250, nullable: false), - Value = table.Column(maxLength: 2000, nullable: false), - ScopeId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ApiScopeProperties", x => x.Id); - table.ForeignKey( - name: "FK_ApiScopeProperties_ApiScopes_ScopeId", - column: x => x.ScopeId, - principalTable: "ApiScopes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Type = table.Column(maxLength: 250, nullable: false), - Value = table.Column(maxLength: 250, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientClaims", x => x.Id); - table.ForeignKey( - name: "FK_ClientClaims_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientCorsOrigins", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Origin = table.Column(maxLength: 150, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id); - table.ForeignKey( - name: "FK_ClientCorsOrigins_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientGrantTypes", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - GrantType = table.Column(maxLength: 250, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientGrantTypes", x => x.Id); - table.ForeignKey( - name: "FK_ClientGrantTypes_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientIdPRestrictions", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Provider = table.Column(maxLength: 200, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id); - table.ForeignKey( - name: "FK_ClientIdPRestrictions_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientPostLogoutRedirectUris", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - PostLogoutRedirectUri = table.Column(maxLength: 2000, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id); - table.ForeignKey( - name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientProperties", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Key = table.Column(maxLength: 250, nullable: false), - Value = table.Column(maxLength: 2000, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientProperties", x => x.Id); - table.ForeignKey( - name: "FK_ClientProperties_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientRedirectUris", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RedirectUri = table.Column(maxLength: 2000, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientRedirectUris", x => x.Id); - table.ForeignKey( - name: "FK_ClientRedirectUris_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientScopes", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Scope = table.Column(maxLength: 200, nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientScopes", x => x.Id); - table.ForeignKey( - name: "FK_ClientScopes_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ClientSecrets", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Description = table.Column(maxLength: 2000, nullable: true), - Value = table.Column(maxLength: 4000, nullable: false), - Expiration = table.Column(nullable: true), - Type = table.Column(maxLength: 250, nullable: false), - Created = table.Column(nullable: false), - ClientId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ClientSecrets", x => x.Id); - table.ForeignKey( - name: "FK_ClientSecrets_Clients_ClientId", - column: x => x.ClientId, - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "IdentityResourceClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Type = table.Column(maxLength: 200, nullable: false), - IdentityResourceId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_IdentityResourceClaims", x => x.Id); - table.ForeignKey( - name: "FK_IdentityResourceClaims_IdentityResources_IdentityResourceId", - column: x => x.IdentityResourceId, - principalTable: "IdentityResources", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "IdentityResourceProperties", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Key = table.Column(maxLength: 250, nullable: false), - Value = table.Column(maxLength: 2000, nullable: false), - IdentityResourceId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_IdentityResourceProperties", x => x.Id); - table.ForeignKey( - name: "FK_IdentityResourceProperties_IdentityResources_IdentityResourceId", - column: x => x.IdentityResourceId, - principalTable: "IdentityResources", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_ApiResourceClaims_ApiResourceId", - table: "ApiResourceClaims", - column: "ApiResourceId"); - - migrationBuilder.CreateIndex( - name: "IX_ApiResourceProperties_ApiResourceId", - table: "ApiResourceProperties", - column: "ApiResourceId"); - - migrationBuilder.CreateIndex( - name: "IX_ApiResources_Name", - table: "ApiResources", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ApiResourceScopes_ApiResourceId", - table: "ApiResourceScopes", - column: "ApiResourceId"); - - migrationBuilder.CreateIndex( - name: "IX_ApiResourceSecrets_ApiResourceId", - table: "ApiResourceSecrets", - column: "ApiResourceId"); - - migrationBuilder.CreateIndex( - name: "IX_ApiScopeClaims_ScopeId", - table: "ApiScopeClaims", - column: "ScopeId"); - - migrationBuilder.CreateIndex( - name: "IX_ApiScopeProperties_ScopeId", - table: "ApiScopeProperties", - column: "ScopeId"); - - migrationBuilder.CreateIndex( - name: "IX_ApiScopes_Name", - table: "ApiScopes", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ClientClaims_ClientId", - table: "ClientClaims", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_ClientCorsOrigins_ClientId", - table: "ClientCorsOrigins", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_ClientGrantTypes_ClientId", - table: "ClientGrantTypes", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_ClientIdPRestrictions_ClientId", - table: "ClientIdPRestrictions", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_ClientPostLogoutRedirectUris_ClientId", - table: "ClientPostLogoutRedirectUris", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_ClientProperties_ClientId", - table: "ClientProperties", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_ClientRedirectUris_ClientId", - table: "ClientRedirectUris", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_Clients_ClientId", - table: "Clients", - column: "ClientId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ClientScopes_ClientId", - table: "ClientScopes", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_ClientSecrets_ClientId", - table: "ClientSecrets", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_IdentityResourceClaims_IdentityResourceId", - table: "IdentityResourceClaims", - column: "IdentityResourceId"); - - migrationBuilder.CreateIndex( - name: "IX_IdentityResourceProperties_IdentityResourceId", - table: "IdentityResourceProperties", - column: "IdentityResourceId"); - - migrationBuilder.CreateIndex( - name: "IX_IdentityResources_Name", - table: "IdentityResources", - column: "Name", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ApiResourceClaims"); - - migrationBuilder.DropTable( - name: "ApiResourceProperties"); - - migrationBuilder.DropTable( - name: "ApiResourceScopes"); - - migrationBuilder.DropTable( - name: "ApiResourceSecrets"); - - migrationBuilder.DropTable( - name: "ApiScopeClaims"); - - migrationBuilder.DropTable( - name: "ApiScopeProperties"); - - migrationBuilder.DropTable( - name: "ClientClaims"); - - migrationBuilder.DropTable( - name: "ClientCorsOrigins"); - - migrationBuilder.DropTable( - name: "ClientGrantTypes"); - - migrationBuilder.DropTable( - name: "ClientIdPRestrictions"); - - migrationBuilder.DropTable( - name: "ClientPostLogoutRedirectUris"); - - migrationBuilder.DropTable( - name: "ClientProperties"); - - migrationBuilder.DropTable( - name: "ClientRedirectUris"); - - migrationBuilder.DropTable( - name: "ClientScopes"); - - migrationBuilder.DropTable( - name: "ClientSecrets"); - - migrationBuilder.DropTable( - name: "IdentityResourceClaims"); - - migrationBuilder.DropTable( - name: "IdentityResourceProperties"); - - migrationBuilder.DropTable( - name: "ApiResources"); - - migrationBuilder.DropTable( - name: "ApiScopes"); - - migrationBuilder.DropTable( - name: "Clients"); - - migrationBuilder.DropTable( - name: "IdentityResources"); - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs b/aspnetcore/src/identityserver/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs deleted file mode 100644 index 9578744e..00000000 --- a/aspnetcore/src/identityserver/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs +++ /dev/null @@ -1,903 +0,0 @@ -// -using System; -using IdentityServer4.EntityFramework.DbContexts; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace identityserver.Migrations.ConfigurationDb -{ - [DbContext(typeof(ConfigurationDbContext))] - partial class ConfigurationDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("AllowedAccessTokenSigningAlgorithms") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DisplayName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("LastAccessed") - .HasColumnType("datetime2"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("NonEditable") - .HasColumnType("bit"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("bit"); - - b.Property("Updated") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("ApiResources"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Scope") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceScopes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ApiResourceId") - .HasColumnType("int"); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("Expiration") - .HasColumnType("datetime2"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(4000)") - .HasMaxLength(4000); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceSecrets"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DisplayName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Emphasize") - .HasColumnType("bit"); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Required") - .HasColumnType("bit"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("bit"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("ApiScopes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ScopeId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.ToTable("ApiScopeClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("ScopeId") - .HasColumnType("int"); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.ToTable("ApiScopeProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("AbsoluteRefreshTokenLifetime") - .HasColumnType("int"); - - b.Property("AccessTokenLifetime") - .HasColumnType("int"); - - b.Property("AccessTokenType") - .HasColumnType("int"); - - b.Property("AllowAccessTokensViaBrowser") - .HasColumnType("bit"); - - b.Property("AllowOfflineAccess") - .HasColumnType("bit"); - - b.Property("AllowPlainTextPkce") - .HasColumnType("bit"); - - b.Property("AllowRememberConsent") - .HasColumnType("bit"); - - b.Property("AllowedIdentityTokenSigningAlgorithms") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("AlwaysIncludeUserClaimsInIdToken") - .HasColumnType("bit"); - - b.Property("AlwaysSendClientClaims") - .HasColumnType("bit"); - - b.Property("AuthorizationCodeLifetime") - .HasColumnType("int"); - - b.Property("BackChannelLogoutSessionRequired") - .HasColumnType("bit"); - - b.Property("BackChannelLogoutUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("ClientClaimsPrefix") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("ConsentLifetime") - .HasColumnType("int"); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DeviceCodeLifetime") - .HasColumnType("int"); - - b.Property("EnableLocalLogin") - .HasColumnType("bit"); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("FrontChannelLogoutSessionRequired") - .HasColumnType("bit"); - - b.Property("FrontChannelLogoutUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("IdentityTokenLifetime") - .HasColumnType("int"); - - b.Property("IncludeJwtId") - .HasColumnType("bit"); - - b.Property("LastAccessed") - .HasColumnType("datetime2"); - - b.Property("LogoUri") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("NonEditable") - .HasColumnType("bit"); - - b.Property("PairWiseSubjectSalt") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ProtocolType") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("RefreshTokenExpiration") - .HasColumnType("int"); - - b.Property("RefreshTokenUsage") - .HasColumnType("int"); - - b.Property("RequireClientSecret") - .HasColumnType("bit"); - - b.Property("RequireConsent") - .HasColumnType("bit"); - - b.Property("RequirePkce") - .HasColumnType("bit"); - - b.Property("RequireRequestObject") - .HasColumnType("bit"); - - b.Property("SlidingRefreshTokenLifetime") - .HasColumnType("int"); - - b.Property("UpdateAccessTokenClaimsOnRefresh") - .HasColumnType("bit"); - - b.Property("Updated") - .HasColumnType("datetime2"); - - b.Property("UserCodeType") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("UserSsoLifetime") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("ClientId") - .IsUnique(); - - b.ToTable("Clients"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Origin") - .IsRequired() - .HasColumnType("nvarchar(150)") - .HasMaxLength(150); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientCorsOrigins"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("GrantType") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientGrantTypes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Provider") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientIdPRestrictions"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("PostLogoutRedirectUri") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientPostLogoutRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("RedirectUri") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Scope") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientScopes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("ClientId") - .HasColumnType("int"); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.Property("Expiration") - .HasColumnType("datetime2"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(4000)") - .HasMaxLength(4000); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientSecrets"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Created") - .HasColumnType("datetime2"); - - b.Property("Description") - .HasColumnType("nvarchar(1000)") - .HasMaxLength(1000); - - b.Property("DisplayName") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Emphasize") - .HasColumnType("bit"); - - b.Property("Enabled") - .HasColumnType("bit"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("NonEditable") - .HasColumnType("bit"); - - b.Property("Required") - .HasColumnType("bit"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("bit"); - - b.Property("Updated") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("IdentityResources"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("IdentityResourceId") - .HasColumnType("int"); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("IdentityResourceId") - .HasColumnType("int"); - - b.Property("Key") - .IsRequired() - .HasColumnType("nvarchar(250)") - .HasMaxLength(250); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(2000)") - .HasMaxLength(2000); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("UserClaims") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Properties") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Scopes") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Secrets") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") - .WithMany("UserClaims") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") - .WithMany("Properties") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("Claims") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedCorsOrigins") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedGrantTypes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("IdentityProviderRestrictions") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("PostLogoutRedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("Properties") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("RedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedScopes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("ClientSecrets") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("UserClaims") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("Properties") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.Designer.cs b/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.Designer.cs deleted file mode 100644 index afa17e67..00000000 --- a/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.Designer.cs +++ /dev/null @@ -1,129 +0,0 @@ -// -using System; -using IdentityServer4.EntityFramework.DbContexts; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace identityserver.Migrations.PersistedGrantDb -{ - [DbContext(typeof(PersistedGrantDbContext))] - [Migration("20200925100637_InitialIdentityServerPersistedGrantDbMigration")] - partial class InitialIdentityServerPersistedGrantDbMigration - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => - { - b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("CreationTime") - .HasColumnType("datetime2"); - - b.Property("Data") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); - - b.Property("Description") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("DeviceCode") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Expiration") - .IsRequired() - .HasColumnType("datetime2"); - - b.Property("SessionId") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("UserCode"); - - b.HasIndex("DeviceCode") - .IsUnique(); - - b.HasIndex("Expiration"); - - b.ToTable("DeviceCodes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => - { - b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ConsumedTime") - .HasColumnType("datetime2"); - - b.Property("CreationTime") - .HasColumnType("datetime2"); - - b.Property("Data") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); - - b.Property("Description") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Expiration") - .HasColumnType("datetime2"); - - b.Property("SessionId") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); - - b.HasKey("Key"); - - b.HasIndex("Expiration"); - - b.HasIndex("SubjectId", "ClientId", "Type"); - - b.HasIndex("SubjectId", "SessionId", "Type"); - - b.ToTable("PersistedGrants"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.cs b/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.cs deleted file mode 100644 index 29a55269..00000000 --- a/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/20200925100637_InitialIdentityServerPersistedGrantDbMigration.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace identityserver.Migrations.PersistedGrantDb -{ - public partial class InitialIdentityServerPersistedGrantDbMigration : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "DeviceCodes", - columns: table => new - { - UserCode = table.Column(maxLength: 200, nullable: false), - DeviceCode = table.Column(maxLength: 200, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - SessionId = table.Column(maxLength: 100, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - Description = table.Column(maxLength: 200, nullable: true), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: false), - Data = table.Column(maxLength: 50000, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_DeviceCodes", x => x.UserCode); - }); - - migrationBuilder.CreateTable( - name: "PersistedGrants", - columns: table => new - { - Key = table.Column(maxLength: 200, nullable: false), - Type = table.Column(maxLength: 50, nullable: false), - SubjectId = table.Column(maxLength: 200, nullable: true), - SessionId = table.Column(maxLength: 100, nullable: true), - ClientId = table.Column(maxLength: 200, nullable: false), - Description = table.Column(maxLength: 200, nullable: true), - CreationTime = table.Column(nullable: false), - Expiration = table.Column(nullable: true), - ConsumedTime = table.Column(nullable: true), - Data = table.Column(maxLength: 50000, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_PersistedGrants", x => x.Key); - }); - - migrationBuilder.CreateIndex( - name: "IX_DeviceCodes_DeviceCode", - table: "DeviceCodes", - column: "DeviceCode", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_DeviceCodes_Expiration", - table: "DeviceCodes", - column: "Expiration"); - - migrationBuilder.CreateIndex( - name: "IX_PersistedGrants_Expiration", - table: "PersistedGrants", - column: "Expiration"); - - migrationBuilder.CreateIndex( - name: "IX_PersistedGrants_SubjectId_ClientId_Type", - table: "PersistedGrants", - columns: new[] { "SubjectId", "ClientId", "Type" }); - - migrationBuilder.CreateIndex( - name: "IX_PersistedGrants_SubjectId_SessionId_Type", - table: "PersistedGrants", - columns: new[] { "SubjectId", "SessionId", "Type" }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "DeviceCodes"); - - migrationBuilder.DropTable( - name: "PersistedGrants"); - } - } -} diff --git a/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs b/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs deleted file mode 100644 index 6fbac3c0..00000000 --- a/aspnetcore/src/identityserver/Migrations/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs +++ /dev/null @@ -1,127 +0,0 @@ -// -using System; -using IdentityServer4.EntityFramework.DbContexts; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace identityserver.Migrations.PersistedGrantDb -{ - [DbContext(typeof(PersistedGrantDbContext))] - partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => - { - b.Property("UserCode") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("CreationTime") - .HasColumnType("datetime2"); - - b.Property("Data") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); - - b.Property("Description") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("DeviceCode") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Expiration") - .IsRequired() - .HasColumnType("datetime2"); - - b.Property("SessionId") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.HasKey("UserCode"); - - b.HasIndex("DeviceCode") - .IsUnique(); - - b.HasIndex("Expiration"); - - b.ToTable("DeviceCodes"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => - { - b.Property("Key") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("ConsumedTime") - .HasColumnType("datetime2"); - - b.Property("CreationTime") - .HasColumnType("datetime2"); - - b.Property("Data") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(50000); - - b.Property("Description") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Expiration") - .HasColumnType("datetime2"); - - b.Property("SessionId") - .HasColumnType("nvarchar(100)") - .HasMaxLength(100); - - b.Property("SubjectId") - .HasColumnType("nvarchar(200)") - .HasMaxLength(200); - - b.Property("Type") - .IsRequired() - .HasColumnType("nvarchar(50)") - .HasMaxLength(50); - - b.HasKey("Key"); - - b.HasIndex("Expiration"); - - b.HasIndex("SubjectId", "ClientId", "Type"); - - b.HasIndex("SubjectId", "SessionId", "Type"); - - b.ToTable("PersistedGrants"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/aspnetcore/src/identityserver/Models/ApplicationUser.cs b/aspnetcore/src/identityserver/Models/ApplicationUser.cs deleted file mode 100644 index a7d4db5c..00000000 --- a/aspnetcore/src/identityserver/Models/ApplicationUser.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace identityserver.Models -{ - // Add profile data for application users by adding properties to the ApplicationUser class - public class ApplicationUser : IdentityUser - { - public string OrcidIdentifier { get; set; } - } -} diff --git a/aspnetcore/src/identityserver/Program.cs b/aspnetcore/src/identityserver/Program.cs deleted file mode 100644 index 3e3851bb..00000000 --- a/aspnetcore/src/identityserver/Program.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Serilog; -using Serilog.Events; -using Serilog.Sinks.SystemConsole.Themes; -using System; -using System.Linq; - -namespace identityserver -{ - public class Program - { - public static int Main(string[] args) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) - .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) - .MinimumLevel.Override("System", LogEventLevel.Warning) - .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) - .Enrich.FromLogContext() - // uncomment to write to Azure diagnostics stream - //.WriteTo.File( - // @"D:\home\LogFiles\Application\identityserver.txt", - // fileSizeLimitBytes: 1_000_000, - // rollOnFileSizeLimit: true, - // shared: true, - // flushToDiskInterval: TimeSpan.FromSeconds(1)) - .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) - .CreateLogger(); - - try - { - var seed = args.Contains("/seed"); - if (seed) - { - args = args.Except(new[] { "/seed" }).ToArray(); - } - - var host = CreateHostBuilder(args).Build(); - - if (seed) - { - Log.Information("Seeding database..."); - var config = host.Services.GetRequiredService(); - var connectionString = config.GetConnectionString("DefaultConnection"); - SeedData.EnsureSeedData(connectionString); - Log.Information("Done seeding database."); - return 0; - } - - Log.Information("Starting host..."); - host.Run(); - return 0; - } - catch (Exception ex) - { - Log.Fatal(ex, "Host terminated unexpectedly."); - return 1; - } - finally - { - Log.CloseAndFlush(); - } - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseSerilog() - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Properties/launchSettings.json b/aspnetcore/src/identityserver/Properties/launchSettings.json deleted file mode 100644 index 943d7696..00000000 --- a/aspnetcore/src/identityserver/Properties/launchSettings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true - }, - "profiles": { - "identityserver": { - "commandName": "Project", - "launchBrowser": false, - "applicationUrl": "http://localhost:5001", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - //"ORCID__CLIENTID": "", - //"ORCID__CLIENTSECRET": "", - //"IDENTITYSERVER__ORIGIN": "https://", - //"JAVASCRIPTCLIENT__DEV__REDIRECTURI": "https:///mydata/redirect", - //"JAVASCRIPTCLIENT__DEV__POSTLOGOUTREDIRECTURI": "https:///mydata", - //"JAVASCRIPTCLIENT__DEV__ALLOWEDCORSORIGIN": "https://", - //"JAVASCRIPTCLIENT__TEST__REDIRECTURI": "https:///mydata/redirect", - //"JAVASCRIPTCLIENT__TEST__POSTLOGOUTREDIRECTURI": "https:///mydata", - //"JAVASCRIPTCLIENT__TEST__ALLOWEDCORSORIGIN": "https://" - } - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Account/AccountController.cs b/aspnetcore/src/identityserver/Quickstart/Account/AccountController.cs deleted file mode 100644 index 3eaa9def..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/AccountController.cs +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityModel; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using identityserver.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace IdentityServerHost.Quickstart.UI -{ - [SecurityHeaders] - [AllowAnonymous] - public class AccountController : Controller - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IAuthenticationSchemeProvider _schemeProvider; - private readonly IEventService _events; - - public AccountController( - UserManager userManager, - SignInManager signInManager, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IAuthenticationSchemeProvider schemeProvider, - IEventService events) - { - _userManager = userManager; - _signInManager = signInManager; - _interaction = interaction; - _clientStore = clientStore; - _schemeProvider = schemeProvider; - _events = events; - } - - ///

- /// Entry point into the login workflow - /// - [HttpGet] - public async Task Login(string returnUrl) - { - // build a model so we know what to show on the login page - var vm = await BuildLoginViewModelAsync(returnUrl); - - if (vm.IsExternalLoginOnly) - { - // we only have one option for logging in and it's an external provider - return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl }); - } - - return View(vm); - } - - /// - /// Handle postback from username/password login - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Login(LoginInputModel model, string button) - { - // check if we are in the context of an authorization request - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - - // the user clicked the "cancel" button - if (button != "login") - { - if (context != null) - { - // if the user cancels, send a result back into IdentityServer as if they - // denied the consent (even if this client does not require consent). - // this will send back an access denied OIDC error response to the client. - await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - return Redirect(model.ReturnUrl); - } - else - { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); - } - } - - if (ModelState.IsValid) - { - var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: true); - if (result.Succeeded) - { - var user = await _userManager.FindByNameAsync(model.Username); - await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId)); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - return Redirect(model.ReturnUrl); - } - - // request for a local page - if (Url.IsLocalUrl(model.ReturnUrl)) - { - return Redirect(model.ReturnUrl); - } - else if (string.IsNullOrEmpty(model.ReturnUrl)) - { - return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - } - - await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.Client.ClientId)); - ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); - } - - // something went wrong, show form with error - var vm = await BuildLoginViewModelAsync(model); - return View(vm); - } - - - /// - /// Show logout page - /// - [HttpGet] - public async Task Logout(string logoutId) - { - // build a model so the logout page knows what to display - var vm = await BuildLogoutViewModelAsync(logoutId); - - if (vm.ShowLogoutPrompt == false) - { - // if the request for logout was properly authenticated from IdentityServer, then - // we don't need to show the prompt and can just log the user out directly. - return await Logout(vm); - } - - return View(vm); - } - - /// - /// Handle logout page postback - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Logout(LogoutInputModel model) - { - // build a model so the logged out page knows what to display - var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); - - if (User?.Identity.IsAuthenticated == true) - { - // delete local authentication cookie - await _signInManager.SignOutAsync(); - - // raise the logout event - await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); - } - - // check if we need to trigger sign-out at an upstream identity provider - //if (vm.TriggerExternalSignout) - //{ - // // build a return URL so the upstream provider will redirect back - // // to us after the user has logged out. this allows us to then - // // complete our single sign-out processing. - // string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); - - // // this triggers a redirect to the external provider for sign-out - // return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); - //} - - return View("LoggedOut", vm); - } - - [HttpGet] - public IActionResult AccessDenied() - { - return View(); - } - - - /*****************************************/ - /* helper APIs for the AccountController */ - /*****************************************/ - private async Task BuildLoginViewModelAsync(string returnUrl) - { - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) - { - var local = context.IdP == IdentityServer4.IdentityServerConstants.LocalIdentityProvider; - - // this is meant to short circuit the UI and only trigger the one external IdP - var vm = new LoginViewModel - { - EnableLocalLogin = local, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - }; - - if (!local) - { - vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; - } - - return vm; - } - - var schemes = await _schemeProvider.GetAllSchemesAsync(); - - var providers = schemes - .Where(x => x.DisplayName != null) - .Select(x => new ExternalProvider - { - DisplayName = x.DisplayName ?? x.Name, - AuthenticationScheme = x.Name - }).ToList(); - - var allowLocal = true; - if (context?.Client.ClientId != null) - { - var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); - if (client != null) - { - allowLocal = client.EnableLocalLogin; - - if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) - { - providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); - } - } - } - - return new LoginViewModel - { - AllowRememberLogin = AccountOptions.AllowRememberLogin, - EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - ExternalProviders = providers.ToArray() - }; - } - - private async Task BuildLoginViewModelAsync(LoginInputModel model) - { - var vm = await BuildLoginViewModelAsync(model.ReturnUrl); - vm.Username = model.Username; - vm.RememberLogin = model.RememberLogin; - return vm; - } - - private async Task BuildLogoutViewModelAsync(string logoutId) - { - var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; - - if (User?.Identity.IsAuthenticated != true) - { - // if the user is not authenticated, then just show logged out page - vm.ShowLogoutPrompt = false; - return vm; - } - - var context = await _interaction.GetLogoutContextAsync(logoutId); - if (context?.ShowSignoutPrompt == false) - { - // it's safe to automatically sign-out - vm.ShowLogoutPrompt = false; - return vm; - } - - // show the logout prompt. this prevents attacks where the user - // is automatically signed out by another malicious web page. - return vm; - } - - private async Task BuildLoggedOutViewModelAsync(string logoutId) - { - // get context information (client name, post logout redirect URI and iframe for federated signout) - var logout = await _interaction.GetLogoutContextAsync(logoutId); - - var vm = new LoggedOutViewModel - { - AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, - PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, - ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, - SignOutIframeUrl = logout?.SignOutIFrameUrl, - LogoutId = logoutId - }; - - if (User?.Identity.IsAuthenticated == true) - { - var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; - if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider) - { - var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); - if (providerSupportsSignout) - { - if (vm.LogoutId == null) - { - // if there's no current logout context, we need to create one - // this captures necessary info from the current logged in user - // before we signout and redirect away to the external IdP for signout - vm.LogoutId = await _interaction.CreateLogoutContextAsync(); - } - - vm.ExternalAuthenticationScheme = idp; - } - } - } - - return vm; - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Account/AccountOptions.cs b/aspnetcore/src/identityserver/Quickstart/Account/AccountOptions.cs deleted file mode 100644 index 57f06a80..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/AccountOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; - -namespace IdentityServerHost.Quickstart.UI -{ - public class AccountOptions - { - public static bool AllowLocalLogin = false; - public static bool AllowRememberLogin = true; - public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); - - public static bool ShowLogoutPrompt = false; - public static bool AutomaticRedirectAfterSignOut = true; - - public static string InvalidCredentialsErrorMessage = "Invalid username or password"; - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Account/ExternalController.cs b/aspnetcore/src/identityserver/Quickstart/Account/ExternalController.cs deleted file mode 100644 index 4a54b6ad..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/ExternalController.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using IdentityModel; -using IdentityServer4; -using IdentityServer4.Events; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using identityserver.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace IdentityServerHost.Quickstart.UI -{ - [SecurityHeaders] - [AllowAnonymous] - public class ExternalController : Controller - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IEventService _events; - private readonly ILogger _logger; - - public ExternalController( - UserManager userManager, - SignInManager signInManager, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IEventService events, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _interaction = interaction; - _clientStore = clientStore; - _events = events; - _logger = logger; - } - - /// - /// initiate roundtrip to external authentication provider - /// - [HttpGet] - public IActionResult Challenge(string scheme, string returnUrl) - { - if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; - - // validate returnUrl - either it is a valid OIDC URL or back to a local page - if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - - // start challenge and roundtrip the return URL and scheme - var props = new AuthenticationProperties - { - RedirectUri = Url.Action(nameof(Callback)), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", scheme }, - } - }; - - return Challenge(props, scheme); - - } - - /// - /// Post processing of external authentication - /// - [HttpGet] - public async Task Callback() - { - // read external identity from the temporary cookie - var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); - if (result?.Succeeded != true) - { - throw new Exception("External authentication error"); - } - - if (_logger.IsEnabled(LogLevel.Debug)) - { - var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); - _logger.LogDebug("External claims: {@claims}", externalClaims); - } - - // lookup our user and external provider info - var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result); - if (user == null) - { - // this might be where you might initiate a custom workflow for user registration - // in this sample we don't show how that would be done, as our sample implementation - // simply auto-provisions new external user - user = await AutoProvisionUserAsync(provider, providerUserId, claims); - } - - // this allows us to collect any additional claims or properties - // for the specific protocols used and store them in the local auth cookie. - // this is typically used to store data needed for signout from those protocols. - var additionalLocalClaims = new List(); - additionalLocalClaims.Add(new Claim("orcid", providerUserId)); - var localSignInProps = new AuthenticationProperties(); - ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); - - // issue authentication cookie for user - // we must issue the cookie maually, and can't use the SignInManager because - // it doesn't expose an API to issue additional claims from the login workflow - var principal = await _signInManager.CreateUserPrincipalAsync(user); - additionalLocalClaims.AddRange(principal.Claims); - var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id; - - var isuser = new IdentityServerUser(user.Id) - { - DisplayName = name, - IdentityProvider = provider, - AdditionalClaims = additionalLocalClaims - }; - - await HttpContext.SignInAsync(isuser, localSignInProps); - - // delete temporary cookie used during external authentication - await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); - - // retrieve return URL - var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; - - // check if external login is in the context of an OIDC request - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, name, true, context?.Client.ClientId)); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", returnUrl); - } - } - - return Redirect(returnUrl); - } - - private async Task<(ApplicationUser user, string provider, string providerUserId, IEnumerable claims)> - FindUserFromExternalProviderAsync(AuthenticateResult result) - { - var externalUser = result.Principal; - - // try to determine the unique id of the external user (issued by the provider) - // the most common claim type for that are the sub claim and the NameIdentifier - // depending on the external provider, some other claim type might be used - var userIdClaim = externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? - throw new Exception("Unknown userid"); - - // remove the user id claim so we don't include it as an extra claim if/when we provision the user - var claims = externalUser.Claims.ToList(); - claims.Remove(userIdClaim); - - var provider = result.Properties.Items["scheme"]; - var providerUserId = userIdClaim.Value; - - // find external user - var user = await _userManager.FindByLoginAsync(provider, providerUserId); - - return (user, provider, providerUserId, claims); - } - - private async Task AutoProvisionUserAsync(string provider, string providerUserId, IEnumerable claims) - { - // create a list of claims that we want to transfer into our store - var filtered = new List(); - - // ORCID ID claim - // filtered.Add(new Claim("orcid", providerUserId)); - - //filtered.Add(new Claim("sub", providerUserId)); - - // user's display name - var name = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Name)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value; - if (name != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, name)); - } - else - { - var first = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value; - var last = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Surname)?.Value; - if (first != null && last != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, first + " " + last)); - } - else if (first != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, first)); - } - else if (last != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, last)); - } - } - - // email - var email = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value; - if (email != null) - { - filtered.Add(new Claim(JwtClaimTypes.Email, email)); - } - - var user = new ApplicationUser - { - UserName = providerUserId, - OrcidIdentifier = providerUserId //// ORCID ID stored to database - }; - var identityResult = await _userManager.CreateAsync(user); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - - if (filtered.Any()) - { - identityResult = await _userManager.AddClaimsAsync(user, filtered); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - } - - identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider)); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - - return user; - } - - // if the external login is OIDC-based, there are certain things we need to preserve to make logout work - // this will be different for WS-Fed, SAML2p or other protocols - private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - // if the external system sent a session id claim, copy it over - // so we can use it for single sign-out - var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); - if (sid != null) - { - localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); - } - - // if the external provider issued an id_token, we'll keep it for signout - var idToken = externalResult.Properties.GetTokenValue("id_token"); - if (idToken != null) - { - localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); - } - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Account/ExternalProvider.cs b/aspnetcore/src/identityserver/Quickstart/Account/ExternalProvider.cs deleted file mode 100644 index a5aa8444..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/ExternalProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class ExternalProvider - { - public string DisplayName { get; set; } - public string AuthenticationScheme { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Account/LoggedOutViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Account/LoggedOutViewModel.cs deleted file mode 100644 index a342bb16..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/LoggedOutViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class LoggedOutViewModel - { - public string PostLogoutRedirectUri { get; set; } - public string ClientName { get; set; } - public string SignOutIframeUrl { get; set; } - - public bool AutomaticRedirectAfterSignOut { get; set; } - - public string LogoutId { get; set; } - public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; - public string ExternalAuthenticationScheme { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Account/LoginInputModel.cs b/aspnetcore/src/identityserver/Quickstart/Account/LoginInputModel.cs deleted file mode 100644 index e316788e..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/LoginInputModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.ComponentModel.DataAnnotations; - -namespace IdentityServerHost.Quickstart.UI -{ - public class LoginInputModel - { - [Required] - public string Username { get; set; } - [Required] - public string Password { get; set; } - public bool RememberLogin { get; set; } - public string ReturnUrl { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Account/LoginViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Account/LoginViewModel.cs deleted file mode 100644 index 854363d7..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/LoginViewModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IdentityServerHost.Quickstart.UI -{ - public class LoginViewModel : LoginInputModel - { - public bool AllowRememberLogin { get; set; } = true; - public bool EnableLocalLogin { get; set; } = false; - - public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); - public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); - - public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; - public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Account/LogoutInputModel.cs b/aspnetcore/src/identityserver/Quickstart/Account/LogoutInputModel.cs deleted file mode 100644 index dbeecd67..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/LogoutInputModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class LogoutInputModel - { - public string LogoutId { get; set; } - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Account/LogoutViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Account/LogoutViewModel.cs deleted file mode 100644 index 7194b14f..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/LogoutViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class LogoutViewModel : LogoutInputModel - { - public bool ShowLogoutPrompt { get; set; } = true; - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Account/RedirectViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Account/RedirectViewModel.cs deleted file mode 100644 index 2cddd85e..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Account/RedirectViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - - -namespace IdentityServerHost.Quickstart.UI -{ - public class RedirectViewModel - { - public string RedirectUrl { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentController.cs b/aspnetcore/src/identityserver/Quickstart/Consent/ConsentController.cs deleted file mode 100644 index b33eaa7f..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentController.cs +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Events; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Extensions; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; -using IdentityServer4.Validation; -using System.Collections.Generic; -using System; - -namespace IdentityServerHost.Quickstart.UI -{ - /// - /// This controller processes the consent UI - /// - [SecurityHeaders] - [Authorize] - public class ConsentController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IEventService _events; - private readonly ILogger _logger; - - public ConsentController( - IIdentityServerInteractionService interaction, - IEventService events, - ILogger logger) - { - _interaction = interaction; - _events = events; - _logger = logger; - } - - /// - /// Shows the consent screen - /// - /// - /// - [HttpGet] - public async Task Index(string returnUrl) - { - var vm = await BuildViewModelAsync(returnUrl); - if (vm != null) - { - return View("Index", vm); - } - - return View("Error"); - } - - /// - /// Handles the consent screen postback - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Index(ConsentInputModel model) - { - var result = await ProcessConsent(model); - - if (result.IsRedirect) - { - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - if (context?.IsNativeClient() == true) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", result.RedirectUri); - } - - return Redirect(result.RedirectUri); - } - - if (result.HasValidationError) - { - ModelState.AddModelError(string.Empty, result.ValidationError); - } - - if (result.ShowView) - { - return View("Index", result.ViewModel); - } - - return View("Error"); - } - - /*****************************************/ - /* helper APIs for the ConsentController */ - /*****************************************/ - private async Task ProcessConsent(ConsentInputModel model) - { - var result = new ProcessConsentResult(); - - // validate return url is still valid - var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - if (request == null) return result; - - ConsentResponse grantedConsent = null; - - // user clicked 'no' - send back the standard 'access_denied' response - if (model?.Button == "no") - { - grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; - - // emit event - await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); - } - // user clicked 'yes' - validate the data - else if (model?.Button == "yes") - { - // if the user consented to some scope, build the response model - if (model.ScopesConsented != null && model.ScopesConsented.Any()) - { - var scopes = model.ScopesConsented; - if (ConsentOptions.EnableOfflineAccess == false) - { - scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); - } - - grantedConsent = new ConsentResponse - { - RememberConsent = model.RememberConsent, - ScopesValuesConsented = scopes.ToArray(), - Description = model.Description - }; - - // emit event - await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); - } - else - { - result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; - } - } - else - { - result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; - } - - if (grantedConsent != null) - { - // communicate outcome of consent back to identityserver - await _interaction.GrantConsentAsync(request, grantedConsent); - - // indicate that's it ok to redirect back to authorization endpoint - result.RedirectUri = model.ReturnUrl; - result.Client = request.Client; - } - else - { - // we need to redisplay the consent UI - result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); - } - - return result; - } - - private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) - { - var request = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (request != null) - { - return CreateConsentViewModel(model, returnUrl, request); - } - else - { - _logger.LogError("No consent request matching request: {0}", returnUrl); - } - - return null; - } - - private ConsentViewModel CreateConsentViewModel( - ConsentInputModel model, string returnUrl, - AuthorizationRequest request) - { - var vm = new ConsentViewModel - { - RememberConsent = model?.RememberConsent ?? true, - ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), - Description = model?.Description, - - ReturnUrl = returnUrl, - - ClientName = request.Client.ClientName ?? request.Client.ClientId, - ClientUrl = request.Client.ClientUri, - ClientLogoUrl = request.Client.LogoUri, - AllowRememberConsent = request.Client.AllowRememberConsent - }; - - vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - - var apiScopes = new List(); - foreach(var parsedScope in request.ValidatedResources.ParsedScopes) - { - var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); - if (apiScope != null) - { - var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); - apiScopes.Add(scopeVm); - } - } - if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) - { - apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); - } - vm.ApiScopes = apiScopes; - - return vm; - } - - private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) - { - return new ScopeViewModel - { - Value = identity.Name, - DisplayName = identity.DisplayName ?? identity.Name, - Description = identity.Description, - Emphasize = identity.Emphasize, - Required = identity.Required, - Checked = check || identity.Required - }; - } - - public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) - { - var displayName = apiScope.DisplayName ?? apiScope.Name; - if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) - { - displayName += ":" + parsedScopeValue.ParsedParameter; - } - - return new ScopeViewModel - { - Value = parsedScopeValue.RawValue, - DisplayName = displayName, - Description = apiScope.Description, - Emphasize = apiScope.Emphasize, - Required = apiScope.Required, - Checked = check || apiScope.Required - }; - } - - private ScopeViewModel GetOfflineAccessScope(bool check) - { - return new ScopeViewModel - { - Value = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, - DisplayName = ConsentOptions.OfflineAccessDisplayName, - Description = ConsentOptions.OfflineAccessDescription, - Emphasize = true, - Checked = check - }; - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentInputModel.cs b/aspnetcore/src/identityserver/Quickstart/Consent/ConsentInputModel.cs deleted file mode 100644 index fe0f3a8f..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentInputModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace IdentityServerHost.Quickstart.UI -{ - public class ConsentInputModel - { - public string Button { get; set; } - public IEnumerable ScopesConsented { get; set; } - public bool RememberConsent { get; set; } - public string ReturnUrl { get; set; } - public string Description { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentOptions.cs b/aspnetcore/src/identityserver/Quickstart/Consent/ConsentOptions.cs deleted file mode 100644 index f0ddc9d5..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class ConsentOptions - { - public static bool EnableOfflineAccess = true; - public static string OfflineAccessDisplayName = "Offline Access"; - public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; - - public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; - public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Consent/ConsentViewModel.cs deleted file mode 100644 index 33aede70..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Consent/ConsentViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace IdentityServerHost.Quickstart.UI -{ - public class ConsentViewModel : ConsentInputModel - { - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public bool AllowRememberConsent { get; set; } - - public IEnumerable IdentityScopes { get; set; } - public IEnumerable ApiScopes { get; set; } - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Consent/ProcessConsentResult.cs b/aspnetcore/src/identityserver/Quickstart/Consent/ProcessConsentResult.cs deleted file mode 100644 index 86b2e1d0..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Consent/ProcessConsentResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; - -namespace IdentityServerHost.Quickstart.UI -{ - public class ProcessConsentResult - { - public bool IsRedirect => RedirectUri != null; - public string RedirectUri { get; set; } - public Client Client { get; set; } - - public bool ShowView => ViewModel != null; - public ConsentViewModel ViewModel { get; set; } - - public bool HasValidationError => ValidationError != null; - public string ValidationError { get; set; } - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Consent/ScopeViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Consent/ScopeViewModel.cs deleted file mode 100644 index 52ff7704..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Consent/ScopeViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class ScopeViewModel - { - public string Value { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public bool Emphasize { get; set; } - public bool Required { get; set; } - public bool Checked { get; set; } - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationInputModel.cs b/aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationInputModel.cs deleted file mode 100644 index abada5cc..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationInputModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class DeviceAuthorizationInputModel : ConsentInputModel - { - public string UserCode { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationViewModel.cs deleted file mode 100644 index b4556e79..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Device/DeviceAuthorizationViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class DeviceAuthorizationViewModel : ConsentViewModel - { - public string UserCode { get; set; } - public bool ConfirmUserCode { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Device/DeviceController.cs b/aspnetcore/src/identityserver/Quickstart/Device/DeviceController.cs deleted file mode 100644 index 0f05fe65..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Device/DeviceController.cs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using IdentityServer4.Configuration; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Validation; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace IdentityServerHost.Quickstart.UI -{ - [Authorize] - [SecurityHeaders] - public class DeviceController : Controller - { - private readonly IDeviceFlowInteractionService _interaction; - private readonly IEventService _events; - private readonly IOptions _options; - private readonly ILogger _logger; - - public DeviceController( - IDeviceFlowInteractionService interaction, - IEventService eventService, - IOptions options, - ILogger logger) - { - _interaction = interaction; - _events = eventService; - _options = options; - _logger = logger; - } - - [HttpGet] - public async Task Index() - { - string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; - string userCode = Request.Query[userCodeParamName]; - if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); - - var vm = await BuildViewModelAsync(userCode); - if (vm == null) return View("Error"); - - vm.ConfirmUserCode = true; - return View("UserCodeConfirmation", vm); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task UserCodeCapture(string userCode) - { - var vm = await BuildViewModelAsync(userCode); - if (vm == null) return View("Error"); - - return View("UserCodeConfirmation", vm); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Callback(DeviceAuthorizationInputModel model) - { - if (model == null) throw new ArgumentNullException(nameof(model)); - - var result = await ProcessConsent(model); - if (result.HasValidationError) return View("Error"); - - return View("Success"); - } - - private async Task ProcessConsent(DeviceAuthorizationInputModel model) - { - var result = new ProcessConsentResult(); - - var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); - if (request == null) return result; - - ConsentResponse grantedConsent = null; - - // user clicked 'no' - send back the standard 'access_denied' response - if (model.Button == "no") - { - grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; - - // emit event - await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); - } - // user clicked 'yes' - validate the data - else if (model.Button == "yes") - { - // if the user consented to some scope, build the response model - if (model.ScopesConsented != null && model.ScopesConsented.Any()) - { - var scopes = model.ScopesConsented; - if (ConsentOptions.EnableOfflineAccess == false) - { - scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); - } - - grantedConsent = new ConsentResponse - { - RememberConsent = model.RememberConsent, - ScopesValuesConsented = scopes.ToArray(), - Description = model.Description - }; - - // emit event - await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); - } - else - { - result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; - } - } - else - { - result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; - } - - if (grantedConsent != null) - { - // communicate outcome of consent back to identityserver - await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); - - // indicate that's it ok to redirect back to authorization endpoint - result.RedirectUri = model.ReturnUrl; - result.Client = request.Client; - } - else - { - // we need to redisplay the consent UI - result.ViewModel = await BuildViewModelAsync(model.UserCode, model); - } - - return result; - } - - private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) - { - var request = await _interaction.GetAuthorizationContextAsync(userCode); - if (request != null) - { - return CreateConsentViewModel(userCode, model, request); - } - - return null; - } - - private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) - { - var vm = new DeviceAuthorizationViewModel - { - UserCode = userCode, - Description = model?.Description, - - RememberConsent = model?.RememberConsent ?? true, - ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), - - ClientName = request.Client.ClientName ?? request.Client.ClientId, - ClientUrl = request.Client.ClientUri, - ClientLogoUrl = request.Client.LogoUri, - AllowRememberConsent = request.Client.AllowRememberConsent - }; - - vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - - var apiScopes = new List(); - foreach (var parsedScope in request.ValidatedResources.ParsedScopes) - { - var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); - if (apiScope != null) - { - var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); - apiScopes.Add(scopeVm); - } - } - if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) - { - apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); - } - vm.ApiScopes = apiScopes; - - return vm; - } - - private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) - { - return new ScopeViewModel - { - Value = identity.Name, - DisplayName = identity.DisplayName ?? identity.Name, - Description = identity.Description, - Emphasize = identity.Emphasize, - Required = identity.Required, - Checked = check || identity.Required - }; - } - - public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) - { - return new ScopeViewModel - { - Value = parsedScopeValue.RawValue, - // todo: use the parsed scope value in the display? - DisplayName = apiScope.DisplayName ?? apiScope.Name, - Description = apiScope.Description, - Emphasize = apiScope.Emphasize, - Required = apiScope.Required, - Checked = check || apiScope.Required - }; - } - private ScopeViewModel GetOfflineAccessScope(bool check) - { - return new ScopeViewModel - { - Value = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, - DisplayName = ConsentOptions.OfflineAccessDisplayName, - Description = ConsentOptions.OfflineAccessDescription, - Emphasize = true, - Checked = check - }; - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsController.cs b/aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsController.cs deleted file mode 100644 index 06c80df2..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsController.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace IdentityServerHost.Quickstart.UI -{ - [SecurityHeaders] - [Authorize] - public class DiagnosticsController : Controller - { - public async Task Index() - { - var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; - if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) - { - return NotFound(); - } - - var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); - return View(model); - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsViewModel.cs deleted file mode 100644 index 02a0e689..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Diagnostics/DiagnosticsViewModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityModel; -using Microsoft.AspNetCore.Authentication; -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Text; - -namespace IdentityServerHost.Quickstart.UI -{ - public class DiagnosticsViewModel - { - public DiagnosticsViewModel(AuthenticateResult result) - { - AuthenticateResult = result; - - if (result.Properties.Items.ContainsKey("client_list")) - { - var encoded = result.Properties.Items["client_list"]; - var bytes = Base64Url.Decode(encoded); - var value = Encoding.UTF8.GetString(bytes); - - Clients = JsonConvert.DeserializeObject(value); - } - } - - public AuthenticateResult AuthenticateResult { get; } - public IEnumerable Clients { get; } = new List(); - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Extensions.cs b/aspnetcore/src/identityserver/Quickstart/Extensions.cs deleted file mode 100644 index b43571fc..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Extensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using IdentityServer4.Models; -using Microsoft.AspNetCore.Mvc; - -namespace IdentityServerHost.Quickstart.UI -{ - public static class Extensions - { - /// - /// Checks if the redirect URI is for a native client. - /// - /// - public static bool IsNativeClient(this AuthorizationRequest context) - { - return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) - && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); - } - - public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) - { - controller.HttpContext.Response.StatusCode = 200; - controller.HttpContext.Response.Headers["Location"] = ""; - - return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); - } - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/Grants/GrantsController.cs b/aspnetcore/src/identityserver/Quickstart/Grants/GrantsController.cs deleted file mode 100644 index 85fb1713..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Grants/GrantsController.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using IdentityServer4.Events; -using IdentityServer4.Extensions; - -namespace IdentityServerHost.Quickstart.UI -{ - /// - /// This sample controller allows a user to revoke grants given to clients - /// - [SecurityHeaders] - [Authorize] - public class GrantsController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clients; - private readonly IResourceStore _resources; - private readonly IEventService _events; - - public GrantsController(IIdentityServerInteractionService interaction, - IClientStore clients, - IResourceStore resources, - IEventService events) - { - _interaction = interaction; - _clients = clients; - _resources = resources; - _events = events; - } - - /// - /// Show list of grants - /// - [HttpGet] - public async Task Index() - { - return View("Index", await BuildViewModelAsync()); - } - - /// - /// Handle postback to revoke a client - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Revoke(string clientId) - { - await _interaction.RevokeUserConsentAsync(clientId); - await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); - - return RedirectToAction("Index"); - } - - private async Task BuildViewModelAsync() - { - var grants = await _interaction.GetAllUserGrantsAsync(); - - var list = new List(); - foreach(var grant in grants) - { - var client = await _clients.FindClientByIdAsync(grant.ClientId); - if (client != null) - { - var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); - - var item = new GrantViewModel() - { - ClientId = client.ClientId, - ClientName = client.ClientName ?? client.ClientId, - ClientLogoUrl = client.LogoUri, - ClientUrl = client.ClientUri, - Description = grant.Description, - Created = grant.CreationTime, - Expires = grant.Expiration, - IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), - ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() - }; - - list.Add(item); - } - } - - return new GrantsViewModel - { - Grants = list - }; - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Grants/GrantsViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Grants/GrantsViewModel.cs deleted file mode 100644 index bc75e70c..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Grants/GrantsViewModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; - -namespace IdentityServerHost.Quickstart.UI -{ - public class GrantsViewModel - { - public IEnumerable Grants { get; set; } - } - - public class GrantViewModel - { - public string ClientId { get; set; } - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public string Description { get; set; } - public DateTime Created { get; set; } - public DateTime? Expires { get; set; } - public IEnumerable IdentityGrantNames { get; set; } - public IEnumerable ApiGrantNames { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Home/ErrorViewModel.cs b/aspnetcore/src/identityserver/Quickstart/Home/ErrorViewModel.cs deleted file mode 100644 index bea3ff28..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Home/ErrorViewModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; - -namespace IdentityServerHost.Quickstart.UI -{ - public class ErrorViewModel - { - public ErrorViewModel() - { - } - - public ErrorViewModel(string error) - { - Error = new ErrorMessage { Error = error }; - } - - public ErrorMessage Error { get; set; } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/Home/HomeController.cs b/aspnetcore/src/identityserver/Quickstart/Home/HomeController.cs deleted file mode 100644 index 054bb3b5..00000000 --- a/aspnetcore/src/identityserver/Quickstart/Home/HomeController.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Services; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks; - -namespace IdentityServerHost.Quickstart.UI -{ - [SecurityHeaders] - [AllowAnonymous] - public class HomeController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IWebHostEnvironment _environment; - private readonly ILogger _logger; - - public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) - { - _interaction = interaction; - _environment = environment; - _logger = logger; - } - - public IActionResult Index() - { - if (_environment.IsDevelopment()) - { - // only show in development - return View(); - } - - _logger.LogInformation("Homepage is disabled in production. Returning 404."); - return NotFound(); - } - - /// - /// Shows the error page - /// - public async Task Error(string errorId) - { - var vm = new ErrorViewModel(); - - // retrieve error details from identityserver - var message = await _interaction.GetErrorContextAsync(errorId); - if (message != null) - { - vm.Error = message; - - if (!_environment.IsDevelopment()) - { - // only show in development - message.ErrorDescription = null; - } - } - - return View("Error", vm); - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Quickstart/SecurityHeadersAttribute.cs b/aspnetcore/src/identityserver/Quickstart/SecurityHeadersAttribute.cs deleted file mode 100644 index 4bf08db8..00000000 --- a/aspnetcore/src/identityserver/Quickstart/SecurityHeadersAttribute.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace IdentityServerHost.Quickstart.UI -{ - public class SecurityHeadersAttribute : ActionFilterAttribute - { - public override void OnResultExecuting(ResultExecutingContext context) - { - var result = context.Result; - if (result is ViewResult) - { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options - if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) - { - context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff"); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options - if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) - { - context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy - var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; - // also consider adding upgrade-insecure-requests once you have HTTPS in place for production - //csp += "upgrade-insecure-requests;"; - // also an example if you need client images to be displayed from twitter - // csp += "img-src 'self' https://pbs.twimg.com;"; - - // once for standards compliant browsers - if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) - { - context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp); - } - // and once again for IE - if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) - { - context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy - var referrer_policy = "no-referrer"; - if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) - { - context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy); - } - } - } - } -} diff --git a/aspnetcore/src/identityserver/Quickstart/TestUsers.cs b/aspnetcore/src/identityserver/Quickstart/TestUsers.cs deleted file mode 100644 index bdab5897..00000000 --- a/aspnetcore/src/identityserver/Quickstart/TestUsers.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityModel; -using IdentityServer4.Test; -using System.Collections.Generic; -using System.Security.Claims; - -namespace IdentityServerHost.Quickstart.UI -{ - public class TestUsers - { - public static List Users = new List - { - new TestUser{SubjectId = "818727", Username = "alice", Password = "alice", - Claims = - { - new Claim(JwtClaimTypes.Name, "Alice Smith"), - new Claim(JwtClaimTypes.GivenName, "Alice"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), - new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), - new Claim(JwtClaimTypes.WebSite, "http://alice.com"), - new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json) - } - }, - new TestUser{SubjectId = "88421113", Username = "bob", Password = "bob", - Claims = - { - new Claim(JwtClaimTypes.Name, "Bob Smith"), - new Claim(JwtClaimTypes.GivenName, "Bob"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), - new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), - new Claim(JwtClaimTypes.WebSite, "http://bob.com"), - new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json), - new Claim("location", "somewhere") - } - } - }; - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/SeedData.cs b/aspnetcore/src/identityserver/SeedData.cs deleted file mode 100644 index 88234e01..00000000 --- a/aspnetcore/src/identityserver/SeedData.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Linq; -using System.Security.Claims; -using IdentityModel; -using identityserver.Data; -using identityserver.Models; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Serilog; - -namespace identityserver -{ - public class SeedData - { - public static void EnsureSeedData(string connectionString) - { - var services = new ServiceCollection(); - services.AddLogging(); - services.AddDbContext(options => - //options.UseSqlite(connectionString)); - options.UseSqlServer(connectionString)); - - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); - - using (var serviceProvider = services.BuildServiceProvider()) - { - using (var scope = serviceProvider.GetRequiredService().CreateScope()) - { - var context = scope.ServiceProvider.GetService(); - context.Database.Migrate(); - - var userMgr = scope.ServiceProvider.GetRequiredService>(); - var alice = userMgr.FindByNameAsync("alice").Result; - if (alice == null) - { - alice = new ApplicationUser - { - UserName = "alice", - Email = "AliceSmith@email.com", - EmailConfirmed = true, - }; - var result = userMgr.CreateAsync(alice, "Pass123$").Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - - result = userMgr.AddClaimsAsync(alice, new Claim[]{ - new Claim(JwtClaimTypes.Name, "Alice Smith"), - new Claim(JwtClaimTypes.GivenName, "Alice"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.WebSite, "http://alice.com"), - }).Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - Log.Debug("alice created"); - } - else - { - Log.Debug("alice already exists"); - } - - var bob = userMgr.FindByNameAsync("bob").Result; - if (bob == null) - { - bob = new ApplicationUser - { - UserName = "bob", - Email = "BobSmith@email.com", - EmailConfirmed = true - }; - var result = userMgr.CreateAsync(bob, "Pass123$").Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - - result = userMgr.AddClaimsAsync(bob, new Claim[]{ - new Claim(JwtClaimTypes.Name, "Bob Smith"), - new Claim(JwtClaimTypes.GivenName, "Bob"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.WebSite, "http://bob.com"), - new Claim("location", "somewhere") - }).Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - Log.Debug("bob created"); - } - else - { - Log.Debug("bob already exists"); - } - } - } - } - } -} diff --git a/aspnetcore/src/identityserver/Services/ProfileService.cs b/aspnetcore/src/identityserver/Services/ProfileService.cs deleted file mode 100644 index 7ef846c1..00000000 --- a/aspnetcore/src/identityserver/Services/ProfileService.cs +++ /dev/null @@ -1,46 +0,0 @@ -using identityserver.Models; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using Microsoft.AspNetCore.Identity; -using System; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace identityserver.Services -{ - public class ProfileService : IProfileService - { - private readonly IUserClaimsPrincipalFactory _claimsFactory; - private readonly UserManager _userManager; - - public ProfileService(UserManager userManager, IUserClaimsPrincipalFactory claimsFactory) - { - _userManager = userManager; - _claimsFactory = claimsFactory; - } - - public async Task GetProfileDataAsync(ProfileDataRequestContext context) - { - var sub = context.Subject.GetSubjectId(); - var user = await _userManager.FindByIdAsync(sub); - var principal = await _claimsFactory.CreateAsync(user); - - var claims = principal.Claims.ToList(); - claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList(); - - // Add custom claims in token - claims.Add(new Claim("orcid", user.OrcidIdentifier ?? string.Empty)); - - context.IssuedClaims = claims; - } - - public async Task IsActiveAsync(IsActiveContext context) - { - var sub = context.Subject.GetSubjectId(); - var user = await _userManager.FindByIdAsync(sub); - context.IsActive = user != null; - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Startup.cs b/aspnetcore/src/identityserver/Startup.cs deleted file mode 100644 index f0d5850d..00000000 --- a/aspnetcore/src/identityserver/Startup.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4; -using identityserver.Data; -using identityserver.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using System; -using System.Linq; -using System.Security.Claims; -using IdentityModel; -using Microsoft.IdentityModel.Tokens; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using System.Reflection; -using IdentityServer4.EntityFramework.DbContexts; -using IdentityServer4.EntityFramework.Mappers; -using IdentityServer4.Services; -using identityserver.Services; -using Microsoft.AspNetCore.HttpOverrides; -using IdentityServer4.Extensions; -using IdentityServer4.Models; - -namespace identityserver -{ - public class Startup - { - public IWebHostEnvironment Environment { get; } - public IConfiguration Configuration { get; } - - public Startup(IWebHostEnvironment environment, IConfiguration configuration) - { - Environment = environment; - Configuration = configuration; - } - - public void ConfigureServices(IServiceCollection services) - { - var connectionString = Configuration.GetConnectionString("DefaultConnection"); - var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; - - services.Configure(options => - { - options.ForwardedHeaders = - ForwardedHeaders.XForwardedFor; // | ForwardedHeaders.XForwardedProto; - }); - - - services.AddControllersWithViews(); - - services.AddDbContext(options => - // options.UseSqlite(connectionString)); - options.UseSqlServer(connectionString)); - - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); - - var builder = services.AddIdentityServer(options => - { - options.Events.RaiseErrorEvents = true; - options.Events.RaiseInformationEvents = true; - options.Events.RaiseFailureEvents = true; - options.Events.RaiseSuccessEvents = true; - - // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html - options.EmitStaticAudienceClaim = true; - }) - //.AddInMemoryIdentityResources(Config.IdentityResources) - //.AddInMemoryApiScopes(Config.ApiScopes) - //.AddInMemoryClients(Config.Clients) - .AddConfigurationStore(options => - { - options.ConfigureDbContext = b => b.UseSqlServer(connectionString, - sql => sql.MigrationsAssembly(migrationsAssembly)); - }) - .AddOperationalStore(options => - { - options.ConfigureDbContext = b => b.UseSqlServer(connectionString, - sql => sql.MigrationsAssembly(migrationsAssembly)); - }) - .AddAspNetIdentity(); - - // not recommended for production - you need to store your key material somewhere secure - builder.AddDeveloperSigningCredential(); - - services.AddAuthentication() - .AddOpenIdConnect("oidc", "ORCID", options => - { - options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; - options.SignOutScheme = IdentityServerConstants.SignoutScheme; - options.SaveTokens = true; - - options.Authority = "https://orcid.org"; - options.ClientId = Configuration["ORCID:ClientId"]; - options.ClientSecret = Configuration["ORCID:ClientSecret"]; - options.ResponseType = OidcConstants.ResponseTypes.Code; - - options.TokenValidationParameters = new TokenValidationParameters - { - NameClaimType = "name", - RoleClaimType = "role", - RequireExpirationTime = false, - }; - }); - - services.AddScoped(); - } - - public void Configure(IApplicationBuilder app) - { - app.UseForwardedHeaders(); - - // This setting enables IdentityServer to work behind a reverse proxy. - // The domain of the IdentityServer must be set in configuration. - app.Use(async (ctx, next) => - { - ctx.SetIdentityServerOrigin(Configuration["IdentityServer:Origin"]); - await next(); - }); - - // Initialize IdentityServer database - InitializeDatabase(app); - - if (Environment.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseDatabaseErrorPage(); - } - - app.UseStaticFiles(); - - app.UseRouting(); - app.UseIdentityServer(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - }); - } - - // Initialize IdentityServer database - private void InitializeDatabase(IApplicationBuilder app) - { - using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) - { - serviceScope.ServiceProvider.GetRequiredService().Database.Migrate(); - serviceScope.ServiceProvider.GetRequiredService().Database.Migrate(); - - var context = serviceScope.ServiceProvider.GetRequiredService(); - context.Database.Migrate(); - - // Remove old Javascript client to ensure AllowedCorsOrigins, RedirectUris and PostLogoutRedirectUris are updated - var oldJsClient = context.Clients.FirstOrDefault(c => c.ClientId == "js"); - if (oldJsClient != null) - { - context.Clients.Remove(oldJsClient); - context.SaveChanges(); - } - - // New Javascript client - var newJsClient = new Client() - { - ClientId = "js", - ClientName = "JavaScript Client", - AllowedGrantTypes = GrantTypes.Code, - RequireClientSecret = false, - - RedirectUris = { Configuration["JavaScriptClient:Dev:RedirectUri"], Configuration["JavaScriptClient:Test:RedirectUri"] }, - PostLogoutRedirectUris = { Configuration["JavaScriptClient:Dev:PostLogoutRedirectUri"], Configuration["JavaScriptClient:Test:PostLogoutRedirectUri"] }, - AllowedCorsOrigins = { Configuration["JavaScriptClient:Dev:AllowedCorsOrigin"], Configuration["JavaScriptClient:Test:AllowedCorsOrigin"] }, - - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "api1" - }, - AlwaysIncludeUserClaimsInIdToken = true - }; - context.Clients.Add(newJsClient.ToEntity()); - context.SaveChanges(); - - - // Add identity resource - OpenId - var newIdentityResourceOpenId = new IdentityResources.OpenId(); - var existingIdentityResourceOpenId = context.IdentityResources.FirstOrDefault(i => i.Name == newIdentityResourceOpenId.Name); - if (existingIdentityResourceOpenId == null) - { - context.IdentityResources.Add(newIdentityResourceOpenId.ToEntity()); - context.SaveChanges(); - } - - // Add identity resource - Profile - var newIdentityResourceProfile = new IdentityResources.Profile(); - var existingIdentityResourceProfile = context.IdentityResources.FirstOrDefault(i => i.Name == newIdentityResourceProfile.Name); - if (existingIdentityResourceProfile == null) - { - context.IdentityResources.Add(newIdentityResourceProfile.ToEntity()); - context.SaveChanges(); - } - - // Add api scope - var newApiScope = new ApiScope("api1", "Researcher profile API"); - var existingApiScope = context.ApiScopes.FirstOrDefault(a => a.Name == newApiScope.Name); - if (existingApiScope == null) - { - context.ApiScopes.Add(newApiScope.ToEntity()); - context.SaveChanges(); - } - } - } - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Views/Account/AccessDenied.cshtml b/aspnetcore/src/identityserver/Views/Account/AccessDenied.cshtml deleted file mode 100644 index 1f831785..00000000 --- a/aspnetcore/src/identityserver/Views/Account/AccessDenied.cshtml +++ /dev/null @@ -1,7 +0,0 @@ - -
-
-

Access Denied

-

You do not have access to that resource.

-
-
\ No newline at end of file diff --git a/aspnetcore/src/identityserver/Views/Account/LoggedOut.cshtml b/aspnetcore/src/identityserver/Views/Account/LoggedOut.cshtml deleted file mode 100644 index 1a7463a3..00000000 --- a/aspnetcore/src/identityserver/Views/Account/LoggedOut.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@model LoggedOutViewModel - -@{ - // set this so the layout rendering sees an anonymous user - ViewData["signed-out"] = true; -} - -
-

- Logout - You are now logged out -

- - @if (Model.PostLogoutRedirectUri != null) - { -
- Click here to return to the - @Model.ClientName application. -
- } - - @if (Model.SignOutIframeUrl != null) - { - - } -
- -@section scripts -{ - @if (Model.AutomaticRedirectAfterSignOut) - { - - } -} diff --git a/aspnetcore/src/identityserver/Views/Account/Login.cshtml b/aspnetcore/src/identityserver/Views/Account/Login.cshtml deleted file mode 100644 index 38c2a840..00000000 --- a/aspnetcore/src/identityserver/Views/Account/Login.cshtml +++ /dev/null @@ -1,92 +0,0 @@ -@model LoginViewModel - - \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Views/Account/Logout.cshtml b/aspnetcore/src/identityserver/Views/Account/Logout.cshtml deleted file mode 100644 index 8d4a8c83..00000000 --- a/aspnetcore/src/identityserver/Views/Account/Logout.cshtml +++ /dev/null @@ -1,15 +0,0 @@ -@model LogoutViewModel - -
-
-

Logout

-

Would you like to logut of IdentityServer?

-
- -
- -
- -
-
-
diff --git a/aspnetcore/src/identityserver/Views/Consent/Index.cshtml b/aspnetcore/src/identityserver/Views/Consent/Index.cshtml deleted file mode 100644 index 4c7c49e0..00000000 --- a/aspnetcore/src/identityserver/Views/Consent/Index.cshtml +++ /dev/null @@ -1,104 +0,0 @@ -@model ConsentViewModel - - diff --git a/aspnetcore/src/identityserver/Views/Device/Success.cshtml b/aspnetcore/src/identityserver/Views/Device/Success.cshtml deleted file mode 100644 index fbe66f0f..00000000 --- a/aspnetcore/src/identityserver/Views/Device/Success.cshtml +++ /dev/null @@ -1,7 +0,0 @@ - -
-
-

Success

-

You have successfully authorized the device

-
-
diff --git a/aspnetcore/src/identityserver/Views/Device/UserCodeCapture.cshtml b/aspnetcore/src/identityserver/Views/Device/UserCodeCapture.cshtml deleted file mode 100644 index 3284ea15..00000000 --- a/aspnetcore/src/identityserver/Views/Device/UserCodeCapture.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -@model string - -
-
-

User Code

-

Please enter the code displayed on your device.

-
- - - -
-
-
-
- - -
- - -
-
-
-
diff --git a/aspnetcore/src/identityserver/Views/Device/UserCodeConfirmation.cshtml b/aspnetcore/src/identityserver/Views/Device/UserCodeConfirmation.cshtml deleted file mode 100644 index 97275620..00000000 --- a/aspnetcore/src/identityserver/Views/Device/UserCodeConfirmation.cshtml +++ /dev/null @@ -1,108 +0,0 @@ -@model DeviceAuthorizationViewModel - -
-
- @if (Model.ClientLogoUrl != null) - { - - } -

- @Model.ClientName - is requesting your permission -

- @if (Model.ConfirmUserCode) - { -

Please confirm that the authorization request quotes the code: @Model.UserCode.

- } -

Uncheck the permissions you do not wish to grant.

-
- -
-
- -
-
- -
- -
-
- @if (Model.IdentityScopes.Any()) - { -
-
-
- - Personal Information -
-
    - @foreach (var scope in Model.IdentityScopes) - { - - } -
-
-
- } - - @if (Model.ApiScopes.Any()) - { -
-
-
- - Application Access -
-
    - @foreach (var scope in Model.ApiScopes) - { - - } -
-
-
- } - -
-
-
- - Description -
-
- -
-
-
- - @if (Model.AllowRememberConsent) - { -
-
- - -
-
- } -
-
- -
-
- - -
-
- @if (Model.ClientUrl != null) - { - - - @Model.ClientName - - } -
-
-
-
diff --git a/aspnetcore/src/identityserver/Views/Diagnostics/Index.cshtml b/aspnetcore/src/identityserver/Views/Diagnostics/Index.cshtml deleted file mode 100644 index 807789d0..00000000 --- a/aspnetcore/src/identityserver/Views/Diagnostics/Index.cshtml +++ /dev/null @@ -1,64 +0,0 @@ -@model DiagnosticsViewModel - -
-
-

Authentication Cookie

-
- -
-
-
-
-

Claims

-
-
-
- @foreach (var claim in Model.AuthenticateResult.Principal.Claims) - { -
@claim.Type
-
@claim.Value
- } -
-
-
-
- -
-
-
-

Properties

-
-
-
- @foreach (var prop in Model.AuthenticateResult.Properties.Items) - { -
@prop.Key
-
@prop.Value
- } - @if (Model.Clients.Any()) - { -
Clients
-
- @{ - var clients = Model.Clients.ToArray(); - for(var i = 0; i < clients.Length; i++) - { - @clients[i] - if (i < clients.Length - 1) - { - , - } - } - } -
- } -
-
-
-
-
-
- - - - diff --git a/aspnetcore/src/identityserver/Views/Grants/Index.cshtml b/aspnetcore/src/identityserver/Views/Grants/Index.cshtml deleted file mode 100644 index 286f6e2d..00000000 --- a/aspnetcore/src/identityserver/Views/Grants/Index.cshtml +++ /dev/null @@ -1,87 +0,0 @@ -@model GrantsViewModel - -
-
-

Client Application Permissions

-

Below is the list of applications you have given permission to and the resources they have access to.

-
- - @if (Model.Grants.Any() == false) - { -
-
-
- You have not given access to any applications -
-
-
- } - else - { - foreach (var grant in Model.Grants) - { -
-
-
-
- @if (grant.ClientLogoUrl != null) - { - - } - @grant.ClientName -
- -
-
- - -
-
-
-
- -
    - @if (grant.Description != null) - { -
  • - @grant.Description -
  • - } -
  • - @grant.Created.ToString("yyyy-MM-dd") -
  • - @if (grant.Expires.HasValue) - { -
  • - @grant.Expires.Value.ToString("yyyy-MM-dd") -
  • - } - @if (grant.IdentityGrantNames.Any()) - { -
  • - -
      - @foreach (var name in grant.IdentityGrantNames) - { -
    • @name
    • - } -
    -
  • - } - @if (grant.ApiGrantNames.Any()) - { -
  • - -
      - @foreach (var name in grant.ApiGrantNames) - { -
    • @name
    • - } -
    -
  • - } -
-
- } - } -
\ No newline at end of file diff --git a/aspnetcore/src/identityserver/Views/Home/Index.cshtml b/aspnetcore/src/identityserver/Views/Home/Index.cshtml deleted file mode 100644 index 38b4ce95..00000000 --- a/aspnetcore/src/identityserver/Views/Home/Index.cshtml +++ /dev/null @@ -1,32 +0,0 @@ -@using System.Diagnostics - -@{ - var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); -} - -
-

- - Welcome to IdentityServer4 - (version @version) -

- - -
diff --git a/aspnetcore/src/identityserver/Views/Shared/Error.cshtml b/aspnetcore/src/identityserver/Views/Shared/Error.cshtml deleted file mode 100644 index dbe81199..00000000 --- a/aspnetcore/src/identityserver/Views/Shared/Error.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -@model ErrorViewModel - -@{ - var error = Model?.Error?.Error; - var errorDescription = Model?.Error?.ErrorDescription; - var request_id = Model?.Error?.RequestId; -} - -
-
-

Error

-
- -
-
-
- Sorry, there was an error - - @if (error != null) - { - - - : @error - - - - if (errorDescription != null) - { -
@errorDescription
- } - } -
- - @if (request_id != null) - { -
Request Id: @request_id
- } -
-
-
diff --git a/aspnetcore/src/identityserver/Views/Shared/Redirect.cshtml b/aspnetcore/src/identityserver/Views/Shared/Redirect.cshtml deleted file mode 100644 index f58a190a..00000000 --- a/aspnetcore/src/identityserver/Views/Shared/Redirect.cshtml +++ /dev/null @@ -1,11 +0,0 @@ -@model RedirectViewModel - -
-
-

You are now being returned to the application

-

Once complete, you may close this tab.

-
-
- - - diff --git a/aspnetcore/src/identityserver/Views/Shared/_Layout.cshtml b/aspnetcore/src/identityserver/Views/Shared/_Layout.cshtml deleted file mode 100644 index a504a626..00000000 --- a/aspnetcore/src/identityserver/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - IdentityServer4 - - - - - - - - - - -
- @RenderBody() -
- - - - - @RenderSection("scripts", required: false) - - diff --git a/aspnetcore/src/identityserver/Views/Shared/_Nav.cshtml b/aspnetcore/src/identityserver/Views/Shared/_Nav.cshtml deleted file mode 100644 index 3b6f23dc..00000000 --- a/aspnetcore/src/identityserver/Views/Shared/_Nav.cshtml +++ /dev/null @@ -1,33 +0,0 @@ -@using IdentityServer4.Extensions - -@{ - string name = null; - if (!true.Equals(ViewData["signed-out"])) - { - name = Context.User?.GetDisplayName(); - } -} - - diff --git a/aspnetcore/src/identityserver/Views/Shared/_ScopeListItem.cshtml b/aspnetcore/src/identityserver/Views/Shared/_ScopeListItem.cshtml deleted file mode 100644 index b8c6dc11..00000000 --- a/aspnetcore/src/identityserver/Views/Shared/_ScopeListItem.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@model ScopeViewModel - -
  • - - @if (Model.Required) - { - (required) - } - @if (Model.Description != null) - { - - } -
  • \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Views/Shared/_ValidationSummary.cshtml b/aspnetcore/src/identityserver/Views/Shared/_ValidationSummary.cshtml deleted file mode 100644 index 3d77c9d1..00000000 --- a/aspnetcore/src/identityserver/Views/Shared/_ValidationSummary.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@if (ViewContext.ModelState.IsValid == false) -{ -
    - Error -
    -
    -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/Views/_ViewImports.cshtml b/aspnetcore/src/identityserver/Views/_ViewImports.cshtml deleted file mode 100644 index 17f64c93..00000000 --- a/aspnetcore/src/identityserver/Views/_ViewImports.cshtml +++ /dev/null @@ -1,2 +0,0 @@ -@using IdentityServerHost.Quickstart.UI -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/aspnetcore/src/identityserver/Views/_ViewStart.cshtml b/aspnetcore/src/identityserver/Views/_ViewStart.cshtml deleted file mode 100644 index 6e88aa32..00000000 --- a/aspnetcore/src/identityserver/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "_Layout"; -} diff --git a/aspnetcore/src/identityserver/appsettings.json b/aspnetcore/src/identityserver/appsettings.json deleted file mode 100644 index ca8c5b54..00000000 --- a/aspnetcore/src/identityserver/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "DefaultConnection": "Server=localhost;User Id=sa;Password=Test1234;database=;" - } -} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/identityserver.csproj b/aspnetcore/src/identityserver/identityserver.csproj deleted file mode 100644 index 66ba798b..00000000 --- a/aspnetcore/src/identityserver/identityserver.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - netcoreapp3.1 - a979022a-df51-4182-9df0-3f4e641337f3 - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - - - \ No newline at end of file diff --git a/aspnetcore/src/identityserver/tempkey.jwk b/aspnetcore/src/identityserver/tempkey.jwk deleted file mode 100644 index 73bcd2c2..00000000 --- a/aspnetcore/src/identityserver/tempkey.jwk +++ /dev/null @@ -1 +0,0 @@ -{"alg":"RS256","d":"D3lVfy2etfJXWjz3pmZr4yAzjYOU0MZjiRQSTpcmRxBAvUy_Y5Cx1C1FJLaNsbkQApqY5186axKxnr91z8kqF_03euluVj8_-qo-qFrEH_f7xWAJM5rBiWSi0tar_HzgdSAsbwvVSaYK8AO_hctvaFgzIAtJi3u5ShXcC0wOmCRrutFS9sDL-eVEZn1sFPohYij9bK5RwFr5B8klcrnLLI6njC33LPKD02L53Y9h03PLuGUsTXJfIugpGmOGXghGBtkHlieFSHnm3_RV9W2zFw9ulLWZbLjPa1lP8ZfAF43WFfQ1EzP4q9Iymkh_LiDuuzZaRLnmzFwz2nzeXW6vAQ","dp":"Uiz_EbqDIfEPlKSqolSuFC7MDv2Qy3uuyWDv3_EFCJ24HfKfRcfDa1tOZnsVqGjjE8L5gaczqND7lvHufj3vOgvA_SZCU3K9NcvmOy8xY1XlJhNwCDrqNigkeK4ahh9g1vBwyC_AuwzEQ1yOHb6nJ-_cmeJBhRAOmHkvvSynvrE","dq":"KqzzMLCj-136j17yCjml0W_ns941tphmxuYCUi2SuqFv8pk6_PUQlOf_ihuykts35BODJAEAgnjWBZGlWfrI3bU2FIKU5Fbnoz15KPEwsM0rBVwFWHTc-sbOPZOwahdc3kmIvjkqRna720mFk1jKWvylzLO7w-HonqnmRvVRYRE","e":"AQAB","kid":"F19757984D1A0585B5B80BA281121593","kty":"RSA","n":"x92J9xNeU2bjrKrURomM174kabTXWkVwe-S5ZhcM7UIVeHTicz1SFL3FX1dn2MkX5eVV-Iohak-MordGSzmCu3MbjdHZCKg5AgeIqGJOpK9czTv3tSALgsic-_AzBC5DTghaX3iZp5Jx7SpBqshCYqq1CejfJLFuySovfATwXpuL2e6gxqXIMZmCSMwYggwxK0ZrkxrG-698N63v8EAl3iUklyzGI1iqN3zmzES6qe8PouDKC4oGttvlNRlN6uq-aSC2-fy13CkMKBo-sTVJPpy5hKO7n53rCgmVuZbVRinsEzY21wdJvxFmf8qGx8r5GcXQWgzhoEAnTkeyMEDD2Q","p":"8dR4XS0MZaqhadQUZ3MeVaB__G6gzt_ohA3-H_eYvsLsJ8JacxRoC5KVx_dPqBE8rfhht5Z9Bd623b0RZ-Y4scwfRw4iF3Zulz6mTaHt3dPo3wPo3h9u5VXDFZ4kmoS8UcTpOTrMJHryF5kE7GXby_Akh3KCWUR8J7mmu1TqLKk","q":"05OWD4c_CWRLoZmohtv16XPZum92hDvUOBlukIHs1N__xK9o6NvLv_LDIm_835i3RErkjIXeQQ6rb616yv9qgObqthexRoIF97Gw_pfSpP8CRqrmWWXV0FEMHHd9CtwsCBpRrm2pCggj098_0_f6B-u4N359ItVOu_A3K9FLq7E","qi":"O8o_w6UDXg8ztqD5a-65zzNhmV4q7YsAp3fDPrO5y-CrIe-vofnueWRNmXhfYrOek86zqTpWw0hh6DqaMBwJQSJb21AxNdtiY1giyv_BPtKnMR6AO-f73g7uFVinqpNRu9brLPaPoq066AV4UBzvb656GCP7154PUxIGEqo3sTE"} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/updateUI.ps1 b/aspnetcore/src/identityserver/updateUI.ps1 deleted file mode 100644 index 4145377a..00000000 --- a/aspnetcore/src/identityserver/updateUI.ps1 +++ /dev/null @@ -1,172 +0,0 @@ -iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI.AspNetIdentity/main/getmain.ps1')) -# SIG # Begin signature block -# MIIfnQYJKoZIhvcNAQcCoIIfjjCCH4oCAQExDzANBglghkgBZQMEAgEFADB5Bgor -# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAmjChzA8Tk4hB4 -# nAolI/h9hR7E40n+o1koeC4dCBgwCqCCDgcwggPFMIICraADAgECAhACrFwmagtA -# m48LefKuRiV3MA0GCSqGSIb3DQEBBQUAMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -# BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwHhcNMDYxMTEw -# MDAwMDAwWhcNMzExMTEwMDAwMDAwWjBsMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM -# RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSswKQYDVQQD -# EyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMIIBIjANBgkqhkiG -# 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxszlc+b71LvlLS0ypt/lgT/JzSVJtnEqw9WU -# NGeiChywX2mmQLHEt7KP0JikqUFZOtPclNY823Q4pErMTSWC90qlUxI47vNJbXGR -# fmO2q6Zfw6SE+E9iUb74xezbOJLjBuUIkQzEKEFV+8taiRV+ceg1v01yCT2+OjhQ -# W3cxG42zxyRFmqesbQAUWgS3uhPrUQqYQUEiTmVhh4FBUKZ5XIneGUpX1S7mXRxT -# LH6YzRoGFqRoc9A0BBNcoXHTWnxV215k4TeHMFYE5RG0KYAS8Xk5iKICEXwnZreI -# t3jyygqoOKsKZMK/Zl2VhMGhJR6HXRpQCyASzEG7bgtROLhLywIDAQABo2MwYTAO -# BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUsT7DaQP4 -# v0cB1JgmGggC72NkK8MwHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8Mw -# DQYJKoZIhvcNAQEFBQADggEBABwaBpfc15yfPIhmBghXIdshR/gqZ6q/GDJ2QBBX -# wYrzetkRZY41+p78RbWe2UwxS7iR6EMsjrN4ztvjU3lx1uUhlAHaVYeaJGT2imbM -# 3pw3zag0sWmbI8ieeCIrcEPjVUcxYRnvWMWFL04w9qAxFiPI5+JlFjPLvxoboD34 -# yl6LMYtgCIktDAZcUrfE+QqY0RVfnxK+fDZjOL1EpH/kJisKxJdpDemM4sAQV7jI -# dhKRVfJIadi8KgJbD0TUIDHb9LpwJl2QYJ68SxcJL7TLHkNoyQcnwdJc9+ohuWgS -# nDycv578gFybY83sR6olJ2egN/MAgn1U16n46S4To3foH0owggSRMIIDeaADAgEC -# AhAHsEGNpR4UjDMbvN63E4MjMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVT -# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -# b20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0Ew -# HhcNMTgwNDI3MTI0MTU5WhcNMjgwNDI3MTI0MTU5WjBaMQswCQYDVQQGEwJVUzEY -# MBYGA1UEChMPLk5FVCBGb3VuZGF0aW9uMTEwLwYDVQQDEyguTkVUIEZvdW5kYXRp -# b24gUHJvamVjdHMgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC -# AQ8AMIIBCgKCAQEAwQqv4aI0CI20XeYqTTZmyoxsSQgcCBGQnXnufbuDLhAB6GoT -# NB7HuEhNSS8ftV+6yq8GztBzYAJ0lALdBjWypMfL451/84AO5ZiZB3V7MB2uxgWo -# cV1ekDduU9bm1Q48jmR4SVkLItC+oQO/FIA2SBudVZUvYKeCJS5Ri9ibV7La4oo7 -# BJChFiP8uR+v3OU33dgm5BBhWmth4oTyq22zCfP3NO6gBWEIPFR5S+KcefUTYmn2 -# o7IvhvxzJsMCrNH1bxhwOyMl+DQcdWiVPuJBKDOO/hAKIxBG4i6ryQYBaKdhDgaA -# NSCik0UgZasz8Qgl8n0A73+dISPumD8L/4mdywIDAQABo4IBPzCCATswHQYDVR0O -# BBYEFMtck66Im/5Db1ZQUgJtePys4bFaMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY -# JhoIAu9jZCvDMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzAS -# BgNVHRMBAf8ECDAGAQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY -# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEsGA1UdHwREMEIwQKA+oDyGOmh0dHA6 -# Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RD -# QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v -# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQELBQADggEBALNGxKTz6gq6 -# clMF01GjC3RmJ/ZAoK1V7rwkqOkY3JDl++v1F4KrFWEzS8MbZsI/p4W31Eketazo -# Nxy23RT0zDsvJrwEC3R+/MRdkB7aTecsYmMeMHgtUrl3xEO3FubnQ0kKEU/HBCTd -# hR14GsQEccQQE6grFVlglrew+FzehWUu3SUQEp9t+iWpX/KfviDWx0H1azilMX15 -# lzJUxK7kCzmflrk5jCOCjKqhOdGJoQqstmwP+07qXO18bcCzEC908P+TYkh0z9gV -# rlj7tyW9K9zPVPJZsLRaBp/QjMcH65o9Y1hD1uWtFQYmbEYkT1K9tuXHtQYx1Rpf -# /dC8Nbl4iukwggWlMIIEjaADAgECAhAL5Ofkz0TFYBmonpg7pehYMA0GCSqGSIb3 -# DQEBCwUAMFoxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw8uTkVUIEZvdW5kYXRpb24x -# MTAvBgNVBAMTKC5ORVQgRm91bmRhdGlvbiBQcm9qZWN0cyBDb2RlIFNpZ25pbmcg -# Q0EwHhcNMTgwNjExMDAwMDAwWhcNMjEwNjE1MTIwMDAwWjCBoDEUMBIGA1UEBRML -# NjAzIDM4OSAwNjgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw -# DgYDVQQHEwdSZWRtb25kMSkwJwYDVQQKEyBJZGVudGl0eVNlcnZlciAoLk5FVCBG -# b3VuZGF0aW9uKTEpMCcGA1UEAxMgSWRlbnRpdHlTZXJ2ZXIgKC5ORVQgRm91bmRh -# dGlvbikwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwEhFpSIYi3hrr -# v/X9BtZkzufk7puhmTCVmQAPNm2R1eZZyMPhfxm5Sh/w/42CzlCG9LFgooha69z3 -# uoMMOKJEEQKZ6ByIV+r81o4lrHtSFbe4VlXavjQVFaVVjPSG6vWGykfHVCAeVpjx -# fVk/HH6tEX506lBiHgOrQGogoQrwdVnObc3c6RiVSIuvFeCoHvk2GgiqyzFER7iO -# R1055npVSAAAdxBvPA6KREcLb/qHukYCJZX4mY/SajBXwxupSnhRDbYhb+qHpFIL -# x7s/azxg7tVRpVh49oJimHA2uZ/jzh/KgUsUe9MFzT7KPduBK/pfX/fXED9Pt1NN -# 48VfPSuzAgMBAAGjggIeMIICGjAfBgNVHSMEGDAWgBTLXJOuiJv+Q29WUFICbXj8 -# rOGxWjAdBgNVHQ4EFgQU3CQnBPLvFkovKU/is0/LgQF/49swNAYDVR0RBC0wK6Ap -# BggrBgEFBQcIA6AdMBsMGVVTLVdBU0hJTkdUT04tNjAzIDM4OSAwNjgwDgYDVR0P -# AQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIGZBgNVHR8EgZEwgY4wRaBD -# oEGGP2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9ORVRGb3VuZGF0aW9uUHJvamVj -# dHNDb2RlU2lnbmluZ0NBLmNybDBFoEOgQYY/aHR0cDovL2NybDQuZGlnaWNlcnQu -# Y29tL05FVEZvdW5kYXRpb25Qcm9qZWN0c0NvZGVTaWduaW5nQ0EuY3JsMEwGA1Ud -# IARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRp -# Z2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYIKwYB -# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0 -# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL05FVEZvdW5kYXRpb25Qcm9qZWN0c0Nv -# ZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEB -# AH0FqXX4p5RlC27jX6tcTEf2HT4mAiqosnYU/napWgQE2U9m73IZO+2MuiQWkUQi -# 2PLjbQQcOwfMwkt0SDaSAlfC1zhjZkZb2NcpJRg0cUAjcDzqh6hTzXRVJPD/UrW2 -# a5qBhYnSQDSWbYnVwfAQFFvnQcR5i/xnoOxq7+3LIvHoJafpsxcAFS57Vdsuw91u -# keB6uasOfvdd06Mpl9BLWZHpyEdnPIKMv6ALibTdw9lNzCQ+EmdT5Fwky8wHE8BH -# hhAdjSuGiyd+AzR3IuL96Q41h34c7pL827atOHwkkjUx+QTVkXbYoal6wwBKhi6I -# QJEhT0s/yyFFM7BrLhQpRSsxghDsMIIQ6AIBATBuMFoxCzAJBgNVBAYTAlVTMRgw -# FgYDVQQKEw8uTkVUIEZvdW5kYXRpb24xMTAvBgNVBAMTKC5ORVQgRm91bmRhdGlv -# biBQcm9qZWN0cyBDb2RlIFNpZ25pbmcgQ0ECEAvk5+TPRMVgGaiemDul6FgwDQYJ -# YIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG -# 9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIB -# FTAvBgkqhkiG9w0BCQQxIgQg3M8y2Uovs3GZZGILEzOaZ5eGje/9PkpzoByKnbYv -# zTYwDQYJKoZIhvcNAQEBBQAEggEAnU9xhSBP/Rv/Cgvmzp0LcG8pjxPlbjvPef7V -# xvkeQzTvMHYg0LiveMH1y94PyfBdxqUAGRuCblBOmlECw24w8Hdv8dY2LQMhqJu8 -# Qt08UVYtiJQBHwclFmnK8ER8g/Wc5LPsuvzoYzTvvr/FS2wSCaMV7p2WHIwjJxXR -# sLaccoU2fuPbmE1WVPsjy3ttzkmfyhbl3Mc2QXiLNTQJ+gSNBg2s2eSzf7eDcM2Z -# kysuAEdu4kKhSsHOhkkpBHffplg9pYrehbUq8QJ+T/9e0vHPbbP5xIPaRqvBEMWy -# ydh0LWvK6GnJ+/yPHjddtrjpY4ncTlK5ii+wyzyZc9k4rryOKaGCDsgwgg7EBgor -# BgEEAYI3AwMBMYIOtDCCDrAGCSqGSIb3DQEHAqCCDqEwgg6dAgEDMQ8wDQYJYIZI -# AWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG/WwHATAxMA0G -# CWCGSAFlAwQCAQUABCDIQfwOJSjewgHOlovqbOQetKuLbiiSjibgzLTQTnzqLAIQ -# Lslu2kB0PjG9aO0ua2h6ihgPMjAyMDA3MDEwNzMyNTBaoIILuzCCBoIwggVqoAMC -# AQICEATNP4VornbGG7D+cWDMp20wDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC -# VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 -# LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFt -# cGluZyBDQTAeFw0xOTEwMDEwMDAwMDBaFw0zMDEwMTcwMDAwMDBaMEwxCzAJBgNV -# BAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEkMCIGA1UEAxMbVElNRVNU -# QU1QLVNIQTI1Ni0yMDE5LTEwLTE1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -# CgKCAQEA6WQ1nPqpmGVkG+QX3LgpNsxnCViFTTDgyf/lOzwRKFCvBzHiXQkYwvaJ -# jGkIBCPgdy2dFeW46KFqjv/UrtJ6Fu/4QbUdOXXBzy+nrEV+lG2sAwGZPGI+fnr9 -# RZcxtPq32UI+p1Wb31pPWAKoMmkiE76Lgi3GmKtrm7TJ8mURDHQNsvAIlnTE6LJI -# oqEUpfj64YlwRDuN7/uk9MO5vRQs6wwoJyWAqxBLFhJgC2kijE7NxtWyZVkh4Hws -# Eo1wDo+KyuDT17M5d1DQQiwues6cZ3o4d1RA/0+VBCDU68jOhxQI/h2A3dDnK3jq -# vx9wxu5CFlM2RZtTGUlinXoCm5UUowIDAQABo4IDODCCAzQwDgYDVR0PAQH/BAQD -# AgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwggG/BgNV -# HSAEggG2MIIBsjCCAaEGCWCGSAGG/WwHATCCAZIwKAYIKwYBBQUHAgEWHGh0dHBz -# Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwggFkBggrBgEFBQcCAjCCAVYeggFSAEEA -# bgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEA -# dABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMA -# ZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMA -# IABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEA -# ZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEA -# YgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEA -# dABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4w -# CwYJYIZIAYb9bAMVMB8GA1UdIwQYMBaAFPS24SAd/imu0uRhpbKiJbLIFzVuMB0G -# A1UdDgQWBBRWUw/BxgenTdfYbldygFBM5OyewTBxBgNVHR8EajBoMDKgMKAuhixo -# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDAyoDCg -# LoYsaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmww -# gYUGCCsGAQUFBwEBBHkwdzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl -# cnQuY29tME8GCCsGAQUFBzAChkNodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v -# RGlnaUNlcnRTSEEyQXNzdXJlZElEVGltZXN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3 -# DQEBCwUAA4IBAQAug6FEBUoE47kyUvrZgfAau/gJjSO5PdiSoeZGHEovbno8Y243 -# F6Mav1gjskOclINOOQmwLOjH4eLM7ct5a87eIwFH7ZVUgeCAexKxrwKGqTpzav74 -# n8GN0SGM5CmCw4oLYAACnR9HxJ+0CmhTf1oQpvgi5vhTkjFf2IKDLW0TQq6DwRBO -# pCT0R5zeDyJyd1x/T+k5mCtXkkTX726T2UPHBDNjUTdWnkcEEcOjWFQh2OKOVtdJ -# P1f8Cp8jXnv0lI3dnRq733oqptJFplUMj/ZMivKWz4lG3DGykZCjXzMwYFX1/Gsw -# rKHt5EdOM55naii1TcLtW5eC+MupCGxTCbT3MIIFMTCCBBmgAwIBAgIQCqEl1tYy -# G35B5AXaNpfCFTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UE -# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD -# VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTYwMTA3MTIwMDAw -# WhcNMzEwMTA3MTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl -# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdp -# Q2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMIIBIjANBgkqhkiG -# 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdAy7kvNj3/dqbqCmcU5VChXtiNKxA4HRTNR -# EH3Q+X1NaH7ntqD0jbOI5Je/YyGQmL8TvFfTw+F+CNZqFAA49y4eO+7MpvYyWf5f -# ZT/gm+vjRkcGGlV+Cyd+wKL1oODeIj8O/36V+/OjuiI+GKwR5PCZA207hXwJ0+5d -# yJoLVOOoCXFr4M8iEA91z3FyTgqt30A6XLdR4aF5FMZNJCMwXbzsPGBqrC8HzP3w -# 6kfZiFBe/WZuVmEnKYmEUeaC50ZQ/ZQqLKfkdT66mA+Ef58xFNat1fJky3seBdCE -# GXIX8RcG7z3N1k3vBkL9olMqT4UdxB08r8/arBD13ays6Vb/kwIDAQABo4IBzjCC -# AcowHQYDVR0OBBYEFPS24SAd/imu0uRhpbKiJbLIFzVuMB8GA1UdIwQYMBaAFEXr -# oq/0ksuCMS1Ri6enIZ3zbcgPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/ -# BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHkGCCsGAQUFBwEBBG0wazAkBggr -# BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdo -# dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290 -# Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5j -# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3Js -# My5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMFAGA1Ud -# IARJMEcwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k -# aWdpY2VydC5jb20vQ1BTMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAQEA -# cZUS6VGHVmnN793afKpjerN4zwY3QITvS4S/ys8DAv3Fp8MOIEIsr3fzKx8MIVoq -# twU0HWqumfgnoma/Capg33akOpMP+LLR2HwZYuhegiUexLoceywh4tZbLBQ1QwRo -# stt1AuByx5jWPGTlH0gQGF+JOGFNYkYkh2OMkVIsrymJ5Xgf1gsUpYDXEkdws3XV -# k4WTfraSZ/tTYYmo9WuWwPRYaQ18yAGxuSh1t5ljhSKMYcp5lH5Z/IwP42+1ASa2 -# bKXuh1Eh5Fhgm7oMLSttosR+u8QlK0cCCHxJrhO24XxCQijGGFbPQTS2Zl22dHv1 -# VjMiLyI2skuiSpXY9aaOUjGCAk0wggJJAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUw -# EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x -# MTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcg -# Q0ECEATNP4VornbGG7D+cWDMp20wDQYJYIZIAWUDBAIBBQCggZgwGgYJKoZIhvcN -# AQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yMDA3MDEwNzMyNTBa -# MCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFAMlvVBe2pYwLcIvT6AeTCi+KDTFMC8G -# CSqGSIb3DQEJBDEiBCBZP0neSFIOf2DML1gBlDUAamr0e9Eq+Rq4UdHiDVVe4zAN -# BgkqhkiG9w0BAQEFAASCAQAxWJwxav4qQZCfWAiXHhmKYZ8QwsfDeyq3kuGvhN1K -# muulWU/5dlfTaQQ8wF5BKdqHm31YJfAwRqQmJyuuh66Fx7pmRTvD5zQaH2NKaW73 -# hKZawvMaQlrMj4vT2WuepoQbQEyjhpunPXgF4vf8Gmr787CbY6D/M2v9in1MFjRV -# HJk2+rgQmH+hfg7vgLbzHHGFMQ25K6F0/61r9MXq7NzT3iksdGYh7wM7q0KWxy5Q -# blWF6iyA39GqTC6ZghHWIL+lWv7JKtzjwF06U+PaqCvZMhzlFNxYFYiKJ7uqcSf5 -# zroMfSjDExq6ZFVH9qiiXEA4RXrN5cDwXjwKnOdYD5dA -# SIG # End signature block diff --git a/aspnetcore/src/identityserver/wwwroot/css/site.css b/aspnetcore/src/identityserver/wwwroot/css/site.css deleted file mode 100644 index 3679b659..00000000 --- a/aspnetcore/src/identityserver/wwwroot/css/site.css +++ /dev/null @@ -1,24 +0,0 @@ -.body-container { - margin-top: 60px; - padding-bottom: 40px; } - -.welcome-page li { - list-style: none; - padding: 4px; } - -.logged-out-page iframe { - display: none; - width: 0; - height: 0; } - -.grants-page .card { - margin-top: 20px; - border-bottom: 1px solid lightgray; } - .grants-page .card .card-title { - font-size: 120%; - font-weight: bold; } - .grants-page .card .card-title img { - width: 100px; - height: 100px; } - .grants-page .card label { - font-weight: bold; } diff --git a/aspnetcore/src/identityserver/wwwroot/css/site.min.css b/aspnetcore/src/identityserver/wwwroot/css/site.min.css deleted file mode 100644 index cc3fdade..00000000 --- a/aspnetcore/src/identityserver/wwwroot/css/site.min.css +++ /dev/null @@ -1 +0,0 @@ -.body-container{margin-top:60px;padding-bottom:40px;}.welcome-page li{list-style:none;padding:4px;}.logged-out-page iframe{display:none;width:0;height:0;}.grants-page .card{margin-top:20px;border-bottom:1px solid #d3d3d3;}.grants-page .card .card-title{font-size:120%;font-weight:bold;}.grants-page .card .card-title img{width:100px;height:100px;}.grants-page .card label{font-weight:bold;} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/css/site.scss b/aspnetcore/src/identityserver/wwwroot/css/site.scss deleted file mode 100644 index 48166e33..00000000 --- a/aspnetcore/src/identityserver/wwwroot/css/site.scss +++ /dev/null @@ -1,42 +0,0 @@ -.body-container { - margin-top: 60px; - padding-bottom:40px; -} - -.welcome-page { - li { - list-style: none; - padding: 4px; - } -} - -.logged-out-page { - iframe { - display: none; - width: 0; - height: 0; - } -} - -.grants-page { - .card { - margin-top: 20px; - border-bottom: 1px solid lightgray; - - .card-title { - img { - width: 100px; - height: 100px; - } - - font-size: 120%; - font-weight: bold; - } - - label { - font-weight: bold; - } - } -} - - diff --git a/aspnetcore/src/identityserver/wwwroot/favicon.ico b/aspnetcore/src/identityserver/wwwroot/favicon.ico deleted file mode 100644 index ee470e42facff8210727fb7caca55407becc0135..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmZuwTS!z<6g@hck8yM!&Zsl@&OLKS$1=k5M+Y>f(jbcrA}oV`>cgK33H&H}Kv5(Z zN?N|n2$JeUixi;;r6mj%rB)CVRD?uSls_Xy?$%Z>X?ccq_ugx-bV(Vh-K0jAfHK4LS;MUkI;{y8Cq>Dr=dFzQ>w!tkQ3dZ1P|rQYw^H$vw5E8tR&G+4dLc?m z^I|{uc2@3gaHePSd%pq-m_sD#un%$0!_`UMT1%AFOSEWx?yV1%ZqR)>{B7O<7UmFt zOSk(Q=R9)ysN2&tP452x{Wfd5$DO4beuee$*Yf{2$A}TG_dlkFxE3O%?N>F|etU99 zXkF<*Z|}7&M-Cswnx-*$`2EYd&jWd*%jKpLnKrz=b?kE74WhEfXJ>=D^RC10B1ovI zsd?|g12r-sK6xDa0ibQxJpWK^md&dy;!I)vSYJ{f(YB^=Pq8Oq+?k$rD-g&##wB6I?{Pz) m)CkcK#5)uTp%)7gn1?Qf;LB5l6}{+nK{L8Wh<7K2VDBGM+S%6t diff --git a/aspnetcore/src/identityserver/wwwroot/icon.jpg b/aspnetcore/src/identityserver/wwwroot/icon.jpg deleted file mode 100644 index e6525028aca7ed7a339915a3986c38034e31ab1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19482 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 diff --git a/aspnetcore/src/identityserver/wwwroot/icon.png b/aspnetcore/src/identityserver/wwwroot/icon.png deleted file mode 100644 index cd386d5170151f6fb166bf1eab2486b1ffb3a421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20796 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 - - Bootstrap logo - -

    - -

    Bootstrap

    - -

    - Sleek, intuitive, and powerful front-end framework for faster and easier web development. -
    - Explore Bootstrap docs » -
    -
    - Report bug - · - Request feature - · - Themes - · - Blog -

    - - -## Table of contents - -- [Quick start](#quick-start) -- [Status](#status) -- [What's included](#whats-included) -- [Bugs and feature requests](#bugs-and-feature-requests) -- [Documentation](#documentation) -- [Contributing](#contributing) -- [Community](#community) -- [Versioning](#versioning) -- [Creators](#creators) -- [Thanks](#thanks) -- [Copyright and license](#copyright-and-license) - - -## Quick start - -Several quick start options are available: - -- [Download the latest release.](https://github.com/twbs/bootstrap/archive/v4.4.1.zip) -- Clone the repo: `git clone https://github.com/twbs/bootstrap.git` -- Install with [npm](https://www.npmjs.com/): `npm install bootstrap` -- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@4.4.1` -- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:4.4.1` -- Install with [NuGet](https://www.nuget.org/): CSS: `Install-Package bootstrap` Sass: `Install-Package bootstrap.sass` - -Read the [Getting started page](https://getbootstrap.com/docs/4.4/getting-started/introduction/) for information on the framework contents, templates and examples, and more. - - -## Status - -[![Slack](https://bootstrap-slack.herokuapp.com/badge.svg)](https://bootstrap-slack.herokuapp.com/) -[![Build Status](https://github.com/twbs/bootstrap/workflows/Tests/badge.svg)](https://github.com/twbs/bootstrap/actions?workflow=Tests) -[![npm version](https://img.shields.io/npm/v/bootstrap.svg)](https://www.npmjs.com/package/bootstrap) -[![Gem version](https://img.shields.io/gem/v/bootstrap.svg)](https://rubygems.org/gems/bootstrap) -[![Meteor Atmosphere](https://img.shields.io/badge/meteor-twbs%3Abootstrap-blue.svg)](https://atmospherejs.com/twbs/bootstrap) -[![Packagist Prerelease](https://img.shields.io/packagist/vpre/twbs/bootstrap.svg)](https://packagist.org/packages/twbs/bootstrap) -[![NuGet](https://img.shields.io/nuget/vpre/bootstrap.svg)](https://www.nuget.org/packages/bootstrap/absoluteLatest) -[![peerDependencies Status](https://img.shields.io/david/peer/twbs/bootstrap.svg)](https://david-dm.org/twbs/bootstrap?type=peer) -[![devDependency Status](https://img.shields.io/david/dev/twbs/bootstrap.svg)](https://david-dm.org/twbs/bootstrap?type=dev) -[![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/v4-dev.svg)](https://coveralls.io/github/twbs/bootstrap?branch=v4-dev) -[![CSS gzip size](https://img.badgesize.io/twbs/bootstrap/v4-dev/dist/css/bootstrap.min.css?compression=gzip&label=CSS+gzip+size)](https://github.com/twbs/bootstrap/tree/v4-dev/dist/css/bootstrap.min.css) -[![JS gzip size](https://img.badgesize.io/twbs/bootstrap/v4-dev/dist/js/bootstrap.min.js?compression=gzip&label=JS+gzip+size)](https://github.com/twbs/bootstrap/tree/v4-dev/dist/js/bootstrap.min.js) -[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)](https://www.browserstack.com/automate/public-build/SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229) -[![Backers on Open Collective](https://opencollective.com/bootstrap/backers/badge.svg)](#backers) -[![Sponsors on Open Collective](https://opencollective.com/bootstrap/sponsors/badge.svg)](#sponsors) - - -## What's included - -Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this: - -```text -bootstrap/ -└── dist/ - ├── css/ - │ ├── bootstrap-grid.css - │ ├── bootstrap-grid.css.map - │ ├── bootstrap-grid.min.css - │ ├── bootstrap-grid.min.css.map - │ ├── bootstrap-reboot.css - │ ├── bootstrap-reboot.css.map - │ ├── bootstrap-reboot.min.css - │ ├── bootstrap-reboot.min.css.map - │ ├── bootstrap.css - │ ├── bootstrap.css.map - │ ├── bootstrap.min.css - │ └── bootstrap.min.css.map - └── js/ - ├── bootstrap.bundle.js - ├── bootstrap.bundle.js.map - ├── bootstrap.bundle.min.js - ├── bootstrap.bundle.min.js.map - ├── bootstrap.js - ├── bootstrap.js.map - ├── bootstrap.min.js - └── bootstrap.min.js.map -``` - -We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). [source maps](https://developers.google.com/web/tools/chrome-devtools/javascript/source-maps) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Bundled JS files (`bootstrap.bundle.js` and minified `bootstrap.bundle.min.js`) include [Popper](https://popper.js.org/), but not [jQuery](https://jquery.com/). - - -## Bugs and feature requests - -Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new). - - -## Documentation - -Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](https://jekyllrb.com/) and publicly hosted on GitHub Pages at . The docs may also be run locally. - -Documentation search is powered by [Algolia's DocSearch](https://community.algolia.com/docsearch/). Working on our search? Be sure to set `debug: true` in `site/docs/4.4/assets/js/src/search.js` file. - -### Running documentation locally - -1. Run through the [tooling setup](https://getbootstrap.com/docs/4.4/getting-started/build-tools/#tooling-setup) to install Jekyll (the site builder) and other Ruby dependencies with `bundle install`. -2. Run `npm install` to install Node.js dependencies. -3. Run `npm start` to compile CSS and JavaScript files, generate our docs, and watch for changes. -4. Open `http://localhost:9001` in your browser, and voilà. - -Learn more about using Jekyll by reading its [documentation](https://jekyllrb.com/docs/). - -### Documentation for previous releases - -You can find all our previous releases docs on . - -[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download. - - -## Contributing - -Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. - -Moreover, if your pull request contains JavaScript patches or features, you must include [relevant unit tests](https://github.com/twbs/bootstrap/tree/master/js/tests). All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo). - -Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at . - - -## Community - -Get updates on Bootstrap's development and chat with the project maintainers and community members. - -- Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap). -- Read and subscribe to [The Official Bootstrap Blog](https://blog.getbootstrap.com/). -- Join [the official Slack room](https://bootstrap-slack.herokuapp.com/). -- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel. -- Implementation help may be found at Stack Overflow (tagged [`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4)). -- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability. - - -## Versioning - -For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](https://semver.org/). Sometimes we screw up, but we adhere to those rules whenever possible. - -See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap. Release announcement posts on [the official Bootstrap blog](https://blog.getbootstrap.com/) contain summaries of the most noteworthy changes made in each release. - - -## Creators - -**Mark Otto** - -- -- - -**Jacob Thornton** - -- -- - - -## Thanks - - - BrowserStack Logo - - -Thanks to [BrowserStack](https://www.browserstack.com/) for providing the infrastructure that allows us to test in real browsers! - - -## Backers - -Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/bootstrap#backer)] - -[![Bakers](https://opencollective.com/bootstrap/backers.svg?width=890)](https://opencollective.com/bootstrap#backers) - - -## Sponsors - -Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/bootstrap#sponsor)] - -[![](https://opencollective.com/bootstrap/sponsor/0/avatar.svg)](https://opencollective.com/bootstrap/sponsor/0/website) -[![](https://opencollective.com/bootstrap/sponsor/1/avatar.svg)](https://opencollective.com/bootstrap/sponsor/1/website) -[![](https://opencollective.com/bootstrap/sponsor/2/avatar.svg)](https://opencollective.com/bootstrap/sponsor/2/website) -[![](https://opencollective.com/bootstrap/sponsor/3/avatar.svg)](https://opencollective.com/bootstrap/sponsor/3/website) -[![](https://opencollective.com/bootstrap/sponsor/4/avatar.svg)](https://opencollective.com/bootstrap/sponsor/4/website) -[![](https://opencollective.com/bootstrap/sponsor/5/avatar.svg)](https://opencollective.com/bootstrap/sponsor/5/website) -[![](https://opencollective.com/bootstrap/sponsor/6/avatar.svg)](https://opencollective.com/bootstrap/sponsor/6/website) -[![](https://opencollective.com/bootstrap/sponsor/7/avatar.svg)](https://opencollective.com/bootstrap/sponsor/7/website) -[![](https://opencollective.com/bootstrap/sponsor/8/avatar.svg)](https://opencollective.com/bootstrap/sponsor/8/website) -[![](https://opencollective.com/bootstrap/sponsor/9/avatar.svg)](https://opencollective.com/bootstrap/sponsor/9/website) - - -## Copyright and license - -Code and documentation copyright 2011-2019 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors) and [Twitter, Inc.](https://twitter.com) Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/). diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css deleted file mode 100644 index dadf02d3..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css +++ /dev/null @@ -1,3899 +0,0 @@ -/*! - * Bootstrap Grid v4.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -html { - box-sizing: border-box; - -ms-overflow-style: scrollbar; -} - -*, -*::before, -*::after { - box-sizing: inherit; -} - -.container { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container { - max-width: 540px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 720px; - } -} - -@media (min-width: 992px) { - .container { - max-width: 960px; - } -} - -@media (min-width: 1200px) { - .container { - max-width: 1140px; - } -} - -.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container, .container-sm { - max-width: 540px; - } -} - -@media (min-width: 768px) { - .container, .container-sm, .container-md { - max-width: 720px; - } -} - -@media (min-width: 992px) { - .container, .container-sm, .container-md, .container-lg { - max-width: 960px; - } -} - -@media (min-width: 1200px) { - .container, .container-sm, .container-md, .container-lg, .container-xl { - max-width: 1140px; - } -} - -.row { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -15px; - margin-left: -15px; -} - -.no-gutters { - margin-right: 0; - margin-left: 0; -} - -.no-gutters > .col, -.no-gutters > [class*="col-"] { - padding-right: 0; - padding-left: 0; -} - -.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, -.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, -.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, -.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, -.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, -.col-xl-auto { - position: relative; - width: 100%; - padding-right: 15px; - padding-left: 15px; -} - -.col { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; -} - -.row-cols-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.row-cols-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.row-cols-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; -} - -.row-cols-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.row-cols-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; -} - -.row-cols-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; -} - -.col-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; -} - -.col-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; -} - -.col-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; -} - -.col-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.col-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; -} - -.col-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; -} - -.col-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.col-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; -} - -.col-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; -} - -.col-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; -} - -.col-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; -} - -.col-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; -} - -.col-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.order-first { - -ms-flex-order: -1; - order: -1; -} - -.order-last { - -ms-flex-order: 13; - order: 13; -} - -.order-0 { - -ms-flex-order: 0; - order: 0; -} - -.order-1 { - -ms-flex-order: 1; - order: 1; -} - -.order-2 { - -ms-flex-order: 2; - order: 2; -} - -.order-3 { - -ms-flex-order: 3; - order: 3; -} - -.order-4 { - -ms-flex-order: 4; - order: 4; -} - -.order-5 { - -ms-flex-order: 5; - order: 5; -} - -.order-6 { - -ms-flex-order: 6; - order: 6; -} - -.order-7 { - -ms-flex-order: 7; - order: 7; -} - -.order-8 { - -ms-flex-order: 8; - order: 8; -} - -.order-9 { - -ms-flex-order: 9; - order: 9; -} - -.order-10 { - -ms-flex-order: 10; - order: 10; -} - -.order-11 { - -ms-flex-order: 11; - order: 11; -} - -.order-12 { - -ms-flex-order: 12; - order: 12; -} - -.offset-1 { - margin-left: 8.333333%; -} - -.offset-2 { - margin-left: 16.666667%; -} - -.offset-3 { - margin-left: 25%; -} - -.offset-4 { - margin-left: 33.333333%; -} - -.offset-5 { - margin-left: 41.666667%; -} - -.offset-6 { - margin-left: 50%; -} - -.offset-7 { - margin-left: 58.333333%; -} - -.offset-8 { - margin-left: 66.666667%; -} - -.offset-9 { - margin-left: 75%; -} - -.offset-10 { - margin-left: 83.333333%; -} - -.offset-11 { - margin-left: 91.666667%; -} - -@media (min-width: 576px) { - .col-sm { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-sm-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-sm-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-sm-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-sm-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-sm-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-sm-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-sm-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-sm-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-sm-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-sm-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-sm-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-sm-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-sm-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-sm-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-sm-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-sm-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-sm-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-sm-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-sm-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-sm-first { - -ms-flex-order: -1; - order: -1; - } - .order-sm-last { - -ms-flex-order: 13; - order: 13; - } - .order-sm-0 { - -ms-flex-order: 0; - order: 0; - } - .order-sm-1 { - -ms-flex-order: 1; - order: 1; - } - .order-sm-2 { - -ms-flex-order: 2; - order: 2; - } - .order-sm-3 { - -ms-flex-order: 3; - order: 3; - } - .order-sm-4 { - -ms-flex-order: 4; - order: 4; - } - .order-sm-5 { - -ms-flex-order: 5; - order: 5; - } - .order-sm-6 { - -ms-flex-order: 6; - order: 6; - } - .order-sm-7 { - -ms-flex-order: 7; - order: 7; - } - .order-sm-8 { - -ms-flex-order: 8; - order: 8; - } - .order-sm-9 { - -ms-flex-order: 9; - order: 9; - } - .order-sm-10 { - -ms-flex-order: 10; - order: 10; - } - .order-sm-11 { - -ms-flex-order: 11; - order: 11; - } - .order-sm-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-sm-0 { - margin-left: 0; - } - .offset-sm-1 { - margin-left: 8.333333%; - } - .offset-sm-2 { - margin-left: 16.666667%; - } - .offset-sm-3 { - margin-left: 25%; - } - .offset-sm-4 { - margin-left: 33.333333%; - } - .offset-sm-5 { - margin-left: 41.666667%; - } - .offset-sm-6 { - margin-left: 50%; - } - .offset-sm-7 { - margin-left: 58.333333%; - } - .offset-sm-8 { - margin-left: 66.666667%; - } - .offset-sm-9 { - margin-left: 75%; - } - .offset-sm-10 { - margin-left: 83.333333%; - } - .offset-sm-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 768px) { - .col-md { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-md-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-md-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-md-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-md-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-md-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-md-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-md-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-md-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-md-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-md-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-md-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-md-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-md-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-md-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-md-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-md-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-md-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-md-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-md-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-md-first { - -ms-flex-order: -1; - order: -1; - } - .order-md-last { - -ms-flex-order: 13; - order: 13; - } - .order-md-0 { - -ms-flex-order: 0; - order: 0; - } - .order-md-1 { - -ms-flex-order: 1; - order: 1; - } - .order-md-2 { - -ms-flex-order: 2; - order: 2; - } - .order-md-3 { - -ms-flex-order: 3; - order: 3; - } - .order-md-4 { - -ms-flex-order: 4; - order: 4; - } - .order-md-5 { - -ms-flex-order: 5; - order: 5; - } - .order-md-6 { - -ms-flex-order: 6; - order: 6; - } - .order-md-7 { - -ms-flex-order: 7; - order: 7; - } - .order-md-8 { - -ms-flex-order: 8; - order: 8; - } - .order-md-9 { - -ms-flex-order: 9; - order: 9; - } - .order-md-10 { - -ms-flex-order: 10; - order: 10; - } - .order-md-11 { - -ms-flex-order: 11; - order: 11; - } - .order-md-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-md-0 { - margin-left: 0; - } - .offset-md-1 { - margin-left: 8.333333%; - } - .offset-md-2 { - margin-left: 16.666667%; - } - .offset-md-3 { - margin-left: 25%; - } - .offset-md-4 { - margin-left: 33.333333%; - } - .offset-md-5 { - margin-left: 41.666667%; - } - .offset-md-6 { - margin-left: 50%; - } - .offset-md-7 { - margin-left: 58.333333%; - } - .offset-md-8 { - margin-left: 66.666667%; - } - .offset-md-9 { - margin-left: 75%; - } - .offset-md-10 { - margin-left: 83.333333%; - } - .offset-md-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 992px) { - .col-lg { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-lg-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-lg-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-lg-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-lg-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-lg-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-lg-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-lg-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-lg-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-lg-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-lg-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-lg-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-lg-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-lg-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-lg-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-lg-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-lg-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-lg-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-lg-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-lg-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-lg-first { - -ms-flex-order: -1; - order: -1; - } - .order-lg-last { - -ms-flex-order: 13; - order: 13; - } - .order-lg-0 { - -ms-flex-order: 0; - order: 0; - } - .order-lg-1 { - -ms-flex-order: 1; - order: 1; - } - .order-lg-2 { - -ms-flex-order: 2; - order: 2; - } - .order-lg-3 { - -ms-flex-order: 3; - order: 3; - } - .order-lg-4 { - -ms-flex-order: 4; - order: 4; - } - .order-lg-5 { - -ms-flex-order: 5; - order: 5; - } - .order-lg-6 { - -ms-flex-order: 6; - order: 6; - } - .order-lg-7 { - -ms-flex-order: 7; - order: 7; - } - .order-lg-8 { - -ms-flex-order: 8; - order: 8; - } - .order-lg-9 { - -ms-flex-order: 9; - order: 9; - } - .order-lg-10 { - -ms-flex-order: 10; - order: 10; - } - .order-lg-11 { - -ms-flex-order: 11; - order: 11; - } - .order-lg-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-lg-0 { - margin-left: 0; - } - .offset-lg-1 { - margin-left: 8.333333%; - } - .offset-lg-2 { - margin-left: 16.666667%; - } - .offset-lg-3 { - margin-left: 25%; - } - .offset-lg-4 { - margin-left: 33.333333%; - } - .offset-lg-5 { - margin-left: 41.666667%; - } - .offset-lg-6 { - margin-left: 50%; - } - .offset-lg-7 { - margin-left: 58.333333%; - } - .offset-lg-8 { - margin-left: 66.666667%; - } - .offset-lg-9 { - margin-left: 75%; - } - .offset-lg-10 { - margin-left: 83.333333%; - } - .offset-lg-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 1200px) { - .col-xl { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-xl-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-xl-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-xl-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-xl-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-xl-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-xl-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-xl-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-xl-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-xl-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-xl-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-xl-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-xl-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-xl-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-xl-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-xl-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-xl-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-xl-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-xl-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-xl-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-xl-first { - -ms-flex-order: -1; - order: -1; - } - .order-xl-last { - -ms-flex-order: 13; - order: 13; - } - .order-xl-0 { - -ms-flex-order: 0; - order: 0; - } - .order-xl-1 { - -ms-flex-order: 1; - order: 1; - } - .order-xl-2 { - -ms-flex-order: 2; - order: 2; - } - .order-xl-3 { - -ms-flex-order: 3; - order: 3; - } - .order-xl-4 { - -ms-flex-order: 4; - order: 4; - } - .order-xl-5 { - -ms-flex-order: 5; - order: 5; - } - .order-xl-6 { - -ms-flex-order: 6; - order: 6; - } - .order-xl-7 { - -ms-flex-order: 7; - order: 7; - } - .order-xl-8 { - -ms-flex-order: 8; - order: 8; - } - .order-xl-9 { - -ms-flex-order: 9; - order: 9; - } - .order-xl-10 { - -ms-flex-order: 10; - order: 10; - } - .order-xl-11 { - -ms-flex-order: 11; - order: 11; - } - .order-xl-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-xl-0 { - margin-left: 0; - } - .offset-xl-1 { - margin-left: 8.333333%; - } - .offset-xl-2 { - margin-left: 16.666667%; - } - .offset-xl-3 { - margin-left: 25%; - } - .offset-xl-4 { - margin-left: 33.333333%; - } - .offset-xl-5 { - margin-left: 41.666667%; - } - .offset-xl-6 { - margin-left: 50%; - } - .offset-xl-7 { - margin-left: 58.333333%; - } - .offset-xl-8 { - margin-left: 66.666667%; - } - .offset-xl-9 { - margin-left: 75%; - } - .offset-xl-10 { - margin-left: 83.333333%; - } - .offset-xl-11 { - margin-left: 91.666667%; - } -} - -.d-none { - display: none !important; -} - -.d-inline { - display: inline !important; -} - -.d-inline-block { - display: inline-block !important; -} - -.d-block { - display: block !important; -} - -.d-table { - display: table !important; -} - -.d-table-row { - display: table-row !important; -} - -.d-table-cell { - display: table-cell !important; -} - -.d-flex { - display: -ms-flexbox !important; - display: flex !important; -} - -.d-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; -} - -@media (min-width: 576px) { - .d-sm-none { - display: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-sm-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 768px) { - .d-md-none { - display: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-md-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 992px) { - .d-lg-none { - display: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-lg-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 1200px) { - .d-xl-none { - display: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-xl-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media print { - .d-print-none { - display: none !important; - } - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-print-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -.flex-row { - -ms-flex-direction: row !important; - flex-direction: row !important; -} - -.flex-column { - -ms-flex-direction: column !important; - flex-direction: column !important; -} - -.flex-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; -} - -.flex-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; -} - -.flex-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; -} - -.flex-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; -} - -.flex-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; -} - -.flex-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; -} - -.flex-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; -} - -.flex-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; -} - -.flex-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; -} - -.flex-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; -} - -.justify-content-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; -} - -.justify-content-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; -} - -.justify-content-center { - -ms-flex-pack: center !important; - justify-content: center !important; -} - -.justify-content-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; -} - -.justify-content-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; -} - -.align-items-start { - -ms-flex-align: start !important; - align-items: flex-start !important; -} - -.align-items-end { - -ms-flex-align: end !important; - align-items: flex-end !important; -} - -.align-items-center { - -ms-flex-align: center !important; - align-items: center !important; -} - -.align-items-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; -} - -.align-items-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; -} - -.align-content-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; -} - -.align-content-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; -} - -.align-content-center { - -ms-flex-line-pack: center !important; - align-content: center !important; -} - -.align-content-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; -} - -.align-content-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; -} - -.align-content-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; -} - -.align-self-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; -} - -.align-self-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; -} - -.align-self-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; -} - -.align-self-center { - -ms-flex-item-align: center !important; - align-self: center !important; -} - -.align-self-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; -} - -.align-self-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; -} - -@media (min-width: 576px) { - .flex-sm-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-sm-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-sm-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-sm-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-sm-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-sm-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-sm-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-sm-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-sm-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-sm-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-sm-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-sm-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-sm-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-sm-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-sm-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-sm-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-sm-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-sm-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-sm-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-sm-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-sm-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-sm-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-sm-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-sm-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-sm-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-sm-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-sm-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-sm-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 768px) { - .flex-md-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-md-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-md-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-md-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-md-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-md-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-md-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-md-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-md-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-md-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-md-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-md-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-md-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-md-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-md-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-md-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-md-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-md-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-md-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-md-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-md-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-md-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-md-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-md-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-md-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-md-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-md-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-md-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-md-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-md-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 992px) { - .flex-lg-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-lg-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-lg-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-lg-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-lg-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-lg-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-lg-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-lg-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-lg-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-lg-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-lg-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-lg-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-lg-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-lg-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-lg-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-lg-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-lg-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-lg-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-lg-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-lg-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-lg-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-lg-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-lg-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-lg-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-lg-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-lg-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-lg-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-lg-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 1200px) { - .flex-xl-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-xl-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-xl-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-xl-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-xl-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-xl-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-xl-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-xl-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-xl-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-xl-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-xl-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-xl-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-xl-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-xl-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-xl-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-xl-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-xl-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-xl-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-xl-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-xl-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-xl-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-xl-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-xl-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-xl-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-xl-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-xl-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-xl-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-xl-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -.m-0 { - margin: 0 !important; -} - -.mt-0, -.my-0 { - margin-top: 0 !important; -} - -.mr-0, -.mx-0 { - margin-right: 0 !important; -} - -.mb-0, -.my-0 { - margin-bottom: 0 !important; -} - -.ml-0, -.mx-0 { - margin-left: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.mt-1, -.my-1 { - margin-top: 0.25rem !important; -} - -.mr-1, -.mx-1 { - margin-right: 0.25rem !important; -} - -.mb-1, -.my-1 { - margin-bottom: 0.25rem !important; -} - -.ml-1, -.mx-1 { - margin-left: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.mt-2, -.my-2 { - margin-top: 0.5rem !important; -} - -.mr-2, -.mx-2 { - margin-right: 0.5rem !important; -} - -.mb-2, -.my-2 { - margin-bottom: 0.5rem !important; -} - -.ml-2, -.mx-2 { - margin-left: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.mt-3, -.my-3 { - margin-top: 1rem !important; -} - -.mr-3, -.mx-3 { - margin-right: 1rem !important; -} - -.mb-3, -.my-3 { - margin-bottom: 1rem !important; -} - -.ml-3, -.mx-3 { - margin-left: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.mt-4, -.my-4 { - margin-top: 1.5rem !important; -} - -.mr-4, -.mx-4 { - margin-right: 1.5rem !important; -} - -.mb-4, -.my-4 { - margin-bottom: 1.5rem !important; -} - -.ml-4, -.mx-4 { - margin-left: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.mt-5, -.my-5 { - margin-top: 3rem !important; -} - -.mr-5, -.mx-5 { - margin-right: 3rem !important; -} - -.mb-5, -.my-5 { - margin-bottom: 3rem !important; -} - -.ml-5, -.mx-5 { - margin-left: 3rem !important; -} - -.p-0 { - padding: 0 !important; -} - -.pt-0, -.py-0 { - padding-top: 0 !important; -} - -.pr-0, -.px-0 { - padding-right: 0 !important; -} - -.pb-0, -.py-0 { - padding-bottom: 0 !important; -} - -.pl-0, -.px-0 { - padding-left: 0 !important; -} - -.p-1 { - padding: 0.25rem !important; -} - -.pt-1, -.py-1 { - padding-top: 0.25rem !important; -} - -.pr-1, -.px-1 { - padding-right: 0.25rem !important; -} - -.pb-1, -.py-1 { - padding-bottom: 0.25rem !important; -} - -.pl-1, -.px-1 { - padding-left: 0.25rem !important; -} - -.p-2 { - padding: 0.5rem !important; -} - -.pt-2, -.py-2 { - padding-top: 0.5rem !important; -} - -.pr-2, -.px-2 { - padding-right: 0.5rem !important; -} - -.pb-2, -.py-2 { - padding-bottom: 0.5rem !important; -} - -.pl-2, -.px-2 { - padding-left: 0.5rem !important; -} - -.p-3 { - padding: 1rem !important; -} - -.pt-3, -.py-3 { - padding-top: 1rem !important; -} - -.pr-3, -.px-3 { - padding-right: 1rem !important; -} - -.pb-3, -.py-3 { - padding-bottom: 1rem !important; -} - -.pl-3, -.px-3 { - padding-left: 1rem !important; -} - -.p-4 { - padding: 1.5rem !important; -} - -.pt-4, -.py-4 { - padding-top: 1.5rem !important; -} - -.pr-4, -.px-4 { - padding-right: 1.5rem !important; -} - -.pb-4, -.py-4 { - padding-bottom: 1.5rem !important; -} - -.pl-4, -.px-4 { - padding-left: 1.5rem !important; -} - -.p-5 { - padding: 3rem !important; -} - -.pt-5, -.py-5 { - padding-top: 3rem !important; -} - -.pr-5, -.px-5 { - padding-right: 3rem !important; -} - -.pb-5, -.py-5 { - padding-bottom: 3rem !important; -} - -.pl-5, -.px-5 { - padding-left: 3rem !important; -} - -.m-n1 { - margin: -0.25rem !important; -} - -.mt-n1, -.my-n1 { - margin-top: -0.25rem !important; -} - -.mr-n1, -.mx-n1 { - margin-right: -0.25rem !important; -} - -.mb-n1, -.my-n1 { - margin-bottom: -0.25rem !important; -} - -.ml-n1, -.mx-n1 { - margin-left: -0.25rem !important; -} - -.m-n2 { - margin: -0.5rem !important; -} - -.mt-n2, -.my-n2 { - margin-top: -0.5rem !important; -} - -.mr-n2, -.mx-n2 { - margin-right: -0.5rem !important; -} - -.mb-n2, -.my-n2 { - margin-bottom: -0.5rem !important; -} - -.ml-n2, -.mx-n2 { - margin-left: -0.5rem !important; -} - -.m-n3 { - margin: -1rem !important; -} - -.mt-n3, -.my-n3 { - margin-top: -1rem !important; -} - -.mr-n3, -.mx-n3 { - margin-right: -1rem !important; -} - -.mb-n3, -.my-n3 { - margin-bottom: -1rem !important; -} - -.ml-n3, -.mx-n3 { - margin-left: -1rem !important; -} - -.m-n4 { - margin: -1.5rem !important; -} - -.mt-n4, -.my-n4 { - margin-top: -1.5rem !important; -} - -.mr-n4, -.mx-n4 { - margin-right: -1.5rem !important; -} - -.mb-n4, -.my-n4 { - margin-bottom: -1.5rem !important; -} - -.ml-n4, -.mx-n4 { - margin-left: -1.5rem !important; -} - -.m-n5 { - margin: -3rem !important; -} - -.mt-n5, -.my-n5 { - margin-top: -3rem !important; -} - -.mr-n5, -.mx-n5 { - margin-right: -3rem !important; -} - -.mb-n5, -.my-n5 { - margin-bottom: -3rem !important; -} - -.ml-n5, -.mx-n5 { - margin-left: -3rem !important; -} - -.m-auto { - margin: auto !important; -} - -.mt-auto, -.my-auto { - margin-top: auto !important; -} - -.mr-auto, -.mx-auto { - margin-right: auto !important; -} - -.mb-auto, -.my-auto { - margin-bottom: auto !important; -} - -.ml-auto, -.mx-auto { - margin-left: auto !important; -} - -@media (min-width: 576px) { - .m-sm-0 { - margin: 0 !important; - } - .mt-sm-0, - .my-sm-0 { - margin-top: 0 !important; - } - .mr-sm-0, - .mx-sm-0 { - margin-right: 0 !important; - } - .mb-sm-0, - .my-sm-0 { - margin-bottom: 0 !important; - } - .ml-sm-0, - .mx-sm-0 { - margin-left: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .mt-sm-1, - .my-sm-1 { - margin-top: 0.25rem !important; - } - .mr-sm-1, - .mx-sm-1 { - margin-right: 0.25rem !important; - } - .mb-sm-1, - .my-sm-1 { - margin-bottom: 0.25rem !important; - } - .ml-sm-1, - .mx-sm-1 { - margin-left: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .mt-sm-2, - .my-sm-2 { - margin-top: 0.5rem !important; - } - .mr-sm-2, - .mx-sm-2 { - margin-right: 0.5rem !important; - } - .mb-sm-2, - .my-sm-2 { - margin-bottom: 0.5rem !important; - } - .ml-sm-2, - .mx-sm-2 { - margin-left: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .mt-sm-3, - .my-sm-3 { - margin-top: 1rem !important; - } - .mr-sm-3, - .mx-sm-3 { - margin-right: 1rem !important; - } - .mb-sm-3, - .my-sm-3 { - margin-bottom: 1rem !important; - } - .ml-sm-3, - .mx-sm-3 { - margin-left: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .mt-sm-4, - .my-sm-4 { - margin-top: 1.5rem !important; - } - .mr-sm-4, - .mx-sm-4 { - margin-right: 1.5rem !important; - } - .mb-sm-4, - .my-sm-4 { - margin-bottom: 1.5rem !important; - } - .ml-sm-4, - .mx-sm-4 { - margin-left: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .mt-sm-5, - .my-sm-5 { - margin-top: 3rem !important; - } - .mr-sm-5, - .mx-sm-5 { - margin-right: 3rem !important; - } - .mb-sm-5, - .my-sm-5 { - margin-bottom: 3rem !important; - } - .ml-sm-5, - .mx-sm-5 { - margin-left: 3rem !important; - } - .p-sm-0 { - padding: 0 !important; - } - .pt-sm-0, - .py-sm-0 { - padding-top: 0 !important; - } - .pr-sm-0, - .px-sm-0 { - padding-right: 0 !important; - } - .pb-sm-0, - .py-sm-0 { - padding-bottom: 0 !important; - } - .pl-sm-0, - .px-sm-0 { - padding-left: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .pt-sm-1, - .py-sm-1 { - padding-top: 0.25rem !important; - } - .pr-sm-1, - .px-sm-1 { - padding-right: 0.25rem !important; - } - .pb-sm-1, - .py-sm-1 { - padding-bottom: 0.25rem !important; - } - .pl-sm-1, - .px-sm-1 { - padding-left: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .pt-sm-2, - .py-sm-2 { - padding-top: 0.5rem !important; - } - .pr-sm-2, - .px-sm-2 { - padding-right: 0.5rem !important; - } - .pb-sm-2, - .py-sm-2 { - padding-bottom: 0.5rem !important; - } - .pl-sm-2, - .px-sm-2 { - padding-left: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .pt-sm-3, - .py-sm-3 { - padding-top: 1rem !important; - } - .pr-sm-3, - .px-sm-3 { - padding-right: 1rem !important; - } - .pb-sm-3, - .py-sm-3 { - padding-bottom: 1rem !important; - } - .pl-sm-3, - .px-sm-3 { - padding-left: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .pt-sm-4, - .py-sm-4 { - padding-top: 1.5rem !important; - } - .pr-sm-4, - .px-sm-4 { - padding-right: 1.5rem !important; - } - .pb-sm-4, - .py-sm-4 { - padding-bottom: 1.5rem !important; - } - .pl-sm-4, - .px-sm-4 { - padding-left: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .pt-sm-5, - .py-sm-5 { - padding-top: 3rem !important; - } - .pr-sm-5, - .px-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-5, - .py-sm-5 { - padding-bottom: 3rem !important; - } - .pl-sm-5, - .px-sm-5 { - padding-left: 3rem !important; - } - .m-sm-n1 { - margin: -0.25rem !important; - } - .mt-sm-n1, - .my-sm-n1 { - margin-top: -0.25rem !important; - } - .mr-sm-n1, - .mx-sm-n1 { - margin-right: -0.25rem !important; - } - .mb-sm-n1, - .my-sm-n1 { - margin-bottom: -0.25rem !important; - } - .ml-sm-n1, - .mx-sm-n1 { - margin-left: -0.25rem !important; - } - .m-sm-n2 { - margin: -0.5rem !important; - } - .mt-sm-n2, - .my-sm-n2 { - margin-top: -0.5rem !important; - } - .mr-sm-n2, - .mx-sm-n2 { - margin-right: -0.5rem !important; - } - .mb-sm-n2, - .my-sm-n2 { - margin-bottom: -0.5rem !important; - } - .ml-sm-n2, - .mx-sm-n2 { - margin-left: -0.5rem !important; - } - .m-sm-n3 { - margin: -1rem !important; - } - .mt-sm-n3, - .my-sm-n3 { - margin-top: -1rem !important; - } - .mr-sm-n3, - .mx-sm-n3 { - margin-right: -1rem !important; - } - .mb-sm-n3, - .my-sm-n3 { - margin-bottom: -1rem !important; - } - .ml-sm-n3, - .mx-sm-n3 { - margin-left: -1rem !important; - } - .m-sm-n4 { - margin: -1.5rem !important; - } - .mt-sm-n4, - .my-sm-n4 { - margin-top: -1.5rem !important; - } - .mr-sm-n4, - .mx-sm-n4 { - margin-right: -1.5rem !important; - } - .mb-sm-n4, - .my-sm-n4 { - margin-bottom: -1.5rem !important; - } - .ml-sm-n4, - .mx-sm-n4 { - margin-left: -1.5rem !important; - } - .m-sm-n5 { - margin: -3rem !important; - } - .mt-sm-n5, - .my-sm-n5 { - margin-top: -3rem !important; - } - .mr-sm-n5, - .mx-sm-n5 { - margin-right: -3rem !important; - } - .mb-sm-n5, - .my-sm-n5 { - margin-bottom: -3rem !important; - } - .ml-sm-n5, - .mx-sm-n5 { - margin-left: -3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mt-sm-auto, - .my-sm-auto { - margin-top: auto !important; - } - .mr-sm-auto, - .mx-sm-auto { - margin-right: auto !important; - } - .mb-sm-auto, - .my-sm-auto { - margin-bottom: auto !important; - } - .ml-sm-auto, - .mx-sm-auto { - margin-left: auto !important; - } -} - -@media (min-width: 768px) { - .m-md-0 { - margin: 0 !important; - } - .mt-md-0, - .my-md-0 { - margin-top: 0 !important; - } - .mr-md-0, - .mx-md-0 { - margin-right: 0 !important; - } - .mb-md-0, - .my-md-0 { - margin-bottom: 0 !important; - } - .ml-md-0, - .mx-md-0 { - margin-left: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .mt-md-1, - .my-md-1 { - margin-top: 0.25rem !important; - } - .mr-md-1, - .mx-md-1 { - margin-right: 0.25rem !important; - } - .mb-md-1, - .my-md-1 { - margin-bottom: 0.25rem !important; - } - .ml-md-1, - .mx-md-1 { - margin-left: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .mt-md-2, - .my-md-2 { - margin-top: 0.5rem !important; - } - .mr-md-2, - .mx-md-2 { - margin-right: 0.5rem !important; - } - .mb-md-2, - .my-md-2 { - margin-bottom: 0.5rem !important; - } - .ml-md-2, - .mx-md-2 { - margin-left: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .mt-md-3, - .my-md-3 { - margin-top: 1rem !important; - } - .mr-md-3, - .mx-md-3 { - margin-right: 1rem !important; - } - .mb-md-3, - .my-md-3 { - margin-bottom: 1rem !important; - } - .ml-md-3, - .mx-md-3 { - margin-left: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .mt-md-4, - .my-md-4 { - margin-top: 1.5rem !important; - } - .mr-md-4, - .mx-md-4 { - margin-right: 1.5rem !important; - } - .mb-md-4, - .my-md-4 { - margin-bottom: 1.5rem !important; - } - .ml-md-4, - .mx-md-4 { - margin-left: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .mt-md-5, - .my-md-5 { - margin-top: 3rem !important; - } - .mr-md-5, - .mx-md-5 { - margin-right: 3rem !important; - } - .mb-md-5, - .my-md-5 { - margin-bottom: 3rem !important; - } - .ml-md-5, - .mx-md-5 { - margin-left: 3rem !important; - } - .p-md-0 { - padding: 0 !important; - } - .pt-md-0, - .py-md-0 { - padding-top: 0 !important; - } - .pr-md-0, - .px-md-0 { - padding-right: 0 !important; - } - .pb-md-0, - .py-md-0 { - padding-bottom: 0 !important; - } - .pl-md-0, - .px-md-0 { - padding-left: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .pt-md-1, - .py-md-1 { - padding-top: 0.25rem !important; - } - .pr-md-1, - .px-md-1 { - padding-right: 0.25rem !important; - } - .pb-md-1, - .py-md-1 { - padding-bottom: 0.25rem !important; - } - .pl-md-1, - .px-md-1 { - padding-left: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .pt-md-2, - .py-md-2 { - padding-top: 0.5rem !important; - } - .pr-md-2, - .px-md-2 { - padding-right: 0.5rem !important; - } - .pb-md-2, - .py-md-2 { - padding-bottom: 0.5rem !important; - } - .pl-md-2, - .px-md-2 { - padding-left: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .pt-md-3, - .py-md-3 { - padding-top: 1rem !important; - } - .pr-md-3, - .px-md-3 { - padding-right: 1rem !important; - } - .pb-md-3, - .py-md-3 { - padding-bottom: 1rem !important; - } - .pl-md-3, - .px-md-3 { - padding-left: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .pt-md-4, - .py-md-4 { - padding-top: 1.5rem !important; - } - .pr-md-4, - .px-md-4 { - padding-right: 1.5rem !important; - } - .pb-md-4, - .py-md-4 { - padding-bottom: 1.5rem !important; - } - .pl-md-4, - .px-md-4 { - padding-left: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .pt-md-5, - .py-md-5 { - padding-top: 3rem !important; - } - .pr-md-5, - .px-md-5 { - padding-right: 3rem !important; - } - .pb-md-5, - .py-md-5 { - padding-bottom: 3rem !important; - } - .pl-md-5, - .px-md-5 { - padding-left: 3rem !important; - } - .m-md-n1 { - margin: -0.25rem !important; - } - .mt-md-n1, - .my-md-n1 { - margin-top: -0.25rem !important; - } - .mr-md-n1, - .mx-md-n1 { - margin-right: -0.25rem !important; - } - .mb-md-n1, - .my-md-n1 { - margin-bottom: -0.25rem !important; - } - .ml-md-n1, - .mx-md-n1 { - margin-left: -0.25rem !important; - } - .m-md-n2 { - margin: -0.5rem !important; - } - .mt-md-n2, - .my-md-n2 { - margin-top: -0.5rem !important; - } - .mr-md-n2, - .mx-md-n2 { - margin-right: -0.5rem !important; - } - .mb-md-n2, - .my-md-n2 { - margin-bottom: -0.5rem !important; - } - .ml-md-n2, - .mx-md-n2 { - margin-left: -0.5rem !important; - } - .m-md-n3 { - margin: -1rem !important; - } - .mt-md-n3, - .my-md-n3 { - margin-top: -1rem !important; - } - .mr-md-n3, - .mx-md-n3 { - margin-right: -1rem !important; - } - .mb-md-n3, - .my-md-n3 { - margin-bottom: -1rem !important; - } - .ml-md-n3, - .mx-md-n3 { - margin-left: -1rem !important; - } - .m-md-n4 { - margin: -1.5rem !important; - } - .mt-md-n4, - .my-md-n4 { - margin-top: -1.5rem !important; - } - .mr-md-n4, - .mx-md-n4 { - margin-right: -1.5rem !important; - } - .mb-md-n4, - .my-md-n4 { - margin-bottom: -1.5rem !important; - } - .ml-md-n4, - .mx-md-n4 { - margin-left: -1.5rem !important; - } - .m-md-n5 { - margin: -3rem !important; - } - .mt-md-n5, - .my-md-n5 { - margin-top: -3rem !important; - } - .mr-md-n5, - .mx-md-n5 { - margin-right: -3rem !important; - } - .mb-md-n5, - .my-md-n5 { - margin-bottom: -3rem !important; - } - .ml-md-n5, - .mx-md-n5 { - margin-left: -3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mt-md-auto, - .my-md-auto { - margin-top: auto !important; - } - .mr-md-auto, - .mx-md-auto { - margin-right: auto !important; - } - .mb-md-auto, - .my-md-auto { - margin-bottom: auto !important; - } - .ml-md-auto, - .mx-md-auto { - margin-left: auto !important; - } -} - -@media (min-width: 992px) { - .m-lg-0 { - margin: 0 !important; - } - .mt-lg-0, - .my-lg-0 { - margin-top: 0 !important; - } - .mr-lg-0, - .mx-lg-0 { - margin-right: 0 !important; - } - .mb-lg-0, - .my-lg-0 { - margin-bottom: 0 !important; - } - .ml-lg-0, - .mx-lg-0 { - margin-left: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .mt-lg-1, - .my-lg-1 { - margin-top: 0.25rem !important; - } - .mr-lg-1, - .mx-lg-1 { - margin-right: 0.25rem !important; - } - .mb-lg-1, - .my-lg-1 { - margin-bottom: 0.25rem !important; - } - .ml-lg-1, - .mx-lg-1 { - margin-left: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .mt-lg-2, - .my-lg-2 { - margin-top: 0.5rem !important; - } - .mr-lg-2, - .mx-lg-2 { - margin-right: 0.5rem !important; - } - .mb-lg-2, - .my-lg-2 { - margin-bottom: 0.5rem !important; - } - .ml-lg-2, - .mx-lg-2 { - margin-left: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .mt-lg-3, - .my-lg-3 { - margin-top: 1rem !important; - } - .mr-lg-3, - .mx-lg-3 { - margin-right: 1rem !important; - } - .mb-lg-3, - .my-lg-3 { - margin-bottom: 1rem !important; - } - .ml-lg-3, - .mx-lg-3 { - margin-left: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .mt-lg-4, - .my-lg-4 { - margin-top: 1.5rem !important; - } - .mr-lg-4, - .mx-lg-4 { - margin-right: 1.5rem !important; - } - .mb-lg-4, - .my-lg-4 { - margin-bottom: 1.5rem !important; - } - .ml-lg-4, - .mx-lg-4 { - margin-left: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .mt-lg-5, - .my-lg-5 { - margin-top: 3rem !important; - } - .mr-lg-5, - .mx-lg-5 { - margin-right: 3rem !important; - } - .mb-lg-5, - .my-lg-5 { - margin-bottom: 3rem !important; - } - .ml-lg-5, - .mx-lg-5 { - margin-left: 3rem !important; - } - .p-lg-0 { - padding: 0 !important; - } - .pt-lg-0, - .py-lg-0 { - padding-top: 0 !important; - } - .pr-lg-0, - .px-lg-0 { - padding-right: 0 !important; - } - .pb-lg-0, - .py-lg-0 { - padding-bottom: 0 !important; - } - .pl-lg-0, - .px-lg-0 { - padding-left: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .pt-lg-1, - .py-lg-1 { - padding-top: 0.25rem !important; - } - .pr-lg-1, - .px-lg-1 { - padding-right: 0.25rem !important; - } - .pb-lg-1, - .py-lg-1 { - padding-bottom: 0.25rem !important; - } - .pl-lg-1, - .px-lg-1 { - padding-left: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .pt-lg-2, - .py-lg-2 { - padding-top: 0.5rem !important; - } - .pr-lg-2, - .px-lg-2 { - padding-right: 0.5rem !important; - } - .pb-lg-2, - .py-lg-2 { - padding-bottom: 0.5rem !important; - } - .pl-lg-2, - .px-lg-2 { - padding-left: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .pt-lg-3, - .py-lg-3 { - padding-top: 1rem !important; - } - .pr-lg-3, - .px-lg-3 { - padding-right: 1rem !important; - } - .pb-lg-3, - .py-lg-3 { - padding-bottom: 1rem !important; - } - .pl-lg-3, - .px-lg-3 { - padding-left: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .pt-lg-4, - .py-lg-4 { - padding-top: 1.5rem !important; - } - .pr-lg-4, - .px-lg-4 { - padding-right: 1.5rem !important; - } - .pb-lg-4, - .py-lg-4 { - padding-bottom: 1.5rem !important; - } - .pl-lg-4, - .px-lg-4 { - padding-left: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .pt-lg-5, - .py-lg-5 { - padding-top: 3rem !important; - } - .pr-lg-5, - .px-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-5, - .py-lg-5 { - padding-bottom: 3rem !important; - } - .pl-lg-5, - .px-lg-5 { - padding-left: 3rem !important; - } - .m-lg-n1 { - margin: -0.25rem !important; - } - .mt-lg-n1, - .my-lg-n1 { - margin-top: -0.25rem !important; - } - .mr-lg-n1, - .mx-lg-n1 { - margin-right: -0.25rem !important; - } - .mb-lg-n1, - .my-lg-n1 { - margin-bottom: -0.25rem !important; - } - .ml-lg-n1, - .mx-lg-n1 { - margin-left: -0.25rem !important; - } - .m-lg-n2 { - margin: -0.5rem !important; - } - .mt-lg-n2, - .my-lg-n2 { - margin-top: -0.5rem !important; - } - .mr-lg-n2, - .mx-lg-n2 { - margin-right: -0.5rem !important; - } - .mb-lg-n2, - .my-lg-n2 { - margin-bottom: -0.5rem !important; - } - .ml-lg-n2, - .mx-lg-n2 { - margin-left: -0.5rem !important; - } - .m-lg-n3 { - margin: -1rem !important; - } - .mt-lg-n3, - .my-lg-n3 { - margin-top: -1rem !important; - } - .mr-lg-n3, - .mx-lg-n3 { - margin-right: -1rem !important; - } - .mb-lg-n3, - .my-lg-n3 { - margin-bottom: -1rem !important; - } - .ml-lg-n3, - .mx-lg-n3 { - margin-left: -1rem !important; - } - .m-lg-n4 { - margin: -1.5rem !important; - } - .mt-lg-n4, - .my-lg-n4 { - margin-top: -1.5rem !important; - } - .mr-lg-n4, - .mx-lg-n4 { - margin-right: -1.5rem !important; - } - .mb-lg-n4, - .my-lg-n4 { - margin-bottom: -1.5rem !important; - } - .ml-lg-n4, - .mx-lg-n4 { - margin-left: -1.5rem !important; - } - .m-lg-n5 { - margin: -3rem !important; - } - .mt-lg-n5, - .my-lg-n5 { - margin-top: -3rem !important; - } - .mr-lg-n5, - .mx-lg-n5 { - margin-right: -3rem !important; - } - .mb-lg-n5, - .my-lg-n5 { - margin-bottom: -3rem !important; - } - .ml-lg-n5, - .mx-lg-n5 { - margin-left: -3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mt-lg-auto, - .my-lg-auto { - margin-top: auto !important; - } - .mr-lg-auto, - .mx-lg-auto { - margin-right: auto !important; - } - .mb-lg-auto, - .my-lg-auto { - margin-bottom: auto !important; - } - .ml-lg-auto, - .mx-lg-auto { - margin-left: auto !important; - } -} - -@media (min-width: 1200px) { - .m-xl-0 { - margin: 0 !important; - } - .mt-xl-0, - .my-xl-0 { - margin-top: 0 !important; - } - .mr-xl-0, - .mx-xl-0 { - margin-right: 0 !important; - } - .mb-xl-0, - .my-xl-0 { - margin-bottom: 0 !important; - } - .ml-xl-0, - .mx-xl-0 { - margin-left: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .mt-xl-1, - .my-xl-1 { - margin-top: 0.25rem !important; - } - .mr-xl-1, - .mx-xl-1 { - margin-right: 0.25rem !important; - } - .mb-xl-1, - .my-xl-1 { - margin-bottom: 0.25rem !important; - } - .ml-xl-1, - .mx-xl-1 { - margin-left: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .mt-xl-2, - .my-xl-2 { - margin-top: 0.5rem !important; - } - .mr-xl-2, - .mx-xl-2 { - margin-right: 0.5rem !important; - } - .mb-xl-2, - .my-xl-2 { - margin-bottom: 0.5rem !important; - } - .ml-xl-2, - .mx-xl-2 { - margin-left: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .mt-xl-3, - .my-xl-3 { - margin-top: 1rem !important; - } - .mr-xl-3, - .mx-xl-3 { - margin-right: 1rem !important; - } - .mb-xl-3, - .my-xl-3 { - margin-bottom: 1rem !important; - } - .ml-xl-3, - .mx-xl-3 { - margin-left: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .mt-xl-4, - .my-xl-4 { - margin-top: 1.5rem !important; - } - .mr-xl-4, - .mx-xl-4 { - margin-right: 1.5rem !important; - } - .mb-xl-4, - .my-xl-4 { - margin-bottom: 1.5rem !important; - } - .ml-xl-4, - .mx-xl-4 { - margin-left: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .mt-xl-5, - .my-xl-5 { - margin-top: 3rem !important; - } - .mr-xl-5, - .mx-xl-5 { - margin-right: 3rem !important; - } - .mb-xl-5, - .my-xl-5 { - margin-bottom: 3rem !important; - } - .ml-xl-5, - .mx-xl-5 { - margin-left: 3rem !important; - } - .p-xl-0 { - padding: 0 !important; - } - .pt-xl-0, - .py-xl-0 { - padding-top: 0 !important; - } - .pr-xl-0, - .px-xl-0 { - padding-right: 0 !important; - } - .pb-xl-0, - .py-xl-0 { - padding-bottom: 0 !important; - } - .pl-xl-0, - .px-xl-0 { - padding-left: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .pt-xl-1, - .py-xl-1 { - padding-top: 0.25rem !important; - } - .pr-xl-1, - .px-xl-1 { - padding-right: 0.25rem !important; - } - .pb-xl-1, - .py-xl-1 { - padding-bottom: 0.25rem !important; - } - .pl-xl-1, - .px-xl-1 { - padding-left: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .pt-xl-2, - .py-xl-2 { - padding-top: 0.5rem !important; - } - .pr-xl-2, - .px-xl-2 { - padding-right: 0.5rem !important; - } - .pb-xl-2, - .py-xl-2 { - padding-bottom: 0.5rem !important; - } - .pl-xl-2, - .px-xl-2 { - padding-left: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .pt-xl-3, - .py-xl-3 { - padding-top: 1rem !important; - } - .pr-xl-3, - .px-xl-3 { - padding-right: 1rem !important; - } - .pb-xl-3, - .py-xl-3 { - padding-bottom: 1rem !important; - } - .pl-xl-3, - .px-xl-3 { - padding-left: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .pt-xl-4, - .py-xl-4 { - padding-top: 1.5rem !important; - } - .pr-xl-4, - .px-xl-4 { - padding-right: 1.5rem !important; - } - .pb-xl-4, - .py-xl-4 { - padding-bottom: 1.5rem !important; - } - .pl-xl-4, - .px-xl-4 { - padding-left: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .pt-xl-5, - .py-xl-5 { - padding-top: 3rem !important; - } - .pr-xl-5, - .px-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-5, - .py-xl-5 { - padding-bottom: 3rem !important; - } - .pl-xl-5, - .px-xl-5 { - padding-left: 3rem !important; - } - .m-xl-n1 { - margin: -0.25rem !important; - } - .mt-xl-n1, - .my-xl-n1 { - margin-top: -0.25rem !important; - } - .mr-xl-n1, - .mx-xl-n1 { - margin-right: -0.25rem !important; - } - .mb-xl-n1, - .my-xl-n1 { - margin-bottom: -0.25rem !important; - } - .ml-xl-n1, - .mx-xl-n1 { - margin-left: -0.25rem !important; - } - .m-xl-n2 { - margin: -0.5rem !important; - } - .mt-xl-n2, - .my-xl-n2 { - margin-top: -0.5rem !important; - } - .mr-xl-n2, - .mx-xl-n2 { - margin-right: -0.5rem !important; - } - .mb-xl-n2, - .my-xl-n2 { - margin-bottom: -0.5rem !important; - } - .ml-xl-n2, - .mx-xl-n2 { - margin-left: -0.5rem !important; - } - .m-xl-n3 { - margin: -1rem !important; - } - .mt-xl-n3, - .my-xl-n3 { - margin-top: -1rem !important; - } - .mr-xl-n3, - .mx-xl-n3 { - margin-right: -1rem !important; - } - .mb-xl-n3, - .my-xl-n3 { - margin-bottom: -1rem !important; - } - .ml-xl-n3, - .mx-xl-n3 { - margin-left: -1rem !important; - } - .m-xl-n4 { - margin: -1.5rem !important; - } - .mt-xl-n4, - .my-xl-n4 { - margin-top: -1.5rem !important; - } - .mr-xl-n4, - .mx-xl-n4 { - margin-right: -1.5rem !important; - } - .mb-xl-n4, - .my-xl-n4 { - margin-bottom: -1.5rem !important; - } - .ml-xl-n4, - .mx-xl-n4 { - margin-left: -1.5rem !important; - } - .m-xl-n5 { - margin: -3rem !important; - } - .mt-xl-n5, - .my-xl-n5 { - margin-top: -3rem !important; - } - .mr-xl-n5, - .mx-xl-n5 { - margin-right: -3rem !important; - } - .mb-xl-n5, - .my-xl-n5 { - margin-bottom: -3rem !important; - } - .ml-xl-n5, - .mx-xl-n5 { - margin-left: -3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mt-xl-auto, - .my-xl-auto { - margin-top: auto !important; - } - .mr-xl-auto, - .mx-xl-auto { - margin-right: auto !important; - } - .mb-xl-auto, - .my-xl-auto { - margin-bottom: auto !important; - } - .ml-xl-auto, - .mx-xl-auto { - margin-left: auto !important; - } -} -/*# sourceMappingURL=bootstrap-grid.css.map */ \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map deleted file mode 100644 index 8661e3e3..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap-grid.scss","bootstrap-grid.css","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/_variables.scss","../../scss/mixins/_grid-framework.scss","../../scss/utilities/_display.scss","../../scss/utilities/_flex.scss","../../scss/utilities/_spacing.scss"],"names":[],"mappings":"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Z;ELw1BlB;EKv1BM;IAAwB,iBADZ;IACY,QADZ;EL21BlB;EK11BM;IAAwB,iBADZ;IACY,QADZ;EL81BlB;EK71BM;IAAwB,iBADZ;IACY,QADZ;ELi2BlB;EKh2BM;IAAwB,iBADZ;IACY,QADZ;ELo2BlB;EKn2BM;IAAwB,iBADZ;IACY,QADZ;ELu2BlB;EKt2BM;IAAwB,iBADZ;IACY,QADZ;EL02BlB;EKz2BM;IAAwB,iBADZ;IACY,QADZ;EL62BlB;EK52BM;IAAwB,kBADZ;IACY,SADZ;ELg3BlB;EK/2BM;IAAwB,kBADZ;IACY,SADZ;ELm3BlB;EKl3BM;IAAwB,kBADZ;IACY,SADZ;ELs3BlB;EK/2BQ;IHRR,cAA4B;EF03B5B;EKl3BQ;IHRR,sBAA8C;EF63B9C;EKr3BQ;IHRR,uBAA8C;EFg4B9C;EKx3BQ;IHRR,gBAA8C;EFm4B9C;EK33BQ;IHRR,uBAA8C;EFs4B9C;EK93BQ;IHRR,uBAA8C;EFy4B9C;EKj4BQ;IHRR,gBAA8C;EF44B9C;EKp4BQ;IHRR,uBAA8C;EF+4B9C;EKv4BQ;IHRR,uBAA8C;EFk5B9C;EK14BQ;IHRR,gBAA8C;EFq5B9C;EK74BQ;IHRR,uBAA8C;EFw5B9C;EKh5BQ;IHRR,uBAA8C;EF25B9C;AACF;;AMx8BM;EAAwB,wBAA0B;AN48BxD;;AM58BM;EAAwB,0BAA0B;ANg9BxD;;AMh9BM;EAAwB,gCAA0B;ANo9BxD;;AMp9BM;EAAwB,yBAA0B;ANw9BxD;;AMx9BM;EAAwB,yBAA0B;AN49BxD;;AM59BM;EAAwB,6BAA0B;ANg+BxD;;AMh+BM;EAAwB,8BAA0B;ANo+BxD;;AMp+BM;EAAwB,+BAA0B;EAA1B,wBAA0B;ANw+BxD;;AMx+BM;EAAwB,sCAA0B;EAA1B,+BAA0B;AN4+BxD;;AG37BI;EGjDE;IAAwB,wBAA0B;ENi/BtD;EMj/BI;IAAwB,0BAA0B;ENo/BtD;EMp/BI;IAAwB,gCAA0B;ENu/BtD;EMv/BI;IAAwB,yBAA0B;EN0/BtD;EM1/BI;IAAwB,yBAA0B;EN6/BtD;EM7/BI;IAAwB,6BAA0B;ENggCtD;EMhgCI;IAAwB,8BAA0B;ENmgCtD;EM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A0C;ARurDpD;;AQrrDQ;;EAEE,yBAAsC;ARwrDhD;;AQvsDQ;EAAgC,0BAA4B;AR2sDpE;;AQ1sDQ;;EAEE,8BAAoC;AR6sD9C;;AQ3sDQ;;EAEE,gCAAwC;AR8sDlD;;AQ5sDQ;;EAEE,iCAA0C;AR+sDpD;;AQ7sDQ;;EAEE,+BAAsC;ARgtDhD;;AQ/tDQ;EAAgC,yBAA4B;ARmuDpE;;AQluDQ;;EAEE,6BAAoC;ARquD9C;;AQnuDQ;;EAEE,+BAAwC;ARsuDlD;;AQpuDQ;;EAEE,gCAA0C;ARuuDpD;;AQruDQ;;EAEE,8BAAsC;ARwuDhD;;AQvvDQ;EAAgC,uBAA4B;AR2vDpE;;AQ1vDQ;;EAEE,2BAAoC;AR6vD9C;;AQ3vDQ;;EAEE,6BAAwC;AR8vDlD;;AQ5vDQ;;EAEE,8BAA0C;AR+vDpD;;AQ7vDQ;;EAEE,4BAAsC;ARgwDhD;;AQ/wDQ;EAAgC,yBAA4B;ARmxDpE;;AQlxDQ;;EAEE,6BAAoC;ARqxD9C;;AQnxDQ;;EAEE,+BAAwC;ARsxDlD;;AQpxDQ;;EAEE,gCAA0C;ARuxDpD;;AQrxDQ;;EAEE,8BAAsC;ARwxDhD;;AQvyDQ;EAAgC,uBAA4B;AR2yDpE;;AQ1yDQ;;EAEE,2BAAoC;AR6yD9C;;AQ3yDQ;;EAEE,6BAAwC;AR8yDlD;;AQ5yDQ;;EAEE,8BAA0C;AR+yDpD;;AQ7yDQ;;EAEE,4BAAsC;ARgzDhD;;AQ/zDQ;EAAgC,qBAA4B;ARm0DpE;;AQl0DQ;;EAEE,yBAAoC;ARq0D9C;;AQn0DQ;;EAEE,2BAAwC;ARs0DlD;;AQp0DQ;;EAEE,4BAA0C;ARu0DpD;;AQr0DQ;;EAEE,0BAAsC;ARw0DhD;;AQv1DQ;EAAgC,2BAA4B;AR21DpE;;AQ11DQ;;EAEE,+BAAoC;AR61D9C;;AQ31DQ;;EAEE,iCAAwC;AR81DlD;;AQ51DQ;;EAEE,kCAA0C;AR+1DpD;;AQ71DQ;;EAEE,gCAAsC;ARg2DhD;;AQ/2DQ;EAAgC,0BAA4B;ARm3DpE;;AQl3DQ;;EAEE,8BAAoC;ARq3D9C;;AQn3DQ;;EAEE,gCAAwC;ARs3DlD;;AQp3DQ;;EAEE,iCAA0C;ARu3DpD;;AQr3DQ;;EAEE,+BAAsC;ARw3DhD;;AQv4DQ;EAAgC,wBAA4B;AR24DpE;;AQ14DQ;;EAEE,4BAAoC;AR64D9C;;AQ34DQ;;EAEE,8BAAwC;AR84DlD;;AQ54DQ;;EAEE,+BAA0C;AR+4DpD;;AQ74DQ;;EAEE,6BAAsC;ARg5DhD;;AQ/5DQ;EAAgC,0BAA4B;ARm6DpE;;AQl6DQ;;EAEE,8BAAoC;ARq6D9C;;AQn6DQ;;EAEE,gCAAwC;ARs6DlD;;AQp6DQ;;EAEE,iCAA0C;ARu6DpD;;AQr6DQ;;EAEE,+BAAsC;ARw6DhD;;AQv7DQ;EAAgC,wBAA4B;AR27DpE;;AQ17DQ;;EAEE,4BAAoC;AR67D9C;;AQ37DQ;;EAEE,8BAAwC;AR87DlD;;AQ57DQ;;EAEE,+BAA0C;AR+7DpD;;AQ77DQ;;EAEE,6BAAsC;ARg8DhD;;AQx7DQ;EAAwB,2BAA2B;AR47D3D;;AQ37DQ;;EAEE,+BAA+B;AR87DzC;;AQ57DQ;;EAEE,iCAAiC;AR+7D3C;;AQ77DQ;;EAEE,kCAAkC;ARg8D5C;;AQ97DQ;;EAEE,gCAAgC;ARi8D1C;;AQh9DQ;EAAwB,0BAA2B;ARo9D3D;;AQn9DQ;;EAEE,8BAA+B;ARs9DzC;;AQp9DQ;;EAEE,gCAAiC;ARu9D3C;;AQr9DQ;;EAEE,iCAAkC;ARw9D5C;;AQt9DQ;;EAEE,+BAAgC;ARy9D1C;;AQx+DQ;EAAwB,wBAA2B;AR4+D3D;;AQ3+DQ;;EAEE,4BAA+B;AR8+DzC;;AQ5+DQ;;EAEE,8BAAiC;AR++D3C;;AQ7+DQ;;EAEE,+BAAkC;ARg/D5C;;AQ9+DQ;;EAEE,6BAAgC;ARi/D1C;;AQhgEQ;EAAwB,0BAA2B;ARogE3D;;AQngEQ;;EAEE,8BAA+B;ARsgEzC;;AQpgEQ;;EAEE,gCAAiC;ARugE3C;;AQrgEQ;;EAEE,iCAAkC;ARwgE5C;;AQtgEQ;;EAEE,+BAAgC;ARygE1C;;AQxhEQ;EAAwB,wBAA2B;AR4hE3D;;AQ3hEQ;;EAEE,4BAA+B;AR8hEzC;;AQ5hEQ;;EAEE,8BAAiC;AR+hE3C;;AQ7hEQ;;EAEE,+BAAkC;ARgiE5C;;AQ9hEQ;;EAEE,6BAAgC;ARiiE1C;;AQ3hEI;EAAmB,uBAAuB;AR+hE9C;;AQ9hEI;;EAEE,2BAA2B;ARiiEjC;;AQ/hEI;;EAEE,6BAA6B;ARkiEnC;;AQhiEI;;EAEE,8BAA8B;ARmiEpC;;AQjiEI;;EAEE,4BAA4B;ARoiElC;;AG7iEI;EKlDI;IAAgC,oBAA4B;ERomElE;EQnmEM;;IAEE,wBAAoC;ERqmE5C;EQnmEM;;IAEE,0BAAwC;ERqmEhD;EQnmEM;;IAEE,2BAA0C;ERqmElD;EQnmEM;;IAEE,yBAAsC;ERqmE9C;EQpnEM;IAAgC,0BAA4B;ERunElE;EQtnEM;;IAEE,8BAAoC;ERwnE5C;EQtnEM;;IAEE,gCAAwC;ERwnEhD;EQtnEM;;IAEE,iCAA0C;ERwnElD;EQtnEM;;IAEE,+BAAsC;ERwnE9C;EQvoEM;IAAgC,yBAA4B;ER0oElE;EQzoEM;;IAEE,6BAAoC;ER2oE5C;EQzoEM;;IAEE,+BAAwC;ER2oEhD;EQzoEM;;IAEE,gCAA0C;ER2oElD;EQzoEM;;IAEE,8BAAsC;ER2oE9C;EQ1pEM;IAAgC,uBAA4B;ER6pElE;EQ5pEM;;IAEE,2BAAoC;ER8pE5C;EQ5pEM;;IAEE,6BAAwC;ER8pEhD;EQ5pEM;;IAEE,8BAA0C;ER8pElD;EQ5pEM;;IAEE,4BAAsC;ER8pE9C;EQ7qEM;IAAgC,yBAA4B;ERgrElE;EQ/qEM;;IAEE,6BAAoC;ERirE5C;EQ/qEM;;IAEE,+BAAwC;ERirEhD;EQ/qEM;;IAEE,gCAA0C;ERirElD;EQ/qEM;;IAEE,8BAAsC;ERirE9C;EQhsEM;IAAgC,uBAA4B;ERmsElE;EQlsEM;;IAEE,2BAAoC;ERosE5C;EQlsEM;;IAEE,6BAAwC;ERosEhD;EQlsEM;;IAEE,8BAA0C;ERosElD;EQlsEM;;IAEE,4BAAsC;ERosE9C;EQntEM;IAAgC,qBAA4B;ERstElE;EQrtEM;;IAEE,yBAAoC;ERutE5C;EQrtEM;;IAEE,2BAAwC;ERutEhD;EQrtEM;;IAEE,4BAA0C;ERutElD;EQrtEM;;IAEE,0BAAsC;ERutE9C;EQtuEM;IAAgC,2BAA4B;ERyuElE;EQxuEM;;IAEE,+BAAoC;ER0uE5C;EQxuEM;;IAEE,iCAAwC;ER0uEhD;EQxuEM;;IAEE,kCAA0C;ER0uElD;EQxuEM;;IAEE,gCAAsC;ER0uE9C;EQzvEM;IAAgC,0BAA4B;ER4vElE;EQ3vEM;;IAEE,8BAAoC;ER6vE5C;EQ3vEM;;IAEE,gCAAwC;ER6vEhD;EQ3vEM;;IAEE,iCAA0C;ER6vElD;EQ3vEM;;IAEE,+BAAsC;ER6vE9C;EQ5wEM;IAAgC,wBAA4B;ER+wElE;EQ9wEM;;IAEE,4BAAoC;ERgxE5C;EQ9wEM;;IAEE,8BAAwC;ERgxEhD;EQ9wEM;;IAEE,+BAA0C;ERgxElD;EQ9wEM;;IAEE,6BAAsC;ERgxE9C;EQ/xEM;IAAgC,0BAA4B;ERkyElE;EQjyEM;;IAEE,8BAAoC;ERmyE5C;EQjyEM;;IAEE,gCAAwC;ERmyEhD;EQjyEM;;IAEE,iCAA0C;ERmyElD;EQjyEM;;IAEE,+BAAsC;ERmyE9C;EQlzEM;IAAgC,wBAA4B;ERqzElE;EQpzEM;;IAEE,4BAAoC;ERszE5C;EQpzEM;;IAEE,8BAAwC;ERszEhD;EQpzEM;;IAEE,+BAA0C;ERszElD;EQpzEM;;IAEE,6BAAsC;ERszE9C;EQ9yEM;IAAwB,2BAA2B;ERizEzD;EQhzEM;;IAEE,+BAA+B;ERkzEvC;EQhzEM;;IAEE,iCAAiC;ERkzEzC;EQhzEM;;IAEE,kCAAkC;ERkzE1C;EQhzEM;;IAEE,gCAAgC;ERkzExC;EQj0EM;IAAwB,0BAA2B;ERo0EzD;EQn0EM;;IAEE,8BAA+B;ERq0EvC;EQn0EM;;IAEE,gCAAiC;ERq0EzC;EQn0EM;;IAEE,iCAAkC;ERq0E1C;EQn0EM;;IAEE,+BAAgC;ERq0ExC;EQp1EM;IAAwB,wBAA2B;ERu1EzD;EQt1EM;;IAEE,4BAA+B;ERw1EvC;EQt1EM;;IAEE,8BAAiC;ERw1EzC;EQt1EM;;IAEE,+BAAkC;ERw1E1C;EQt1EM;;IAEE,6BAAgC;ERw1ExC;EQv2EM;IAAwB,0BAA2B;ER02EzD;EQz2EM;;IAEE,8BAA+B;ER22EvC;EQz2EM;;IAEE,gCAAiC;ER22EzC;EQz2EM;;IAEE,iCAAkC;ER22E1C;EQz2EM;;IAEE,+BAAgC;ER22ExC;EQ13EM;IAAwB,wBAA2B;ER63EzD;EQ53EM;;IAEE,4BAA+B;ER83EvC;EQ53EM;;IAEE,8BAAiC;ER83EzC;EQ53EM;;IAEE,+BAAkC;ER83E1C;EQ53EM;;IAEE,6BAAgC;ER83ExC;EQx3EE;IAAmB,uBAAuB;ER23E5C;EQ13EE;;IAEE,2BAA2B;ER43E/B;EQ13EE;;IAEE,6BAA6B;ER43EjC;EQ13EE;;IAEE,8BAA8B;ER43ElC;EQ13EE;;IAEE,4BAA4B;ER43EhC;AACF;;AGt4EI;EKlDI;IAAgC,oBAA4B;ER67ElE;EQ57EM;;IAEE,wBAAoC;ER87E5C;EQ57EM;;IAEE,0BAAwC;ER87EhD;EQ57EM;;IAEE,2BAA0C;ER87ElD;EQ57EM;;IAEE,yBAAsC;ER87E9C;EQ78EM;IAAgC,0BAA4B;ERg9ElE;EQ/8EM;;IAEE,8BAAoC;ERi9E5C;EQ/8EM;;IAEE,gCAAwC;ERi9EhD;EQ/8EM;;IAEE,iCAA0C;ERi9ElD;EQ/8EM;;IAEE,+BAAsC;ERi9E9C;EQh+EM;IAAgC,yBAA4B;ERm+ElE;EQl+EM;;IAEE,6BAAoC;ERo+E5C;EQl+EM;;IAEE,+BAAwC;ERo+EhD;EQl+EM;;IAEE,gCAA0C;ERo+ElD;EQl+EM;;IAEE,8BAAsC;ERo+E9C;EQn/EM;IAAgC,uBAA4B;ERs/ElE;EQr/EM;;IAEE,2BAAoC;ERu/E5C;EQr/EM;;IAEE,6BAAwC;ERu/EhD;EQr/EM;;IAEE,8BAA0C;ERu/ElD;EQr/EM;;IAEE,4BAAsC;ERu/E9C;EQtgFM;IAAgC,yBAA4B;ERygFlE;EQxgFM;;IAEE,6BAAoC;ER0gF5C;EQxgFM;;IAEE,+BAAwC;ER0gFhD;EQxgFM;;IAEE,gCAA0C;ER0gFlD;EQxgFM;;IAEE,8BAAsC;ER0gF9C;EQzhFM;IAAgC,uBAA4B;ER4hFlE;EQ3hFM;;IAEE,2BAAoC;ER6hF5C;EQ3hFM;;IAEE,6BAAwC;ER6hFhD;EQ3hFM;;IAEE,8BAA0C;ER6hFlD;EQ3hFM;;IAEE,4BAAsC;ER6hF9C;EQ5iFM;IAAgC,qBAA4B;ER+iFlE;EQ9iFM;;IAEE,yBAAoC;ERgjF5C;EQ9iFM;;IAEE,2BAAwC;ERgjFhD;EQ9iFM;;IAEE,4BAA0C;ERgjFlD;EQ9iFM;;IAEE,0BAAsC;ERgjF9C;EQ/jFM;IAAgC,2BAA4B;ERkkFlE;EQjkFM;;IAEE,+BAAoC;ERmkF5C;EQjkFM;;IAEE,iCAAwC;ERmkFhD;EQjkFM;;IAEE,kCAA0C;ERmkFlD;EQjkFM;;IAEE,gCAAsC;ERmkF9C;EQllFM;IAAgC,0BAA4B;ERqlFlE;EQplFM;;IAEE,8BAAoC;ERslF5C;EQplFM;;IAEE,gCAAwC;ERslFhD;EQplFM;;IAEE,iCAA0C;ERslFlD;EQplFM;;IAEE,+BAAsC;ERslF9C;EQrmFM;IAAgC,wBAA4B;ERwmFlE;EQvmFM;;IAEE,4BAAoC;ERymF5C;EQvmFM;;IAEE,8BAAwC;ERymFhD;EQvmFM;;IAEE,+BAA0C;ERymFlD;EQvmFM;;IAEE,6BAAsC;ERymF9C;EQxnFM;IAAgC,0BAA4B;ER2nFlE;EQ1nFM;;IAEE,8BAAoC;ER4nF5C;EQ1nFM;;IAEE,gCAAwC;ER4nFhD;EQ1nFM;;IAEE,iCAA0C;ER4nFlD;EQ1nFM;;IAEE,+BAAsC;ER4nF9C;EQ3oFM;IAAgC,wBAA4B;ER8oFlE;EQ7oFM;;IAEE,4BAAoC;ER+oF5C;EQ7oFM;;IAEE,8BAAwC;ER+oFhD;EQ7oFM;;IAEE,+BAA0C;ER+oFlD;EQ7oFM;;IAEE,6BAAsC;ER+oF9C;EQvoFM;IAAwB,2BAA2B;ER0oFzD;EQzoFM;;IAEE,+BAA+B;ER2oFvC;EQzoFM;;IAEE,iCAAiC;ER2oFzC;EQzoFM;;IAEE,kCAAkC;ER2oF1C;EQzoFM;;IAEE,gCAAgC;ER2oFxC;EQ1pFM;IAAwB,0BAA2B;ER6pFzD;EQ5pFM;;IAEE,8BAA+B;ER8pFvC;EQ5pFM;;IAEE,gCAAiC;ER8pFzC;EQ5pFM;;IAEE,iCAAkC;ER8pF1C;EQ5pFM;;IAEE,+BAAgC;ER8pFxC;EQ7qFM;IAAwB,wBAA2B;ERgrFzD;EQ/qFM;;IAEE,4BAA+B;ERirFvC;EQ/qFM;;IAEE,8BAAiC;ERirFzC;EQ/qFM;;IAEE,+BAAkC;ERirF1C;EQ/qFM;;IAEE,6BAAgC;ERirFxC;EQhsFM;IAAwB,0BAA2B;ERmsFzD;EQlsFM;;IAEE,8BAA+B;ERosFvC;EQlsFM;;IAEE,gCAAiC;ERosFzC;EQlsFM;;IAEE,iCAAkC;ERosF1C;EQlsFM;;IAEE,+BAAgC;ERosFxC;EQntFM;IAAwB,wBAA2B;ERstFzD;EQrtFM;;IAEE,4BAA+B;ERutFvC;EQrtFM;;IAEE,8BAAiC;ERutFzC;EQrtFM;;IAEE,+BAAkC;ERutF1C;EQrtFM;;IAEE,6BAAgC;ERutFxC;EQjtFE;IAAmB,uBAAuB;ERotF5C;EQntFE;;IAEE,2BAA2B;ERqtF/B;EQntFE;;IAEE,6BAA6B;ERqtFjC;EQntFE;;IAEE,8BAA8B;ERqtFlC;EQntFE;;IAEE,4BAA4B;ERqtFhC;AACF;;AG/tFI;EKlDI;IAAgC,oBAA4B;ERsxFlE;EQrxFM;;IAEE,wBAAoC;ERuxF5C;EQrxFM;;IAEE,0BAAwC;ERuxFhD;EQrxFM;;IAEE,2BAA0C;ERuxFlD;EQrxFM;;IAEE,yBAAsC;ERuxF9C;EQtyFM;IAAgC,0BAA4B;ERyyFlE;EQxyFM;;IAEE,8BAAoC;ER0yF5C;EQxyFM;;IAEE,gCAAwC;ER0yFhD;EQxyFM;;IAEE,iCAA0C;ER0yFlD;EQxyFM;;IAEE,+BAAsC;ER0yF9C;EQzzFM;IAAgC,yBAA4B;ER4zFlE;EQ3zFM;;IAEE,6BAAoC;ER6zF5C;EQ3zFM;;IAEE,+BAAwC;ER6zFhD;EQ3zFM;;IAEE,gCAA0C;ER6zFlD;EQ3zFM;;IAEE,8BAAsC;ER6zF9C;EQ50FM;IAAgC,uBAA4B;ER+0FlE;EQ90FM;;IAEE,2BAAoC;ERg1F5C;EQ90FM;;IAEE,6BAAwC;ERg1FhD;EQ90FM;;IAEE,8BAA0C;ERg1FlD;EQ90FM;;IAEE,4BAAsC;ERg1F9C;EQ/1FM;IAAgC,yBAA4B;ERk2FlE;EQj2FM;;IAEE,6BAAoC;ERm2F5C;EQj2FM;;IAEE,+BAAwC;ERm2FhD;EQj2FM;;IAEE,gCAA0C;ERm2FlD;EQj2FM;;IAEE,8BAAsC;ERm2F9C;EQl3FM;IAAgC,uBAA4B;ERq3FlE;EQp3FM;;IAEE,2BAAoC;ERs3F5C;EQp3FM;;IAEE,6BAAwC;ERs3FhD;EQp3FM;;IAEE,8BAA0C;ERs3FlD;EQp3FM;;IAEE,4BAAsC;ERs3F9C;EQr4FM;IAAgC,qBAA4B;ERw4FlE;EQv4FM;;IAEE,yBAAoC;ERy4F5C;EQv4FM;;IAEE,2BAAwC;ERy4FhD;EQv4FM;;IAEE,4BAA0C;ERy4FlD;EQv4FM;;IAEE,0BAAsC;ERy4F9C;EQx5FM;IAAgC,2BAA4B;ER25FlE;EQ15FM;;IAEE,+BAAoC;ER45F5C;EQ15FM;;IAEE,iCAAwC;ER45FhD;EQ15FM;;IAEE,kCAA0C;ER45FlD;EQ15FM;;IAEE,gCAAsC;ER45F9C;EQ36FM;IAAgC,0BAA4B;ER86FlE;EQ76FM;;IAEE,8BAAoC;ER+6F5C;EQ76FM;;IAEE,gCAAwC;ER+6FhD;EQ76FM;;IAEE,iCAA0C;ER+6FlD;EQ76FM;;IAEE,+BAAsC;ER+6F9C;EQ97FM;IAAgC,wBAA4B;ERi8FlE;EQh8FM;;IAEE,4BAAoC;ERk8F5C;EQh8FM;;IAEE,8BAAwC;ERk8FhD;EQh8FM;;IAEE,+BAA0C;ERk8FlD;EQh8FM;;IAEE,6BAAsC;ERk8F9C;EQj9FM;IAAgC,0BAA4B;ERo9FlE;EQn9FM;;IAEE,8BAAoC;ERq9F5C;EQn9FM;;IAEE,gCAAwC;ERq9FhD;EQn9FM;;IAEE,iCAA0C;ERq9FlD;EQn9FM;;IAEE,+BAAsC;ERq9F9C;EQp+FM;IAAgC,wBAA4B;ERu+FlE;EQt+FM;;IAEE,4BAAoC;ERw+F5C;EQt+FM;;IAEE,8BAAwC;ERw+FhD;EQt+FM;;IAEE,+BAA0C;ERw+FlD;EQt+FM;;IAEE,6BAAsC;ERw+F9C;EQh+FM;IAAwB,2BAA2B;ERm+FzD;EQl+FM;;IAEE,+BAA+B;ERo+FvC;EQl+FM;;IAEE,iCAAiC;ERo+FzC;EQl+FM;;IAEE,kCAAkC;ERo+F1C;EQl+FM;;IAEE,gCAAgC;ERo+FxC;EQn/FM;IAAwB,0BAA2B;ERs/FzD;EQr/FM;;IAEE,8BAA+B;ERu/FvC;EQr/FM;;IAEE,gCAAiC;ERu/FzC;EQr/FM;;IAEE,iCAAkC;ERu/F1C;EQr/FM;;IAEE,+BAAgC;ERu/FxC;EQtgGM;IAAwB,wBAA2B;ERygGzD;EQxgGM;;IAEE,4BAA+B;ER0gGvC;EQxgGM;;IAEE,8BAAiC;ER0gGzC;EQxgGM;;IAEE,+BAAkC;ER0gG1C;EQxgGM;;IAEE,6BAAgC;ER0gGxC;EQzhGM;IAAwB,0BAA2B;ER4hGzD;EQ3hGM;;IAEE,8BAA+B;ER6hGvC;EQ3hGM;;IAEE,gCAAiC;ER6hGzC;EQ3hGM;;IAEE,iCAAkC;ER6hG1C;EQ3hGM;;IAEE,+BAAgC;ER6hGxC;EQ5iGM;IAAwB,wBAA2B;ER+iGzD;EQ9iGM;;IAEE,4BAA+B;ERgjGvC;EQ9iGM;;IAEE,8BAAiC;ERgjGzC;EQ9iGM;;IAEE,+BAAkC;ERgjG1C;EQ9iGM;;IAEE,6BAAgC;ERgjGxC;EQ1iGE;IAAmB,uBAAuB;ER6iG5C;EQ5iGE;;IAEE,2BAA2B;ER8iG/B;EQ5iGE;;IAEE,6BAA6B;ER8iGjC;EQ5iGE;;IAEE,8BAA8B;ER8iGlC;EQ5iGE;;IAEE,4BAA4B;ER8iGhC;AACF;;AGxjGI;EKlDI;IAAgC,oBAA4B;ER+mGlE;EQ9mGM;;IAEE,wBAAoC;ERgnG5C;EQ9mGM;;IAEE,0BAAwC;ERgnGhD;EQ9mGM;;IAEE,2BAA0C;ERgnGlD;EQ9mGM;;IAEE,yBAAsC;ERgnG9C;EQ/nGM;IAAgC,0BAA4B;ERkoGlE;EQjoGM;;IAEE,8BAAoC;ERmoG5C;EQjoGM;;IAEE,gCAAwC;ERmoGhD;EQjoGM;;IAEE,iCAA0C;ERmoGlD;EQjoGM;;IAEE,+BAAsC;ERmoG9C;EQlpGM;IAAgC,yBAA4B;ERqpGlE;EQppGM;;IAEE,6BAAoC;ERspG5C;EQppGM;;IAEE,+BAAwC;ERspGhD;EQppGM;;IAEE,gCAA0C;ERspGlD;EQppGM;;IAEE,8BAAsC;ERspG9C;EQrqGM;IAAgC,uBAA4B;ERwqGlE;EQvqGM;;IAEE,2BAAoC;ERyqG5C;EQvqGM;;IAEE,6BAAwC;ERyqGhD;EQvqGM;;IAEE,8BAA0C;ERyqGlD;EQvqGM;;IAEE,4BAAsC;ERyqG9C;EQxrGM;IAAgC,yBAA4B;ER2rGlE;EQ1rGM;;IAEE,6BAAoC;ER4rG5C;EQ1rGM;;IAEE,+BAAwC;ER4rGhD;EQ1rGM;;IAEE,gCAA0C;ER4rGlD;EQ1rGM;;IAEE,8BAAsC;ER4rG9C;EQ3sGM;IAAgC,uBAA4B;ER8sGlE;EQ7sGM;;IAEE,2BAAoC;ER+sG5C;EQ7sGM;;IAEE,6BAAwC;ER+sGhD;EQ7sGM;;IAEE,8BAA0C;ER+sGlD;EQ7sGM;;IAEE,4BAAsC;ER+sG9C;EQ9tGM;IAAgC,qBAA4B;ERiuGlE;EQhuGM;;IAEE,yBAAoC;ERkuG5C;EQhuGM;;IAEE,2BAAwC;ERkuGhD;EQhuGM;;IAEE,4BAA0C;ERkuGlD;EQhuGM;;IAEE,0BAAsC;ERkuG9C;EQjvGM;IAAgC,2BAA4B;ERovGlE;EQnvGM;;IAEE,+BAAoC;ERqvG5C;EQnvGM;;IAEE,iCAAwC;ERqvGhD;EQnvGM;;IAEE,kCAA0C;ERqvGlD;EQnvGM;;IAEE,gCAAsC;ERqvG9C;EQpwGM;IAAgC,0BAA4B;ERuwGlE;EQtwGM;;IAEE,8BAAoC;ERwwG5C;EQtwGM;;IAEE,gCAAwC;ERwwGhD;EQtwGM;;IAEE,iCAA0C;ERwwGlD;EQtwGM;;IAEE,+BAAsC;ERwwG9C;EQvxGM;IAAgC,wBAA4B;ER0xGlE;EQzxGM;;IAEE,4BAAoC;ER2xG5C;EQzxGM;;IAEE,8BAAwC;ER2xGhD;EQzxGM;;IAEE,+BAA0C;ER2xGlD;EQzxGM;;IAEE,6BAAsC;ER2xG9C;EQ1yGM;IAAgC,0BAA4B;ER6yGlE;EQ5yGM;;IAEE,8BAAoC;ER8yG5C;EQ5yGM;;IAEE,gCAAwC;ER8yGhD;EQ5yGM;;IAEE,iCAA0C;ER8yGlD;EQ5yGM;;IAEE,+BAAsC;ER8yG9C;EQ7zGM;IAAgC,wBAA4B;ERg0GlE;EQ/zGM;;IAEE,4BAAoC;ERi0G5C;EQ/zGM;;IAEE,8BAAwC;ERi0GhD;EQ/zGM;;IAEE,+BAA0C;ERi0GlD;EQ/zGM;;IAEE,6BAAsC;ERi0G9C;EQzzGM;IAAwB,2BAA2B;ER4zGzD;EQ3zGM;;IAEE,+BAA+B;ER6zGvC;EQ3zGM;;IAEE,iCAAiC;ER6zGzC;EQ3zGM;;IAEE,kCAAkC;ER6zG1C;EQ3zGM;;IAEE,gCAAgC;ER6zGxC;EQ50GM;IAAwB,0BAA2B;ER+0GzD;EQ90GM;;IAEE,8BAA+B;ERg1GvC;EQ90GM;;IAEE,gCAAiC;ERg1GzC;EQ90GM;;IAEE,iCAAkC;ERg1G1C;EQ90GM;;IAEE,+BAAgC;ERg1GxC;EQ/1GM;IAAwB,wBAA2B;ERk2GzD;EQj2GM;;IAEE,4BAA+B;ERm2GvC;EQj2GM;;IAEE,8BAAiC;ERm2GzC;EQj2GM;;IAEE,+BAAkC;ERm2G1C;EQj2GM;;IAEE,6BAAgC;ERm2GxC;EQl3GM;IAAwB,0BAA2B;ERq3GzD;EQp3GM;;IAEE,8BAA+B;ERs3GvC;EQp3GM;;IAEE,gCAAiC;ERs3GzC;EQp3GM;;IAEE,iCAAkC;ERs3G1C;EQp3GM;;IAEE,+BAAgC;ERs3GxC;EQr4GM;IAAwB,wBAA2B;ERw4GzD;EQv4GM;;IAEE,4BAA+B;ERy4GvC;EQv4GM;;IAEE,8BAAiC;ERy4GzC;EQv4GM;;IAEE,+BAAkC;ERy4G1C;EQv4GM;;IAEE,6BAAgC;ERy4GxC;EQn4GE;IAAmB,uBAAuB;ERs4G5C;EQr4GE;;IAEE,2BAA2B;ERu4G/B;EQr4GE;;IAEE,6BAA6B;ERu4GjC;EQr4GE;;IAEE,8BAA8B;ERu4GlC;EQr4GE;;IAEE,4BAA4B;ERu4GhC;AACF","file":"bootstrap-grid.css","sourcesContent":["/*!\n * Bootstrap Grid v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\nhtml {\n box-sizing: border-box;\n -ms-overflow-style: scrollbar;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n@import \"functions\";\n@import \"variables\";\n\n@import \"mixins/breakpoints\";\n@import \"mixins/grid-framework\";\n@import \"mixins/grid\";\n\n@import \"grid\";\n@import \"utilities/display\";\n@import \"utilities/flex\";\n@import \"utilities/spacing\";\n","/*!\n * Bootstrap Grid v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\nhtml {\n box-sizing: border-box;\n -ms-overflow-style: scrollbar;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container, .container-sm {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container, .container-sm, .container-md {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container, .container-sm, .container-md, .container-lg {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container, .container-sm, .container-md, .container-lg, .container-xl {\n max-width: 1140px;\n }\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.row-cols-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n}\n\n.col-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n order: -1;\n}\n\n.order-last {\n order: 13;\n}\n\n.order-0 {\n order: 0;\n}\n\n.order-1 {\n order: 1;\n}\n\n.order-2 {\n order: 2;\n}\n\n.order-3 {\n order: 3;\n}\n\n.order-4 {\n order: 4;\n}\n\n.order-5 {\n order: 5;\n}\n\n.order-6 {\n order: 6;\n}\n\n.order-7 {\n order: 7;\n}\n\n.order-8 {\n order: 8;\n}\n\n.order-9 {\n order: 9;\n}\n\n.order-10 {\n order: 10;\n}\n\n.order-11 {\n order: 11;\n}\n\n.order-12 {\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-sm-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-sm-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-sm-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-sm-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-sm-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-sm-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-sm-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n order: -1;\n }\n .order-sm-last {\n order: 13;\n }\n .order-sm-0 {\n order: 0;\n }\n .order-sm-1 {\n order: 1;\n }\n .order-sm-2 {\n order: 2;\n }\n .order-sm-3 {\n order: 3;\n }\n .order-sm-4 {\n order: 4;\n }\n .order-sm-5 {\n order: 5;\n }\n .order-sm-6 {\n order: 6;\n }\n .order-sm-7 {\n order: 7;\n }\n .order-sm-8 {\n order: 8;\n }\n .order-sm-9 {\n order: 9;\n }\n .order-sm-10 {\n order: 10;\n }\n .order-sm-11 {\n order: 11;\n }\n .order-sm-12 {\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-md-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-md-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-md-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-md-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-md-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-md-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-md-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n order: -1;\n }\n .order-md-last {\n order: 13;\n }\n .order-md-0 {\n order: 0;\n }\n .order-md-1 {\n order: 1;\n }\n .order-md-2 {\n order: 2;\n }\n .order-md-3 {\n order: 3;\n }\n .order-md-4 {\n order: 4;\n }\n .order-md-5 {\n order: 5;\n }\n .order-md-6 {\n order: 6;\n }\n .order-md-7 {\n order: 7;\n }\n .order-md-8 {\n order: 8;\n }\n .order-md-9 {\n order: 9;\n }\n .order-md-10 {\n order: 10;\n }\n .order-md-11 {\n order: 11;\n }\n .order-md-12 {\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-lg-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-lg-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-lg-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-lg-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-lg-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-lg-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-lg-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n order: -1;\n }\n .order-lg-last {\n order: 13;\n }\n .order-lg-0 {\n order: 0;\n }\n .order-lg-1 {\n order: 1;\n }\n .order-lg-2 {\n order: 2;\n }\n .order-lg-3 {\n order: 3;\n }\n .order-lg-4 {\n order: 4;\n }\n .order-lg-5 {\n order: 5;\n }\n .order-lg-6 {\n order: 6;\n }\n .order-lg-7 {\n order: 7;\n }\n .order-lg-8 {\n order: 8;\n }\n .order-lg-9 {\n order: 9;\n }\n .order-lg-10 {\n order: 10;\n }\n .order-lg-11 {\n order: 11;\n }\n .order-lg-12 {\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-xl-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-xl-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-xl-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-xl-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-xl-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-xl-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-xl-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n order: -1;\n }\n .order-xl-last {\n order: 13;\n }\n .order-xl-0 {\n order: 0;\n }\n .order-xl-1 {\n order: 1;\n }\n .order-xl-2 {\n order: 2;\n }\n .order-xl-3 {\n order: 3;\n }\n .order-xl-4 {\n order: 4;\n }\n .order-xl-5 {\n order: 5;\n }\n .order-xl-6 {\n order: 6;\n }\n .order-xl-7 {\n order: 7;\n }\n .order-xl-8 {\n order: 8;\n }\n .order-xl-9 {\n order: 9;\n }\n .order-xl-10 {\n order: 10;\n }\n .order-xl-11 {\n order: 11;\n }\n .order-xl-12 {\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-n1 {\n margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n margin-left: -1rem !important;\n}\n\n.m-n4 {\n margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n margin-left: -3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-n1 {\n margin: -0.25rem !important;\n }\n .mt-sm-n1,\n .my-sm-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-sm-n1,\n .mx-sm-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-sm-n1,\n .my-sm-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-sm-n1,\n .mx-sm-n1 {\n margin-left: -0.25rem !important;\n }\n .m-sm-n2 {\n margin: -0.5rem !important;\n }\n .mt-sm-n2,\n .my-sm-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-sm-n2,\n .mx-sm-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-sm-n2,\n .my-sm-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-sm-n2,\n .mx-sm-n2 {\n margin-left: -0.5rem !important;\n }\n .m-sm-n3 {\n margin: -1rem !important;\n }\n .mt-sm-n3,\n .my-sm-n3 {\n margin-top: -1rem !important;\n }\n .mr-sm-n3,\n .mx-sm-n3 {\n margin-right: -1rem !important;\n }\n .mb-sm-n3,\n .my-sm-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-sm-n3,\n .mx-sm-n3 {\n margin-left: -1rem !important;\n }\n .m-sm-n4 {\n margin: -1.5rem !important;\n }\n .mt-sm-n4,\n .my-sm-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-sm-n4,\n .mx-sm-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-sm-n4,\n .my-sm-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-sm-n4,\n .mx-sm-n4 {\n margin-left: -1.5rem !important;\n }\n .m-sm-n5 {\n margin: -3rem !important;\n }\n .mt-sm-n5,\n .my-sm-n5 {\n margin-top: -3rem !important;\n }\n .mr-sm-n5,\n .mx-sm-n5 {\n margin-right: -3rem !important;\n }\n .mb-sm-n5,\n .my-sm-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-sm-n5,\n .mx-sm-n5 {\n margin-left: -3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-n1 {\n margin: -0.25rem !important;\n }\n .mt-md-n1,\n .my-md-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-md-n1,\n .mx-md-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-md-n1,\n .my-md-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-md-n1,\n .mx-md-n1 {\n margin-left: -0.25rem !important;\n }\n .m-md-n2 {\n margin: -0.5rem !important;\n }\n .mt-md-n2,\n .my-md-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-md-n2,\n .mx-md-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-md-n2,\n .my-md-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-md-n2,\n .mx-md-n2 {\n margin-left: -0.5rem !important;\n }\n .m-md-n3 {\n margin: -1rem !important;\n }\n .mt-md-n3,\n .my-md-n3 {\n margin-top: -1rem !important;\n }\n .mr-md-n3,\n .mx-md-n3 {\n margin-right: -1rem !important;\n }\n .mb-md-n3,\n .my-md-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-md-n3,\n .mx-md-n3 {\n margin-left: -1rem !important;\n }\n .m-md-n4 {\n margin: -1.5rem !important;\n }\n .mt-md-n4,\n .my-md-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-md-n4,\n .mx-md-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-md-n4,\n .my-md-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-md-n4,\n .mx-md-n4 {\n margin-left: -1.5rem !important;\n }\n .m-md-n5 {\n margin: -3rem !important;\n }\n .mt-md-n5,\n .my-md-n5 {\n margin-top: -3rem !important;\n }\n .mr-md-n5,\n .mx-md-n5 {\n margin-right: -3rem !important;\n }\n .mb-md-n5,\n .my-md-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-md-n5,\n .mx-md-n5 {\n margin-left: -3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-n1 {\n margin: -0.25rem !important;\n }\n .mt-lg-n1,\n .my-lg-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-lg-n1,\n .mx-lg-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-lg-n1,\n .my-lg-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-lg-n1,\n .mx-lg-n1 {\n margin-left: -0.25rem !important;\n }\n .m-lg-n2 {\n margin: -0.5rem !important;\n }\n .mt-lg-n2,\n .my-lg-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-lg-n2,\n .mx-lg-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-lg-n2,\n .my-lg-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-lg-n2,\n .mx-lg-n2 {\n margin-left: -0.5rem !important;\n }\n .m-lg-n3 {\n margin: -1rem !important;\n }\n .mt-lg-n3,\n .my-lg-n3 {\n margin-top: -1rem !important;\n }\n .mr-lg-n3,\n .mx-lg-n3 {\n margin-right: -1rem !important;\n }\n .mb-lg-n3,\n .my-lg-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-lg-n3,\n .mx-lg-n3 {\n margin-left: -1rem !important;\n }\n .m-lg-n4 {\n margin: -1.5rem !important;\n }\n .mt-lg-n4,\n .my-lg-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-lg-n4,\n .mx-lg-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-lg-n4,\n .my-lg-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-lg-n4,\n .mx-lg-n4 {\n margin-left: -1.5rem !important;\n }\n .m-lg-n5 {\n margin: -3rem !important;\n }\n .mt-lg-n5,\n .my-lg-n5 {\n margin-top: -3rem !important;\n }\n .mr-lg-n5,\n .mx-lg-n5 {\n margin-right: -3rem !important;\n }\n .mb-lg-n5,\n .my-lg-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-lg-n5,\n .mx-lg-n5 {\n margin-left: -3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-n1 {\n margin: -0.25rem !important;\n }\n .mt-xl-n1,\n .my-xl-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-xl-n1,\n .mx-xl-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-xl-n1,\n .my-xl-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-xl-n1,\n .mx-xl-n1 {\n margin-left: -0.25rem !important;\n }\n .m-xl-n2 {\n margin: -0.5rem !important;\n }\n .mt-xl-n2,\n .my-xl-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-xl-n2,\n .mx-xl-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-xl-n2,\n .my-xl-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-xl-n2,\n .mx-xl-n2 {\n margin-left: -0.5rem !important;\n }\n .m-xl-n3 {\n margin: -1rem !important;\n }\n .mt-xl-n3,\n .my-xl-n3 {\n margin-top: -1rem !important;\n }\n .mr-xl-n3,\n .mx-xl-n3 {\n margin-right: -1rem !important;\n }\n .mb-xl-n3,\n .my-xl-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-xl-n3,\n .mx-xl-n3 {\n margin-left: -1rem !important;\n }\n .m-xl-n4 {\n margin: -1.5rem !important;\n }\n .mt-xl-n4,\n .my-xl-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-xl-n4,\n .mx-xl-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-xl-n4,\n .my-xl-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-xl-n4,\n .mx-xl-n4 {\n margin-left: -1.5rem !important;\n }\n .m-xl-n5 {\n margin: -3rem !important;\n }\n .mt-xl-n5,\n .my-xl-n5 {\n margin-top: -3rem !important;\n }\n .mr-xl-n5,\n .mx-xl-n5 {\n margin-right: -3rem !important;\n }\n .mb-xl-n5,\n .my-xl-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-xl-n5,\n .mx-xl-n5 {\n margin-left: -3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap-grid.css.map */","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n // Single container class with breakpoint max-widths\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n @each $name, $width in $grid-breakpoints {\n @if ($container-max-width > $width or $breakpoint == $name) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n }\n }\n }\n }\n}\n\n\n// Row\n//\n// Rows contain your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container($gutter: $grid-gutter-width) {\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row($gutter: $grid-gutter-width) {\n display: flex;\n flex-wrap: wrap;\n margin-right: -$gutter / 2;\n margin-left: -$gutter / 2;\n}\n\n@mixin make-col-ready($gutter: $grid-gutter-width) {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%; // Reset earlier grid tiers\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// numberof columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n & > * {\n flex: 0 0 100% / $count;\n max-width: 100% / $count;\n }\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n != null and $n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n// Color system\n\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n\n$grays: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$grays: map-merge(\n (\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n ),\n $grays\n);\n\n$blue: #007bff !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #e83e8c !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #28a745 !default;\n$teal: #20c997 !default;\n$cyan: #17a2b8 !default;\n\n$colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$colors: map-merge(\n (\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n ),\n $colors\n);\n\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-800 !default;\n\n$theme-colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$theme-colors: map-merge(\n (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n ),\n $theme-colors\n);\n\n// Set a specific jump point for requesting color jumps\n$theme-color-interval: 8% !default;\n\n// The yiq lightness value that determines when the lightness of color changes from \"dark\" to \"light\". Acceptable values are between 0 and 255.\n$yiq-contrasted-threshold: 150 !default;\n\n// Customize the light and dark text colors for use in our YIQ color contrast function.\n$yiq-text-dark: $gray-900 !default;\n$yiq-text-light: $white !default;\n\n// Characters which are escaped by the escape-svg function\n$escaped-characters: (\n (\"<\",\"%3c\"),\n (\">\",\"%3e\"),\n (\"#\",\"%23\"),\n) !default;\n\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-prefers-reduced-motion-media-query: true !default;\n$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS\n$enable-grid-classes: true !default;\n$enable-pointer-cursor-for-buttons: true !default;\n$enable-print-styles: true !default;\n$enable-responsive-font-sizes: false !default;\n$enable-validation-icons: true !default;\n$enable-deprecation-messages: true !default;\n\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n$spacer: 1rem !default;\n$spacers: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$spacers: map-merge(\n (\n 0: 0,\n 1: ($spacer * .25),\n 2: ($spacer * .5),\n 3: $spacer,\n 4: ($spacer * 1.5),\n 5: ($spacer * 3)\n ),\n $spacers\n);\n\n// This variable affects the `.h-*` and `.w-*` classes.\n$sizes: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$sizes: map-merge(\n (\n 25: 25%,\n 50: 50%,\n 75: 75%,\n 100: 100%,\n auto: auto\n ),\n $sizes\n);\n\n\n// Body\n//\n// Settings for the `` element.\n\n$body-bg: $white !default;\n$body-color: $gray-900 !default;\n\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: theme-color(\"primary\") !default;\n$link-decoration: none !default;\n$link-hover-color: darken($link-color, 15%) !default;\n$link-hover-decoration: underline !default;\n// Darken percentage for links with `.text-*` class (e.g. `.text-success`)\n$emphasized-link-hover-darken-percentage: 15% !default;\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px\n) !default;\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints, \"$grid-breakpoints\");\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px\n) !default;\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 30px !default;\n$grid-row-columns: 6 !default;\n\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n$line-height-lg: 1.5 !default;\n$line-height-sm: 1.5 !default;\n\n$border-width: 1px !default;\n$border-color: $gray-300 !default;\n\n$border-radius: .25rem !default;\n$border-radius-lg: .3rem !default;\n$border-radius-sm: .2rem !default;\n\n$rounded-pill: 50rem !default;\n\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n\n$component-active-color: $white !default;\n$component-active-bg: theme-color(\"primary\") !default;\n\n$caret-width: .3em !default;\n$caret-vertical-align: $caret-width * .85 !default;\n$caret-spacing: $caret-width * .85 !default;\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n$transition-collapse: height .35s ease !default;\n\n$embed-responsive-aspect-ratios: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$embed-responsive-aspect-ratios: join(\n (\n (21 9),\n (16 9),\n (4 3),\n (1 1),\n ),\n $embed-responsive-aspect-ratios\n);\n\n// Typography\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n$font-family-base: $font-family-sans-serif !default;\n// stylelint-enable value-keyword-case\n\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-lg: $font-size-base * 1.25 !default;\n$font-size-sm: $font-size-base * .875 !default;\n\n$font-weight-lighter: lighter !default;\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-bold: 700 !default;\n$font-weight-bolder: bolder !default;\n\n$font-weight-base: $font-weight-normal !default;\n$line-height-base: 1.5 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n\n$headings-margin-bottom: $spacer / 2 !default;\n$headings-font-family: null !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: null !default;\n\n$display1-size: 6rem !default;\n$display2-size: 5.5rem !default;\n$display3-size: 4.5rem !default;\n$display4-size: 3.5rem !default;\n\n$display1-weight: 300 !default;\n$display2-weight: 300 !default;\n$display3-weight: 300 !default;\n$display4-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n\n$lead-font-size: $font-size-base * 1.25 !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: 80% !default;\n\n$text-muted: $gray-600 !default;\n\n$blockquote-small-color: $gray-600 !default;\n$blockquote-small-font-size: $small-font-size !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n\n$hr-border-color: rgba($black, .1) !default;\n$hr-border-width: $border-width !default;\n\n$mark-padding: .2em !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default;\n$nested-kbd-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-bg: #fcf8e3 !default;\n\n$hr-margin-y: $spacer !default;\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n$table-cell-padding: .75rem !default;\n$table-cell-padding-sm: .3rem !default;\n\n$table-color: $body-color !default;\n$table-bg: null !default;\n$table-accent-bg: rgba($black, .05) !default;\n$table-hover-color: $table-color !default;\n$table-hover-bg: rgba($black, .075) !default;\n$table-active-bg: $table-hover-bg !default;\n\n$table-border-width: $border-width !default;\n$table-border-color: $border-color !default;\n\n$table-head-bg: $gray-200 !default;\n$table-head-color: $gray-700 !default;\n\n$table-dark-color: $white !default;\n$table-dark-bg: $gray-800 !default;\n$table-dark-accent-bg: rgba($white, .05) !default;\n$table-dark-hover-color: $table-dark-color !default;\n$table-dark-hover-bg: rgba($white, .075) !default;\n$table-dark-border-color: lighten($table-dark-bg, 7.5%) !default;\n\n$table-striped-order: odd !default;\n\n$table-caption-color: $text-muted !default;\n\n$table-bg-level: -9 !default;\n$table-border-level: -6 !default;\n\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-font-family: null !default;\n$input-btn-font-size: $font-size-base !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: .2rem !default;\n$input-btn-focus-color: rgba($component-active-bg, .25) !default;\n$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-font-size-sm: $font-size-sm !default;\n$input-btn-line-height-sm: $line-height-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-font-size-lg: $font-size-lg !default;\n$input-btn-line-height-lg: $line-height-lg !default;\n\n$input-btn-border-width: $border-width !default;\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-font-family: $input-btn-font-family !default;\n$btn-font-size: $input-btn-font-size !default;\n$btn-line-height: $input-btn-line-height !default;\n$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-font-size-sm: $input-btn-font-size-sm !default;\n$btn-line-height-sm: $input-btn-line-height-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-font-size-lg: $input-btn-font-size-lg !default;\n$btn-line-height-lg: $input-btn-line-height-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-disabled-color: $gray-600 !default;\n\n$btn-block-spacing-y: .5rem !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: $border-radius !default;\n$btn-border-radius-lg: $border-radius-lg !default;\n$btn-border-radius-sm: $border-radius-sm !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n\n// Forms\n\n$label-margin-bottom: .5rem !default;\n\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-font-family: $input-btn-font-family !default;\n$input-font-size: $input-btn-font-size !default;\n$input-font-weight: $font-weight-base !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-font-size-sm: $input-btn-font-size-sm !default;\n$input-line-height-sm: $input-btn-line-height-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-font-size-lg: $input-btn-font-size-lg !default;\n$input-line-height-lg: $input-btn-line-height-lg !default;\n\n$input-bg: $white !default;\n$input-disabled-bg: $gray-200 !default;\n\n$input-color: $gray-700 !default;\n$input-border-color: $gray-400 !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: inset 0 1px 1px rgba($black, .075) !default;\n\n$input-border-radius: $border-radius !default;\n$input-border-radius-lg: $border-radius-lg !default;\n$input-border-radius-sm: $border-radius-sm !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: lighten($component-active-bg, 25%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: $gray-600 !default;\n$input-plaintext-color: $body-color !default;\n\n$input-height-border: $input-border-width * 2 !default;\n\n$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;\n$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;\n$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y / 2) !default;\n\n$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;\n$input-height-sm: add($input-line-height-sm * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;\n$input-height-lg: add($input-line-height-lg * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-text-margin-top: .25rem !default;\n\n$form-check-input-gutter: 1.25rem !default;\n$form-check-input-margin-y: .3rem !default;\n$form-check-input-margin-x: .25rem !default;\n\n$form-check-inline-margin-x: .75rem !default;\n$form-check-inline-input-margin-x: .3125rem !default;\n\n$form-grid-gutter-width: 10px !default;\n$form-group-margin-bottom: 1rem !default;\n\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: $gray-200 !default;\n$input-group-addon-border-color: $input-border-color !default;\n\n$custom-forms-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$custom-control-gutter: .5rem !default;\n$custom-control-spacer-x: 1rem !default;\n$custom-control-cursor: null !default;\n\n$custom-control-indicator-size: 1rem !default;\n$custom-control-indicator-bg: $input-bg !default;\n\n$custom-control-indicator-bg-size: 50% 50% !default;\n$custom-control-indicator-box-shadow: $input-box-shadow !default;\n$custom-control-indicator-border-color: $gray-500 !default;\n$custom-control-indicator-border-width: $input-border-width !default;\n\n$custom-control-label-color: null !default;\n\n$custom-control-indicator-disabled-bg: $input-disabled-bg !default;\n$custom-control-label-disabled-color: $gray-600 !default;\n\n$custom-control-indicator-checked-color: $component-active-color !default;\n$custom-control-indicator-checked-bg: $component-active-bg !default;\n$custom-control-indicator-checked-disabled-bg: rgba(theme-color(\"primary\"), .5) !default;\n$custom-control-indicator-checked-box-shadow: none !default;\n$custom-control-indicator-checked-border-color: $custom-control-indicator-checked-bg !default;\n\n$custom-control-indicator-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-control-indicator-focus-border-color: $input-focus-border-color !default;\n\n$custom-control-indicator-active-color: $component-active-color !default;\n$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-control-indicator-active-box-shadow: none !default;\n$custom-control-indicator-active-border-color: $custom-control-indicator-active-bg !default;\n\n$custom-checkbox-indicator-border-radius: $border-radius !default;\n$custom-checkbox-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default;\n$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;\n$custom-checkbox-indicator-icon-indeterminate: url(\"data:image/svg+xml,\") !default;\n$custom-checkbox-indicator-indeterminate-box-shadow: none !default;\n$custom-checkbox-indicator-indeterminate-border-color: $custom-checkbox-indicator-indeterminate-bg !default;\n\n$custom-radio-indicator-border-radius: 50% !default;\n$custom-radio-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-switch-width: $custom-control-indicator-size * 1.75 !default;\n$custom-switch-indicator-border-radius: $custom-control-indicator-size / 2 !default;\n$custom-switch-indicator-size: subtract($custom-control-indicator-size, $custom-control-indicator-border-width * 4) !default;\n\n$custom-select-padding-y: $input-padding-y !default;\n$custom-select-padding-x: $input-padding-x !default;\n$custom-select-font-family: $input-font-family !default;\n$custom-select-font-size: $input-font-size !default;\n$custom-select-height: $input-height !default;\n$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator\n$custom-select-font-weight: $input-font-weight !default;\n$custom-select-line-height: $input-line-height !default;\n$custom-select-color: $input-color !default;\n$custom-select-disabled-color: $gray-600 !default;\n$custom-select-bg: $input-bg !default;\n$custom-select-disabled-bg: $gray-200 !default;\n$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions\n$custom-select-indicator-color: $gray-800 !default;\n$custom-select-indicator: url(\"data:image/svg+xml,\") !default;\n$custom-select-background: escape-svg($custom-select-indicator) no-repeat right $custom-select-padding-x center / $custom-select-bg-size !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)\n\n$custom-select-feedback-icon-padding-right: add(1em * .75, (2 * $custom-select-padding-y * .75) + $custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-position: center right ($custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;\n\n$custom-select-border-width: $input-border-width !default;\n$custom-select-border-color: $input-border-color !default;\n$custom-select-border-radius: $border-radius !default;\n$custom-select-box-shadow: inset 0 1px 2px rgba($black, .075) !default;\n\n$custom-select-focus-border-color: $input-focus-border-color !default;\n$custom-select-focus-width: $input-focus-width !default;\n$custom-select-focus-box-shadow: 0 0 0 $custom-select-focus-width $input-btn-focus-color !default;\n\n$custom-select-padding-y-sm: $input-padding-y-sm !default;\n$custom-select-padding-x-sm: $input-padding-x-sm !default;\n$custom-select-font-size-sm: $input-font-size-sm !default;\n$custom-select-height-sm: $input-height-sm !default;\n\n$custom-select-padding-y-lg: $input-padding-y-lg !default;\n$custom-select-padding-x-lg: $input-padding-x-lg !default;\n$custom-select-font-size-lg: $input-font-size-lg !default;\n$custom-select-height-lg: $input-height-lg !default;\n\n$custom-range-track-width: 100% !default;\n$custom-range-track-height: .5rem !default;\n$custom-range-track-cursor: pointer !default;\n$custom-range-track-bg: $gray-300 !default;\n$custom-range-track-border-radius: 1rem !default;\n$custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;\n\n$custom-range-thumb-width: 1rem !default;\n$custom-range-thumb-height: $custom-range-thumb-width !default;\n$custom-range-thumb-bg: $component-active-bg !default;\n$custom-range-thumb-border: 0 !default;\n$custom-range-thumb-border-radius: 1rem !default;\n$custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;\n$custom-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in IE/Edge\n$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-range-thumb-disabled-bg: $gray-500 !default;\n\n$custom-file-height: $input-height !default;\n$custom-file-height-inner: $input-height-inner !default;\n$custom-file-focus-border-color: $input-focus-border-color !default;\n$custom-file-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-file-disabled-bg: $input-disabled-bg !default;\n\n$custom-file-padding-y: $input-padding-y !default;\n$custom-file-padding-x: $input-padding-x !default;\n$custom-file-line-height: $input-line-height !default;\n$custom-file-font-family: $input-font-family !default;\n$custom-file-font-weight: $input-font-weight !default;\n$custom-file-color: $input-color !default;\n$custom-file-bg: $input-bg !default;\n$custom-file-border-width: $input-border-width !default;\n$custom-file-border-color: $input-border-color !default;\n$custom-file-border-radius: $input-border-radius !default;\n$custom-file-box-shadow: $input-box-shadow !default;\n$custom-file-button-color: $custom-file-color !default;\n$custom-file-button-bg: $input-group-addon-bg !default;\n$custom-file-text: (\n en: \"Browse\"\n) !default;\n\n\n// Form validation\n\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $small-font-size !default;\n$form-feedback-valid-color: theme-color(\"success\") !default;\n$form-feedback-invalid-color: theme-color(\"danger\") !default;\n\n$form-feedback-icon-valid-color: $form-feedback-valid-color !default;\n$form-feedback-icon-valid: url(\"data:image/svg+xml,\") !default;\n$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;\n$form-feedback-icon-invalid: url(\"data:image/svg+xml,\") !default;\n\n$form-validation-states: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$form-validation-states: map-merge(\n (\n \"valid\": (\n \"color\": $form-feedback-valid-color,\n \"icon\": $form-feedback-icon-valid\n ),\n \"invalid\": (\n \"color\": $form-feedback-invalid-color,\n \"icon\": $form-feedback-icon-invalid\n ),\n ),\n $form-validation-states\n);\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-modal-backdrop: 1040 !default;\n$zindex-modal: 1050 !default;\n$zindex-popover: 1060 !default;\n$zindex-tooltip: 1070 !default;\n\n\n// Navs\n\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-disabled-color: $gray-600 !default;\n\n$nav-tabs-border-color: $gray-300 !default;\n$nav-tabs-border-width: $border-width !default;\n$nav-tabs-border-radius: $border-radius !default;\n$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: $gray-700 !default;\n$nav-tabs-link-active-bg: $body-bg !default;\n$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: $border-radius !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-divider-color: $gray-200 !default;\n$nav-divider-margin-y: $spacer / 2 !default;\n\n\n// Navbar\n\n$navbar-padding-y: $spacer / 2 !default;\n$navbar-padding-x: $spacer !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n\n$navbar-dark-color: rgba($white, .5) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n\n$navbar-light-color: rgba($black, .5) !default;\n$navbar-light-hover-color: rgba($black, .7) !default;\n$navbar-light-active-color: rgba($black, .9) !default;\n$navbar-light-disabled-color: rgba($black, .3) !default;\n$navbar-light-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-light-toggler-border-color: rgba($black, .1) !default;\n\n$navbar-light-brand-color: $navbar-light-active-color !default;\n$navbar-light-brand-hover-color: $navbar-light-active-color !default;\n$navbar-dark-brand-color: $navbar-dark-active-color !default;\n$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-font-size: $font-size-base !default;\n$dropdown-color: $body-color !default;\n$dropdown-bg: $white !default;\n$dropdown-border-color: rgba($black, .15) !default;\n$dropdown-border-radius: $border-radius !default;\n$dropdown-border-width: $border-width !default;\n$dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default;\n$dropdown-divider-bg: $gray-200 !default;\n$dropdown-divider-margin-y: $nav-divider-margin-y !default;\n$dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;\n\n$dropdown-link-color: $gray-900 !default;\n$dropdown-link-hover-color: darken($gray-900, 5%) !default;\n$dropdown-link-hover-bg: $gray-100 !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: $gray-600 !default;\n\n$dropdown-item-padding-y: .25rem !default;\n$dropdown-item-padding-x: 1.5rem !default;\n\n$dropdown-header-color: $gray-600 !default;\n\n\n// Pagination\n\n$pagination-padding-y: .5rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n$pagination-line-height: 1.25 !default;\n\n$pagination-color: $link-color !default;\n$pagination-bg: $white !default;\n$pagination-border-width: $border-width !default;\n$pagination-border-color: $gray-300 !default;\n\n$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: $link-hover-color !default;\n$pagination-hover-bg: $gray-200 !default;\n$pagination-hover-border-color: $gray-300 !default;\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $pagination-active-bg !default;\n\n$pagination-disabled-color: $gray-600 !default;\n$pagination-disabled-bg: $white !default;\n$pagination-disabled-border-color: $gray-300 !default;\n\n\n// Jumbotron\n\n$jumbotron-padding: 2rem !default;\n$jumbotron-color: null !default;\n$jumbotron-bg: $gray-200 !default;\n\n\n// Cards\n\n$card-spacer-y: .75rem !default;\n$card-spacer-x: 1.25rem !default;\n$card-border-width: $border-width !default;\n$card-border-radius: $border-radius !default;\n$card-border-color: rgba($black, .125) !default;\n$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;\n$card-cap-bg: rgba($black, .03) !default;\n$card-cap-color: null !default;\n$card-height: null !default;\n$card-color: null !default;\n$card-bg: $white !default;\n\n$card-img-overlay-padding: 1.25rem !default;\n\n$card-group-margin: $grid-gutter-width / 2 !default;\n$card-deck-margin: $card-group-margin !default;\n\n$card-columns-count: 3 !default;\n$card-columns-gap: 1.25rem !default;\n$card-columns-margin: $card-spacer-y !default;\n\n\n// Tooltips\n\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: $white !default;\n$tooltip-bg: $black !default;\n$tooltip-border-radius: $border-radius !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: .25rem !default;\n$tooltip-padding-x: .5rem !default;\n$tooltip-margin: 0 !default;\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n$tooltip-arrow-color: $tooltip-bg !default;\n\n// Form tooltips must come after regular tooltips\n$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;\n$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;\n$form-feedback-tooltip-font-size: $tooltip-font-size !default;\n$form-feedback-tooltip-line-height: $line-height-base !default;\n$form-feedback-tooltip-opacity: $tooltip-opacity !default;\n$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;\n\n\n// Popovers\n\n$popover-font-size: $font-size-sm !default;\n$popover-bg: $white !default;\n$popover-max-width: 276px !default;\n$popover-border-width: $border-width !default;\n$popover-border-color: rgba($black, .2) !default;\n$popover-border-radius: $border-radius-lg !default;\n$popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default;\n$popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default;\n\n$popover-header-bg: darken($popover-bg, 3%) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: .75rem !default;\n\n$popover-body-color: $body-color !default;\n$popover-body-padding-y: $popover-header-padding-y !default;\n$popover-body-padding-x: $popover-header-padding-x !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n$popover-arrow-color: $popover-bg !default;\n\n$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;\n\n\n// Toasts\n\n$toast-max-width: 350px !default;\n$toast-padding-x: .75rem !default;\n$toast-padding-y: .25rem !default;\n$toast-font-size: .875rem !default;\n$toast-color: null !default;\n$toast-background-color: rgba($white, .85) !default;\n$toast-border-width: 1px !default;\n$toast-border-color: rgba(0, 0, 0, .1) !default;\n$toast-border-radius: .25rem !default;\n$toast-box-shadow: 0 .25rem .75rem rgba($black, .1) !default;\n\n$toast-header-color: $gray-600 !default;\n$toast-header-background-color: rgba($white, .85) !default;\n$toast-header-border-color: rgba(0, 0, 0, .05) !default;\n\n\n// Badges\n\n$badge-font-size: 75% !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-padding-y: .25em !default;\n$badge-padding-x: .4em !default;\n$badge-border-radius: $border-radius !default;\n\n$badge-transition: $btn-transition !default;\n$badge-focus-width: $input-btn-focus-width !default;\n\n$badge-pill-padding-x: .6em !default;\n// Use a higher than normal value to ensure completely rounded edges when\n// customizing padding or font-size on labels.\n$badge-pill-border-radius: 10rem !default;\n\n\n// Modals\n\n// Padding applied to the modal body\n$modal-inner-padding: 1rem !default;\n\n// Margin between elements in footer, must be lower than or equal to 2 * $modal-inner-padding\n$modal-footer-margin-between: .5rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-color: null !default;\n$modal-content-bg: $white !default;\n$modal-content-border-color: rgba($black, .2) !default;\n$modal-content-border-width: $border-width !default;\n$modal-content-border-radius: $border-radius-lg !default;\n$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;\n$modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default;\n$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n$modal-header-border-color: $border-color !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n$modal-header-padding-y: 1rem !default;\n$modal-header-padding-x: 1rem !default;\n$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility\n\n$modal-xl: 1140px !default;\n$modal-lg: 800px !default;\n$modal-md: 500px !default;\n$modal-sm: 300px !default;\n\n$modal-fade-transform: translate(0, -50px) !default;\n$modal-show-transform: none !default;\n$modal-transition: transform .3s ease-out !default;\n$modal-scale-transform: scale(1.02) !default;\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n$alert-padding-y: .75rem !default;\n$alert-padding-x: 1.25rem !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: $border-radius !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: $border-width !default;\n\n$alert-bg-level: -10 !default;\n$alert-border-level: -9 !default;\n$alert-color-level: 6 !default;\n\n\n// Progress bars\n\n$progress-height: 1rem !default;\n$progress-font-size: $font-size-base * .75 !default;\n$progress-bg: $gray-200 !default;\n$progress-border-radius: $border-radius !default;\n$progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: theme-color(\"primary\") !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n\n\n// List group\n\n$list-group-color: null !default;\n$list-group-bg: $white !default;\n$list-group-border-color: rgba($black, .125) !default;\n$list-group-border-width: $border-width !default;\n$list-group-border-radius: $border-radius !default;\n\n$list-group-item-padding-y: .75rem !default;\n$list-group-item-padding-x: 1.25rem !default;\n\n$list-group-hover-bg: $gray-100 !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: $gray-600 !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: $gray-700 !default;\n$list-group-action-hover-color: $list-group-action-color !default;\n\n$list-group-action-active-color: $body-color !default;\n$list-group-action-active-bg: $gray-200 !default;\n\n\n// Image thumbnails\n\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: $body-bg !default;\n$thumbnail-border-width: $border-width !default;\n$thumbnail-border-color: $gray-300 !default;\n$thumbnail-border-radius: $border-radius !default;\n$thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default;\n\n\n// Figures\n\n$figure-caption-font-size: 90% !default;\n$figure-caption-color: $gray-600 !default;\n\n\n// Breadcrumbs\n\n$breadcrumb-font-size: null !default;\n\n$breadcrumb-padding-y: .75rem !default;\n$breadcrumb-padding-x: 1rem !default;\n$breadcrumb-item-padding: .5rem !default;\n\n$breadcrumb-margin-bottom: 1rem !default;\n\n$breadcrumb-bg: $gray-200 !default;\n$breadcrumb-divider-color: $gray-600 !default;\n$breadcrumb-active-color: $gray-600 !default;\n$breadcrumb-divider: quote(\"/\") !default;\n\n$breadcrumb-border-radius: $border-radius !default;\n\n\n// Carousel\n\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n$carousel-control-hover-opacity: .9 !default;\n$carousel-control-transition: opacity .15s ease !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-hit-area-height: 10px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-active-bg: $white !default;\n$carousel-indicator-transition: opacity .6s ease !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n\n$carousel-control-icon-width: 20px !default;\n\n$carousel-control-prev-icon-bg: url(\"data:image/svg+xml,\") !default;\n$carousel-control-next-icon-bg: url(\"data:image/svg+xml,\") !default;\n\n$carousel-transition-duration: .6s !default;\n$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n\n\n// Spinners\n\n$spinner-width: 2rem !default;\n$spinner-height: $spinner-width !default;\n$spinner-border-width: .25em !default;\n\n$spinner-width-sm: 1rem !default;\n$spinner-height-sm: $spinner-width-sm !default;\n$spinner-border-width-sm: .2em !default;\n\n\n// Close\n\n$close-font-size: $font-size-base * 1.5 !default;\n$close-font-weight: $font-weight-bold !default;\n$close-color: $black !default;\n$close-text-shadow: 0 1px 0 $white !default;\n\n\n// Code\n\n$code-font-size: 87.5% !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .2rem !default;\n$kbd-padding-x: .4rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: $white !default;\n$kbd-bg: $gray-900 !default;\n\n$pre-color: $gray-900 !default;\n$pre-scrollable-max-height: 340px !default;\n\n\n// Utilities\n\n$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default;\n$overflows: auto, hidden !default;\n$positions: static, relative, absolute, fixed, sticky !default;\n\n\n// Printing\n\n$print-page-size: a3 !default;\n$print-body-min-width: map-get($grid-breakpoints, \"lg\") !default;\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","// stylelint-disable declaration-no-important\n\n//\n// Utilities for common `display` values\n//\n\n@each $breakpoint in map-keys($grid-breakpoints) {\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @each $value in $displays {\n .d#{$infix}-#{$value} { display: $value !important; }\n }\n }\n}\n\n\n//\n// Utilities for toggling `display` in print\n//\n\n@media print {\n @each $value in $displays {\n .d-print-#{$value} { display: $value !important; }\n }\n}\n","// stylelint-disable declaration-no-important\n\n// Flex variation\n//\n// Custom styles for additional flex alignment options.\n\n@each $breakpoint in map-keys($grid-breakpoints) {\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n .flex#{$infix}-row { flex-direction: row !important; }\n .flex#{$infix}-column { flex-direction: column !important; }\n .flex#{$infix}-row-reverse { flex-direction: row-reverse !important; }\n .flex#{$infix}-column-reverse { flex-direction: column-reverse !important; }\n\n .flex#{$infix}-wrap { flex-wrap: wrap !important; }\n .flex#{$infix}-nowrap { flex-wrap: nowrap !important; }\n .flex#{$infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }\n .flex#{$infix}-fill { flex: 1 1 auto !important; }\n .flex#{$infix}-grow-0 { flex-grow: 0 !important; }\n .flex#{$infix}-grow-1 { flex-grow: 1 !important; }\n .flex#{$infix}-shrink-0 { flex-shrink: 0 !important; }\n .flex#{$infix}-shrink-1 { flex-shrink: 1 !important; }\n\n .justify-content#{$infix}-start { justify-content: flex-start !important; }\n .justify-content#{$infix}-end { justify-content: flex-end !important; }\n .justify-content#{$infix}-center { justify-content: center !important; }\n .justify-content#{$infix}-between { justify-content: space-between !important; }\n .justify-content#{$infix}-around { justify-content: space-around !important; }\n\n .align-items#{$infix}-start { align-items: flex-start !important; }\n .align-items#{$infix}-end { align-items: flex-end !important; }\n .align-items#{$infix}-center { align-items: center !important; }\n .align-items#{$infix}-baseline { align-items: baseline !important; }\n .align-items#{$infix}-stretch { align-items: stretch !important; }\n\n .align-content#{$infix}-start { align-content: flex-start !important; }\n .align-content#{$infix}-end { align-content: flex-end !important; }\n .align-content#{$infix}-center { align-content: center !important; }\n .align-content#{$infix}-between { align-content: space-between !important; }\n .align-content#{$infix}-around { align-content: space-around !important; }\n .align-content#{$infix}-stretch { align-content: stretch !important; }\n\n .align-self#{$infix}-auto { align-self: auto !important; }\n .align-self#{$infix}-start { align-self: flex-start !important; }\n .align-self#{$infix}-end { align-self: flex-end !important; }\n .align-self#{$infix}-center { align-self: center !important; }\n .align-self#{$infix}-baseline { align-self: baseline !important; }\n .align-self#{$infix}-stretch { align-self: stretch !important; }\n }\n}\n","// stylelint-disable declaration-no-important\n\n// Margin and Padding\n\n@each $breakpoint in map-keys($grid-breakpoints) {\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @each $prop, $abbrev in (margin: m, padding: p) {\n @each $size, $length in $spacers {\n .#{$abbrev}#{$infix}-#{$size} { #{$prop}: $length !important; }\n .#{$abbrev}t#{$infix}-#{$size},\n .#{$abbrev}y#{$infix}-#{$size} {\n #{$prop}-top: $length !important;\n }\n .#{$abbrev}r#{$infix}-#{$size},\n .#{$abbrev}x#{$infix}-#{$size} {\n #{$prop}-right: $length !important;\n }\n .#{$abbrev}b#{$infix}-#{$size},\n .#{$abbrev}y#{$infix}-#{$size} {\n #{$prop}-bottom: $length !important;\n }\n .#{$abbrev}l#{$infix}-#{$size},\n .#{$abbrev}x#{$infix}-#{$size} {\n #{$prop}-left: $length !important;\n }\n }\n }\n\n // Negative margins (e.g., where `.mb-n1` is negative version of `.mb-1`)\n @each $size, $length in $spacers {\n @if $size != 0 {\n .m#{$infix}-n#{$size} { margin: -$length !important; }\n .mt#{$infix}-n#{$size},\n .my#{$infix}-n#{$size} {\n margin-top: -$length !important;\n }\n .mr#{$infix}-n#{$size},\n .mx#{$infix}-n#{$size} {\n margin-right: -$length !important;\n }\n .mb#{$infix}-n#{$size},\n .my#{$infix}-n#{$size} {\n margin-bottom: -$length !important;\n }\n .ml#{$infix}-n#{$size},\n .mx#{$infix}-n#{$size} {\n margin-left: -$length !important;\n }\n }\n }\n\n // Some special margin utils\n .m#{$infix}-auto { margin: auto !important; }\n .mt#{$infix}-auto,\n .my#{$infix}-auto {\n margin-top: auto !important;\n }\n .mr#{$infix}-auto,\n .mx#{$infix}-auto {\n margin-right: auto !important;\n }\n .mb#{$infix}-auto,\n .my#{$infix}-auto {\n margin-bottom: auto !important;\n }\n .ml#{$infix}-auto,\n .mx#{$infix}-auto {\n margin-left: auto !important;\n }\n }\n}\n"]} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css deleted file mode 100644 index 69bc340d..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap Grid v4.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */html{box-sizing:border-box;-ms-overflow-style:scrollbar}*,::after,::before{box-sizing:inherit}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}} -/*# sourceMappingURL=bootstrap-grid.min.css.map */ \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map deleted file mode 100644 index 1b393db3..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap-grid.scss","dist/css/bootstrap-grid.css","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-framework.scss","../../scss/utilities/_display.scss","../../scss/utilities/_flex.scss","../../scss/utilities/_spacing.scss"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Bootstrap Grid v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\nhtml {\n box-sizing: border-box;\n -ms-overflow-style: scrollbar;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n@import \"functions\";\n@import \"variables\";\n\n@import \"mixins/breakpoints\";\n@import \"mixins/grid-framework\";\n@import \"mixins/grid\";\n\n@import \"grid\";\n@import \"utilities/display\";\n@import \"utilities/flex\";\n@import \"utilities/spacing\";\n","/*!\n * Bootstrap Grid v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\nhtml {\n box-sizing: border-box;\n -ms-overflow-style: scrollbar;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container, .container-sm {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container, .container-sm, .container-md {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container, .container-sm, .container-md, .container-lg {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container, .container-sm, .container-md, .container-lg, .container-xl {\n max-width: 1140px;\n }\n}\n\n.row {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.row-cols-1 > * {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.row-cols-2 > * {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.row-cols-3 > * {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.row-cols-4 > * {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.row-cols-5 > * {\n -ms-flex: 0 0 20%;\n flex: 0 0 20%;\n max-width: 20%;\n}\n\n.row-cols-6 > * {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n}\n\n.col-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n -ms-flex-order: -1;\n order: -1;\n}\n\n.order-last {\n -ms-flex-order: 13;\n order: 13;\n}\n\n.order-0 {\n -ms-flex-order: 0;\n order: 0;\n}\n\n.order-1 {\n -ms-flex-order: 1;\n order: 1;\n}\n\n.order-2 {\n -ms-flex-order: 2;\n order: 2;\n}\n\n.order-3 {\n -ms-flex-order: 3;\n order: 3;\n}\n\n.order-4 {\n -ms-flex-order: 4;\n order: 4;\n}\n\n.order-5 {\n -ms-flex-order: 5;\n order: 5;\n}\n\n.order-6 {\n -ms-flex-order: 6;\n order: 6;\n}\n\n.order-7 {\n -ms-flex-order: 7;\n order: 7;\n}\n\n.order-8 {\n -ms-flex-order: 8;\n order: 8;\n}\n\n.order-9 {\n -ms-flex-order: 9;\n order: 9;\n}\n\n.order-10 {\n -ms-flex-order: 10;\n order: 10;\n}\n\n.order-11 {\n -ms-flex-order: 11;\n order: 11;\n}\n\n.order-12 {\n -ms-flex-order: 12;\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-sm-1 > * {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-sm-2 > * {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-sm-3 > * {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-sm-4 > * {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-sm-5 > * {\n -ms-flex: 0 0 20%;\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-sm-6 > * {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-sm-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-sm-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-sm-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-sm-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-sm-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-sm-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-sm-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-sm-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-sm-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-sm-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-sm-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-sm-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-sm-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-sm-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-sm-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-md-1 > * {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-md-2 > * {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-md-3 > * {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-md-4 > * {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-md-5 > * {\n -ms-flex: 0 0 20%;\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-md-6 > * {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-md-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-md-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-md-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-md-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-md-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-md-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-md-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-md-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-md-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-md-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-md-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-md-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-md-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-md-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-md-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-lg-1 > * {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-lg-2 > * {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-lg-3 > * {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-lg-4 > * {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-lg-5 > * {\n -ms-flex: 0 0 20%;\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-lg-6 > * {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-lg-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-lg-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-lg-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-lg-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-lg-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-lg-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-lg-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-lg-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-lg-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-lg-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-lg-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-lg-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-lg-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-lg-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-lg-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-xl-1 > * {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-xl-2 > * {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-xl-3 > * {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-xl-4 > * {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-xl-5 > * {\n -ms-flex: 0 0 20%;\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-xl-6 > * {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-xl-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-xl-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-xl-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-xl-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-xl-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-xl-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-xl-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-xl-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-xl-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-xl-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-xl-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-xl-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-xl-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-xl-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-xl-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n}\n\n.d-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-md-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-print-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n.flex-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n}\n\n.flex-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n}\n\n.justify-content-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n}\n\n.align-items-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n}\n\n.align-items-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n}\n\n.align-items-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n}\n\n.align-items-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n}\n\n.align-content-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n}\n\n.align-content-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n}\n\n.align-content-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n}\n\n.align-content-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n}\n\n.align-content-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n}\n\n.align-self-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n}\n\n.align-self-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n}\n\n.align-self-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n}\n\n.align-self-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n}\n\n.align-self-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-sm-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-sm-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-sm-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-sm-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-sm-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-sm-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-sm-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-sm-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-md-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-md-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-md-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-md-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-md-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-md-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-md-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-md-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-md-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-md-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-md-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-md-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-md-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-md-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-md-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-md-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-lg-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-lg-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-lg-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-lg-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-lg-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-lg-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-lg-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-lg-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-xl-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-xl-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-xl-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-xl-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-xl-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-xl-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-xl-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-xl-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-n1 {\n margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n margin-left: -1rem !important;\n}\n\n.m-n4 {\n margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n margin-left: -3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-n1 {\n margin: -0.25rem !important;\n }\n .mt-sm-n1,\n .my-sm-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-sm-n1,\n .mx-sm-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-sm-n1,\n .my-sm-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-sm-n1,\n .mx-sm-n1 {\n margin-left: -0.25rem !important;\n }\n .m-sm-n2 {\n margin: -0.5rem !important;\n }\n .mt-sm-n2,\n .my-sm-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-sm-n2,\n .mx-sm-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-sm-n2,\n .my-sm-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-sm-n2,\n .mx-sm-n2 {\n margin-left: -0.5rem !important;\n }\n .m-sm-n3 {\n margin: -1rem !important;\n }\n .mt-sm-n3,\n .my-sm-n3 {\n margin-top: -1rem !important;\n }\n .mr-sm-n3,\n .mx-sm-n3 {\n margin-right: -1rem !important;\n }\n .mb-sm-n3,\n .my-sm-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-sm-n3,\n .mx-sm-n3 {\n margin-left: -1rem !important;\n }\n .m-sm-n4 {\n margin: -1.5rem !important;\n }\n .mt-sm-n4,\n .my-sm-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-sm-n4,\n .mx-sm-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-sm-n4,\n .my-sm-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-sm-n4,\n .mx-sm-n4 {\n margin-left: -1.5rem !important;\n }\n .m-sm-n5 {\n margin: -3rem !important;\n }\n .mt-sm-n5,\n .my-sm-n5 {\n margin-top: -3rem !important;\n }\n .mr-sm-n5,\n .mx-sm-n5 {\n margin-right: -3rem !important;\n }\n .mb-sm-n5,\n .my-sm-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-sm-n5,\n .mx-sm-n5 {\n margin-left: -3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-n1 {\n margin: -0.25rem !important;\n }\n .mt-md-n1,\n .my-md-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-md-n1,\n .mx-md-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-md-n1,\n .my-md-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-md-n1,\n .mx-md-n1 {\n margin-left: -0.25rem !important;\n }\n .m-md-n2 {\n margin: -0.5rem !important;\n }\n .mt-md-n2,\n .my-md-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-md-n2,\n .mx-md-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-md-n2,\n .my-md-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-md-n2,\n .mx-md-n2 {\n margin-left: -0.5rem !important;\n }\n .m-md-n3 {\n margin: -1rem !important;\n }\n .mt-md-n3,\n .my-md-n3 {\n margin-top: -1rem !important;\n }\n .mr-md-n3,\n .mx-md-n3 {\n margin-right: -1rem !important;\n }\n .mb-md-n3,\n .my-md-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-md-n3,\n .mx-md-n3 {\n margin-left: -1rem !important;\n }\n .m-md-n4 {\n margin: -1.5rem !important;\n }\n .mt-md-n4,\n .my-md-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-md-n4,\n .mx-md-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-md-n4,\n .my-md-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-md-n4,\n .mx-md-n4 {\n margin-left: -1.5rem !important;\n }\n .m-md-n5 {\n margin: -3rem !important;\n }\n .mt-md-n5,\n .my-md-n5 {\n margin-top: -3rem !important;\n }\n .mr-md-n5,\n .mx-md-n5 {\n margin-right: -3rem !important;\n }\n .mb-md-n5,\n .my-md-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-md-n5,\n .mx-md-n5 {\n margin-left: -3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-n1 {\n margin: -0.25rem !important;\n }\n .mt-lg-n1,\n .my-lg-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-lg-n1,\n .mx-lg-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-lg-n1,\n .my-lg-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-lg-n1,\n .mx-lg-n1 {\n margin-left: -0.25rem !important;\n }\n .m-lg-n2 {\n margin: -0.5rem !important;\n }\n .mt-lg-n2,\n .my-lg-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-lg-n2,\n .mx-lg-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-lg-n2,\n .my-lg-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-lg-n2,\n .mx-lg-n2 {\n margin-left: -0.5rem !important;\n }\n .m-lg-n3 {\n margin: -1rem !important;\n }\n .mt-lg-n3,\n .my-lg-n3 {\n margin-top: -1rem !important;\n }\n .mr-lg-n3,\n .mx-lg-n3 {\n margin-right: -1rem !important;\n }\n .mb-lg-n3,\n .my-lg-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-lg-n3,\n .mx-lg-n3 {\n margin-left: -1rem !important;\n }\n .m-lg-n4 {\n margin: -1.5rem !important;\n }\n .mt-lg-n4,\n .my-lg-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-lg-n4,\n .mx-lg-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-lg-n4,\n .my-lg-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-lg-n4,\n .mx-lg-n4 {\n margin-left: -1.5rem !important;\n }\n .m-lg-n5 {\n margin: -3rem !important;\n }\n .mt-lg-n5,\n .my-lg-n5 {\n margin-top: -3rem !important;\n }\n .mr-lg-n5,\n .mx-lg-n5 {\n margin-right: -3rem !important;\n }\n .mb-lg-n5,\n .my-lg-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-lg-n5,\n .mx-lg-n5 {\n margin-left: -3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-n1 {\n margin: -0.25rem !important;\n }\n .mt-xl-n1,\n .my-xl-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-xl-n1,\n .mx-xl-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-xl-n1,\n .my-xl-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-xl-n1,\n .mx-xl-n1 {\n margin-left: -0.25rem !important;\n }\n .m-xl-n2 {\n margin: -0.5rem !important;\n }\n .mt-xl-n2,\n .my-xl-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-xl-n2,\n .mx-xl-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-xl-n2,\n .my-xl-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-xl-n2,\n .mx-xl-n2 {\n margin-left: -0.5rem !important;\n }\n .m-xl-n3 {\n margin: -1rem !important;\n }\n .mt-xl-n3,\n .my-xl-n3 {\n margin-top: -1rem !important;\n }\n .mr-xl-n3,\n .mx-xl-n3 {\n margin-right: -1rem !important;\n }\n .mb-xl-n3,\n .my-xl-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-xl-n3,\n .mx-xl-n3 {\n margin-left: -1rem !important;\n }\n .m-xl-n4 {\n margin: -1.5rem !important;\n }\n .mt-xl-n4,\n .my-xl-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-xl-n4,\n .mx-xl-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-xl-n4,\n .my-xl-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-xl-n4,\n .mx-xl-n4 {\n margin-left: -1.5rem !important;\n }\n .m-xl-n5 {\n margin: -3rem !important;\n }\n .mt-xl-n5,\n .my-xl-n5 {\n margin-top: -3rem !important;\n }\n .mr-xl-n5,\n .mx-xl-n5 {\n margin-right: -3rem !important;\n }\n .mb-xl-n5,\n .my-xl-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-xl-n5,\n .mx-xl-n5 {\n margin-left: -3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n/*# sourceMappingURL=bootstrap-grid.css.map */","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n // Single container class with breakpoint max-widths\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n @each $name, $width in $grid-breakpoints {\n @if ($container-max-width > $width or $breakpoint == $name) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n }\n }\n }\n }\n}\n\n\n// Row\n//\n// Rows contain your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container($gutter: $grid-gutter-width) {\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row($gutter: $grid-gutter-width) {\n display: flex;\n flex-wrap: wrap;\n margin-right: -$gutter / 2;\n margin-left: -$gutter / 2;\n}\n\n@mixin make-col-ready($gutter: $grid-gutter-width) {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%; // Reset earlier grid tiers\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// numberof columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n & > * {\n flex: 0 0 100% / $count;\n max-width: 100% / $count;\n }\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n != null and $n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","// stylelint-disable declaration-no-important\n\n//\n// Utilities for common `display` values\n//\n\n@each $breakpoint in map-keys($grid-breakpoints) {\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @each $value in $displays {\n .d#{$infix}-#{$value} { display: $value !important; }\n }\n }\n}\n\n\n//\n// Utilities for toggling `display` in print\n//\n\n@media print {\n @each $value in $displays {\n .d-print-#{$value} { display: $value !important; }\n }\n}\n","// stylelint-disable declaration-no-important\n\n// Flex variation\n//\n// Custom styles for additional flex alignment options.\n\n@each $breakpoint in map-keys($grid-breakpoints) {\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n .flex#{$infix}-row { flex-direction: row !important; }\n .flex#{$infix}-column { flex-direction: column !important; }\n .flex#{$infix}-row-reverse { flex-direction: row-reverse !important; }\n .flex#{$infix}-column-reverse { flex-direction: column-reverse !important; }\n\n .flex#{$infix}-wrap { flex-wrap: wrap !important; }\n .flex#{$infix}-nowrap { flex-wrap: nowrap !important; }\n .flex#{$infix}-wrap-reverse { flex-wrap: wrap-reverse !important; }\n .flex#{$infix}-fill { flex: 1 1 auto !important; }\n .flex#{$infix}-grow-0 { flex-grow: 0 !important; }\n .flex#{$infix}-grow-1 { flex-grow: 1 !important; }\n .flex#{$infix}-shrink-0 { flex-shrink: 0 !important; }\n .flex#{$infix}-shrink-1 { flex-shrink: 1 !important; }\n\n .justify-content#{$infix}-start { justify-content: flex-start !important; }\n .justify-content#{$infix}-end { justify-content: flex-end !important; }\n .justify-content#{$infix}-center { justify-content: center !important; }\n .justify-content#{$infix}-between { justify-content: space-between !important; }\n .justify-content#{$infix}-around { justify-content: space-around !important; }\n\n .align-items#{$infix}-start { align-items: flex-start !important; }\n .align-items#{$infix}-end { align-items: flex-end !important; }\n .align-items#{$infix}-center { align-items: center !important; }\n .align-items#{$infix}-baseline { align-items: baseline !important; }\n .align-items#{$infix}-stretch { align-items: stretch !important; }\n\n .align-content#{$infix}-start { align-content: flex-start !important; }\n .align-content#{$infix}-end { align-content: flex-end !important; }\n .align-content#{$infix}-center { align-content: center !important; }\n .align-content#{$infix}-between { align-content: space-between !important; }\n .align-content#{$infix}-around { align-content: space-around !important; }\n .align-content#{$infix}-stretch { align-content: stretch !important; }\n\n .align-self#{$infix}-auto { align-self: auto !important; }\n .align-self#{$infix}-start { align-self: flex-start !important; }\n .align-self#{$infix}-end { align-self: flex-end !important; }\n .align-self#{$infix}-center { align-self: center !important; }\n .align-self#{$infix}-baseline { align-self: baseline !important; }\n .align-self#{$infix}-stretch { align-self: stretch !important; }\n }\n}\n","// stylelint-disable declaration-no-important\n\n// Margin and Padding\n\n@each $breakpoint in map-keys($grid-breakpoints) {\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @each $prop, $abbrev in (margin: m, padding: p) {\n @each $size, $length in $spacers {\n .#{$abbrev}#{$infix}-#{$size} { #{$prop}: $length !important; }\n .#{$abbrev}t#{$infix}-#{$size},\n .#{$abbrev}y#{$infix}-#{$size} {\n #{$prop}-top: $length !important;\n }\n .#{$abbrev}r#{$infix}-#{$size},\n .#{$abbrev}x#{$infix}-#{$size} {\n #{$prop}-right: $length !important;\n }\n .#{$abbrev}b#{$infix}-#{$size},\n .#{$abbrev}y#{$infix}-#{$size} {\n #{$prop}-bottom: $length !important;\n }\n .#{$abbrev}l#{$infix}-#{$size},\n .#{$abbrev}x#{$infix}-#{$size} {\n #{$prop}-left: $length !important;\n }\n }\n }\n\n // Negative margins (e.g., where `.mb-n1` is negative version of `.mb-1`)\n @each $size, $length in $spacers {\n @if $size != 0 {\n .m#{$infix}-n#{$size} { margin: -$length !important; }\n .mt#{$infix}-n#{$size},\n .my#{$infix}-n#{$size} {\n margin-top: -$length !important;\n }\n .mr#{$infix}-n#{$size},\n .mx#{$infix}-n#{$size} {\n margin-right: -$length !important;\n }\n .mb#{$infix}-n#{$size},\n .my#{$infix}-n#{$size} {\n margin-bottom: -$length !important;\n }\n .ml#{$infix}-n#{$size},\n .mx#{$infix}-n#{$size} {\n margin-left: -$length !important;\n }\n }\n }\n\n // Some special margin utils\n .m#{$infix}-auto { margin: auto !important; }\n .mt#{$infix}-auto,\n .my#{$infix}-auto {\n margin-top: auto !important;\n }\n .mr#{$infix}-auto,\n .mx#{$infix}-auto {\n margin-right: auto !important;\n }\n .mb#{$infix}-auto,\n .my#{$infix}-auto {\n margin-bottom: auto !important;\n }\n .ml#{$infix}-auto,\n .mx#{$infix}-auto {\n margin-left: auto !important;\n }\n }\n}\n"]} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css deleted file mode 100644 index 68473445..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css +++ /dev/null @@ -1,327 +0,0 @@ -/*! - * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) - */ -*, -*::before, -*::after { - box-sizing: border-box; -} - -html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { - display: block; -} - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #212529; - text-align: left; - background-color: #fff; -} - -[tabindex="-1"]:focus:not(:focus-visible) { - outline: 0 !important; -} - -hr { - box-sizing: content-box; - height: 0; - overflow: visible; -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: 0.5rem; -} - -p { - margin-top: 0; - margin-bottom: 1rem; -} - -abbr[title], -abbr[data-original-title] { - text-decoration: underline; - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - border-bottom: 0; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; -} - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -dt { - font-weight: 700; -} - -dd { - margin-bottom: .5rem; - margin-left: 0; -} - -blockquote { - margin: 0 0 1rem; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 80%; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sub { - bottom: -.25em; -} - -sup { - top: -.5em; -} - -a { - color: #007bff; - text-decoration: none; - background-color: transparent; -} - -a:hover { - color: #0056b3; - text-decoration: underline; -} - -a:not([href]) { - color: inherit; - text-decoration: none; -} - -a:not([href]):hover { - color: inherit; - text-decoration: none; -} - -pre, -code, -kbd, -samp { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-size: 1em; -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; -} - -figure { - margin: 0 0 1rem; -} - -img { - vertical-align: middle; - border-style: none; -} - -svg { - overflow: hidden; - vertical-align: middle; -} - -table { - border-collapse: collapse; -} - -caption { - padding-top: 0.75rem; - padding-bottom: 0.75rem; - color: #6c757d; - text-align: left; - caption-side: bottom; -} - -th { - text-align: inherit; -} - -label { - display: inline-block; - margin-bottom: 0.5rem; -} - -button { - border-radius: 0; -} - -button:focus { - outline: 1px dotted; - outline: 5px auto -webkit-focus-ring-color; -} - -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -button, -input { - overflow: visible; -} - -button, -select { - text-transform: none; -} - -select { - word-wrap: normal; -} - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -button:not(:disabled), -[type="button"]:not(:disabled), -[type="reset"]:not(:disabled), -[type="submit"]:not(:disabled) { - cursor: pointer; -} - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - padding: 0; - border-style: none; -} - -input[type="radio"], -input[type="checkbox"] { - box-sizing: border-box; - padding: 0; -} - -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - -webkit-appearance: listbox; -} - -textarea { - overflow: auto; - resize: vertical; -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - max-width: 100%; - padding: 0; - margin-bottom: .5rem; - font-size: 1.5rem; - line-height: inherit; - color: inherit; - white-space: normal; -} - -progress { - vertical-align: baseline; -} - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - outline-offset: -2px; - -webkit-appearance: none; -} - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; -} - -output { - display: inline-block; -} - -summary { - display: list-item; - cursor: pointer; -} - -template { - display: none; -} - -[hidden] { - display: none !important; -} -/*# sourceMappingURL=bootstrap-reboot.css.map */ \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map deleted file mode 100644 index 701f6715..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap-reboot.scss","bootstrap-reboot.css","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss"],"names":[],"mappings":"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","file":"bootstrap-reboot.css","sourcesContent":["/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"reboot\";\n","/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -webkit-tap-highlight-color: rgba($black, 0); // 5\n}\n\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\n// TODO: remove in v5\n// stylelint-disable-next-line selector-list-comma-newline-after\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline\n// on elements that programmatically receive focus but wouldn't normally show a visible\n// focus outline. In general, this would mean that the outline is only applied if the\n// interaction that led to the element receiving programmatic focus was a keyboard interaction,\n// or the browser has somehow determined that the user is primarily a keyboard user and/or\n// wants focus outlines to always be presented.\n//\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible\n// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

    `-`

    ` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

    `s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

    `s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n @include font-size(1.5rem);\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n// Color system\n\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n\n$grays: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$grays: map-merge(\n (\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n ),\n $grays\n);\n\n$blue: #007bff !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #e83e8c !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #28a745 !default;\n$teal: #20c997 !default;\n$cyan: #17a2b8 !default;\n\n$colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$colors: map-merge(\n (\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n ),\n $colors\n);\n\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-800 !default;\n\n$theme-colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$theme-colors: map-merge(\n (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n ),\n $theme-colors\n);\n\n// Set a specific jump point for requesting color jumps\n$theme-color-interval: 8% !default;\n\n// The yiq lightness value that determines when the lightness of color changes from \"dark\" to \"light\". Acceptable values are between 0 and 255.\n$yiq-contrasted-threshold: 150 !default;\n\n// Customize the light and dark text colors for use in our YIQ color contrast function.\n$yiq-text-dark: $gray-900 !default;\n$yiq-text-light: $white !default;\n\n// Characters which are escaped by the escape-svg function\n$escaped-characters: (\n (\"<\",\"%3c\"),\n (\">\",\"%3e\"),\n (\"#\",\"%23\"),\n) !default;\n\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-prefers-reduced-motion-media-query: true !default;\n$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS\n$enable-grid-classes: true !default;\n$enable-pointer-cursor-for-buttons: true !default;\n$enable-print-styles: true !default;\n$enable-responsive-font-sizes: false !default;\n$enable-validation-icons: true !default;\n$enable-deprecation-messages: true !default;\n\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n$spacer: 1rem !default;\n$spacers: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$spacers: map-merge(\n (\n 0: 0,\n 1: ($spacer * .25),\n 2: ($spacer * .5),\n 3: $spacer,\n 4: ($spacer * 1.5),\n 5: ($spacer * 3)\n ),\n $spacers\n);\n\n// This variable affects the `.h-*` and `.w-*` classes.\n$sizes: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$sizes: map-merge(\n (\n 25: 25%,\n 50: 50%,\n 75: 75%,\n 100: 100%,\n auto: auto\n ),\n $sizes\n);\n\n\n// Body\n//\n// Settings for the `` element.\n\n$body-bg: $white !default;\n$body-color: $gray-900 !default;\n\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: theme-color(\"primary\") !default;\n$link-decoration: none !default;\n$link-hover-color: darken($link-color, 15%) !default;\n$link-hover-decoration: underline !default;\n// Darken percentage for links with `.text-*` class (e.g. `.text-success`)\n$emphasized-link-hover-darken-percentage: 15% !default;\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px\n) !default;\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints, \"$grid-breakpoints\");\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px\n) !default;\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 30px !default;\n$grid-row-columns: 6 !default;\n\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n$line-height-lg: 1.5 !default;\n$line-height-sm: 1.5 !default;\n\n$border-width: 1px !default;\n$border-color: $gray-300 !default;\n\n$border-radius: .25rem !default;\n$border-radius-lg: .3rem !default;\n$border-radius-sm: .2rem !default;\n\n$rounded-pill: 50rem !default;\n\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n\n$component-active-color: $white !default;\n$component-active-bg: theme-color(\"primary\") !default;\n\n$caret-width: .3em !default;\n$caret-vertical-align: $caret-width * .85 !default;\n$caret-spacing: $caret-width * .85 !default;\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n$transition-collapse: height .35s ease !default;\n\n$embed-responsive-aspect-ratios: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$embed-responsive-aspect-ratios: join(\n (\n (21 9),\n (16 9),\n (4 3),\n (1 1),\n ),\n $embed-responsive-aspect-ratios\n);\n\n// Typography\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n$font-family-base: $font-family-sans-serif !default;\n// stylelint-enable value-keyword-case\n\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-lg: $font-size-base * 1.25 !default;\n$font-size-sm: $font-size-base * .875 !default;\n\n$font-weight-lighter: lighter !default;\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-bold: 700 !default;\n$font-weight-bolder: bolder !default;\n\n$font-weight-base: $font-weight-normal !default;\n$line-height-base: 1.5 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n\n$headings-margin-bottom: $spacer / 2 !default;\n$headings-font-family: null !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: null !default;\n\n$display1-size: 6rem !default;\n$display2-size: 5.5rem !default;\n$display3-size: 4.5rem !default;\n$display4-size: 3.5rem !default;\n\n$display1-weight: 300 !default;\n$display2-weight: 300 !default;\n$display3-weight: 300 !default;\n$display4-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n\n$lead-font-size: $font-size-base * 1.25 !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: 80% !default;\n\n$text-muted: $gray-600 !default;\n\n$blockquote-small-color: $gray-600 !default;\n$blockquote-small-font-size: $small-font-size !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n\n$hr-border-color: rgba($black, .1) !default;\n$hr-border-width: $border-width !default;\n\n$mark-padding: .2em !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default;\n$nested-kbd-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-bg: #fcf8e3 !default;\n\n$hr-margin-y: $spacer !default;\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n$table-cell-padding: .75rem !default;\n$table-cell-padding-sm: .3rem !default;\n\n$table-color: $body-color !default;\n$table-bg: null !default;\n$table-accent-bg: rgba($black, .05) !default;\n$table-hover-color: $table-color !default;\n$table-hover-bg: rgba($black, .075) !default;\n$table-active-bg: $table-hover-bg !default;\n\n$table-border-width: $border-width !default;\n$table-border-color: $border-color !default;\n\n$table-head-bg: $gray-200 !default;\n$table-head-color: $gray-700 !default;\n\n$table-dark-color: $white !default;\n$table-dark-bg: $gray-800 !default;\n$table-dark-accent-bg: rgba($white, .05) !default;\n$table-dark-hover-color: $table-dark-color !default;\n$table-dark-hover-bg: rgba($white, .075) !default;\n$table-dark-border-color: lighten($table-dark-bg, 7.5%) !default;\n\n$table-striped-order: odd !default;\n\n$table-caption-color: $text-muted !default;\n\n$table-bg-level: -9 !default;\n$table-border-level: -6 !default;\n\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-font-family: null !default;\n$input-btn-font-size: $font-size-base !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: .2rem !default;\n$input-btn-focus-color: rgba($component-active-bg, .25) !default;\n$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-font-size-sm: $font-size-sm !default;\n$input-btn-line-height-sm: $line-height-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-font-size-lg: $font-size-lg !default;\n$input-btn-line-height-lg: $line-height-lg !default;\n\n$input-btn-border-width: $border-width !default;\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-font-family: $input-btn-font-family !default;\n$btn-font-size: $input-btn-font-size !default;\n$btn-line-height: $input-btn-line-height !default;\n$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-font-size-sm: $input-btn-font-size-sm !default;\n$btn-line-height-sm: $input-btn-line-height-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-font-size-lg: $input-btn-font-size-lg !default;\n$btn-line-height-lg: $input-btn-line-height-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-disabled-color: $gray-600 !default;\n\n$btn-block-spacing-y: .5rem !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: $border-radius !default;\n$btn-border-radius-lg: $border-radius-lg !default;\n$btn-border-radius-sm: $border-radius-sm !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n\n// Forms\n\n$label-margin-bottom: .5rem !default;\n\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-font-family: $input-btn-font-family !default;\n$input-font-size: $input-btn-font-size !default;\n$input-font-weight: $font-weight-base !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-font-size-sm: $input-btn-font-size-sm !default;\n$input-line-height-sm: $input-btn-line-height-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-font-size-lg: $input-btn-font-size-lg !default;\n$input-line-height-lg: $input-btn-line-height-lg !default;\n\n$input-bg: $white !default;\n$input-disabled-bg: $gray-200 !default;\n\n$input-color: $gray-700 !default;\n$input-border-color: $gray-400 !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: inset 0 1px 1px rgba($black, .075) !default;\n\n$input-border-radius: $border-radius !default;\n$input-border-radius-lg: $border-radius-lg !default;\n$input-border-radius-sm: $border-radius-sm !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: lighten($component-active-bg, 25%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: $gray-600 !default;\n$input-plaintext-color: $body-color !default;\n\n$input-height-border: $input-border-width * 2 !default;\n\n$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;\n$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;\n$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y / 2) !default;\n\n$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;\n$input-height-sm: add($input-line-height-sm * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;\n$input-height-lg: add($input-line-height-lg * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-text-margin-top: .25rem !default;\n\n$form-check-input-gutter: 1.25rem !default;\n$form-check-input-margin-y: .3rem !default;\n$form-check-input-margin-x: .25rem !default;\n\n$form-check-inline-margin-x: .75rem !default;\n$form-check-inline-input-margin-x: .3125rem !default;\n\n$form-grid-gutter-width: 10px !default;\n$form-group-margin-bottom: 1rem !default;\n\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: $gray-200 !default;\n$input-group-addon-border-color: $input-border-color !default;\n\n$custom-forms-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$custom-control-gutter: .5rem !default;\n$custom-control-spacer-x: 1rem !default;\n$custom-control-cursor: null !default;\n\n$custom-control-indicator-size: 1rem !default;\n$custom-control-indicator-bg: $input-bg !default;\n\n$custom-control-indicator-bg-size: 50% 50% !default;\n$custom-control-indicator-box-shadow: $input-box-shadow !default;\n$custom-control-indicator-border-color: $gray-500 !default;\n$custom-control-indicator-border-width: $input-border-width !default;\n\n$custom-control-label-color: null !default;\n\n$custom-control-indicator-disabled-bg: $input-disabled-bg !default;\n$custom-control-label-disabled-color: $gray-600 !default;\n\n$custom-control-indicator-checked-color: $component-active-color !default;\n$custom-control-indicator-checked-bg: $component-active-bg !default;\n$custom-control-indicator-checked-disabled-bg: rgba(theme-color(\"primary\"), .5) !default;\n$custom-control-indicator-checked-box-shadow: none !default;\n$custom-control-indicator-checked-border-color: $custom-control-indicator-checked-bg !default;\n\n$custom-control-indicator-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-control-indicator-focus-border-color: $input-focus-border-color !default;\n\n$custom-control-indicator-active-color: $component-active-color !default;\n$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-control-indicator-active-box-shadow: none !default;\n$custom-control-indicator-active-border-color: $custom-control-indicator-active-bg !default;\n\n$custom-checkbox-indicator-border-radius: $border-radius !default;\n$custom-checkbox-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default;\n$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;\n$custom-checkbox-indicator-icon-indeterminate: url(\"data:image/svg+xml,\") !default;\n$custom-checkbox-indicator-indeterminate-box-shadow: none !default;\n$custom-checkbox-indicator-indeterminate-border-color: $custom-checkbox-indicator-indeterminate-bg !default;\n\n$custom-radio-indicator-border-radius: 50% !default;\n$custom-radio-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-switch-width: $custom-control-indicator-size * 1.75 !default;\n$custom-switch-indicator-border-radius: $custom-control-indicator-size / 2 !default;\n$custom-switch-indicator-size: subtract($custom-control-indicator-size, $custom-control-indicator-border-width * 4) !default;\n\n$custom-select-padding-y: $input-padding-y !default;\n$custom-select-padding-x: $input-padding-x !default;\n$custom-select-font-family: $input-font-family !default;\n$custom-select-font-size: $input-font-size !default;\n$custom-select-height: $input-height !default;\n$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator\n$custom-select-font-weight: $input-font-weight !default;\n$custom-select-line-height: $input-line-height !default;\n$custom-select-color: $input-color !default;\n$custom-select-disabled-color: $gray-600 !default;\n$custom-select-bg: $input-bg !default;\n$custom-select-disabled-bg: $gray-200 !default;\n$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions\n$custom-select-indicator-color: $gray-800 !default;\n$custom-select-indicator: url(\"data:image/svg+xml,\") !default;\n$custom-select-background: escape-svg($custom-select-indicator) no-repeat right $custom-select-padding-x center / $custom-select-bg-size !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)\n\n$custom-select-feedback-icon-padding-right: add(1em * .75, (2 * $custom-select-padding-y * .75) + $custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-position: center right ($custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;\n\n$custom-select-border-width: $input-border-width !default;\n$custom-select-border-color: $input-border-color !default;\n$custom-select-border-radius: $border-radius !default;\n$custom-select-box-shadow: inset 0 1px 2px rgba($black, .075) !default;\n\n$custom-select-focus-border-color: $input-focus-border-color !default;\n$custom-select-focus-width: $input-focus-width !default;\n$custom-select-focus-box-shadow: 0 0 0 $custom-select-focus-width $input-btn-focus-color !default;\n\n$custom-select-padding-y-sm: $input-padding-y-sm !default;\n$custom-select-padding-x-sm: $input-padding-x-sm !default;\n$custom-select-font-size-sm: $input-font-size-sm !default;\n$custom-select-height-sm: $input-height-sm !default;\n\n$custom-select-padding-y-lg: $input-padding-y-lg !default;\n$custom-select-padding-x-lg: $input-padding-x-lg !default;\n$custom-select-font-size-lg: $input-font-size-lg !default;\n$custom-select-height-lg: $input-height-lg !default;\n\n$custom-range-track-width: 100% !default;\n$custom-range-track-height: .5rem !default;\n$custom-range-track-cursor: pointer !default;\n$custom-range-track-bg: $gray-300 !default;\n$custom-range-track-border-radius: 1rem !default;\n$custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;\n\n$custom-range-thumb-width: 1rem !default;\n$custom-range-thumb-height: $custom-range-thumb-width !default;\n$custom-range-thumb-bg: $component-active-bg !default;\n$custom-range-thumb-border: 0 !default;\n$custom-range-thumb-border-radius: 1rem !default;\n$custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;\n$custom-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in IE/Edge\n$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-range-thumb-disabled-bg: $gray-500 !default;\n\n$custom-file-height: $input-height !default;\n$custom-file-height-inner: $input-height-inner !default;\n$custom-file-focus-border-color: $input-focus-border-color !default;\n$custom-file-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-file-disabled-bg: $input-disabled-bg !default;\n\n$custom-file-padding-y: $input-padding-y !default;\n$custom-file-padding-x: $input-padding-x !default;\n$custom-file-line-height: $input-line-height !default;\n$custom-file-font-family: $input-font-family !default;\n$custom-file-font-weight: $input-font-weight !default;\n$custom-file-color: $input-color !default;\n$custom-file-bg: $input-bg !default;\n$custom-file-border-width: $input-border-width !default;\n$custom-file-border-color: $input-border-color !default;\n$custom-file-border-radius: $input-border-radius !default;\n$custom-file-box-shadow: $input-box-shadow !default;\n$custom-file-button-color: $custom-file-color !default;\n$custom-file-button-bg: $input-group-addon-bg !default;\n$custom-file-text: (\n en: \"Browse\"\n) !default;\n\n\n// Form validation\n\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $small-font-size !default;\n$form-feedback-valid-color: theme-color(\"success\") !default;\n$form-feedback-invalid-color: theme-color(\"danger\") !default;\n\n$form-feedback-icon-valid-color: $form-feedback-valid-color !default;\n$form-feedback-icon-valid: url(\"data:image/svg+xml,\") !default;\n$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;\n$form-feedback-icon-invalid: url(\"data:image/svg+xml,\") !default;\n\n$form-validation-states: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$form-validation-states: map-merge(\n (\n \"valid\": (\n \"color\": $form-feedback-valid-color,\n \"icon\": $form-feedback-icon-valid\n ),\n \"invalid\": (\n \"color\": $form-feedback-invalid-color,\n \"icon\": $form-feedback-icon-invalid\n ),\n ),\n $form-validation-states\n);\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-modal-backdrop: 1040 !default;\n$zindex-modal: 1050 !default;\n$zindex-popover: 1060 !default;\n$zindex-tooltip: 1070 !default;\n\n\n// Navs\n\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-disabled-color: $gray-600 !default;\n\n$nav-tabs-border-color: $gray-300 !default;\n$nav-tabs-border-width: $border-width !default;\n$nav-tabs-border-radius: $border-radius !default;\n$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: $gray-700 !default;\n$nav-tabs-link-active-bg: $body-bg !default;\n$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: $border-radius !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-divider-color: $gray-200 !default;\n$nav-divider-margin-y: $spacer / 2 !default;\n\n\n// Navbar\n\n$navbar-padding-y: $spacer / 2 !default;\n$navbar-padding-x: $spacer !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n\n$navbar-dark-color: rgba($white, .5) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n\n$navbar-light-color: rgba($black, .5) !default;\n$navbar-light-hover-color: rgba($black, .7) !default;\n$navbar-light-active-color: rgba($black, .9) !default;\n$navbar-light-disabled-color: rgba($black, .3) !default;\n$navbar-light-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-light-toggler-border-color: rgba($black, .1) !default;\n\n$navbar-light-brand-color: $navbar-light-active-color !default;\n$navbar-light-brand-hover-color: $navbar-light-active-color !default;\n$navbar-dark-brand-color: $navbar-dark-active-color !default;\n$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-font-size: $font-size-base !default;\n$dropdown-color: $body-color !default;\n$dropdown-bg: $white !default;\n$dropdown-border-color: rgba($black, .15) !default;\n$dropdown-border-radius: $border-radius !default;\n$dropdown-border-width: $border-width !default;\n$dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default;\n$dropdown-divider-bg: $gray-200 !default;\n$dropdown-divider-margin-y: $nav-divider-margin-y !default;\n$dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;\n\n$dropdown-link-color: $gray-900 !default;\n$dropdown-link-hover-color: darken($gray-900, 5%) !default;\n$dropdown-link-hover-bg: $gray-100 !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: $gray-600 !default;\n\n$dropdown-item-padding-y: .25rem !default;\n$dropdown-item-padding-x: 1.5rem !default;\n\n$dropdown-header-color: $gray-600 !default;\n\n\n// Pagination\n\n$pagination-padding-y: .5rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n$pagination-line-height: 1.25 !default;\n\n$pagination-color: $link-color !default;\n$pagination-bg: $white !default;\n$pagination-border-width: $border-width !default;\n$pagination-border-color: $gray-300 !default;\n\n$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: $link-hover-color !default;\n$pagination-hover-bg: $gray-200 !default;\n$pagination-hover-border-color: $gray-300 !default;\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $pagination-active-bg !default;\n\n$pagination-disabled-color: $gray-600 !default;\n$pagination-disabled-bg: $white !default;\n$pagination-disabled-border-color: $gray-300 !default;\n\n\n// Jumbotron\n\n$jumbotron-padding: 2rem !default;\n$jumbotron-color: null !default;\n$jumbotron-bg: $gray-200 !default;\n\n\n// Cards\n\n$card-spacer-y: .75rem !default;\n$card-spacer-x: 1.25rem !default;\n$card-border-width: $border-width !default;\n$card-border-radius: $border-radius !default;\n$card-border-color: rgba($black, .125) !default;\n$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;\n$card-cap-bg: rgba($black, .03) !default;\n$card-cap-color: null !default;\n$card-height: null !default;\n$card-color: null !default;\n$card-bg: $white !default;\n\n$card-img-overlay-padding: 1.25rem !default;\n\n$card-group-margin: $grid-gutter-width / 2 !default;\n$card-deck-margin: $card-group-margin !default;\n\n$card-columns-count: 3 !default;\n$card-columns-gap: 1.25rem !default;\n$card-columns-margin: $card-spacer-y !default;\n\n\n// Tooltips\n\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: $white !default;\n$tooltip-bg: $black !default;\n$tooltip-border-radius: $border-radius !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: .25rem !default;\n$tooltip-padding-x: .5rem !default;\n$tooltip-margin: 0 !default;\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n$tooltip-arrow-color: $tooltip-bg !default;\n\n// Form tooltips must come after regular tooltips\n$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;\n$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;\n$form-feedback-tooltip-font-size: $tooltip-font-size !default;\n$form-feedback-tooltip-line-height: $line-height-base !default;\n$form-feedback-tooltip-opacity: $tooltip-opacity !default;\n$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;\n\n\n// Popovers\n\n$popover-font-size: $font-size-sm !default;\n$popover-bg: $white !default;\n$popover-max-width: 276px !default;\n$popover-border-width: $border-width !default;\n$popover-border-color: rgba($black, .2) !default;\n$popover-border-radius: $border-radius-lg !default;\n$popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default;\n$popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default;\n\n$popover-header-bg: darken($popover-bg, 3%) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: .75rem !default;\n\n$popover-body-color: $body-color !default;\n$popover-body-padding-y: $popover-header-padding-y !default;\n$popover-body-padding-x: $popover-header-padding-x !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n$popover-arrow-color: $popover-bg !default;\n\n$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;\n\n\n// Toasts\n\n$toast-max-width: 350px !default;\n$toast-padding-x: .75rem !default;\n$toast-padding-y: .25rem !default;\n$toast-font-size: .875rem !default;\n$toast-color: null !default;\n$toast-background-color: rgba($white, .85) !default;\n$toast-border-width: 1px !default;\n$toast-border-color: rgba(0, 0, 0, .1) !default;\n$toast-border-radius: .25rem !default;\n$toast-box-shadow: 0 .25rem .75rem rgba($black, .1) !default;\n\n$toast-header-color: $gray-600 !default;\n$toast-header-background-color: rgba($white, .85) !default;\n$toast-header-border-color: rgba(0, 0, 0, .05) !default;\n\n\n// Badges\n\n$badge-font-size: 75% !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-padding-y: .25em !default;\n$badge-padding-x: .4em !default;\n$badge-border-radius: $border-radius !default;\n\n$badge-transition: $btn-transition !default;\n$badge-focus-width: $input-btn-focus-width !default;\n\n$badge-pill-padding-x: .6em !default;\n// Use a higher than normal value to ensure completely rounded edges when\n// customizing padding or font-size on labels.\n$badge-pill-border-radius: 10rem !default;\n\n\n// Modals\n\n// Padding applied to the modal body\n$modal-inner-padding: 1rem !default;\n\n// Margin between elements in footer, must be lower than or equal to 2 * $modal-inner-padding\n$modal-footer-margin-between: .5rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-color: null !default;\n$modal-content-bg: $white !default;\n$modal-content-border-color: rgba($black, .2) !default;\n$modal-content-border-width: $border-width !default;\n$modal-content-border-radius: $border-radius-lg !default;\n$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;\n$modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default;\n$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n$modal-header-border-color: $border-color !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n$modal-header-padding-y: 1rem !default;\n$modal-header-padding-x: 1rem !default;\n$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility\n\n$modal-xl: 1140px !default;\n$modal-lg: 800px !default;\n$modal-md: 500px !default;\n$modal-sm: 300px !default;\n\n$modal-fade-transform: translate(0, -50px) !default;\n$modal-show-transform: none !default;\n$modal-transition: transform .3s ease-out !default;\n$modal-scale-transform: scale(1.02) !default;\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n$alert-padding-y: .75rem !default;\n$alert-padding-x: 1.25rem !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: $border-radius !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: $border-width !default;\n\n$alert-bg-level: -10 !default;\n$alert-border-level: -9 !default;\n$alert-color-level: 6 !default;\n\n\n// Progress bars\n\n$progress-height: 1rem !default;\n$progress-font-size: $font-size-base * .75 !default;\n$progress-bg: $gray-200 !default;\n$progress-border-radius: $border-radius !default;\n$progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: theme-color(\"primary\") !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n\n\n// List group\n\n$list-group-color: null !default;\n$list-group-bg: $white !default;\n$list-group-border-color: rgba($black, .125) !default;\n$list-group-border-width: $border-width !default;\n$list-group-border-radius: $border-radius !default;\n\n$list-group-item-padding-y: .75rem !default;\n$list-group-item-padding-x: 1.25rem !default;\n\n$list-group-hover-bg: $gray-100 !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: $gray-600 !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: $gray-700 !default;\n$list-group-action-hover-color: $list-group-action-color !default;\n\n$list-group-action-active-color: $body-color !default;\n$list-group-action-active-bg: $gray-200 !default;\n\n\n// Image thumbnails\n\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: $body-bg !default;\n$thumbnail-border-width: $border-width !default;\n$thumbnail-border-color: $gray-300 !default;\n$thumbnail-border-radius: $border-radius !default;\n$thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default;\n\n\n// Figures\n\n$figure-caption-font-size: 90% !default;\n$figure-caption-color: $gray-600 !default;\n\n\n// Breadcrumbs\n\n$breadcrumb-font-size: null !default;\n\n$breadcrumb-padding-y: .75rem !default;\n$breadcrumb-padding-x: 1rem !default;\n$breadcrumb-item-padding: .5rem !default;\n\n$breadcrumb-margin-bottom: 1rem !default;\n\n$breadcrumb-bg: $gray-200 !default;\n$breadcrumb-divider-color: $gray-600 !default;\n$breadcrumb-active-color: $gray-600 !default;\n$breadcrumb-divider: quote(\"/\") !default;\n\n$breadcrumb-border-radius: $border-radius !default;\n\n\n// Carousel\n\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n$carousel-control-hover-opacity: .9 !default;\n$carousel-control-transition: opacity .15s ease !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-hit-area-height: 10px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-active-bg: $white !default;\n$carousel-indicator-transition: opacity .6s ease !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n\n$carousel-control-icon-width: 20px !default;\n\n$carousel-control-prev-icon-bg: url(\"data:image/svg+xml,\") !default;\n$carousel-control-next-icon-bg: url(\"data:image/svg+xml,\") !default;\n\n$carousel-transition-duration: .6s !default;\n$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n\n\n// Spinners\n\n$spinner-width: 2rem !default;\n$spinner-height: $spinner-width !default;\n$spinner-border-width: .25em !default;\n\n$spinner-width-sm: 1rem !default;\n$spinner-height-sm: $spinner-width-sm !default;\n$spinner-border-width-sm: .2em !default;\n\n\n// Close\n\n$close-font-size: $font-size-base * 1.5 !default;\n$close-font-weight: $font-weight-bold !default;\n$close-color: $black !default;\n$close-text-shadow: 0 1px 0 $white !default;\n\n\n// Code\n\n$code-font-size: 87.5% !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .2rem !default;\n$kbd-padding-x: .4rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: $white !default;\n$kbd-bg: $gray-900 !default;\n\n$pre-color: $gray-900 !default;\n$pre-scrollable-max-height: 340px !default;\n\n\n// Utilities\n\n$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default;\n$overflows: auto, hidden !default;\n$positions: static, relative, absolute, fixed, sticky !default;\n\n\n// Printing\n\n$print-page-size: a3 !default;\n$print-body-min-width: map-get($grid-breakpoints, \"lg\") !default;\n","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated font-resizing\n//\n// See https://github.com/twbs/rfs\n\n// Configuration\n\n// Base font size\n$rfs-base-font-size: 1.25rem !default;\n$rfs-font-size-unit: rem !default;\n\n// Breakpoint at where font-size starts decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n// Resize font-size based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != \"number\" or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-responsive-font-sizes to false\n$enable-responsive-font-sizes: true !default;\n\n// Cache $rfs-base-font-size unit\n$rfs-base-font-size-unit: unit($rfs-base-font-size);\n\n// Remove px-unit from $rfs-base-font-size for calculations\n@if $rfs-base-font-size-unit == \"px\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1);\n}\n@else if $rfs-base-font-size-unit == \"rem\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1 / $rfs-rem-value);\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == \"px\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == \"rem\" or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);\n}\n\n// Responsive font-size mixin\n@mixin rfs($fs, $important: false) {\n // Cache $fs unit\n $fs-unit: if(type-of($fs) == \"number\", unit($fs), false);\n\n // Add !important suffix if needed\n $rfs-suffix: if($important, \" !important\", \"\");\n\n // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $fs-unit or $fs-unit != \"\" and $fs-unit != \"px\" and $fs-unit != \"rem\" or $fs == 0 {\n font-size: #{$fs}#{$rfs-suffix};\n }\n @else {\n // Variables for storing static and fluid rescaling\n $rfs-static: null;\n $rfs-fluid: null;\n\n // Remove px-unit from $fs for calculations\n @if $fs-unit == \"px\" {\n $fs: $fs / ($fs * 0 + 1);\n }\n @else if $fs-unit == \"rem\" {\n $fs: $fs / ($fs * 0 + 1 / $rfs-rem-value);\n }\n\n // Set default font-size\n @if $rfs-font-size-unit == rem {\n $rfs-static: #{$fs / $rfs-rem-value}rem#{$rfs-suffix};\n }\n @else if $rfs-font-size-unit == px {\n $rfs-static: #{$fs}px#{$rfs-suffix};\n }\n @else {\n @error \"`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.\";\n }\n\n // Only add media query if font-size is bigger as the minimum font-size\n // If $rfs-factor == 1, no rescaling will take place\n @if $fs > $rfs-base-font-size and $enable-responsive-font-sizes {\n $min-width: null;\n $variable-unit: null;\n\n // Calculate minimum font-size for given font-size\n $fs-min: $rfs-base-font-size + ($fs - $rfs-base-font-size) / $rfs-factor;\n\n // Calculate difference between given font-size and minimum font-size for given font-size\n $fs-diff: $fs - $fs-min;\n\n // Base font-size formatting\n // No need to check if the unit is valid, because we did that before\n $min-width: if($rfs-font-size-unit == rem, #{$fs-min / $rfs-rem-value}rem, #{$fs-min}px);\n\n // If two-dimensional, use smallest of screen width and height\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{$fs-diff * 100 / $rfs-breakpoint}#{$variable-unit};\n\n // Set the calculated font-size.\n $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};\n }\n\n // Rendering\n @if $rfs-fluid == null {\n // Only render static font-size if no fluid font-size is available\n font-size: $rfs-static;\n }\n @else {\n $mq-value: null;\n\n // RFS breakpoint formatting\n @if $rfs-breakpoint-unit == em or $rfs-breakpoint-unit == rem {\n $mq-value: #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit};\n }\n @else if $rfs-breakpoint-unit == px {\n $mq-value: #{$rfs-breakpoint}px;\n }\n @else {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n }\n\n @if $rfs-class == \"disable\" {\n // Adding an extra class increases specificity,\n // which prevents the media query to override the font size\n &,\n .disable-responsive-font-size &,\n &.disable-responsive-font-size {\n font-size: $rfs-static;\n }\n }\n @else {\n font-size: $rfs-static;\n }\n\n @if $rfs-two-dimensional {\n @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n @else {\n @media (max-width: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n }\n }\n}\n\n// The font-size & responsive-font-size mixin uses RFS to rescale font sizes\n@mixin font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n\n@mixin responsive-font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover() {\n &:hover { @content; }\n}\n\n@mixin hover-focus() {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus() {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active() {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n"]} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css deleted file mode 100644 index 3b9dd2d1..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css +++ /dev/null @@ -1,8 +0,0 @@ -/*! - * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) - */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} -/*# sourceMappingURL=bootstrap-reboot.min.css.map */ \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map deleted file mode 100644 index b8551f7c..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap-reboot.scss","../../scss/_reboot.scss","dist/css/bootstrap-reboot.css","../../scss/vendor/_rfs.scss","bootstrap-reboot.css","../../scss/mixins/_hover.scss"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"reboot\";\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -webkit-tap-highlight-color: rgba($black, 0); // 5\n}\n\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\n// TODO: remove in v5\n// stylelint-disable-next-line selector-list-comma-newline-after\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline\n// on elements that programmatically receive focus but wouldn't normally show a visible\n// focus outline. In general, this would mean that the outline is only applied if the\n// interaction that led to the element receiving programmatic focus was a keyboard interaction,\n// or the browser has somehow determined that the user is primarily a keyboard user and/or\n// wants focus outlines to always be presented.\n//\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible\n// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

    `-`

    ` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

    `s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

    `s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n @include font-size(1.5rem);\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated font-resizing\n//\n// See https://github.com/twbs/rfs\n\n// Configuration\n\n// Base font size\n$rfs-base-font-size: 1.25rem !default;\n$rfs-font-size-unit: rem !default;\n\n// Breakpoint at where font-size starts decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n// Resize font-size based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != \"number\" or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-responsive-font-sizes to false\n$enable-responsive-font-sizes: true !default;\n\n// Cache $rfs-base-font-size unit\n$rfs-base-font-size-unit: unit($rfs-base-font-size);\n\n// Remove px-unit from $rfs-base-font-size for calculations\n@if $rfs-base-font-size-unit == \"px\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1);\n}\n@else if $rfs-base-font-size-unit == \"rem\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1 / $rfs-rem-value);\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == \"px\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == \"rem\" or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);\n}\n\n// Responsive font-size mixin\n@mixin rfs($fs, $important: false) {\n // Cache $fs unit\n $fs-unit: if(type-of($fs) == \"number\", unit($fs), false);\n\n // Add !important suffix if needed\n $rfs-suffix: if($important, \" !important\", \"\");\n\n // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $fs-unit or $fs-unit != \"\" and $fs-unit != \"px\" and $fs-unit != \"rem\" or $fs == 0 {\n font-size: #{$fs}#{$rfs-suffix};\n }\n @else {\n // Variables for storing static and fluid rescaling\n $rfs-static: null;\n $rfs-fluid: null;\n\n // Remove px-unit from $fs for calculations\n @if $fs-unit == \"px\" {\n $fs: $fs / ($fs * 0 + 1);\n }\n @else if $fs-unit == \"rem\" {\n $fs: $fs / ($fs * 0 + 1 / $rfs-rem-value);\n }\n\n // Set default font-size\n @if $rfs-font-size-unit == rem {\n $rfs-static: #{$fs / $rfs-rem-value}rem#{$rfs-suffix};\n }\n @else if $rfs-font-size-unit == px {\n $rfs-static: #{$fs}px#{$rfs-suffix};\n }\n @else {\n @error \"`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.\";\n }\n\n // Only add media query if font-size is bigger as the minimum font-size\n // If $rfs-factor == 1, no rescaling will take place\n @if $fs > $rfs-base-font-size and $enable-responsive-font-sizes {\n $min-width: null;\n $variable-unit: null;\n\n // Calculate minimum font-size for given font-size\n $fs-min: $rfs-base-font-size + ($fs - $rfs-base-font-size) / $rfs-factor;\n\n // Calculate difference between given font-size and minimum font-size for given font-size\n $fs-diff: $fs - $fs-min;\n\n // Base font-size formatting\n // No need to check if the unit is valid, because we did that before\n $min-width: if($rfs-font-size-unit == rem, #{$fs-min / $rfs-rem-value}rem, #{$fs-min}px);\n\n // If two-dimensional, use smallest of screen width and height\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{$fs-diff * 100 / $rfs-breakpoint}#{$variable-unit};\n\n // Set the calculated font-size.\n $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};\n }\n\n // Rendering\n @if $rfs-fluid == null {\n // Only render static font-size if no fluid font-size is available\n font-size: $rfs-static;\n }\n @else {\n $mq-value: null;\n\n // RFS breakpoint formatting\n @if $rfs-breakpoint-unit == em or $rfs-breakpoint-unit == rem {\n $mq-value: #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit};\n }\n @else if $rfs-breakpoint-unit == px {\n $mq-value: #{$rfs-breakpoint}px;\n }\n @else {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n }\n\n @if $rfs-class == \"disable\" {\n // Adding an extra class increases specificity,\n // which prevents the media query to override the font size\n &,\n .disable-responsive-font-size &,\n &.disable-responsive-font-size {\n font-size: $rfs-static;\n }\n }\n @else {\n font-size: $rfs-static;\n }\n\n @if $rfs-two-dimensional {\n @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n @else {\n @media (max-width: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n }\n }\n}\n\n// The font-size & responsive-font-size mixin uses RFS to rescale font sizes\n@mixin font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n\n@mixin responsive-font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n","/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover() {\n &:hover { @content; }\n}\n\n@mixin hover-focus() {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus() {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active() {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n"]} \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css deleted file mode 100644 index ec7ec973..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css +++ /dev/null @@ -1,10224 +0,0 @@ -/*! - * Bootstrap v4.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -:root { - --blue: #007bff; - --indigo: #6610f2; - --purple: #6f42c1; - --pink: #e83e8c; - --red: #dc3545; - --orange: #fd7e14; - --yellow: #ffc107; - --green: #28a745; - --teal: #20c997; - --cyan: #17a2b8; - --white: #fff; - --gray: #6c757d; - --gray-dark: #343a40; - --primary: #007bff; - --secondary: #6c757d; - --success: #28a745; - --info: #17a2b8; - --warning: #ffc107; - --danger: #dc3545; - --light: #f8f9fa; - --dark: #343a40; - --breakpoint-xs: 0; - --breakpoint-sm: 576px; - --breakpoint-md: 768px; - --breakpoint-lg: 992px; - --breakpoint-xl: 1200px; - --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { - display: block; -} - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #212529; - text-align: left; - background-color: #fff; -} - -[tabindex="-1"]:focus:not(:focus-visible) { - outline: 0 !important; -} - -hr { - box-sizing: content-box; - height: 0; - overflow: visible; -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: 0.5rem; -} - -p { - margin-top: 0; - margin-bottom: 1rem; -} - -abbr[title], -abbr[data-original-title] { - text-decoration: underline; - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - border-bottom: 0; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; -} - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -dt { - font-weight: 700; -} - -dd { - margin-bottom: .5rem; - margin-left: 0; -} - -blockquote { - margin: 0 0 1rem; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 80%; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sub { - bottom: -.25em; -} - -sup { - top: -.5em; -} - -a { - color: #007bff; - text-decoration: none; - background-color: transparent; -} - -a:hover { - color: #0056b3; - text-decoration: underline; -} - -a:not([href]) { - color: inherit; - text-decoration: none; -} - -a:not([href]):hover { - color: inherit; - text-decoration: none; -} - -pre, -code, -kbd, -samp { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-size: 1em; -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; -} - -figure { - margin: 0 0 1rem; -} - -img { - vertical-align: middle; - border-style: none; -} - -svg { - overflow: hidden; - vertical-align: middle; -} - -table { - border-collapse: collapse; -} - -caption { - padding-top: 0.75rem; - padding-bottom: 0.75rem; - color: #6c757d; - text-align: left; - caption-side: bottom; -} - -th { - text-align: inherit; -} - -label { - display: inline-block; - margin-bottom: 0.5rem; -} - -button { - border-radius: 0; -} - -button:focus { - outline: 1px dotted; - outline: 5px auto -webkit-focus-ring-color; -} - -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -button, -input { - overflow: visible; -} - -button, -select { - text-transform: none; -} - -select { - word-wrap: normal; -} - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -button:not(:disabled), -[type="button"]:not(:disabled), -[type="reset"]:not(:disabled), -[type="submit"]:not(:disabled) { - cursor: pointer; -} - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - padding: 0; - border-style: none; -} - -input[type="radio"], -input[type="checkbox"] { - box-sizing: border-box; - padding: 0; -} - -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - -webkit-appearance: listbox; -} - -textarea { - overflow: auto; - resize: vertical; -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - max-width: 100%; - padding: 0; - margin-bottom: .5rem; - font-size: 1.5rem; - line-height: inherit; - color: inherit; - white-space: normal; -} - -progress { - vertical-align: baseline; -} - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - outline-offset: -2px; - -webkit-appearance: none; -} - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; -} - -output { - display: inline-block; -} - -summary { - display: list-item; - cursor: pointer; -} - -template { - display: none; -} - -[hidden] { - display: none !important; -} - -h1, h2, h3, h4, h5, h6, -.h1, .h2, .h3, .h4, .h5, .h6 { - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; -} - -h1, .h1 { - font-size: 2.5rem; -} - -h2, .h2 { - font-size: 2rem; -} - -h3, .h3 { - font-size: 1.75rem; -} - -h4, .h4 { - font-size: 1.5rem; -} - -h5, .h5 { - font-size: 1.25rem; -} - -h6, .h6 { - font-size: 1rem; -} - -.lead { - font-size: 1.25rem; - font-weight: 300; -} - -.display-1 { - font-size: 6rem; - font-weight: 300; - line-height: 1.2; -} - -.display-2 { - font-size: 5.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-3 { - font-size: 4.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-4 { - font-size: 3.5rem; - font-weight: 300; - line-height: 1.2; -} - -hr { - margin-top: 1rem; - margin-bottom: 1rem; - border: 0; - border-top: 1px solid rgba(0, 0, 0, 0.1); -} - -small, -.small { - font-size: 80%; - font-weight: 400; -} - -mark, -.mark { - padding: 0.2em; - background-color: #fcf8e3; -} - -.list-unstyled { - padding-left: 0; - list-style: none; -} - -.list-inline { - padding-left: 0; - list-style: none; -} - -.list-inline-item { - display: inline-block; -} - -.list-inline-item:not(:last-child) { - margin-right: 0.5rem; -} - -.initialism { - font-size: 90%; - text-transform: uppercase; -} - -.blockquote { - margin-bottom: 1rem; - font-size: 1.25rem; -} - -.blockquote-footer { - display: block; - font-size: 80%; - color: #6c757d; -} - -.blockquote-footer::before { - content: "\2014\00A0"; -} - -.img-fluid { - max-width: 100%; - height: auto; -} - -.img-thumbnail { - padding: 0.25rem; - background-color: #fff; - border: 1px solid #dee2e6; - border-radius: 0.25rem; - max-width: 100%; - height: auto; -} - -.figure { - display: inline-block; -} - -.figure-img { - margin-bottom: 0.5rem; - line-height: 1; -} - -.figure-caption { - font-size: 90%; - color: #6c757d; -} - -code { - font-size: 87.5%; - color: #e83e8c; - word-wrap: break-word; -} - -a > code { - color: inherit; -} - -kbd { - padding: 0.2rem 0.4rem; - font-size: 87.5%; - color: #fff; - background-color: #212529; - border-radius: 0.2rem; -} - -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: 700; -} - -pre { - display: block; - font-size: 87.5%; - color: #212529; -} - -pre code { - font-size: inherit; - color: inherit; - word-break: normal; -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} - -.container { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container { - max-width: 540px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 720px; - } -} - -@media (min-width: 992px) { - .container { - max-width: 960px; - } -} - -@media (min-width: 1200px) { - .container { - max-width: 1140px; - } -} - -.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container, .container-sm { - max-width: 540px; - } -} - -@media (min-width: 768px) { - .container, .container-sm, .container-md { - max-width: 720px; - } -} - -@media (min-width: 992px) { - .container, .container-sm, .container-md, .container-lg { - max-width: 960px; - } -} - -@media (min-width: 1200px) { - .container, .container-sm, .container-md, .container-lg, .container-xl { - max-width: 1140px; - } -} - -.row { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -15px; - margin-left: -15px; -} - -.no-gutters { - margin-right: 0; - margin-left: 0; -} - -.no-gutters > .col, -.no-gutters > [class*="col-"] { - padding-right: 0; - padding-left: 0; -} - -.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, -.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, -.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, -.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, -.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, -.col-xl-auto { - position: relative; - width: 100%; - padding-right: 15px; - padding-left: 15px; -} - -.col { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; -} - -.row-cols-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.row-cols-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.row-cols-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; -} - -.row-cols-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.row-cols-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; -} - -.row-cols-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; -} - -.col-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; -} - -.col-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; -} - -.col-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; -} - -.col-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.col-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; -} - -.col-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; -} - -.col-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.col-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; -} - -.col-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; -} - -.col-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; -} - -.col-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; -} - -.col-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; -} - -.col-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.order-first { - -ms-flex-order: -1; - order: -1; -} - -.order-last { - -ms-flex-order: 13; - order: 13; -} - -.order-0 { - -ms-flex-order: 0; - order: 0; -} - -.order-1 { - -ms-flex-order: 1; - order: 1; -} - -.order-2 { - -ms-flex-order: 2; - order: 2; -} - -.order-3 { - -ms-flex-order: 3; - order: 3; -} - -.order-4 { - -ms-flex-order: 4; - order: 4; -} - -.order-5 { - -ms-flex-order: 5; - order: 5; -} - -.order-6 { - -ms-flex-order: 6; - order: 6; -} - -.order-7 { - -ms-flex-order: 7; - order: 7; -} - -.order-8 { - -ms-flex-order: 8; - order: 8; -} - -.order-9 { - -ms-flex-order: 9; - order: 9; -} - -.order-10 { - -ms-flex-order: 10; - order: 10; -} - -.order-11 { - -ms-flex-order: 11; - order: 11; -} - -.order-12 { - -ms-flex-order: 12; - order: 12; -} - -.offset-1 { - margin-left: 8.333333%; -} - -.offset-2 { - margin-left: 16.666667%; -} - -.offset-3 { - margin-left: 25%; -} - -.offset-4 { - margin-left: 33.333333%; -} - -.offset-5 { - margin-left: 41.666667%; -} - -.offset-6 { - margin-left: 50%; -} - -.offset-7 { - margin-left: 58.333333%; -} - -.offset-8 { - margin-left: 66.666667%; -} - -.offset-9 { - margin-left: 75%; -} - -.offset-10 { - margin-left: 83.333333%; -} - -.offset-11 { - margin-left: 91.666667%; -} - -@media (min-width: 576px) { - .col-sm { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-sm-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-sm-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-sm-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-sm-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-sm-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-sm-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-sm-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-sm-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-sm-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-sm-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-sm-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-sm-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-sm-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-sm-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-sm-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-sm-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-sm-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-sm-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-sm-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-sm-first { - -ms-flex-order: -1; - order: -1; - } - .order-sm-last { - -ms-flex-order: 13; - order: 13; - } - .order-sm-0 { - -ms-flex-order: 0; - order: 0; - } - .order-sm-1 { - -ms-flex-order: 1; - order: 1; - } - .order-sm-2 { - -ms-flex-order: 2; - order: 2; - } - .order-sm-3 { - -ms-flex-order: 3; - order: 3; - } - .order-sm-4 { - -ms-flex-order: 4; - order: 4; - } - .order-sm-5 { - -ms-flex-order: 5; - order: 5; - } - .order-sm-6 { - -ms-flex-order: 6; - order: 6; - } - .order-sm-7 { - -ms-flex-order: 7; - order: 7; - } - .order-sm-8 { - -ms-flex-order: 8; - order: 8; - } - .order-sm-9 { - -ms-flex-order: 9; - order: 9; - } - .order-sm-10 { - -ms-flex-order: 10; - order: 10; - } - .order-sm-11 { - -ms-flex-order: 11; - order: 11; - } - .order-sm-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-sm-0 { - margin-left: 0; - } - .offset-sm-1 { - margin-left: 8.333333%; - } - .offset-sm-2 { - margin-left: 16.666667%; - } - .offset-sm-3 { - margin-left: 25%; - } - .offset-sm-4 { - margin-left: 33.333333%; - } - .offset-sm-5 { - margin-left: 41.666667%; - } - .offset-sm-6 { - margin-left: 50%; - } - .offset-sm-7 { - margin-left: 58.333333%; - } - .offset-sm-8 { - margin-left: 66.666667%; - } - .offset-sm-9 { - margin-left: 75%; - } - .offset-sm-10 { - margin-left: 83.333333%; - } - .offset-sm-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 768px) { - .col-md { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-md-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-md-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-md-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-md-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-md-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-md-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-md-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-md-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-md-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-md-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-md-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-md-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-md-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-md-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-md-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-md-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-md-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-md-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-md-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-md-first { - -ms-flex-order: -1; - order: -1; - } - .order-md-last { - -ms-flex-order: 13; - order: 13; - } - .order-md-0 { - -ms-flex-order: 0; - order: 0; - } - .order-md-1 { - -ms-flex-order: 1; - order: 1; - } - .order-md-2 { - -ms-flex-order: 2; - order: 2; - } - .order-md-3 { - -ms-flex-order: 3; - order: 3; - } - .order-md-4 { - -ms-flex-order: 4; - order: 4; - } - .order-md-5 { - -ms-flex-order: 5; - order: 5; - } - .order-md-6 { - -ms-flex-order: 6; - order: 6; - } - .order-md-7 { - -ms-flex-order: 7; - order: 7; - } - .order-md-8 { - -ms-flex-order: 8; - order: 8; - } - .order-md-9 { - -ms-flex-order: 9; - order: 9; - } - .order-md-10 { - -ms-flex-order: 10; - order: 10; - } - .order-md-11 { - -ms-flex-order: 11; - order: 11; - } - .order-md-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-md-0 { - margin-left: 0; - } - .offset-md-1 { - margin-left: 8.333333%; - } - .offset-md-2 { - margin-left: 16.666667%; - } - .offset-md-3 { - margin-left: 25%; - } - .offset-md-4 { - margin-left: 33.333333%; - } - .offset-md-5 { - margin-left: 41.666667%; - } - .offset-md-6 { - margin-left: 50%; - } - .offset-md-7 { - margin-left: 58.333333%; - } - .offset-md-8 { - margin-left: 66.666667%; - } - .offset-md-9 { - margin-left: 75%; - } - .offset-md-10 { - margin-left: 83.333333%; - } - .offset-md-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 992px) { - .col-lg { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-lg-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-lg-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-lg-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-lg-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-lg-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-lg-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-lg-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-lg-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-lg-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-lg-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-lg-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-lg-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-lg-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-lg-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-lg-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-lg-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-lg-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-lg-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-lg-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-lg-first { - -ms-flex-order: -1; - order: -1; - } - .order-lg-last { - -ms-flex-order: 13; - order: 13; - } - .order-lg-0 { - -ms-flex-order: 0; - order: 0; - } - .order-lg-1 { - -ms-flex-order: 1; - order: 1; - } - .order-lg-2 { - -ms-flex-order: 2; - order: 2; - } - .order-lg-3 { - -ms-flex-order: 3; - order: 3; - } - .order-lg-4 { - -ms-flex-order: 4; - order: 4; - } - .order-lg-5 { - -ms-flex-order: 5; - order: 5; - } - .order-lg-6 { - -ms-flex-order: 6; - order: 6; - } - .order-lg-7 { - -ms-flex-order: 7; - order: 7; - } - .order-lg-8 { - -ms-flex-order: 8; - order: 8; - } - .order-lg-9 { - -ms-flex-order: 9; - order: 9; - } - .order-lg-10 { - -ms-flex-order: 10; - order: 10; - } - .order-lg-11 { - -ms-flex-order: 11; - order: 11; - } - .order-lg-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-lg-0 { - margin-left: 0; - } - .offset-lg-1 { - margin-left: 8.333333%; - } - .offset-lg-2 { - margin-left: 16.666667%; - } - .offset-lg-3 { - margin-left: 25%; - } - .offset-lg-4 { - margin-left: 33.333333%; - } - .offset-lg-5 { - margin-left: 41.666667%; - } - .offset-lg-6 { - margin-left: 50%; - } - .offset-lg-7 { - margin-left: 58.333333%; - } - .offset-lg-8 { - margin-left: 66.666667%; - } - .offset-lg-9 { - margin-left: 75%; - } - .offset-lg-10 { - margin-left: 83.333333%; - } - .offset-lg-11 { - margin-left: 91.666667%; - } -} - -@media (min-width: 1200px) { - .col-xl { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - .row-cols-xl-1 > * { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .row-cols-xl-2 > * { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .row-cols-xl-3 > * { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .row-cols-xl-4 > * { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .row-cols-xl-5 > * { - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - .row-cols-xl-6 > * { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-xl-auto { - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - .col-xl-1 { - -ms-flex: 0 0 8.333333%; - flex: 0 0 8.333333%; - max-width: 8.333333%; - } - .col-xl-2 { - -ms-flex: 0 0 16.666667%; - flex: 0 0 16.666667%; - max-width: 16.666667%; - } - .col-xl-3 { - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - .col-xl-4 { - -ms-flex: 0 0 33.333333%; - flex: 0 0 33.333333%; - max-width: 33.333333%; - } - .col-xl-5 { - -ms-flex: 0 0 41.666667%; - flex: 0 0 41.666667%; - max-width: 41.666667%; - } - .col-xl-6 { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - .col-xl-7 { - -ms-flex: 0 0 58.333333%; - flex: 0 0 58.333333%; - max-width: 58.333333%; - } - .col-xl-8 { - -ms-flex: 0 0 66.666667%; - flex: 0 0 66.666667%; - max-width: 66.666667%; - } - .col-xl-9 { - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - .col-xl-10 { - -ms-flex: 0 0 83.333333%; - flex: 0 0 83.333333%; - max-width: 83.333333%; - } - .col-xl-11 { - -ms-flex: 0 0 91.666667%; - flex: 0 0 91.666667%; - max-width: 91.666667%; - } - .col-xl-12 { - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - .order-xl-first { - -ms-flex-order: -1; - order: -1; - } - .order-xl-last { - -ms-flex-order: 13; - order: 13; - } - .order-xl-0 { - -ms-flex-order: 0; - order: 0; - } - .order-xl-1 { - -ms-flex-order: 1; - order: 1; - } - .order-xl-2 { - -ms-flex-order: 2; - order: 2; - } - .order-xl-3 { - -ms-flex-order: 3; - order: 3; - } - .order-xl-4 { - -ms-flex-order: 4; - order: 4; - } - .order-xl-5 { - -ms-flex-order: 5; - order: 5; - } - .order-xl-6 { - -ms-flex-order: 6; - order: 6; - } - .order-xl-7 { - -ms-flex-order: 7; - order: 7; - } - .order-xl-8 { - -ms-flex-order: 8; - order: 8; - } - .order-xl-9 { - -ms-flex-order: 9; - order: 9; - } - .order-xl-10 { - -ms-flex-order: 10; - order: 10; - } - .order-xl-11 { - -ms-flex-order: 11; - order: 11; - } - .order-xl-12 { - -ms-flex-order: 12; - order: 12; - } - .offset-xl-0 { - margin-left: 0; - } - .offset-xl-1 { - margin-left: 8.333333%; - } - .offset-xl-2 { - margin-left: 16.666667%; - } - .offset-xl-3 { - margin-left: 25%; - } - .offset-xl-4 { - margin-left: 33.333333%; - } - .offset-xl-5 { - margin-left: 41.666667%; - } - .offset-xl-6 { - margin-left: 50%; - } - .offset-xl-7 { - margin-left: 58.333333%; - } - .offset-xl-8 { - margin-left: 66.666667%; - } - .offset-xl-9 { - margin-left: 75%; - } - .offset-xl-10 { - margin-left: 83.333333%; - } - .offset-xl-11 { - margin-left: 91.666667%; - } -} - -.table { - width: 100%; - margin-bottom: 1rem; - color: #212529; -} - -.table th, -.table td { - padding: 0.75rem; - vertical-align: top; - border-top: 1px solid #dee2e6; -} - -.table thead th { - vertical-align: bottom; - border-bottom: 2px solid #dee2e6; -} - -.table tbody + tbody { - border-top: 2px solid #dee2e6; -} - -.table-sm th, -.table-sm td { - padding: 0.3rem; -} - -.table-bordered { - border: 1px solid #dee2e6; -} - -.table-bordered th, -.table-bordered td { - border: 1px solid #dee2e6; -} - -.table-bordered thead th, -.table-bordered thead td { - border-bottom-width: 2px; -} - -.table-borderless th, -.table-borderless td, -.table-borderless thead th, -.table-borderless tbody + tbody { - border: 0; -} - -.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(0, 0, 0, 0.05); -} - -.table-hover tbody tr:hover { - color: #212529; - background-color: rgba(0, 0, 0, 0.075); -} - -.table-primary, -.table-primary > th, -.table-primary > td { - background-color: #b8daff; -} - -.table-primary th, -.table-primary td, -.table-primary thead th, -.table-primary tbody + tbody { - border-color: #7abaff; -} - -.table-hover .table-primary:hover { - background-color: #9fcdff; -} - -.table-hover .table-primary:hover > td, -.table-hover .table-primary:hover > th { - background-color: #9fcdff; -} - -.table-secondary, -.table-secondary > th, -.table-secondary > td { - background-color: #d6d8db; -} - -.table-secondary th, -.table-secondary td, -.table-secondary thead th, -.table-secondary tbody + tbody { - border-color: #b3b7bb; -} - -.table-hover .table-secondary:hover { - background-color: #c8cbcf; -} - -.table-hover .table-secondary:hover > td, -.table-hover .table-secondary:hover > th { - background-color: #c8cbcf; -} - -.table-success, -.table-success > th, -.table-success > td { - background-color: #c3e6cb; -} - -.table-success th, -.table-success td, -.table-success thead th, -.table-success tbody + tbody { - border-color: #8fd19e; -} - -.table-hover .table-success:hover { - background-color: #b1dfbb; -} - -.table-hover .table-success:hover > td, -.table-hover .table-success:hover > th { - background-color: #b1dfbb; -} - -.table-info, -.table-info > th, -.table-info > td { - background-color: #bee5eb; -} - -.table-info th, -.table-info td, -.table-info thead th, -.table-info tbody + tbody { - border-color: #86cfda; -} - -.table-hover .table-info:hover { - background-color: #abdde5; -} - -.table-hover .table-info:hover > td, -.table-hover .table-info:hover > th { - background-color: #abdde5; -} - -.table-warning, -.table-warning > th, -.table-warning > td { - background-color: #ffeeba; -} - -.table-warning th, -.table-warning td, -.table-warning thead th, -.table-warning tbody + tbody { - border-color: #ffdf7e; -} - -.table-hover .table-warning:hover { - background-color: #ffe8a1; -} - -.table-hover .table-warning:hover > td, -.table-hover .table-warning:hover > th { - background-color: #ffe8a1; -} - -.table-danger, -.table-danger > th, -.table-danger > td { - background-color: #f5c6cb; -} - -.table-danger th, -.table-danger td, -.table-danger thead th, -.table-danger tbody + tbody { - border-color: #ed969e; -} - -.table-hover .table-danger:hover { - background-color: #f1b0b7; -} - -.table-hover .table-danger:hover > td, -.table-hover .table-danger:hover > th { - background-color: #f1b0b7; -} - -.table-light, -.table-light > th, -.table-light > td { - background-color: #fdfdfe; -} - -.table-light th, -.table-light td, -.table-light thead th, -.table-light tbody + tbody { - border-color: #fbfcfc; -} - -.table-hover .table-light:hover { - background-color: #ececf6; -} - -.table-hover .table-light:hover > td, -.table-hover .table-light:hover > th { - background-color: #ececf6; -} - -.table-dark, -.table-dark > th, -.table-dark > td { - background-color: #c6c8ca; -} - -.table-dark th, -.table-dark td, -.table-dark thead th, -.table-dark tbody + tbody { - border-color: #95999c; -} - -.table-hover .table-dark:hover { - background-color: #b9bbbe; -} - -.table-hover .table-dark:hover > td, -.table-hover .table-dark:hover > th { - background-color: #b9bbbe; -} - -.table-active, -.table-active > th, -.table-active > td { - background-color: rgba(0, 0, 0, 0.075); -} - -.table-hover .table-active:hover { - background-color: rgba(0, 0, 0, 0.075); -} - -.table-hover .table-active:hover > td, -.table-hover .table-active:hover > th { - background-color: rgba(0, 0, 0, 0.075); -} - -.table .thead-dark th { - color: #fff; - background-color: #343a40; - border-color: #454d55; -} - -.table .thead-light th { - color: #495057; - background-color: #e9ecef; - border-color: #dee2e6; -} - -.table-dark { - color: #fff; - background-color: #343a40; -} - -.table-dark th, -.table-dark td, -.table-dark thead th { - border-color: #454d55; -} - -.table-dark.table-bordered { - border: 0; -} - -.table-dark.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(255, 255, 255, 0.05); -} - -.table-dark.table-hover tbody tr:hover { - color: #fff; - background-color: rgba(255, 255, 255, 0.075); -} - -@media (max-width: 575.98px) { - .table-responsive-sm { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-sm > .table-bordered { - border: 0; - } -} - -@media (max-width: 767.98px) { - .table-responsive-md { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-md > .table-bordered { - border: 0; - } -} - -@media (max-width: 991.98px) { - .table-responsive-lg { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-lg > .table-bordered { - border: 0; - } -} - -@media (max-width: 1199.98px) { - .table-responsive-xl { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-xl > .table-bordered { - border: 0; - } -} - -.table-responsive { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; -} - -.table-responsive > .table-bordered { - border: 0; -} - -.form-control { - display: block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ced4da; - border-radius: 0.25rem; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .form-control { - transition: none; - } -} - -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} - -.form-control:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 #495057; -} - -.form-control:focus { - color: #495057; - background-color: #fff; - border-color: #80bdff; - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.form-control::-webkit-input-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control::-moz-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control:-ms-input-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control::-ms-input-placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control::placeholder { - color: #6c757d; - opacity: 1; -} - -.form-control:disabled, .form-control[readonly] { - background-color: #e9ecef; - opacity: 1; -} - -select.form-control:focus::-ms-value { - color: #495057; - background-color: #fff; -} - -.form-control-file, -.form-control-range { - display: block; - width: 100%; -} - -.col-form-label { - padding-top: calc(0.375rem + 1px); - padding-bottom: calc(0.375rem + 1px); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5; -} - -.col-form-label-lg { - padding-top: calc(0.5rem + 1px); - padding-bottom: calc(0.5rem + 1px); - font-size: 1.25rem; - line-height: 1.5; -} - -.col-form-label-sm { - padding-top: calc(0.25rem + 1px); - padding-bottom: calc(0.25rem + 1px); - font-size: 0.875rem; - line-height: 1.5; -} - -.form-control-plaintext { - display: block; - width: 100%; - padding: 0.375rem 0; - margin-bottom: 0; - font-size: 1rem; - line-height: 1.5; - color: #212529; - background-color: transparent; - border: solid transparent; - border-width: 1px 0; -} - -.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { - padding-right: 0; - padding-left: 0; -} - -.form-control-sm { - height: calc(1.5em + 0.5rem + 2px); - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.form-control-lg { - height: calc(1.5em + 1rem + 2px); - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -select.form-control[size], select.form-control[multiple] { - height: auto; -} - -textarea.form-control { - height: auto; -} - -.form-group { - margin-bottom: 1rem; -} - -.form-text { - display: block; - margin-top: 0.25rem; -} - -.form-row { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -5px; - margin-left: -5px; -} - -.form-row > .col, -.form-row > [class*="col-"] { - padding-right: 5px; - padding-left: 5px; -} - -.form-check { - position: relative; - display: block; - padding-left: 1.25rem; -} - -.form-check-input { - position: absolute; - margin-top: 0.3rem; - margin-left: -1.25rem; -} - -.form-check-input[disabled] ~ .form-check-label, -.form-check-input:disabled ~ .form-check-label { - color: #6c757d; -} - -.form-check-label { - margin-bottom: 0; -} - -.form-check-inline { - display: -ms-inline-flexbox; - display: inline-flex; - -ms-flex-align: center; - align-items: center; - padding-left: 0; - margin-right: 0.75rem; -} - -.form-check-inline .form-check-input { - position: static; - margin-top: 0; - margin-right: 0.3125rem; - margin-left: 0; -} - -.valid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #28a745; -} - -.valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: .1rem; - font-size: 0.875rem; - line-height: 1.5; - color: #fff; - background-color: rgba(40, 167, 69, 0.9); - border-radius: 0.25rem; -} - -.was-validated :valid ~ .valid-feedback, -.was-validated :valid ~ .valid-tooltip, -.is-valid ~ .valid-feedback, -.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .form-control:valid, .form-control.is-valid { - border-color: #28a745; - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: #28a745; - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.was-validated textarea.form-control:valid, textarea.form-control.is-valid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); -} - -.was-validated .custom-select:valid, .custom-select.is-valid { - border-color: #28a745; - padding-right: calc(0.75em + 2.3125rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { - border-color: #28a745; - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: #28a745; -} - -.was-validated .form-check-input:valid ~ .valid-feedback, -.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, -.form-check-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { - color: #28a745; -} - -.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { - border-color: #28a745; -} - -.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { - border-color: #34ce57; - background-color: #34ce57; -} - -.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { - border-color: #28a745; -} - -.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { - border-color: #28a745; -} - -.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { - border-color: #28a745; - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.invalid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #dc3545; -} - -.invalid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: .1rem; - font-size: 0.875rem; - line-height: 1.5; - color: #fff; - background-color: rgba(220, 53, 69, 0.9); - border-radius: 0.25rem; -} - -.was-validated :invalid ~ .invalid-feedback, -.was-validated :invalid ~ .invalid-tooltip, -.is-invalid ~ .invalid-feedback, -.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .form-control:invalid, .form-control.is-invalid { - border-color: #dc3545; - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: #dc3545; - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); -} - -.was-validated .custom-select:invalid, .custom-select.is-invalid { - border-color: #dc3545; - padding-right: calc(0.75em + 2.3125rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} - -.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { - border-color: #dc3545; - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: #dc3545; -} - -.was-validated .form-check-input:invalid ~ .invalid-feedback, -.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, -.form-check-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { - color: #dc3545; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { - border-color: #dc3545; -} - -.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { - border-color: #e4606d; - background-color: #e4606d; -} - -.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { - border-color: #dc3545; -} - -.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { - border-color: #dc3545; -} - -.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { - border-color: #dc3545; - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.form-inline { - display: -ms-flexbox; - display: flex; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -ms-flex-align: center; - align-items: center; -} - -.form-inline .form-check { - width: 100%; -} - -@media (min-width: 576px) { - .form-inline label { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - margin-bottom: 0; - } - .form-inline .form-group { - display: -ms-flexbox; - display: flex; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -ms-flex-align: center; - align-items: center; - margin-bottom: 0; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-plaintext { - display: inline-block; - } - .form-inline .input-group, - .form-inline .custom-select { - width: auto; - } - .form-inline .form-check { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - width: auto; - padding-left: 0; - } - .form-inline .form-check-input { - position: relative; - -ms-flex-negative: 0; - flex-shrink: 0; - margin-top: 0; - margin-right: 0.25rem; - margin-left: 0; - } - .form-inline .custom-control { - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - } - .form-inline .custom-control-label { - margin-bottom: 0; - } -} - -.btn { - display: inline-block; - font-weight: 400; - color: #212529; - text-align: center; - vertical-align: middle; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-color: transparent; - border: 1px solid transparent; - padding: 0.375rem 0.75rem; - font-size: 1rem; - line-height: 1.5; - border-radius: 0.25rem; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .btn { - transition: none; - } -} - -.btn:hover { - color: #212529; - text-decoration: none; -} - -.btn:focus, .btn.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.btn.disabled, .btn:disabled { - opacity: 0.65; -} - -a.btn.disabled, -fieldset:disabled a.btn { - pointer-events: none; -} - -.btn-primary { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-primary:hover { - color: #fff; - background-color: #0069d9; - border-color: #0062cc; -} - -.btn-primary:focus, .btn-primary.focus { - color: #fff; - background-color: #0069d9; - border-color: #0062cc; - box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); -} - -.btn-primary.disabled, .btn-primary:disabled { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, -.show > .btn-primary.dropdown-toggle { - color: #fff; - background-color: #0062cc; - border-color: #005cbf; -} - -.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); -} - -.btn-secondary { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-secondary:hover { - color: #fff; - background-color: #5a6268; - border-color: #545b62; -} - -.btn-secondary:focus, .btn-secondary.focus { - color: #fff; - background-color: #5a6268; - border-color: #545b62; - box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); -} - -.btn-secondary.disabled, .btn-secondary:disabled { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, -.show > .btn-secondary.dropdown-toggle { - color: #fff; - background-color: #545b62; - border-color: #4e555b; -} - -.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); -} - -.btn-success { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-success:hover { - color: #fff; - background-color: #218838; - border-color: #1e7e34; -} - -.btn-success:focus, .btn-success.focus { - color: #fff; - background-color: #218838; - border-color: #1e7e34; - box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); -} - -.btn-success.disabled, .btn-success:disabled { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, -.show > .btn-success.dropdown-toggle { - color: #fff; - background-color: #1e7e34; - border-color: #1c7430; -} - -.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-success.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); -} - -.btn-info { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-info:hover { - color: #fff; - background-color: #138496; - border-color: #117a8b; -} - -.btn-info:focus, .btn-info.focus { - color: #fff; - background-color: #138496; - border-color: #117a8b; - box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); -} - -.btn-info.disabled, .btn-info:disabled { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, -.show > .btn-info.dropdown-toggle { - color: #fff; - background-color: #117a8b; - border-color: #10707f; -} - -.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-info.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); -} - -.btn-warning { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-warning:hover { - color: #212529; - background-color: #e0a800; - border-color: #d39e00; -} - -.btn-warning:focus, .btn-warning.focus { - color: #212529; - background-color: #e0a800; - border-color: #d39e00; - box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); -} - -.btn-warning.disabled, .btn-warning:disabled { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, -.show > .btn-warning.dropdown-toggle { - color: #212529; - background-color: #d39e00; - border-color: #c69500; -} - -.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); -} - -.btn-danger { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-danger:hover { - color: #fff; - background-color: #c82333; - border-color: #bd2130; -} - -.btn-danger:focus, .btn-danger.focus { - color: #fff; - background-color: #c82333; - border-color: #bd2130; - box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); -} - -.btn-danger.disabled, .btn-danger:disabled { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, -.show > .btn-danger.dropdown-toggle { - color: #fff; - background-color: #bd2130; - border-color: #b21f2d; -} - -.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); -} - -.btn-light { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-light:hover { - color: #212529; - background-color: #e2e6ea; - border-color: #dae0e5; -} - -.btn-light:focus, .btn-light.focus { - color: #212529; - background-color: #e2e6ea; - border-color: #dae0e5; - box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); -} - -.btn-light.disabled, .btn-light:disabled { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, -.show > .btn-light.dropdown-toggle { - color: #212529; - background-color: #dae0e5; - border-color: #d3d9df; -} - -.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-light.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); -} - -.btn-dark { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-dark:hover { - color: #fff; - background-color: #23272b; - border-color: #1d2124; -} - -.btn-dark:focus, .btn-dark.focus { - color: #fff; - background-color: #23272b; - border-color: #1d2124; - box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); -} - -.btn-dark.disabled, .btn-dark:disabled { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, -.show > .btn-dark.dropdown-toggle { - color: #fff; - background-color: #1d2124; - border-color: #171a1d; -} - -.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); -} - -.btn-outline-primary { - color: #007bff; - border-color: #007bff; -} - -.btn-outline-primary:hover { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-outline-primary:focus, .btn-outline-primary.focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.btn-outline-primary.disabled, .btn-outline-primary:disabled { - color: #007bff; - background-color: transparent; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, -.show > .btn-outline-primary.dropdown-toggle { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.btn-outline-secondary { - color: #6c757d; - border-color: #6c757d; -} - -.btn-outline-secondary:hover { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-outline-secondary:focus, .btn-outline-secondary.focus { - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { - color: #6c757d; - background-color: transparent; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, -.show > .btn-outline-secondary.dropdown-toggle { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} - -.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.btn-outline-success { - color: #28a745; - border-color: #28a745; -} - -.btn-outline-success:hover { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-outline-success:focus, .btn-outline-success.focus { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.btn-outline-success.disabled, .btn-outline-success:disabled { - color: #28a745; - background-color: transparent; -} - -.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, -.show > .btn-outline-success.dropdown-toggle { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} - -.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-success.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.btn-outline-info { - color: #17a2b8; - border-color: #17a2b8; -} - -.btn-outline-info:hover { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-outline-info:focus, .btn-outline-info.focus { - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.btn-outline-info.disabled, .btn-outline-info:disabled { - color: #17a2b8; - background-color: transparent; -} - -.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, -.show > .btn-outline-info.dropdown-toggle { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} - -.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-info.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.btn-outline-warning { - color: #ffc107; - border-color: #ffc107; -} - -.btn-outline-warning:hover { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-outline-warning:focus, .btn-outline-warning.focus { - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.btn-outline-warning.disabled, .btn-outline-warning:disabled { - color: #ffc107; - background-color: transparent; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, -.show > .btn-outline-warning.dropdown-toggle { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} - -.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.btn-outline-danger { - color: #dc3545; - border-color: #dc3545; -} - -.btn-outline-danger:hover { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-outline-danger:focus, .btn-outline-danger.focus { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.btn-outline-danger.disabled, .btn-outline-danger:disabled { - color: #dc3545; - background-color: transparent; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, -.show > .btn-outline-danger.dropdown-toggle { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} - -.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.btn-outline-light { - color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-outline-light:hover { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-outline-light:focus, .btn-outline-light.focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.btn-outline-light.disabled, .btn-outline-light:disabled { - color: #f8f9fa; - background-color: transparent; -} - -.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, -.show > .btn-outline-light.dropdown-toggle { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} - -.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-light.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.btn-outline-dark { - color: #343a40; - border-color: #343a40; -} - -.btn-outline-dark:hover { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-outline-dark:focus, .btn-outline-dark.focus { - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.btn-outline-dark.disabled, .btn-outline-dark:disabled { - color: #343a40; - background-color: transparent; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, -.show > .btn-outline-dark.dropdown-toggle { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} - -.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, -.show > .btn-outline-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.btn-link { - font-weight: 400; - color: #007bff; - text-decoration: none; -} - -.btn-link:hover { - color: #0056b3; - text-decoration: underline; -} - -.btn-link:focus, .btn-link.focus { - text-decoration: underline; - box-shadow: none; -} - -.btn-link:disabled, .btn-link.disabled { - color: #6c757d; - pointer-events: none; -} - -.btn-lg, .btn-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -.btn-sm, .btn-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.btn-block { - display: block; - width: 100%; -} - -.btn-block + .btn-block { - margin-top: 0.5rem; -} - -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} - -.fade { - transition: opacity 0.15s linear; -} - -@media (prefers-reduced-motion: reduce) { - .fade { - transition: none; - } -} - -.fade:not(.show) { - opacity: 0; -} - -.collapse:not(.show) { - display: none; -} - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - transition: height 0.35s ease; -} - -@media (prefers-reduced-motion: reduce) { - .collapsing { - transition: none; - } -} - -.dropup, -.dropright, -.dropdown, -.dropleft { - position: relative; -} - -.dropdown-toggle { - white-space: nowrap; -} - -.dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid; - border-right: 0.3em solid transparent; - border-bottom: 0; - border-left: 0.3em solid transparent; -} - -.dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 10rem; - padding: 0.5rem 0; - margin: 0.125rem 0 0; - font-size: 1rem; - color: #212529; - text-align: left; - list-style: none; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; -} - -.dropdown-menu-left { - right: auto; - left: 0; -} - -.dropdown-menu-right { - right: 0; - left: auto; -} - -@media (min-width: 576px) { - .dropdown-menu-sm-left { - right: auto; - left: 0; - } - .dropdown-menu-sm-right { - right: 0; - left: auto; - } -} - -@media (min-width: 768px) { - .dropdown-menu-md-left { - right: auto; - left: 0; - } - .dropdown-menu-md-right { - right: 0; - left: auto; - } -} - -@media (min-width: 992px) { - .dropdown-menu-lg-left { - right: auto; - left: 0; - } - .dropdown-menu-lg-right { - right: 0; - left: auto; - } -} - -@media (min-width: 1200px) { - .dropdown-menu-xl-left { - right: auto; - left: 0; - } - .dropdown-menu-xl-right { - right: 0; - left: auto; - } -} - -.dropup .dropdown-menu { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: 0.125rem; -} - -.dropup .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0; - border-right: 0.3em solid transparent; - border-bottom: 0.3em solid; - border-left: 0.3em solid transparent; -} - -.dropup .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropright .dropdown-menu { - top: 0; - right: auto; - left: 100%; - margin-top: 0; - margin-left: 0.125rem; -} - -.dropright .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0; - border-bottom: 0.3em solid transparent; - border-left: 0.3em solid; -} - -.dropright .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropright .dropdown-toggle::after { - vertical-align: 0; -} - -.dropleft .dropdown-menu { - top: 0; - right: 100%; - left: auto; - margin-top: 0; - margin-right: 0.125rem; -} - -.dropleft .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; -} - -.dropleft .dropdown-toggle::after { - display: none; -} - -.dropleft .dropdown-toggle::before { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0.3em solid; - border-bottom: 0.3em solid transparent; -} - -.dropleft .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropleft .dropdown-toggle::before { - vertical-align: 0; -} - -.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { - right: auto; - bottom: auto; -} - -.dropdown-divider { - height: 0; - margin: 0.5rem 0; - overflow: hidden; - border-top: 1px solid #e9ecef; -} - -.dropdown-item { - display: block; - width: 100%; - padding: 0.25rem 1.5rem; - clear: both; - font-weight: 400; - color: #212529; - text-align: inherit; - white-space: nowrap; - background-color: transparent; - border: 0; -} - -.dropdown-item:hover, .dropdown-item:focus { - color: #16181b; - text-decoration: none; - background-color: #f8f9fa; -} - -.dropdown-item.active, .dropdown-item:active { - color: #fff; - text-decoration: none; - background-color: #007bff; -} - -.dropdown-item.disabled, .dropdown-item:disabled { - color: #6c757d; - pointer-events: none; - background-color: transparent; -} - -.dropdown-menu.show { - display: block; -} - -.dropdown-header { - display: block; - padding: 0.5rem 1.5rem; - margin-bottom: 0; - font-size: 0.875rem; - color: #6c757d; - white-space: nowrap; -} - -.dropdown-item-text { - display: block; - padding: 0.25rem 1.5rem; - color: #212529; -} - -.btn-group, -.btn-group-vertical { - position: relative; - display: -ms-inline-flexbox; - display: inline-flex; - vertical-align: middle; -} - -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - -ms-flex: 1 1 auto; - flex: 1 1 auto; -} - -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover { - z-index: 1; -} - -.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, -.btn-group-vertical > .btn:focus, -.btn-group-vertical > .btn:active, -.btn-group-vertical > .btn.active { - z-index: 1; -} - -.btn-toolbar { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.btn-toolbar .input-group { - width: auto; -} - -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) { - margin-left: -1px; -} - -.btn-group > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.dropdown-toggle-split { - padding-right: 0.5625rem; - padding-left: 0.5625rem; -} - -.dropdown-toggle-split::after, -.dropup .dropdown-toggle-split::after, -.dropright .dropdown-toggle-split::after { - margin-left: 0; -} - -.dropleft .dropdown-toggle-split::before { - margin-right: 0; -} - -.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { - padding-right: 0.375rem; - padding-left: 0.375rem; -} - -.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { - padding-right: 0.75rem; - padding-left: 0.75rem; -} - -.btn-group-vertical { - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-align: start; - align-items: flex-start; - -ms-flex-pack: center; - justify-content: center; -} - -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group { - width: 100%; -} - -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) { - margin-top: -1px; -} - -.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group-vertical > .btn-group:not(:last-child) > .btn { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.btn-group-toggle > .btn, -.btn-group-toggle > .btn-group > .btn { - margin-bottom: 0; -} - -.btn-group-toggle > .btn input[type="radio"], -.btn-group-toggle > .btn input[type="checkbox"], -.btn-group-toggle > .btn-group > .btn input[type="radio"], -.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} - -.input-group { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: stretch; - align-items: stretch; - width: 100%; -} - -.input-group > .form-control, -.input-group > .form-control-plaintext, -.input-group > .custom-select, -.input-group > .custom-file { - position: relative; - -ms-flex: 1 1 0%; - flex: 1 1 0%; - min-width: 0; - margin-bottom: 0; -} - -.input-group > .form-control + .form-control, -.input-group > .form-control + .custom-select, -.input-group > .form-control + .custom-file, -.input-group > .form-control-plaintext + .form-control, -.input-group > .form-control-plaintext + .custom-select, -.input-group > .form-control-plaintext + .custom-file, -.input-group > .custom-select + .form-control, -.input-group > .custom-select + .custom-select, -.input-group > .custom-select + .custom-file, -.input-group > .custom-file + .form-control, -.input-group > .custom-file + .custom-select, -.input-group > .custom-file + .custom-file { - margin-left: -1px; -} - -.input-group > .form-control:focus, -.input-group > .custom-select:focus, -.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { - z-index: 3; -} - -.input-group > .custom-file .custom-file-input:focus { - z-index: 4; -} - -.input-group > .form-control:not(:last-child), -.input-group > .custom-select:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .form-control:not(:first-child), -.input-group > .custom-select:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.input-group > .custom-file { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; -} - -.input-group > .custom-file:not(:last-child) .custom-file-label, -.input-group > .custom-file:not(:last-child) .custom-file-label::after { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .custom-file:not(:first-child) .custom-file-label { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.input-group-prepend, -.input-group-append { - display: -ms-flexbox; - display: flex; -} - -.input-group-prepend .btn, -.input-group-append .btn { - position: relative; - z-index: 2; -} - -.input-group-prepend .btn:focus, -.input-group-append .btn:focus { - z-index: 3; -} - -.input-group-prepend .btn + .btn, -.input-group-prepend .btn + .input-group-text, -.input-group-prepend .input-group-text + .input-group-text, -.input-group-prepend .input-group-text + .btn, -.input-group-append .btn + .btn, -.input-group-append .btn + .input-group-text, -.input-group-append .input-group-text + .input-group-text, -.input-group-append .input-group-text + .btn { - margin-left: -1px; -} - -.input-group-prepend { - margin-right: -1px; -} - -.input-group-append { - margin-left: -1px; -} - -.input-group-text { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - padding: 0.375rem 0.75rem; - margin-bottom: 0; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - text-align: center; - white-space: nowrap; - background-color: #e9ecef; - border: 1px solid #ced4da; - border-radius: 0.25rem; -} - -.input-group-text input[type="radio"], -.input-group-text input[type="checkbox"] { - margin-top: 0; -} - -.input-group-lg > .form-control:not(textarea), -.input-group-lg > .custom-select { - height: calc(1.5em + 1rem + 2px); -} - -.input-group-lg > .form-control, -.input-group-lg > .custom-select, -.input-group-lg > .input-group-prepend > .input-group-text, -.input-group-lg > .input-group-append > .input-group-text, -.input-group-lg > .input-group-prepend > .btn, -.input-group-lg > .input-group-append > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -.input-group-sm > .form-control:not(textarea), -.input-group-sm > .custom-select { - height: calc(1.5em + 0.5rem + 2px); -} - -.input-group-sm > .form-control, -.input-group-sm > .custom-select, -.input-group-sm > .input-group-prepend > .input-group-text, -.input-group-sm > .input-group-append > .input-group-text, -.input-group-sm > .input-group-prepend > .btn, -.input-group-sm > .input-group-append > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.input-group-lg > .custom-select, -.input-group-sm > .custom-select { - padding-right: 1.75rem; -} - -.input-group > .input-group-prepend > .btn, -.input-group > .input-group-prepend > .input-group-text, -.input-group > .input-group-append:not(:last-child) > .btn, -.input-group > .input-group-append:not(:last-child) > .input-group-text, -.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .input-group-append > .btn, -.input-group > .input-group-append > .input-group-text, -.input-group > .input-group-prepend:not(:first-child) > .btn, -.input-group > .input-group-prepend:not(:first-child) > .input-group-text, -.input-group > .input-group-prepend:first-child > .btn:not(:first-child), -.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.custom-control { - position: relative; - display: block; - min-height: 1.5rem; - padding-left: 1.5rem; -} - -.custom-control-inline { - display: -ms-inline-flexbox; - display: inline-flex; - margin-right: 1rem; -} - -.custom-control-input { - position: absolute; - left: 0; - z-index: -1; - width: 1rem; - height: 1.25rem; - opacity: 0; -} - -.custom-control-input:checked ~ .custom-control-label::before { - color: #fff; - border-color: #007bff; - background-color: #007bff; -} - -.custom-control-input:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { - border-color: #80bdff; -} - -.custom-control-input:not(:disabled):active ~ .custom-control-label::before { - color: #fff; - background-color: #b3d7ff; - border-color: #b3d7ff; -} - -.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { - color: #6c757d; -} - -.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { - background-color: #e9ecef; -} - -.custom-control-label { - position: relative; - margin-bottom: 0; - vertical-align: top; -} - -.custom-control-label::before { - position: absolute; - top: 0.25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - pointer-events: none; - content: ""; - background-color: #fff; - border: #adb5bd solid 1px; -} - -.custom-control-label::after { - position: absolute; - top: 0.25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - content: ""; - background: no-repeat 50% / 50% 50%; -} - -.custom-checkbox .custom-control-label::before { - border-radius: 0.25rem; -} - -.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { - border-color: #007bff; - background-color: #007bff; -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); -} - -.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-radio .custom-control-label::before { - border-radius: 50%; -} - -.custom-radio .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); -} - -.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-switch { - padding-left: 2.25rem; -} - -.custom-switch .custom-control-label::before { - left: -2.25rem; - width: 1.75rem; - pointer-events: all; - border-radius: 0.5rem; -} - -.custom-switch .custom-control-label::after { - top: calc(0.25rem + 2px); - left: calc(-2.25rem + 2px); - width: calc(1rem - 4px); - height: calc(1rem - 4px); - background-color: #adb5bd; - border-radius: 0.5rem; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; - transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .custom-switch .custom-control-label::after { - transition: none; - } -} - -.custom-switch .custom-control-input:checked ~ .custom-control-label::after { - background-color: #fff; - -webkit-transform: translateX(0.75rem); - transform: translateX(0.75rem); -} - -.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-select { - display: inline-block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 1.75rem 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - vertical-align: middle; - background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; - border: 1px solid #ced4da; - border-radius: 0.25rem; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.custom-select:focus { - border-color: #80bdff; - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-select:focus::-ms-value { - color: #495057; - background-color: #fff; -} - -.custom-select[multiple], .custom-select[size]:not([size="1"]) { - height: auto; - padding-right: 0.75rem; - background-image: none; -} - -.custom-select:disabled { - color: #6c757d; - background-color: #e9ecef; -} - -.custom-select::-ms-expand { - display: none; -} - -.custom-select:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 #495057; -} - -.custom-select-sm { - height: calc(1.5em + 0.5rem + 2px); - padding-top: 0.25rem; - padding-bottom: 0.25rem; - padding-left: 0.5rem; - font-size: 0.875rem; -} - -.custom-select-lg { - height: calc(1.5em + 1rem + 2px); - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - font-size: 1.25rem; -} - -.custom-file { - position: relative; - display: inline-block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - margin-bottom: 0; -} - -.custom-file-input { - position: relative; - z-index: 2; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - margin: 0; - opacity: 0; -} - -.custom-file-input:focus ~ .custom-file-label { - border-color: #80bdff; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-file-input[disabled] ~ .custom-file-label, -.custom-file-input:disabled ~ .custom-file-label { - background-color: #e9ecef; -} - -.custom-file-input:lang(en) ~ .custom-file-label::after { - content: "Browse"; -} - -.custom-file-input ~ .custom-file-label[data-browse]::after { - content: attr(data-browse); -} - -.custom-file-label { - position: absolute; - top: 0; - right: 0; - left: 0; - z-index: 1; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 0.75rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - background-color: #fff; - border: 1px solid #ced4da; - border-radius: 0.25rem; -} - -.custom-file-label::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - z-index: 3; - display: block; - height: calc(1.5em + 0.75rem); - padding: 0.375rem 0.75rem; - line-height: 1.5; - color: #495057; - content: "Browse"; - background-color: #e9ecef; - border-left: inherit; - border-radius: 0 0.25rem 0.25rem 0; -} - -.custom-range { - width: 100%; - height: 1.4rem; - padding: 0; - background-color: transparent; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -.custom-range:focus { - outline: none; -} - -.custom-range:focus::-webkit-slider-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-range:focus::-moz-range-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-range:focus::-ms-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.custom-range::-moz-focus-outer { - border: 0; -} - -.custom-range::-webkit-slider-thumb { - width: 1rem; - height: 1rem; - margin-top: -0.25rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - -webkit-appearance: none; - appearance: none; -} - -@media (prefers-reduced-motion: reduce) { - .custom-range::-webkit-slider-thumb { - -webkit-transition: none; - transition: none; - } -} - -.custom-range::-webkit-slider-thumb:active { - background-color: #b3d7ff; -} - -.custom-range::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} - -.custom-range::-moz-range-thumb { - width: 1rem; - height: 1rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - -moz-appearance: none; - appearance: none; -} - -@media (prefers-reduced-motion: reduce) { - .custom-range::-moz-range-thumb { - -moz-transition: none; - transition: none; - } -} - -.custom-range::-moz-range-thumb:active { - background-color: #b3d7ff; -} - -.custom-range::-moz-range-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} - -.custom-range::-ms-thumb { - width: 1rem; - height: 1rem; - margin-top: 0; - margin-right: 0.2rem; - margin-left: 0.2rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - appearance: none; -} - -@media (prefers-reduced-motion: reduce) { - .custom-range::-ms-thumb { - -ms-transition: none; - transition: none; - } -} - -.custom-range::-ms-thumb:active { - background-color: #b3d7ff; -} - -.custom-range::-ms-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: transparent; - border-color: transparent; - border-width: 0.5rem; -} - -.custom-range::-ms-fill-lower { - background-color: #dee2e6; - border-radius: 1rem; -} - -.custom-range::-ms-fill-upper { - margin-right: 15px; - background-color: #dee2e6; - border-radius: 1rem; -} - -.custom-range:disabled::-webkit-slider-thumb { - background-color: #adb5bd; -} - -.custom-range:disabled::-webkit-slider-runnable-track { - cursor: default; -} - -.custom-range:disabled::-moz-range-thumb { - background-color: #adb5bd; -} - -.custom-range:disabled::-moz-range-track { - cursor: default; -} - -.custom-range:disabled::-ms-thumb { - background-color: #adb5bd; -} - -.custom-control-label::before, -.custom-file-label, -.custom-select { - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .custom-control-label::before, - .custom-file-label, - .custom-select { - transition: none; - } -} - -.nav { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.nav-link { - display: block; - padding: 0.5rem 1rem; -} - -.nav-link:hover, .nav-link:focus { - text-decoration: none; -} - -.nav-link.disabled { - color: #6c757d; - pointer-events: none; - cursor: default; -} - -.nav-tabs { - border-bottom: 1px solid #dee2e6; -} - -.nav-tabs .nav-item { - margin-bottom: -1px; -} - -.nav-tabs .nav-link { - border: 1px solid transparent; - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - border-color: #e9ecef #e9ecef #dee2e6; -} - -.nav-tabs .nav-link.disabled { - color: #6c757d; - background-color: transparent; - border-color: transparent; -} - -.nav-tabs .nav-link.active, -.nav-tabs .nav-item.show .nav-link { - color: #495057; - background-color: #fff; - border-color: #dee2e6 #dee2e6 #fff; -} - -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.nav-pills .nav-link { - border-radius: 0.25rem; -} - -.nav-pills .nav-link.active, -.nav-pills .show > .nav-link { - color: #fff; - background-color: #007bff; -} - -.nav-fill .nav-item { - -ms-flex: 1 1 auto; - flex: 1 1 auto; - text-align: center; -} - -.nav-justified .nav-item { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -ms-flex-positive: 1; - flex-grow: 1; - text-align: center; -} - -.tab-content > .tab-pane { - display: none; -} - -.tab-content > .active { - display: block; -} - -.navbar { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 0.5rem 1rem; -} - -.navbar .container, -.navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: justify; - justify-content: space-between; -} - -.navbar-brand { - display: inline-block; - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: 1rem; - font-size: 1.25rem; - line-height: inherit; - white-space: nowrap; -} - -.navbar-brand:hover, .navbar-brand:focus { - text-decoration: none; -} - -.navbar-nav { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.navbar-nav .nav-link { - padding-right: 0; - padding-left: 0; -} - -.navbar-nav .dropdown-menu { - position: static; - float: none; -} - -.navbar-text { - display: inline-block; - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.navbar-collapse { - -ms-flex-preferred-size: 100%; - flex-basis: 100%; - -ms-flex-positive: 1; - flex-grow: 1; - -ms-flex-align: center; - align-items: center; -} - -.navbar-toggler { - padding: 0.25rem 0.75rem; - font-size: 1.25rem; - line-height: 1; - background-color: transparent; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.navbar-toggler:hover, .navbar-toggler:focus { - text-decoration: none; -} - -.navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - content: ""; - background: no-repeat center center; - background-size: 100% 100%; -} - -@media (max-width: 575.98px) { - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 576px) { - .navbar-expand-sm { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-sm .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-sm .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-sm .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-sm .navbar-toggler { - display: none; - } -} - -@media (max-width: 767.98px) { - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 768px) { - .navbar-expand-md { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-md .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-md .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-md .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-md .navbar-toggler { - display: none; - } -} - -@media (max-width: 991.98px) { - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 992px) { - .navbar-expand-lg { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-lg .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-lg .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; - } -} - -@media (max-width: 1199.98px) { - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { - padding-right: 0; - padding-left: 0; - } -} - -@media (min-width: 1200px) { - .navbar-expand-xl { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-xl .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xl .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-xl .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-xl .navbar-toggler { - display: none; - } -} - -.navbar-expand { - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.navbar-expand > .container, -.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { - padding-right: 0; - padding-left: 0; -} - -.navbar-expand .navbar-nav { - -ms-flex-direction: row; - flex-direction: row; -} - -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute; -} - -.navbar-expand .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; -} - -.navbar-expand > .container, -.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; -} - -.navbar-expand .navbar-collapse { - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; -} - -.navbar-expand .navbar-toggler { - display: none; -} - -.navbar-light .navbar-brand { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-nav .nav-link { - color: rgba(0, 0, 0, 0.5); -} - -.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { - color: rgba(0, 0, 0, 0.7); -} - -.navbar-light .navbar-nav .nav-link.disabled { - color: rgba(0, 0, 0, 0.3); -} - -.navbar-light .navbar-nav .show > .nav-link, -.navbar-light .navbar-nav .active > .nav-link, -.navbar-light .navbar-nav .nav-link.show, -.navbar-light .navbar-nav .nav-link.active { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-toggler { - color: rgba(0, 0, 0, 0.5); - border-color: rgba(0, 0, 0, 0.1); -} - -.navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); -} - -.navbar-light .navbar-text { - color: rgba(0, 0, 0, 0.5); -} - -.navbar-light .navbar-text a { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-dark .navbar-brand { - color: #fff; -} - -.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { - color: #fff; -} - -.navbar-dark .navbar-nav .nav-link { - color: rgba(255, 255, 255, 0.5); -} - -.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { - color: rgba(255, 255, 255, 0.75); -} - -.navbar-dark .navbar-nav .nav-link.disabled { - color: rgba(255, 255, 255, 0.25); -} - -.navbar-dark .navbar-nav .show > .nav-link, -.navbar-dark .navbar-nav .active > .nav-link, -.navbar-dark .navbar-nav .nav-link.show, -.navbar-dark .navbar-nav .nav-link.active { - color: #fff; -} - -.navbar-dark .navbar-toggler { - color: rgba(255, 255, 255, 0.5); - border-color: rgba(255, 255, 255, 0.1); -} - -.navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); -} - -.navbar-dark .navbar-text { - color: rgba(255, 255, 255, 0.5); -} - -.navbar-dark .navbar-text a { - color: #fff; -} - -.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { - color: #fff; -} - -.card { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - min-width: 0; - word-wrap: break-word; - background-color: #fff; - background-clip: border-box; - border: 1px solid rgba(0, 0, 0, 0.125); - border-radius: 0.25rem; -} - -.card > hr { - margin-right: 0; - margin-left: 0; -} - -.card > .list-group:first-child .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.card > .list-group:last-child .list-group-item:last-child { - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.card-body { - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-height: 1px; - padding: 1.25rem; -} - -.card-title { - margin-bottom: 0.75rem; -} - -.card-subtitle { - margin-top: -0.375rem; - margin-bottom: 0; -} - -.card-text:last-child { - margin-bottom: 0; -} - -.card-link:hover { - text-decoration: none; -} - -.card-link + .card-link { - margin-left: 1.25rem; -} - -.card-header { - padding: 0.75rem 1.25rem; - margin-bottom: 0; - background-color: rgba(0, 0, 0, 0.03); - border-bottom: 1px solid rgba(0, 0, 0, 0.125); -} - -.card-header:first-child { - border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; -} - -.card-header + .list-group .list-group-item:first-child { - border-top: 0; -} - -.card-footer { - padding: 0.75rem 1.25rem; - background-color: rgba(0, 0, 0, 0.03); - border-top: 1px solid rgba(0, 0, 0, 0.125); -} - -.card-footer:last-child { - border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); -} - -.card-header-tabs { - margin-right: -0.625rem; - margin-bottom: -0.75rem; - margin-left: -0.625rem; - border-bottom: 0; -} - -.card-header-pills { - margin-right: -0.625rem; - margin-left: -0.625rem; -} - -.card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 1.25rem; -} - -.card-img, -.card-img-top, -.card-img-bottom { - -ms-flex-negative: 0; - flex-shrink: 0; - width: 100%; -} - -.card-img, -.card-img-top { - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); -} - -.card-img, -.card-img-bottom { - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); -} - -.card-deck .card { - margin-bottom: 15px; -} - -@media (min-width: 576px) { - .card-deck { - display: -ms-flexbox; - display: flex; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px; - } - .card-deck .card { - -ms-flex: 1 0 0%; - flex: 1 0 0%; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px; - } -} - -.card-group > .card { - margin-bottom: 15px; -} - -@media (min-width: 576px) { - .card-group { - display: -ms-flexbox; - display: flex; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - } - .card-group > .card { - -ms-flex: 1 0 0%; - flex: 1 0 0%; - margin-bottom: 0; - } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; - } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; - } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; - } -} - -.card-columns .card { - margin-bottom: 0.75rem; -} - -@media (min-width: 576px) { - .card-columns { - -webkit-column-count: 3; - -moz-column-count: 3; - column-count: 3; - -webkit-column-gap: 1.25rem; - -moz-column-gap: 1.25rem; - column-gap: 1.25rem; - orphans: 1; - widows: 1; - } - .card-columns .card { - display: inline-block; - width: 100%; - } -} - -.accordion > .card { - overflow: hidden; -} - -.accordion > .card:not(:last-of-type) { - border-bottom: 0; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.accordion > .card:not(:first-of-type) { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.accordion > .card > .card-header { - border-radius: 0; - margin-bottom: -1px; -} - -.breadcrumb { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding: 0.75rem 1rem; - margin-bottom: 1rem; - list-style: none; - background-color: #e9ecef; - border-radius: 0.25rem; -} - -.breadcrumb-item + .breadcrumb-item { - padding-left: 0.5rem; -} - -.breadcrumb-item + .breadcrumb-item::before { - display: inline-block; - padding-right: 0.5rem; - color: #6c757d; - content: "/"; -} - -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: underline; -} - -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: none; -} - -.breadcrumb-item.active { - color: #6c757d; -} - -.pagination { - display: -ms-flexbox; - display: flex; - padding-left: 0; - list-style: none; - border-radius: 0.25rem; -} - -.page-link { - position: relative; - display: block; - padding: 0.5rem 0.75rem; - margin-left: -1px; - line-height: 1.25; - color: #007bff; - background-color: #fff; - border: 1px solid #dee2e6; -} - -.page-link:hover { - z-index: 2; - color: #0056b3; - text-decoration: none; - background-color: #e9ecef; - border-color: #dee2e6; -} - -.page-link:focus { - z-index: 3; - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.page-item:first-child .page-link { - margin-left: 0; - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.page-item:last-child .page-link { - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; -} - -.page-item.active .page-link { - z-index: 3; - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.page-item.disabled .page-link { - color: #6c757d; - pointer-events: none; - cursor: auto; - background-color: #fff; - border-color: #dee2e6; -} - -.pagination-lg .page-link { - padding: 0.75rem 1.5rem; - font-size: 1.25rem; - line-height: 1.5; -} - -.pagination-lg .page-item:first-child .page-link { - border-top-left-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; -} - -.pagination-lg .page-item:last-child .page-link { - border-top-right-radius: 0.3rem; - border-bottom-right-radius: 0.3rem; -} - -.pagination-sm .page-link { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; -} - -.pagination-sm .page-item:first-child .page-link { - border-top-left-radius: 0.2rem; - border-bottom-left-radius: 0.2rem; -} - -.pagination-sm .page-item:last-child .page-link { - border-top-right-radius: 0.2rem; - border-bottom-right-radius: 0.2rem; -} - -.badge { - display: inline-block; - padding: 0.25em 0.4em; - font-size: 75%; - font-weight: 700; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: 0.25rem; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .badge { - transition: none; - } -} - -a.badge:hover, a.badge:focus { - text-decoration: none; -} - -.badge:empty { - display: none; -} - -.btn .badge { - position: relative; - top: -1px; -} - -.badge-pill { - padding-right: 0.6em; - padding-left: 0.6em; - border-radius: 10rem; -} - -.badge-primary { - color: #fff; - background-color: #007bff; -} - -a.badge-primary:hover, a.badge-primary:focus { - color: #fff; - background-color: #0062cc; -} - -a.badge-primary:focus, a.badge-primary.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.badge-secondary { - color: #fff; - background-color: #6c757d; -} - -a.badge-secondary:hover, a.badge-secondary:focus { - color: #fff; - background-color: #545b62; -} - -a.badge-secondary:focus, a.badge-secondary.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.badge-success { - color: #fff; - background-color: #28a745; -} - -a.badge-success:hover, a.badge-success:focus { - color: #fff; - background-color: #1e7e34; -} - -a.badge-success:focus, a.badge-success.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.badge-info { - color: #fff; - background-color: #17a2b8; -} - -a.badge-info:hover, a.badge-info:focus { - color: #fff; - background-color: #117a8b; -} - -a.badge-info:focus, a.badge-info.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.badge-warning { - color: #212529; - background-color: #ffc107; -} - -a.badge-warning:hover, a.badge-warning:focus { - color: #212529; - background-color: #d39e00; -} - -a.badge-warning:focus, a.badge-warning.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.badge-danger { - color: #fff; - background-color: #dc3545; -} - -a.badge-danger:hover, a.badge-danger:focus { - color: #fff; - background-color: #bd2130; -} - -a.badge-danger:focus, a.badge-danger.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.badge-light { - color: #212529; - background-color: #f8f9fa; -} - -a.badge-light:hover, a.badge-light:focus { - color: #212529; - background-color: #dae0e5; -} - -a.badge-light:focus, a.badge-light.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.badge-dark { - color: #fff; - background-color: #343a40; -} - -a.badge-dark:hover, a.badge-dark:focus { - color: #fff; - background-color: #1d2124; -} - -a.badge-dark:focus, a.badge-dark.focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.jumbotron { - padding: 2rem 1rem; - margin-bottom: 2rem; - background-color: #e9ecef; - border-radius: 0.3rem; -} - -@media (min-width: 576px) { - .jumbotron { - padding: 4rem 2rem; - } -} - -.jumbotron-fluid { - padding-right: 0; - padding-left: 0; - border-radius: 0; -} - -.alert { - position: relative; - padding: 0.75rem 1.25rem; - margin-bottom: 1rem; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.alert-heading { - color: inherit; -} - -.alert-link { - font-weight: 700; -} - -.alert-dismissible { - padding-right: 4rem; -} - -.alert-dismissible .close { - position: absolute; - top: 0; - right: 0; - padding: 0.75rem 1.25rem; - color: inherit; -} - -.alert-primary { - color: #004085; - background-color: #cce5ff; - border-color: #b8daff; -} - -.alert-primary hr { - border-top-color: #9fcdff; -} - -.alert-primary .alert-link { - color: #002752; -} - -.alert-secondary { - color: #383d41; - background-color: #e2e3e5; - border-color: #d6d8db; -} - -.alert-secondary hr { - border-top-color: #c8cbcf; -} - -.alert-secondary .alert-link { - color: #202326; -} - -.alert-success { - color: #155724; - background-color: #d4edda; - border-color: #c3e6cb; -} - -.alert-success hr { - border-top-color: #b1dfbb; -} - -.alert-success .alert-link { - color: #0b2e13; -} - -.alert-info { - color: #0c5460; - background-color: #d1ecf1; - border-color: #bee5eb; -} - -.alert-info hr { - border-top-color: #abdde5; -} - -.alert-info .alert-link { - color: #062c33; -} - -.alert-warning { - color: #856404; - background-color: #fff3cd; - border-color: #ffeeba; -} - -.alert-warning hr { - border-top-color: #ffe8a1; -} - -.alert-warning .alert-link { - color: #533f03; -} - -.alert-danger { - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; -} - -.alert-danger hr { - border-top-color: #f1b0b7; -} - -.alert-danger .alert-link { - color: #491217; -} - -.alert-light { - color: #818182; - background-color: #fefefe; - border-color: #fdfdfe; -} - -.alert-light hr { - border-top-color: #ececf6; -} - -.alert-light .alert-link { - color: #686868; -} - -.alert-dark { - color: #1b1e21; - background-color: #d6d8d9; - border-color: #c6c8ca; -} - -.alert-dark hr { - border-top-color: #b9bbbe; -} - -.alert-dark .alert-link { - color: #040505; -} - -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} - -@keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} - -.progress { - display: -ms-flexbox; - display: flex; - height: 1rem; - overflow: hidden; - font-size: 0.75rem; - background-color: #e9ecef; - border-radius: 0.25rem; -} - -.progress-bar { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-pack: center; - justify-content: center; - overflow: hidden; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #007bff; - transition: width 0.6s ease; -} - -@media (prefers-reduced-motion: reduce) { - .progress-bar { - transition: none; - } -} - -.progress-bar-striped { - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: 1rem 1rem; -} - -.progress-bar-animated { - -webkit-animation: progress-bar-stripes 1s linear infinite; - animation: progress-bar-stripes 1s linear infinite; -} - -@media (prefers-reduced-motion: reduce) { - .progress-bar-animated { - -webkit-animation: none; - animation: none; - } -} - -.media { - display: -ms-flexbox; - display: flex; - -ms-flex-align: start; - align-items: flex-start; -} - -.media-body { - -ms-flex: 1; - flex: 1; -} - -.list-group { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; -} - -.list-group-item-action { - width: 100%; - color: #495057; - text-align: inherit; -} - -.list-group-item-action:hover, .list-group-item-action:focus { - z-index: 1; - color: #495057; - text-decoration: none; - background-color: #f8f9fa; -} - -.list-group-item-action:active { - color: #212529; - background-color: #e9ecef; -} - -.list-group-item { - position: relative; - display: block; - padding: 0.75rem 1.25rem; - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.125); -} - -.list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} - -.list-group-item:last-child { - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - -.list-group-item.disabled, .list-group-item:disabled { - color: #6c757d; - pointer-events: none; - background-color: #fff; -} - -.list-group-item.active { - z-index: 2; - color: #fff; - background-color: #007bff; - border-color: #007bff; -} - -.list-group-item + .list-group-item { - border-top-width: 0; -} - -.list-group-item + .list-group-item.active { - margin-top: -1px; - border-top-width: 1px; -} - -.list-group-horizontal { - -ms-flex-direction: row; - flex-direction: row; -} - -.list-group-horizontal .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; -} - -.list-group-horizontal .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; -} - -.list-group-horizontal .list-group-item.active { - margin-top: 0; -} - -.list-group-horizontal .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; -} - -.list-group-horizontal .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; -} - -@media (min-width: 576px) { - .list-group-horizontal-sm { - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-sm .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-sm .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-sm .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-sm .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-sm .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} - -@media (min-width: 768px) { - .list-group-horizontal-md { - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-md .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-md .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-md .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-md .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-md .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} - -@media (min-width: 992px) { - .list-group-horizontal-lg { - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-lg .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-lg .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-lg .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-lg .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-lg .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} - -@media (min-width: 1200px) { - .list-group-horizontal-xl { - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-xl .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-xl .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-xl .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-xl .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-xl .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} - -.list-group-flush .list-group-item { - border-right-width: 0; - border-left-width: 0; - border-radius: 0; -} - -.list-group-flush .list-group-item:first-child { - border-top-width: 0; -} - -.list-group-flush:last-child .list-group-item:last-child { - border-bottom-width: 0; -} - -.list-group-item-primary { - color: #004085; - background-color: #b8daff; -} - -.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { - color: #004085; - background-color: #9fcdff; -} - -.list-group-item-primary.list-group-item-action.active { - color: #fff; - background-color: #004085; - border-color: #004085; -} - -.list-group-item-secondary { - color: #383d41; - background-color: #d6d8db; -} - -.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { - color: #383d41; - background-color: #c8cbcf; -} - -.list-group-item-secondary.list-group-item-action.active { - color: #fff; - background-color: #383d41; - border-color: #383d41; -} - -.list-group-item-success { - color: #155724; - background-color: #c3e6cb; -} - -.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { - color: #155724; - background-color: #b1dfbb; -} - -.list-group-item-success.list-group-item-action.active { - color: #fff; - background-color: #155724; - border-color: #155724; -} - -.list-group-item-info { - color: #0c5460; - background-color: #bee5eb; -} - -.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { - color: #0c5460; - background-color: #abdde5; -} - -.list-group-item-info.list-group-item-action.active { - color: #fff; - background-color: #0c5460; - border-color: #0c5460; -} - -.list-group-item-warning { - color: #856404; - background-color: #ffeeba; -} - -.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { - color: #856404; - background-color: #ffe8a1; -} - -.list-group-item-warning.list-group-item-action.active { - color: #fff; - background-color: #856404; - border-color: #856404; -} - -.list-group-item-danger { - color: #721c24; - background-color: #f5c6cb; -} - -.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { - color: #721c24; - background-color: #f1b0b7; -} - -.list-group-item-danger.list-group-item-action.active { - color: #fff; - background-color: #721c24; - border-color: #721c24; -} - -.list-group-item-light { - color: #818182; - background-color: #fdfdfe; -} - -.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { - color: #818182; - background-color: #ececf6; -} - -.list-group-item-light.list-group-item-action.active { - color: #fff; - background-color: #818182; - border-color: #818182; -} - -.list-group-item-dark { - color: #1b1e21; - background-color: #c6c8ca; -} - -.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { - color: #1b1e21; - background-color: #b9bbbe; -} - -.list-group-item-dark.list-group-item-action.active { - color: #fff; - background-color: #1b1e21; - border-color: #1b1e21; -} - -.close { - float: right; - font-size: 1.5rem; - font-weight: 700; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - opacity: .5; -} - -.close:hover { - color: #000; - text-decoration: none; -} - -.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { - opacity: .75; -} - -button.close { - padding: 0; - background-color: transparent; - border: 0; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -a.close.disabled { - pointer-events: none; -} - -.toast { - max-width: 350px; - overflow: hidden; - font-size: 0.875rem; - background-color: rgba(255, 255, 255, 0.85); - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.1); - box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - opacity: 0; - border-radius: 0.25rem; -} - -.toast:not(:last-child) { - margin-bottom: 0.75rem; -} - -.toast.showing { - opacity: 1; -} - -.toast.show { - display: block; - opacity: 1; -} - -.toast.hide { - display: none; -} - -.toast-header { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - padding: 0.25rem 0.75rem; - color: #6c757d; - background-color: rgba(255, 255, 255, 0.85); - background-clip: padding-box; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); -} - -.toast-body { - padding: 0.75rem; -} - -.modal-open { - overflow: hidden; -} - -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} - -.modal { - position: fixed; - top: 0; - left: 0; - z-index: 1050; - display: none; - width: 100%; - height: 100%; - overflow: hidden; - outline: 0; -} - -.modal-dialog { - position: relative; - width: auto; - margin: 0.5rem; - pointer-events: none; -} - -.modal.fade .modal-dialog { - transition: -webkit-transform 0.3s ease-out; - transition: transform 0.3s ease-out; - transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out; - -webkit-transform: translate(0, -50px); - transform: translate(0, -50px); -} - -@media (prefers-reduced-motion: reduce) { - .modal.fade .modal-dialog { - transition: none; - } -} - -.modal.show .modal-dialog { - -webkit-transform: none; - transform: none; -} - -.modal.modal-static .modal-dialog { - -webkit-transform: scale(1.02); - transform: scale(1.02); -} - -.modal-dialog-scrollable { - display: -ms-flexbox; - display: flex; - max-height: calc(100% - 1rem); -} - -.modal-dialog-scrollable .modal-content { - max-height: calc(100vh - 1rem); - overflow: hidden; -} - -.modal-dialog-scrollable .modal-header, -.modal-dialog-scrollable .modal-footer { - -ms-flex-negative: 0; - flex-shrink: 0; -} - -.modal-dialog-scrollable .modal-body { - overflow-y: auto; -} - -.modal-dialog-centered { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - min-height: calc(100% - 1rem); -} - -.modal-dialog-centered::before { - display: block; - height: calc(100vh - 1rem); - content: ""; -} - -.modal-dialog-centered.modal-dialog-scrollable { - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-pack: center; - justify-content: center; - height: 100%; -} - -.modal-dialog-centered.modal-dialog-scrollable .modal-content { - max-height: none; -} - -.modal-dialog-centered.modal-dialog-scrollable::before { - content: none; -} - -.modal-content { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - width: 100%; - pointer-events: auto; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; - outline: 0; -} - -.modal-backdrop { - position: fixed; - top: 0; - left: 0; - z-index: 1040; - width: 100vw; - height: 100vh; - background-color: #000; -} - -.modal-backdrop.fade { - opacity: 0; -} - -.modal-backdrop.show { - opacity: 0.5; -} - -.modal-header { - display: -ms-flexbox; - display: flex; - -ms-flex-align: start; - align-items: flex-start; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 1rem 1rem; - border-bottom: 1px solid #dee2e6; - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); -} - -.modal-header .close { - padding: 1rem 1rem; - margin: -1rem -1rem -1rem auto; -} - -.modal-title { - margin-bottom: 0; - line-height: 1.5; -} - -.modal-body { - position: relative; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - padding: 1rem; -} - -.modal-footer { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: end; - justify-content: flex-end; - padding: 0.75rem; - border-top: 1px solid #dee2e6; - border-bottom-right-radius: calc(0.3rem - 1px); - border-bottom-left-radius: calc(0.3rem - 1px); -} - -.modal-footer > * { - margin: 0.25rem; -} - -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} - -@media (min-width: 576px) { - .modal-dialog { - max-width: 500px; - margin: 1.75rem auto; - } - .modal-dialog-scrollable { - max-height: calc(100% - 3.5rem); - } - .modal-dialog-scrollable .modal-content { - max-height: calc(100vh - 3.5rem); - } - .modal-dialog-centered { - min-height: calc(100% - 3.5rem); - } - .modal-dialog-centered::before { - height: calc(100vh - 3.5rem); - } - .modal-sm { - max-width: 300px; - } -} - -@media (min-width: 992px) { - .modal-lg, - .modal-xl { - max-width: 800px; - } -} - -@media (min-width: 1200px) { - .modal-xl { - max-width: 1140px; - } -} - -.tooltip { - position: absolute; - z-index: 1070; - display: block; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - opacity: 0; -} - -.tooltip.show { - opacity: 0.9; -} - -.tooltip .arrow { - position: absolute; - display: block; - width: 0.8rem; - height: 0.4rem; -} - -.tooltip .arrow::before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { - padding: 0.4rem 0; -} - -.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { - bottom: 0; -} - -.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { - top: 0; - border-width: 0.4rem 0.4rem 0; - border-top-color: #000; -} - -.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { - padding: 0 0.4rem; -} - -.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { - left: 0; - width: 0.4rem; - height: 0.8rem; -} - -.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { - right: 0; - border-width: 0.4rem 0.4rem 0.4rem 0; - border-right-color: #000; -} - -.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { - padding: 0.4rem 0; -} - -.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { - top: 0; -} - -.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { - bottom: 0; - border-width: 0 0.4rem 0.4rem; - border-bottom-color: #000; -} - -.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { - padding: 0 0.4rem; -} - -.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { - right: 0; - width: 0.4rem; - height: 0.8rem; -} - -.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { - left: 0; - border-width: 0.4rem 0 0.4rem 0.4rem; - border-left-color: #000; -} - -.tooltip-inner { - max-width: 200px; - padding: 0.25rem 0.5rem; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 0.25rem; -} - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: block; - max-width: 276px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; -} - -.popover .arrow { - position: absolute; - display: block; - width: 1rem; - height: 0.5rem; - margin: 0 0.3rem; -} - -.popover .arrow::before, .popover .arrow::after { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-popover-top, .bs-popover-auto[x-placement^="top"] { - margin-bottom: 0.5rem; -} - -.bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { - bottom: calc(-0.5rem - 1px); -} - -.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { - bottom: 0; - border-width: 0.5rem 0.5rem 0; - border-top-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after { - bottom: 1px; - border-width: 0.5rem 0.5rem 0; - border-top-color: #fff; -} - -.bs-popover-right, .bs-popover-auto[x-placement^="right"] { - margin-left: 0.5rem; -} - -.bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { - left: calc(-0.5rem - 1px); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} - -.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before { - left: 0; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after { - left: 1px; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: #fff; -} - -.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { - margin-top: 0.5rem; -} - -.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { - top: calc(-0.5rem - 1px); -} - -.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { - top: 0; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after { - top: 1px; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: #fff; -} - -.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: 1rem; - margin-left: -0.5rem; - content: ""; - border-bottom: 1px solid #f7f7f7; -} - -.bs-popover-left, .bs-popover-auto[x-placement^="left"] { - margin-right: 0.5rem; -} - -.bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { - right: calc(-0.5rem - 1px); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} - -.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before { - right: 0; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: rgba(0, 0, 0, 0.25); -} - -.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after { - right: 1px; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: #fff; -} - -.popover-header { - padding: 0.5rem 0.75rem; - margin-bottom: 0; - font-size: 1rem; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); -} - -.popover-header:empty { - display: none; -} - -.popover-body { - padding: 0.5rem 0.75rem; - color: #212529; -} - -.carousel { - position: relative; -} - -.carousel.pointer-event { - -ms-touch-action: pan-y; - touch-action: pan-y; -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} - -.carousel-inner::after { - display: block; - clear: both; - content: ""; -} - -.carousel-item { - position: relative; - display: none; - float: left; - width: 100%; - margin-right: -100%; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - transition: -webkit-transform 0.6s ease-in-out; - transition: transform 0.6s ease-in-out; - transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-item { - transition: none; - } -} - -.carousel-item.active, -.carousel-item-next, -.carousel-item-prev { - display: block; -} - -.carousel-item-next:not(.carousel-item-left), -.active.carousel-item-right { - -webkit-transform: translateX(100%); - transform: translateX(100%); -} - -.carousel-item-prev:not(.carousel-item-right), -.active.carousel-item-left { - -webkit-transform: translateX(-100%); - transform: translateX(-100%); -} - -.carousel-fade .carousel-item { - opacity: 0; - transition-property: opacity; - -webkit-transform: none; - transform: none; -} - -.carousel-fade .carousel-item.active, -.carousel-fade .carousel-item-next.carousel-item-left, -.carousel-fade .carousel-item-prev.carousel-item-right { - z-index: 1; - opacity: 1; -} - -.carousel-fade .active.carousel-item-left, -.carousel-fade .active.carousel-item-right { - z-index: 0; - opacity: 0; - transition: opacity 0s 0.6s; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-fade .active.carousel-item-left, - .carousel-fade .active.carousel-item-right { - transition: none; - } -} - -.carousel-control-prev, -.carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - z-index: 1; - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - -ms-flex-pack: center; - justify-content: center; - width: 15%; - color: #fff; - text-align: center; - opacity: 0.5; - transition: opacity 0.15s ease; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-control-prev, - .carousel-control-next { - transition: none; - } -} - -.carousel-control-prev:hover, .carousel-control-prev:focus, -.carousel-control-next:hover, -.carousel-control-next:focus { - color: #fff; - text-decoration: none; - outline: 0; - opacity: 0.9; -} - -.carousel-control-prev { - left: 0; -} - -.carousel-control-next { - right: 0; -} - -.carousel-control-prev-icon, -.carousel-control-next-icon { - display: inline-block; - width: 20px; - height: 20px; - background: no-repeat 50% / 100% 100%; -} - -.carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); -} - -.carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); -} - -.carousel-indicators { - position: absolute; - right: 0; - bottom: 0; - left: 0; - z-index: 15; - display: -ms-flexbox; - display: flex; - -ms-flex-pack: center; - justify-content: center; - padding-left: 0; - margin-right: 15%; - margin-left: 15%; - list-style: none; -} - -.carousel-indicators li { - box-sizing: content-box; - -ms-flex: 0 1 auto; - flex: 0 1 auto; - width: 30px; - height: 3px; - margin-right: 3px; - margin-left: 3px; - text-indent: -999px; - cursor: pointer; - background-color: #fff; - background-clip: padding-box; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - opacity: .5; - transition: opacity 0.6s ease; -} - -@media (prefers-reduced-motion: reduce) { - .carousel-indicators li { - transition: none; - } -} - -.carousel-indicators .active { - opacity: 1; -} - -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; -} - -@-webkit-keyframes spinner-border { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -@keyframes spinner-border { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -.spinner-border { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: text-bottom; - border: 0.25em solid currentColor; - border-right-color: transparent; - border-radius: 50%; - -webkit-animation: spinner-border .75s linear infinite; - animation: spinner-border .75s linear infinite; -} - -.spinner-border-sm { - width: 1rem; - height: 1rem; - border-width: 0.2em; -} - -@-webkit-keyframes spinner-grow { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - opacity: 1; - } -} - -@keyframes spinner-grow { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - opacity: 1; - } -} - -.spinner-grow { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: text-bottom; - background-color: currentColor; - border-radius: 50%; - opacity: 0; - -webkit-animation: spinner-grow .75s linear infinite; - animation: spinner-grow .75s linear infinite; -} - -.spinner-grow-sm { - width: 1rem; - height: 1rem; -} - -.align-baseline { - vertical-align: baseline !important; -} - -.align-top { - vertical-align: top !important; -} - -.align-middle { - vertical-align: middle !important; -} - -.align-bottom { - vertical-align: bottom !important; -} - -.align-text-bottom { - vertical-align: text-bottom !important; -} - -.align-text-top { - vertical-align: text-top !important; -} - -.bg-primary { - background-color: #007bff !important; -} - -a.bg-primary:hover, a.bg-primary:focus, -button.bg-primary:hover, -button.bg-primary:focus { - background-color: #0062cc !important; -} - -.bg-secondary { - background-color: #6c757d !important; -} - -a.bg-secondary:hover, a.bg-secondary:focus, -button.bg-secondary:hover, -button.bg-secondary:focus { - background-color: #545b62 !important; -} - -.bg-success { - background-color: #28a745 !important; -} - -a.bg-success:hover, a.bg-success:focus, -button.bg-success:hover, -button.bg-success:focus { - background-color: #1e7e34 !important; -} - -.bg-info { - background-color: #17a2b8 !important; -} - -a.bg-info:hover, a.bg-info:focus, -button.bg-info:hover, -button.bg-info:focus { - background-color: #117a8b !important; -} - -.bg-warning { - background-color: #ffc107 !important; -} - -a.bg-warning:hover, a.bg-warning:focus, -button.bg-warning:hover, -button.bg-warning:focus { - background-color: #d39e00 !important; -} - -.bg-danger { - background-color: #dc3545 !important; -} - -a.bg-danger:hover, a.bg-danger:focus, -button.bg-danger:hover, -button.bg-danger:focus { - background-color: #bd2130 !important; -} - -.bg-light { - background-color: #f8f9fa !important; -} - -a.bg-light:hover, a.bg-light:focus, -button.bg-light:hover, -button.bg-light:focus { - background-color: #dae0e5 !important; -} - -.bg-dark { - background-color: #343a40 !important; -} - -a.bg-dark:hover, a.bg-dark:focus, -button.bg-dark:hover, -button.bg-dark:focus { - background-color: #1d2124 !important; -} - -.bg-white { - background-color: #fff !important; -} - -.bg-transparent { - background-color: transparent !important; -} - -.border { - border: 1px solid #dee2e6 !important; -} - -.border-top { - border-top: 1px solid #dee2e6 !important; -} - -.border-right { - border-right: 1px solid #dee2e6 !important; -} - -.border-bottom { - border-bottom: 1px solid #dee2e6 !important; -} - -.border-left { - border-left: 1px solid #dee2e6 !important; -} - -.border-0 { - border: 0 !important; -} - -.border-top-0 { - border-top: 0 !important; -} - -.border-right-0 { - border-right: 0 !important; -} - -.border-bottom-0 { - border-bottom: 0 !important; -} - -.border-left-0 { - border-left: 0 !important; -} - -.border-primary { - border-color: #007bff !important; -} - -.border-secondary { - border-color: #6c757d !important; -} - -.border-success { - border-color: #28a745 !important; -} - -.border-info { - border-color: #17a2b8 !important; -} - -.border-warning { - border-color: #ffc107 !important; -} - -.border-danger { - border-color: #dc3545 !important; -} - -.border-light { - border-color: #f8f9fa !important; -} - -.border-dark { - border-color: #343a40 !important; -} - -.border-white { - border-color: #fff !important; -} - -.rounded-sm { - border-radius: 0.2rem !important; -} - -.rounded { - border-radius: 0.25rem !important; -} - -.rounded-top { - border-top-left-radius: 0.25rem !important; - border-top-right-radius: 0.25rem !important; -} - -.rounded-right { - border-top-right-radius: 0.25rem !important; - border-bottom-right-radius: 0.25rem !important; -} - -.rounded-bottom { - border-bottom-right-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-left { - border-top-left-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-lg { - border-radius: 0.3rem !important; -} - -.rounded-circle { - border-radius: 50% !important; -} - -.rounded-pill { - border-radius: 50rem !important; -} - -.rounded-0 { - border-radius: 0 !important; -} - -.clearfix::after { - display: block; - clear: both; - content: ""; -} - -.d-none { - display: none !important; -} - -.d-inline { - display: inline !important; -} - -.d-inline-block { - display: inline-block !important; -} - -.d-block { - display: block !important; -} - -.d-table { - display: table !important; -} - -.d-table-row { - display: table-row !important; -} - -.d-table-cell { - display: table-cell !important; -} - -.d-flex { - display: -ms-flexbox !important; - display: flex !important; -} - -.d-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; -} - -@media (min-width: 576px) { - .d-sm-none { - display: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-sm-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 768px) { - .d-md-none { - display: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-md-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 992px) { - .d-lg-none { - display: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-lg-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media (min-width: 1200px) { - .d-xl-none { - display: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-xl-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -@media print { - .d-print-none { - display: none !important; - } - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: -ms-flexbox !important; - display: flex !important; - } - .d-print-inline-flex { - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} - -.embed-responsive { - position: relative; - display: block; - width: 100%; - padding: 0; - overflow: hidden; -} - -.embed-responsive::before { - display: block; - content: ""; -} - -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} - -.embed-responsive-21by9::before { - padding-top: 42.857143%; -} - -.embed-responsive-16by9::before { - padding-top: 56.25%; -} - -.embed-responsive-4by3::before { - padding-top: 75%; -} - -.embed-responsive-1by1::before { - padding-top: 100%; -} - -.flex-row { - -ms-flex-direction: row !important; - flex-direction: row !important; -} - -.flex-column { - -ms-flex-direction: column !important; - flex-direction: column !important; -} - -.flex-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; -} - -.flex-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; -} - -.flex-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; -} - -.flex-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; -} - -.flex-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; -} - -.flex-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; -} - -.flex-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; -} - -.flex-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; -} - -.flex-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; -} - -.flex-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; -} - -.justify-content-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; -} - -.justify-content-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; -} - -.justify-content-center { - -ms-flex-pack: center !important; - justify-content: center !important; -} - -.justify-content-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; -} - -.justify-content-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; -} - -.align-items-start { - -ms-flex-align: start !important; - align-items: flex-start !important; -} - -.align-items-end { - -ms-flex-align: end !important; - align-items: flex-end !important; -} - -.align-items-center { - -ms-flex-align: center !important; - align-items: center !important; -} - -.align-items-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; -} - -.align-items-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; -} - -.align-content-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; -} - -.align-content-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; -} - -.align-content-center { - -ms-flex-line-pack: center !important; - align-content: center !important; -} - -.align-content-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; -} - -.align-content-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; -} - -.align-content-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; -} - -.align-self-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; -} - -.align-self-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; -} - -.align-self-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; -} - -.align-self-center { - -ms-flex-item-align: center !important; - align-self: center !important; -} - -.align-self-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; -} - -.align-self-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; -} - -@media (min-width: 576px) { - .flex-sm-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-sm-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-sm-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-sm-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-sm-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-sm-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-sm-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-sm-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-sm-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-sm-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-sm-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-sm-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-sm-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-sm-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-sm-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-sm-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-sm-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-sm-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-sm-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-sm-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-sm-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-sm-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-sm-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-sm-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-sm-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-sm-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-sm-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-sm-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 768px) { - .flex-md-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-md-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-md-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-md-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-md-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-md-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-md-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-md-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-md-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-md-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-md-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-md-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-md-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-md-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-md-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-md-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-md-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-md-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-md-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-md-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-md-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-md-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-md-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-md-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-md-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-md-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-md-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-md-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-md-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-md-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 992px) { - .flex-lg-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-lg-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-lg-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-lg-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-lg-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-lg-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-lg-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-lg-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-lg-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-lg-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-lg-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-lg-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-lg-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-lg-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-lg-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-lg-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-lg-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-lg-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-lg-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-lg-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-lg-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-lg-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-lg-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-lg-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-lg-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-lg-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-lg-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-lg-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -@media (min-width: 1200px) { - .flex-xl-row { - -ms-flex-direction: row !important; - flex-direction: row !important; - } - .flex-xl-column { - -ms-flex-direction: column !important; - flex-direction: column !important; - } - .flex-xl-row-reverse { - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - .flex-xl-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - .flex-xl-fill { - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - .flex-xl-grow-0 { - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - .justify-content-xl-start { - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - .justify-content-xl-end { - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - .justify-content-xl-center { - -ms-flex-pack: center !important; - justify-content: center !important; - } - .justify-content-xl-between { - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - .justify-content-xl-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - .align-items-xl-start { - -ms-flex-align: start !important; - align-items: flex-start !important; - } - .align-items-xl-end { - -ms-flex-align: end !important; - align-items: flex-end !important; - } - .align-items-xl-center { - -ms-flex-align: center !important; - align-items: center !important; - } - .align-items-xl-baseline { - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - .align-items-xl-stretch { - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - .align-content-xl-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - .align-content-xl-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - .align-content-xl-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - .align-content-xl-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - .align-content-xl-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - .align-content-xl-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - .align-self-xl-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - .align-self-xl-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - .align-self-xl-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - .align-self-xl-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - .align-self-xl-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - .align-self-xl-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} - -.float-left { - float: left !important; -} - -.float-right { - float: right !important; -} - -.float-none { - float: none !important; -} - -@media (min-width: 576px) { - .float-sm-left { - float: left !important; - } - .float-sm-right { - float: right !important; - } - .float-sm-none { - float: none !important; - } -} - -@media (min-width: 768px) { - .float-md-left { - float: left !important; - } - .float-md-right { - float: right !important; - } - .float-md-none { - float: none !important; - } -} - -@media (min-width: 992px) { - .float-lg-left { - float: left !important; - } - .float-lg-right { - float: right !important; - } - .float-lg-none { - float: none !important; - } -} - -@media (min-width: 1200px) { - .float-xl-left { - float: left !important; - } - .float-xl-right { - float: right !important; - } - .float-xl-none { - float: none !important; - } -} - -.overflow-auto { - overflow: auto !important; -} - -.overflow-hidden { - overflow: hidden !important; -} - -.position-static { - position: static !important; -} - -.position-relative { - position: relative !important; -} - -.position-absolute { - position: absolute !important; -} - -.position-fixed { - position: fixed !important; -} - -.position-sticky { - position: -webkit-sticky !important; - position: sticky !important; -} - -.fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1030; -} - -.fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 1030; -} - -@supports ((position: -webkit-sticky) or (position: sticky)) { - .sticky-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.sr-only-focusable:active, .sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - overflow: visible; - clip: auto; - white-space: normal; -} - -.shadow-sm { - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; -} - -.shadow { - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; -} - -.shadow-lg { - box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; -} - -.shadow-none { - box-shadow: none !important; -} - -.w-25 { - width: 25% !important; -} - -.w-50 { - width: 50% !important; -} - -.w-75 { - width: 75% !important; -} - -.w-100 { - width: 100% !important; -} - -.w-auto { - width: auto !important; -} - -.h-25 { - height: 25% !important; -} - -.h-50 { - height: 50% !important; -} - -.h-75 { - height: 75% !important; -} - -.h-100 { - height: 100% !important; -} - -.h-auto { - height: auto !important; -} - -.mw-100 { - max-width: 100% !important; -} - -.mh-100 { - max-height: 100% !important; -} - -.min-vw-100 { - min-width: 100vw !important; -} - -.min-vh-100 { - min-height: 100vh !important; -} - -.vw-100 { - width: 100vw !important; -} - -.vh-100 { - height: 100vh !important; -} - -.stretched-link::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - pointer-events: auto; - content: ""; - background-color: rgba(0, 0, 0, 0); -} - -.m-0 { - margin: 0 !important; -} - -.mt-0, -.my-0 { - margin-top: 0 !important; -} - -.mr-0, -.mx-0 { - margin-right: 0 !important; -} - -.mb-0, -.my-0 { - margin-bottom: 0 !important; -} - -.ml-0, -.mx-0 { - margin-left: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.mt-1, -.my-1 { - margin-top: 0.25rem !important; -} - -.mr-1, -.mx-1 { - margin-right: 0.25rem !important; -} - -.mb-1, -.my-1 { - margin-bottom: 0.25rem !important; -} - -.ml-1, -.mx-1 { - margin-left: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.mt-2, -.my-2 { - margin-top: 0.5rem !important; -} - -.mr-2, -.mx-2 { - margin-right: 0.5rem !important; -} - -.mb-2, -.my-2 { - margin-bottom: 0.5rem !important; -} - -.ml-2, -.mx-2 { - margin-left: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.mt-3, -.my-3 { - margin-top: 1rem !important; -} - -.mr-3, -.mx-3 { - margin-right: 1rem !important; -} - -.mb-3, -.my-3 { - margin-bottom: 1rem !important; -} - -.ml-3, -.mx-3 { - margin-left: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.mt-4, -.my-4 { - margin-top: 1.5rem !important; -} - -.mr-4, -.mx-4 { - margin-right: 1.5rem !important; -} - -.mb-4, -.my-4 { - margin-bottom: 1.5rem !important; -} - -.ml-4, -.mx-4 { - margin-left: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.mt-5, -.my-5 { - margin-top: 3rem !important; -} - -.mr-5, -.mx-5 { - margin-right: 3rem !important; -} - -.mb-5, -.my-5 { - margin-bottom: 3rem !important; -} - -.ml-5, -.mx-5 { - margin-left: 3rem !important; -} - -.p-0 { - padding: 0 !important; -} - -.pt-0, -.py-0 { - padding-top: 0 !important; -} - -.pr-0, -.px-0 { - padding-right: 0 !important; -} - -.pb-0, -.py-0 { - padding-bottom: 0 !important; -} - -.pl-0, -.px-0 { - padding-left: 0 !important; -} - -.p-1 { - padding: 0.25rem !important; -} - -.pt-1, -.py-1 { - padding-top: 0.25rem !important; -} - -.pr-1, -.px-1 { - padding-right: 0.25rem !important; -} - -.pb-1, -.py-1 { - padding-bottom: 0.25rem !important; -} - -.pl-1, -.px-1 { - padding-left: 0.25rem !important; -} - -.p-2 { - padding: 0.5rem !important; -} - -.pt-2, -.py-2 { - padding-top: 0.5rem !important; -} - -.pr-2, -.px-2 { - padding-right: 0.5rem !important; -} - -.pb-2, -.py-2 { - padding-bottom: 0.5rem !important; -} - -.pl-2, -.px-2 { - padding-left: 0.5rem !important; -} - -.p-3 { - padding: 1rem !important; -} - -.pt-3, -.py-3 { - padding-top: 1rem !important; -} - -.pr-3, -.px-3 { - padding-right: 1rem !important; -} - -.pb-3, -.py-3 { - padding-bottom: 1rem !important; -} - -.pl-3, -.px-3 { - padding-left: 1rem !important; -} - -.p-4 { - padding: 1.5rem !important; -} - -.pt-4, -.py-4 { - padding-top: 1.5rem !important; -} - -.pr-4, -.px-4 { - padding-right: 1.5rem !important; -} - -.pb-4, -.py-4 { - padding-bottom: 1.5rem !important; -} - -.pl-4, -.px-4 { - padding-left: 1.5rem !important; -} - -.p-5 { - padding: 3rem !important; -} - -.pt-5, -.py-5 { - padding-top: 3rem !important; -} - -.pr-5, -.px-5 { - padding-right: 3rem !important; -} - -.pb-5, -.py-5 { - padding-bottom: 3rem !important; -} - -.pl-5, -.px-5 { - padding-left: 3rem !important; -} - -.m-n1 { - margin: -0.25rem !important; -} - -.mt-n1, -.my-n1 { - margin-top: -0.25rem !important; -} - -.mr-n1, -.mx-n1 { - margin-right: -0.25rem !important; -} - -.mb-n1, -.my-n1 { - margin-bottom: -0.25rem !important; -} - -.ml-n1, -.mx-n1 { - margin-left: -0.25rem !important; -} - -.m-n2 { - margin: -0.5rem !important; -} - -.mt-n2, -.my-n2 { - margin-top: -0.5rem !important; -} - -.mr-n2, -.mx-n2 { - margin-right: -0.5rem !important; -} - -.mb-n2, -.my-n2 { - margin-bottom: -0.5rem !important; -} - -.ml-n2, -.mx-n2 { - margin-left: -0.5rem !important; -} - -.m-n3 { - margin: -1rem !important; -} - -.mt-n3, -.my-n3 { - margin-top: -1rem !important; -} - -.mr-n3, -.mx-n3 { - margin-right: -1rem !important; -} - -.mb-n3, -.my-n3 { - margin-bottom: -1rem !important; -} - -.ml-n3, -.mx-n3 { - margin-left: -1rem !important; -} - -.m-n4 { - margin: -1.5rem !important; -} - -.mt-n4, -.my-n4 { - margin-top: -1.5rem !important; -} - -.mr-n4, -.mx-n4 { - margin-right: -1.5rem !important; -} - -.mb-n4, -.my-n4 { - margin-bottom: -1.5rem !important; -} - -.ml-n4, -.mx-n4 { - margin-left: -1.5rem !important; -} - -.m-n5 { - margin: -3rem !important; -} - -.mt-n5, -.my-n5 { - margin-top: -3rem !important; -} - -.mr-n5, -.mx-n5 { - margin-right: -3rem !important; -} - -.mb-n5, -.my-n5 { - margin-bottom: -3rem !important; -} - -.ml-n5, -.mx-n5 { - margin-left: -3rem !important; -} - -.m-auto { - margin: auto !important; -} - -.mt-auto, -.my-auto { - margin-top: auto !important; -} - -.mr-auto, -.mx-auto { - margin-right: auto !important; -} - -.mb-auto, -.my-auto { - margin-bottom: auto !important; -} - -.ml-auto, -.mx-auto { - margin-left: auto !important; -} - -@media (min-width: 576px) { - .m-sm-0 { - margin: 0 !important; - } - .mt-sm-0, - .my-sm-0 { - margin-top: 0 !important; - } - .mr-sm-0, - .mx-sm-0 { - margin-right: 0 !important; - } - .mb-sm-0, - .my-sm-0 { - margin-bottom: 0 !important; - } - .ml-sm-0, - .mx-sm-0 { - margin-left: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .mt-sm-1, - .my-sm-1 { - margin-top: 0.25rem !important; - } - .mr-sm-1, - .mx-sm-1 { - margin-right: 0.25rem !important; - } - .mb-sm-1, - .my-sm-1 { - margin-bottom: 0.25rem !important; - } - .ml-sm-1, - .mx-sm-1 { - margin-left: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .mt-sm-2, - .my-sm-2 { - margin-top: 0.5rem !important; - } - .mr-sm-2, - .mx-sm-2 { - margin-right: 0.5rem !important; - } - .mb-sm-2, - .my-sm-2 { - margin-bottom: 0.5rem !important; - } - .ml-sm-2, - .mx-sm-2 { - margin-left: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .mt-sm-3, - .my-sm-3 { - margin-top: 1rem !important; - } - .mr-sm-3, - .mx-sm-3 { - margin-right: 1rem !important; - } - .mb-sm-3, - .my-sm-3 { - margin-bottom: 1rem !important; - } - .ml-sm-3, - .mx-sm-3 { - margin-left: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .mt-sm-4, - .my-sm-4 { - margin-top: 1.5rem !important; - } - .mr-sm-4, - .mx-sm-4 { - margin-right: 1.5rem !important; - } - .mb-sm-4, - .my-sm-4 { - margin-bottom: 1.5rem !important; - } - .ml-sm-4, - .mx-sm-4 { - margin-left: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .mt-sm-5, - .my-sm-5 { - margin-top: 3rem !important; - } - .mr-sm-5, - .mx-sm-5 { - margin-right: 3rem !important; - } - .mb-sm-5, - .my-sm-5 { - margin-bottom: 3rem !important; - } - .ml-sm-5, - .mx-sm-5 { - margin-left: 3rem !important; - } - .p-sm-0 { - padding: 0 !important; - } - .pt-sm-0, - .py-sm-0 { - padding-top: 0 !important; - } - .pr-sm-0, - .px-sm-0 { - padding-right: 0 !important; - } - .pb-sm-0, - .py-sm-0 { - padding-bottom: 0 !important; - } - .pl-sm-0, - .px-sm-0 { - padding-left: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .pt-sm-1, - .py-sm-1 { - padding-top: 0.25rem !important; - } - .pr-sm-1, - .px-sm-1 { - padding-right: 0.25rem !important; - } - .pb-sm-1, - .py-sm-1 { - padding-bottom: 0.25rem !important; - } - .pl-sm-1, - .px-sm-1 { - padding-left: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .pt-sm-2, - .py-sm-2 { - padding-top: 0.5rem !important; - } - .pr-sm-2, - .px-sm-2 { - padding-right: 0.5rem !important; - } - .pb-sm-2, - .py-sm-2 { - padding-bottom: 0.5rem !important; - } - .pl-sm-2, - .px-sm-2 { - padding-left: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .pt-sm-3, - .py-sm-3 { - padding-top: 1rem !important; - } - .pr-sm-3, - .px-sm-3 { - padding-right: 1rem !important; - } - .pb-sm-3, - .py-sm-3 { - padding-bottom: 1rem !important; - } - .pl-sm-3, - .px-sm-3 { - padding-left: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .pt-sm-4, - .py-sm-4 { - padding-top: 1.5rem !important; - } - .pr-sm-4, - .px-sm-4 { - padding-right: 1.5rem !important; - } - .pb-sm-4, - .py-sm-4 { - padding-bottom: 1.5rem !important; - } - .pl-sm-4, - .px-sm-4 { - padding-left: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .pt-sm-5, - .py-sm-5 { - padding-top: 3rem !important; - } - .pr-sm-5, - .px-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-5, - .py-sm-5 { - padding-bottom: 3rem !important; - } - .pl-sm-5, - .px-sm-5 { - padding-left: 3rem !important; - } - .m-sm-n1 { - margin: -0.25rem !important; - } - .mt-sm-n1, - .my-sm-n1 { - margin-top: -0.25rem !important; - } - .mr-sm-n1, - .mx-sm-n1 { - margin-right: -0.25rem !important; - } - .mb-sm-n1, - .my-sm-n1 { - margin-bottom: -0.25rem !important; - } - .ml-sm-n1, - .mx-sm-n1 { - margin-left: -0.25rem !important; - } - .m-sm-n2 { - margin: -0.5rem !important; - } - .mt-sm-n2, - .my-sm-n2 { - margin-top: -0.5rem !important; - } - .mr-sm-n2, - .mx-sm-n2 { - margin-right: -0.5rem !important; - } - .mb-sm-n2, - .my-sm-n2 { - margin-bottom: -0.5rem !important; - } - .ml-sm-n2, - .mx-sm-n2 { - margin-left: -0.5rem !important; - } - .m-sm-n3 { - margin: -1rem !important; - } - .mt-sm-n3, - .my-sm-n3 { - margin-top: -1rem !important; - } - .mr-sm-n3, - .mx-sm-n3 { - margin-right: -1rem !important; - } - .mb-sm-n3, - .my-sm-n3 { - margin-bottom: -1rem !important; - } - .ml-sm-n3, - .mx-sm-n3 { - margin-left: -1rem !important; - } - .m-sm-n4 { - margin: -1.5rem !important; - } - .mt-sm-n4, - .my-sm-n4 { - margin-top: -1.5rem !important; - } - .mr-sm-n4, - .mx-sm-n4 { - margin-right: -1.5rem !important; - } - .mb-sm-n4, - .my-sm-n4 { - margin-bottom: -1.5rem !important; - } - .ml-sm-n4, - .mx-sm-n4 { - margin-left: -1.5rem !important; - } - .m-sm-n5 { - margin: -3rem !important; - } - .mt-sm-n5, - .my-sm-n5 { - margin-top: -3rem !important; - } - .mr-sm-n5, - .mx-sm-n5 { - margin-right: -3rem !important; - } - .mb-sm-n5, - .my-sm-n5 { - margin-bottom: -3rem !important; - } - .ml-sm-n5, - .mx-sm-n5 { - margin-left: -3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mt-sm-auto, - .my-sm-auto { - margin-top: auto !important; - } - .mr-sm-auto, - .mx-sm-auto { - margin-right: auto !important; - } - .mb-sm-auto, - .my-sm-auto { - margin-bottom: auto !important; - } - .ml-sm-auto, - .mx-sm-auto { - margin-left: auto !important; - } -} - -@media (min-width: 768px) { - .m-md-0 { - margin: 0 !important; - } - .mt-md-0, - .my-md-0 { - margin-top: 0 !important; - } - .mr-md-0, - .mx-md-0 { - margin-right: 0 !important; - } - .mb-md-0, - .my-md-0 { - margin-bottom: 0 !important; - } - .ml-md-0, - .mx-md-0 { - margin-left: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .mt-md-1, - .my-md-1 { - margin-top: 0.25rem !important; - } - .mr-md-1, - .mx-md-1 { - margin-right: 0.25rem !important; - } - .mb-md-1, - .my-md-1 { - margin-bottom: 0.25rem !important; - } - .ml-md-1, - .mx-md-1 { - margin-left: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .mt-md-2, - .my-md-2 { - margin-top: 0.5rem !important; - } - .mr-md-2, - .mx-md-2 { - margin-right: 0.5rem !important; - } - .mb-md-2, - .my-md-2 { - margin-bottom: 0.5rem !important; - } - .ml-md-2, - .mx-md-2 { - margin-left: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .mt-md-3, - .my-md-3 { - margin-top: 1rem !important; - } - .mr-md-3, - .mx-md-3 { - margin-right: 1rem !important; - } - .mb-md-3, - .my-md-3 { - margin-bottom: 1rem !important; - } - .ml-md-3, - .mx-md-3 { - margin-left: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .mt-md-4, - .my-md-4 { - margin-top: 1.5rem !important; - } - .mr-md-4, - .mx-md-4 { - margin-right: 1.5rem !important; - } - .mb-md-4, - .my-md-4 { - margin-bottom: 1.5rem !important; - } - .ml-md-4, - .mx-md-4 { - margin-left: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .mt-md-5, - .my-md-5 { - margin-top: 3rem !important; - } - .mr-md-5, - .mx-md-5 { - margin-right: 3rem !important; - } - .mb-md-5, - .my-md-5 { - margin-bottom: 3rem !important; - } - .ml-md-5, - .mx-md-5 { - margin-left: 3rem !important; - } - .p-md-0 { - padding: 0 !important; - } - .pt-md-0, - .py-md-0 { - padding-top: 0 !important; - } - .pr-md-0, - .px-md-0 { - padding-right: 0 !important; - } - .pb-md-0, - .py-md-0 { - padding-bottom: 0 !important; - } - .pl-md-0, - .px-md-0 { - padding-left: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .pt-md-1, - .py-md-1 { - padding-top: 0.25rem !important; - } - .pr-md-1, - .px-md-1 { - padding-right: 0.25rem !important; - } - .pb-md-1, - .py-md-1 { - padding-bottom: 0.25rem !important; - } - .pl-md-1, - .px-md-1 { - padding-left: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .pt-md-2, - .py-md-2 { - padding-top: 0.5rem !important; - } - .pr-md-2, - .px-md-2 { - padding-right: 0.5rem !important; - } - .pb-md-2, - .py-md-2 { - padding-bottom: 0.5rem !important; - } - .pl-md-2, - .px-md-2 { - padding-left: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .pt-md-3, - .py-md-3 { - padding-top: 1rem !important; - } - .pr-md-3, - .px-md-3 { - padding-right: 1rem !important; - } - .pb-md-3, - .py-md-3 { - padding-bottom: 1rem !important; - } - .pl-md-3, - .px-md-3 { - padding-left: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .pt-md-4, - .py-md-4 { - padding-top: 1.5rem !important; - } - .pr-md-4, - .px-md-4 { - padding-right: 1.5rem !important; - } - .pb-md-4, - .py-md-4 { - padding-bottom: 1.5rem !important; - } - .pl-md-4, - .px-md-4 { - padding-left: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .pt-md-5, - .py-md-5 { - padding-top: 3rem !important; - } - .pr-md-5, - .px-md-5 { - padding-right: 3rem !important; - } - .pb-md-5, - .py-md-5 { - padding-bottom: 3rem !important; - } - .pl-md-5, - .px-md-5 { - padding-left: 3rem !important; - } - .m-md-n1 { - margin: -0.25rem !important; - } - .mt-md-n1, - .my-md-n1 { - margin-top: -0.25rem !important; - } - .mr-md-n1, - .mx-md-n1 { - margin-right: -0.25rem !important; - } - .mb-md-n1, - .my-md-n1 { - margin-bottom: -0.25rem !important; - } - .ml-md-n1, - .mx-md-n1 { - margin-left: -0.25rem !important; - } - .m-md-n2 { - margin: -0.5rem !important; - } - .mt-md-n2, - .my-md-n2 { - margin-top: -0.5rem !important; - } - .mr-md-n2, - .mx-md-n2 { - margin-right: -0.5rem !important; - } - .mb-md-n2, - .my-md-n2 { - margin-bottom: -0.5rem !important; - } - .ml-md-n2, - .mx-md-n2 { - margin-left: -0.5rem !important; - } - .m-md-n3 { - margin: -1rem !important; - } - .mt-md-n3, - .my-md-n3 { - margin-top: -1rem !important; - } - .mr-md-n3, - .mx-md-n3 { - margin-right: -1rem !important; - } - .mb-md-n3, - .my-md-n3 { - margin-bottom: -1rem !important; - } - .ml-md-n3, - .mx-md-n3 { - margin-left: -1rem !important; - } - .m-md-n4 { - margin: -1.5rem !important; - } - .mt-md-n4, - .my-md-n4 { - margin-top: -1.5rem !important; - } - .mr-md-n4, - .mx-md-n4 { - margin-right: -1.5rem !important; - } - .mb-md-n4, - .my-md-n4 { - margin-bottom: -1.5rem !important; - } - .ml-md-n4, - .mx-md-n4 { - margin-left: -1.5rem !important; - } - .m-md-n5 { - margin: -3rem !important; - } - .mt-md-n5, - .my-md-n5 { - margin-top: -3rem !important; - } - .mr-md-n5, - .mx-md-n5 { - margin-right: -3rem !important; - } - .mb-md-n5, - .my-md-n5 { - margin-bottom: -3rem !important; - } - .ml-md-n5, - .mx-md-n5 { - margin-left: -3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mt-md-auto, - .my-md-auto { - margin-top: auto !important; - } - .mr-md-auto, - .mx-md-auto { - margin-right: auto !important; - } - .mb-md-auto, - .my-md-auto { - margin-bottom: auto !important; - } - .ml-md-auto, - .mx-md-auto { - margin-left: auto !important; - } -} - -@media (min-width: 992px) { - .m-lg-0 { - margin: 0 !important; - } - .mt-lg-0, - .my-lg-0 { - margin-top: 0 !important; - } - .mr-lg-0, - .mx-lg-0 { - margin-right: 0 !important; - } - .mb-lg-0, - .my-lg-0 { - margin-bottom: 0 !important; - } - .ml-lg-0, - .mx-lg-0 { - margin-left: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .mt-lg-1, - .my-lg-1 { - margin-top: 0.25rem !important; - } - .mr-lg-1, - .mx-lg-1 { - margin-right: 0.25rem !important; - } - .mb-lg-1, - .my-lg-1 { - margin-bottom: 0.25rem !important; - } - .ml-lg-1, - .mx-lg-1 { - margin-left: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .mt-lg-2, - .my-lg-2 { - margin-top: 0.5rem !important; - } - .mr-lg-2, - .mx-lg-2 { - margin-right: 0.5rem !important; - } - .mb-lg-2, - .my-lg-2 { - margin-bottom: 0.5rem !important; - } - .ml-lg-2, - .mx-lg-2 { - margin-left: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .mt-lg-3, - .my-lg-3 { - margin-top: 1rem !important; - } - .mr-lg-3, - .mx-lg-3 { - margin-right: 1rem !important; - } - .mb-lg-3, - .my-lg-3 { - margin-bottom: 1rem !important; - } - .ml-lg-3, - .mx-lg-3 { - margin-left: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .mt-lg-4, - .my-lg-4 { - margin-top: 1.5rem !important; - } - .mr-lg-4, - .mx-lg-4 { - margin-right: 1.5rem !important; - } - .mb-lg-4, - .my-lg-4 { - margin-bottom: 1.5rem !important; - } - .ml-lg-4, - .mx-lg-4 { - margin-left: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .mt-lg-5, - .my-lg-5 { - margin-top: 3rem !important; - } - .mr-lg-5, - .mx-lg-5 { - margin-right: 3rem !important; - } - .mb-lg-5, - .my-lg-5 { - margin-bottom: 3rem !important; - } - .ml-lg-5, - .mx-lg-5 { - margin-left: 3rem !important; - } - .p-lg-0 { - padding: 0 !important; - } - .pt-lg-0, - .py-lg-0 { - padding-top: 0 !important; - } - .pr-lg-0, - .px-lg-0 { - padding-right: 0 !important; - } - .pb-lg-0, - .py-lg-0 { - padding-bottom: 0 !important; - } - .pl-lg-0, - .px-lg-0 { - padding-left: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .pt-lg-1, - .py-lg-1 { - padding-top: 0.25rem !important; - } - .pr-lg-1, - .px-lg-1 { - padding-right: 0.25rem !important; - } - .pb-lg-1, - .py-lg-1 { - padding-bottom: 0.25rem !important; - } - .pl-lg-1, - .px-lg-1 { - padding-left: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .pt-lg-2, - .py-lg-2 { - padding-top: 0.5rem !important; - } - .pr-lg-2, - .px-lg-2 { - padding-right: 0.5rem !important; - } - .pb-lg-2, - .py-lg-2 { - padding-bottom: 0.5rem !important; - } - .pl-lg-2, - .px-lg-2 { - padding-left: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .pt-lg-3, - .py-lg-3 { - padding-top: 1rem !important; - } - .pr-lg-3, - .px-lg-3 { - padding-right: 1rem !important; - } - .pb-lg-3, - .py-lg-3 { - padding-bottom: 1rem !important; - } - .pl-lg-3, - .px-lg-3 { - padding-left: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .pt-lg-4, - .py-lg-4 { - padding-top: 1.5rem !important; - } - .pr-lg-4, - .px-lg-4 { - padding-right: 1.5rem !important; - } - .pb-lg-4, - .py-lg-4 { - padding-bottom: 1.5rem !important; - } - .pl-lg-4, - .px-lg-4 { - padding-left: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .pt-lg-5, - .py-lg-5 { - padding-top: 3rem !important; - } - .pr-lg-5, - .px-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-5, - .py-lg-5 { - padding-bottom: 3rem !important; - } - .pl-lg-5, - .px-lg-5 { - padding-left: 3rem !important; - } - .m-lg-n1 { - margin: -0.25rem !important; - } - .mt-lg-n1, - .my-lg-n1 { - margin-top: -0.25rem !important; - } - .mr-lg-n1, - .mx-lg-n1 { - margin-right: -0.25rem !important; - } - .mb-lg-n1, - .my-lg-n1 { - margin-bottom: -0.25rem !important; - } - .ml-lg-n1, - .mx-lg-n1 { - margin-left: -0.25rem !important; - } - .m-lg-n2 { - margin: -0.5rem !important; - } - .mt-lg-n2, - .my-lg-n2 { - margin-top: -0.5rem !important; - } - .mr-lg-n2, - .mx-lg-n2 { - margin-right: -0.5rem !important; - } - .mb-lg-n2, - .my-lg-n2 { - margin-bottom: -0.5rem !important; - } - .ml-lg-n2, - .mx-lg-n2 { - margin-left: -0.5rem !important; - } - .m-lg-n3 { - margin: -1rem !important; - } - .mt-lg-n3, - .my-lg-n3 { - margin-top: -1rem !important; - } - .mr-lg-n3, - .mx-lg-n3 { - margin-right: -1rem !important; - } - .mb-lg-n3, - .my-lg-n3 { - margin-bottom: -1rem !important; - } - .ml-lg-n3, - .mx-lg-n3 { - margin-left: -1rem !important; - } - .m-lg-n4 { - margin: -1.5rem !important; - } - .mt-lg-n4, - .my-lg-n4 { - margin-top: -1.5rem !important; - } - .mr-lg-n4, - .mx-lg-n4 { - margin-right: -1.5rem !important; - } - .mb-lg-n4, - .my-lg-n4 { - margin-bottom: -1.5rem !important; - } - .ml-lg-n4, - .mx-lg-n4 { - margin-left: -1.5rem !important; - } - .m-lg-n5 { - margin: -3rem !important; - } - .mt-lg-n5, - .my-lg-n5 { - margin-top: -3rem !important; - } - .mr-lg-n5, - .mx-lg-n5 { - margin-right: -3rem !important; - } - .mb-lg-n5, - .my-lg-n5 { - margin-bottom: -3rem !important; - } - .ml-lg-n5, - .mx-lg-n5 { - margin-left: -3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mt-lg-auto, - .my-lg-auto { - margin-top: auto !important; - } - .mr-lg-auto, - .mx-lg-auto { - margin-right: auto !important; - } - .mb-lg-auto, - .my-lg-auto { - margin-bottom: auto !important; - } - .ml-lg-auto, - .mx-lg-auto { - margin-left: auto !important; - } -} - -@media (min-width: 1200px) { - .m-xl-0 { - margin: 0 !important; - } - .mt-xl-0, - .my-xl-0 { - margin-top: 0 !important; - } - .mr-xl-0, - .mx-xl-0 { - margin-right: 0 !important; - } - .mb-xl-0, - .my-xl-0 { - margin-bottom: 0 !important; - } - .ml-xl-0, - .mx-xl-0 { - margin-left: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .mt-xl-1, - .my-xl-1 { - margin-top: 0.25rem !important; - } - .mr-xl-1, - .mx-xl-1 { - margin-right: 0.25rem !important; - } - .mb-xl-1, - .my-xl-1 { - margin-bottom: 0.25rem !important; - } - .ml-xl-1, - .mx-xl-1 { - margin-left: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .mt-xl-2, - .my-xl-2 { - margin-top: 0.5rem !important; - } - .mr-xl-2, - .mx-xl-2 { - margin-right: 0.5rem !important; - } - .mb-xl-2, - .my-xl-2 { - margin-bottom: 0.5rem !important; - } - .ml-xl-2, - .mx-xl-2 { - margin-left: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .mt-xl-3, - .my-xl-3 { - margin-top: 1rem !important; - } - .mr-xl-3, - .mx-xl-3 { - margin-right: 1rem !important; - } - .mb-xl-3, - .my-xl-3 { - margin-bottom: 1rem !important; - } - .ml-xl-3, - .mx-xl-3 { - margin-left: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .mt-xl-4, - .my-xl-4 { - margin-top: 1.5rem !important; - } - .mr-xl-4, - .mx-xl-4 { - margin-right: 1.5rem !important; - } - .mb-xl-4, - .my-xl-4 { - margin-bottom: 1.5rem !important; - } - .ml-xl-4, - .mx-xl-4 { - margin-left: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .mt-xl-5, - .my-xl-5 { - margin-top: 3rem !important; - } - .mr-xl-5, - .mx-xl-5 { - margin-right: 3rem !important; - } - .mb-xl-5, - .my-xl-5 { - margin-bottom: 3rem !important; - } - .ml-xl-5, - .mx-xl-5 { - margin-left: 3rem !important; - } - .p-xl-0 { - padding: 0 !important; - } - .pt-xl-0, - .py-xl-0 { - padding-top: 0 !important; - } - .pr-xl-0, - .px-xl-0 { - padding-right: 0 !important; - } - .pb-xl-0, - .py-xl-0 { - padding-bottom: 0 !important; - } - .pl-xl-0, - .px-xl-0 { - padding-left: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .pt-xl-1, - .py-xl-1 { - padding-top: 0.25rem !important; - } - .pr-xl-1, - .px-xl-1 { - padding-right: 0.25rem !important; - } - .pb-xl-1, - .py-xl-1 { - padding-bottom: 0.25rem !important; - } - .pl-xl-1, - .px-xl-1 { - padding-left: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .pt-xl-2, - .py-xl-2 { - padding-top: 0.5rem !important; - } - .pr-xl-2, - .px-xl-2 { - padding-right: 0.5rem !important; - } - .pb-xl-2, - .py-xl-2 { - padding-bottom: 0.5rem !important; - } - .pl-xl-2, - .px-xl-2 { - padding-left: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .pt-xl-3, - .py-xl-3 { - padding-top: 1rem !important; - } - .pr-xl-3, - .px-xl-3 { - padding-right: 1rem !important; - } - .pb-xl-3, - .py-xl-3 { - padding-bottom: 1rem !important; - } - .pl-xl-3, - .px-xl-3 { - padding-left: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .pt-xl-4, - .py-xl-4 { - padding-top: 1.5rem !important; - } - .pr-xl-4, - .px-xl-4 { - padding-right: 1.5rem !important; - } - .pb-xl-4, - .py-xl-4 { - padding-bottom: 1.5rem !important; - } - .pl-xl-4, - .px-xl-4 { - padding-left: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .pt-xl-5, - .py-xl-5 { - padding-top: 3rem !important; - } - .pr-xl-5, - .px-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-5, - .py-xl-5 { - padding-bottom: 3rem !important; - } - .pl-xl-5, - .px-xl-5 { - padding-left: 3rem !important; - } - .m-xl-n1 { - margin: -0.25rem !important; - } - .mt-xl-n1, - .my-xl-n1 { - margin-top: -0.25rem !important; - } - .mr-xl-n1, - .mx-xl-n1 { - margin-right: -0.25rem !important; - } - .mb-xl-n1, - .my-xl-n1 { - margin-bottom: -0.25rem !important; - } - .ml-xl-n1, - .mx-xl-n1 { - margin-left: -0.25rem !important; - } - .m-xl-n2 { - margin: -0.5rem !important; - } - .mt-xl-n2, - .my-xl-n2 { - margin-top: -0.5rem !important; - } - .mr-xl-n2, - .mx-xl-n2 { - margin-right: -0.5rem !important; - } - .mb-xl-n2, - .my-xl-n2 { - margin-bottom: -0.5rem !important; - } - .ml-xl-n2, - .mx-xl-n2 { - margin-left: -0.5rem !important; - } - .m-xl-n3 { - margin: -1rem !important; - } - .mt-xl-n3, - .my-xl-n3 { - margin-top: -1rem !important; - } - .mr-xl-n3, - .mx-xl-n3 { - margin-right: -1rem !important; - } - .mb-xl-n3, - .my-xl-n3 { - margin-bottom: -1rem !important; - } - .ml-xl-n3, - .mx-xl-n3 { - margin-left: -1rem !important; - } - .m-xl-n4 { - margin: -1.5rem !important; - } - .mt-xl-n4, - .my-xl-n4 { - margin-top: -1.5rem !important; - } - .mr-xl-n4, - .mx-xl-n4 { - margin-right: -1.5rem !important; - } - .mb-xl-n4, - .my-xl-n4 { - margin-bottom: -1.5rem !important; - } - .ml-xl-n4, - .mx-xl-n4 { - margin-left: -1.5rem !important; - } - .m-xl-n5 { - margin: -3rem !important; - } - .mt-xl-n5, - .my-xl-n5 { - margin-top: -3rem !important; - } - .mr-xl-n5, - .mx-xl-n5 { - margin-right: -3rem !important; - } - .mb-xl-n5, - .my-xl-n5 { - margin-bottom: -3rem !important; - } - .ml-xl-n5, - .mx-xl-n5 { - margin-left: -3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mt-xl-auto, - .my-xl-auto { - margin-top: auto !important; - } - .mr-xl-auto, - .mx-xl-auto { - margin-right: auto !important; - } - .mb-xl-auto, - .my-xl-auto { - margin-bottom: auto !important; - } - .ml-xl-auto, - .mx-xl-auto { - margin-left: auto !important; - } -} - -.text-monospace { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; -} - -.text-justify { - text-align: justify !important; -} - -.text-wrap { - white-space: normal !important; -} - -.text-nowrap { - white-space: nowrap !important; -} - -.text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.text-left { - text-align: left !important; -} - -.text-right { - text-align: right !important; -} - -.text-center { - text-align: center !important; -} - -@media (min-width: 576px) { - .text-sm-left { - text-align: left !important; - } - .text-sm-right { - text-align: right !important; - } - .text-sm-center { - text-align: center !important; - } -} - -@media (min-width: 768px) { - .text-md-left { - text-align: left !important; - } - .text-md-right { - text-align: right !important; - } - .text-md-center { - text-align: center !important; - } -} - -@media (min-width: 992px) { - .text-lg-left { - text-align: left !important; - } - .text-lg-right { - text-align: right !important; - } - .text-lg-center { - text-align: center !important; - } -} - -@media (min-width: 1200px) { - .text-xl-left { - text-align: left !important; - } - .text-xl-right { - text-align: right !important; - } - .text-xl-center { - text-align: center !important; - } -} - -.text-lowercase { - text-transform: lowercase !important; -} - -.text-uppercase { - text-transform: uppercase !important; -} - -.text-capitalize { - text-transform: capitalize !important; -} - -.font-weight-light { - font-weight: 300 !important; -} - -.font-weight-lighter { - font-weight: lighter !important; -} - -.font-weight-normal { - font-weight: 400 !important; -} - -.font-weight-bold { - font-weight: 700 !important; -} - -.font-weight-bolder { - font-weight: bolder !important; -} - -.font-italic { - font-style: italic !important; -} - -.text-white { - color: #fff !important; -} - -.text-primary { - color: #007bff !important; -} - -a.text-primary:hover, a.text-primary:focus { - color: #0056b3 !important; -} - -.text-secondary { - color: #6c757d !important; -} - -a.text-secondary:hover, a.text-secondary:focus { - color: #494f54 !important; -} - -.text-success { - color: #28a745 !important; -} - -a.text-success:hover, a.text-success:focus { - color: #19692c !important; -} - -.text-info { - color: #17a2b8 !important; -} - -a.text-info:hover, a.text-info:focus { - color: #0f6674 !important; -} - -.text-warning { - color: #ffc107 !important; -} - -a.text-warning:hover, a.text-warning:focus { - color: #ba8b00 !important; -} - -.text-danger { - color: #dc3545 !important; -} - -a.text-danger:hover, a.text-danger:focus { - color: #a71d2a !important; -} - -.text-light { - color: #f8f9fa !important; -} - -a.text-light:hover, a.text-light:focus { - color: #cbd3da !important; -} - -.text-dark { - color: #343a40 !important; -} - -a.text-dark:hover, a.text-dark:focus { - color: #121416 !important; -} - -.text-body { - color: #212529 !important; -} - -.text-muted { - color: #6c757d !important; -} - -.text-black-50 { - color: rgba(0, 0, 0, 0.5) !important; -} - -.text-white-50 { - color: rgba(255, 255, 255, 0.5) !important; -} - -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.text-decoration-none { - text-decoration: none !important; -} - -.text-break { - word-break: break-word !important; - overflow-wrap: break-word !important; -} - -.text-reset { - color: inherit !important; -} - -.visible { - visibility: visible !important; -} - -.invisible { - visibility: hidden !important; -} - -@media print { - *, - *::before, - *::after { - text-shadow: none !important; - box-shadow: none !important; - } - a:not(.btn) { - text-decoration: underline; - } - abbr[title]::after { - content: " (" attr(title) ")"; - } - pre { - white-space: pre-wrap !important; - } - pre, - blockquote { - border: 1px solid #adb5bd; - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - @page { - size: a3; - } - body { - min-width: 992px !important; - } - .container { - min-width: 992px !important; - } - .navbar { - display: none; - } - .badge { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #dee2e6 !important; - } - .table-dark { - color: inherit; - } - .table-dark th, - .table-dark td, - .table-dark thead th, - .table-dark tbody + tbody { - border-color: #dee2e6; - } - .table .thead-dark th { - color: inherit; - border-color: #dee2e6; - } -} -/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file diff --git a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map b/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map deleted file mode 100644 index 521afc5b..00000000 --- a/aspnetcore/src/identityserver/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap.scss","bootstrap.css","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-framework.scss","../../scss/_tables.scss","../../scss/mixins/_table-row.scss","../../scss/_functions.scss","../../scss/_forms.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_forms.scss","../../scss/mixins/_gradients.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/mixins/_nav-divider.scss","../../scss/_button-group.scss","../../scss/_input-group.scss","../../scss/_custom-forms.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/mixins/_badge.scss","../../scss/_jumbotron.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_media.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/utilities/_align.scss","../../scss/mixins/_background-variant.scss","../../scss/utilities/_background.scss","../../scss/utilities/_borders.scss","../../scss/utilities/_display.scss","../../scss/utilities/_embed.scss","../../scss/utilities/_flex.scss","../../scss/utilities/_float.scss","../../scss/utilities/_overflow.scss","../../scss/utilities/_position.scss","../../scss/utilities/_screenreaders.scss","../../scss/mixins/_screen-reader.scss","../../scss/utilities/_shadows.scss","../../scss/utilities/_sizing.scss","../../scss/utilities/_stretched-link.scss","../../scss/utilities/_spacing.scss","../../scss/utilities/_text.scss","../../scss/mixins/_text-truncate.scss","../../scss/mixins/_text-emphasis.scss","../../scss/mixins/_text-hide.scss","../../scss/utilities/_visibility.scss","../../scss/_print.scss"],"names":[],"mappings":"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AAwB;AFpH1B;;AE4HA;EACE,aAAa;EACb,0BAA0B;AFzH5B;;AEgIA;EACE,qBAAqB;AF7HvB;;AEgIA;EACE,kBAAkB;EAClB,eAAe;AF7HjB;;AEgIA;EACE,aAAa;AF7Hf;;AAEA;EEiIE,wBAAwB;AF/H1B;;AM3VA;;EAEE,qBHySuC;EGvSvC,gBHyS+B;EGxS/B,gBHyS+B;AHoDjC;;AMzVA;EFgHM,iBAtCY;AJmRlB;;AM5VA;EF+GM,eAtCY;AJuRlB;;AM/VA;EF8GM,kBAtCY;AJ2RlB;;AMlWA;EF6GM,iBAtCY;AJ+RlB;;AMrWA;EF4GM,kBAtCY;AJmSlB;;AMxWA;EF2GM,eAtCY;AJuSlB;;AM1WA;EFyGM,kBAtCY;EEjEhB,gBH2S+B;AHkEjC;;AMzWA;EFmGM,eAtCY;EE3DhB,gBH8R+B;EG7R/B,gBHqR+B;AHuFjC;;AM1WA;EF8FM,iBAtCY;EEtDhB,gBH0R+B;EGzR/B,gBHgR+B;AH6FjC;;AM3WA;EFyFM,iBAtCY;EEjDhB,gBHsR+B;EGrR/B,gBH2Q+B;AHmGjC;;AM5WA;EFoFM,iBAtCY;EE5ChB,gBHkR+B;EGjR/B,gBHsQ+B;AHyGjC;;AElVA;EIpBE,gBHiFW;EGhFX,mBHgFW;EG/EX,SAAS;EACT,wCHzCa;AHmZf;;AMlWA;;EFMI,cAAW;EEHb,gBH8N+B;AHuIjC;;AMlWA;;EAEE,cHsQgC;EGrQhC,yBH8QmC;AHuFrC;;AM7VA;EC/EE,eAAe;EACf,gBAAgB;APgblB;;AM7VA;ECpFE,eAAe;EACf,gBAAgB;APqblB;;AM/VA;EACE,qBAAqB;ANkWvB;;AMnWA;EAII,oBHwP+B;AH2GnC;;AMzVA;EFjCI,cAAW;EEmCb,yBAAyB;AN4V3B;;AMxVA;EACE,mBHwBW;ECTP,kBAtCY;AJmXlB;;AMxVA;EACE,cAAc;EF7CZ,cAAW;EE+Cb,cH1GgB;AHqclB;;AM9VA;EAMI,qBAAqB;AN4VzB;;AQ/cA;ECIE,eAAe;EAGf,YAAY;AT6cd;;AQ9cA;EACE,gBLigCwC;EKhgCxC,sBLRa;EKSb,yBLNgB;EOLd,sBP6OgC;EMvOlC,eAAe;EAGf,YAAY;ATsdd;;AQxcA;EAEE,qBAAqB;AR0cvB;;AQvcA;EACE,qBAA0B;EAC1B,cAAc;AR0chB;;AQvcA;EJkCI,cAAW;EIhCb,cL3BgB;AHqelB;;AWjfA;EPuEI,gBAAW;EOrEb,cRoCe;EQnCf,qBAAqB;AXofvB;;AWjfE;EACE,cAAc;AXoflB;;AW/eA;EACE,sBRqlCuC;EC3hCrC,gBAAW;EOxDb,WRTa;EQUb,yBRDgB;EOXd,qBP+O+B;AHgRnC;;AWvfA;EASI,UAAU;EPkDV,eAAW;EOhDX,gBR4Q6B;AHsOjC;;AE1SA;ESjME,cAAc;EPyCZ,gBAAW;EOvCb,cRjBgB;AHggBlB;;AWlfA;EP0CI,kBAAW;EOlCX,cAAc;EACd,kBAAkB;AX+etB;;AW1eA;EACE,iBR4jCuC;EQ3jCvC,kBAAkB;AX6epB;;AYrhBE;ECDA,WAAW;EACX,mBAA0B;EAC1B,kBAAyB;EACzB,kBAAkB;EAClB,iBAAiB;Ab0hBnB;;AcveI;EFtDF;ICWI,gBVqMK;EHkVT;AACF;;Ac7eI;EFtDF;ICWI,gBVsMK;EHuVT;AACF;;AcnfI;EFtDF;ICWI,gBVuMK;EH4VT;AACF;;AczfI;EFtDF;ICWI,iBVwMM;EHiWV;AACF;;AY/iBE;ECPA,WAAW;EACX,mBAA0B;EAC1B,kBAAyB;EACzB,kBAAkB;EAClB,iBAAiB;Ab0jBnB;;AcvgBI;EFrCE;IACE,gBT8LG;EHkXT;AACF;;Ac7gBI;EFrCE;IACE,gBT+LG;EHuXT;AACF;;AcnhBI;EFrCE;IACE,gBTgMG;EH4XT;AACF;;AczhBI;EFrCE;IACE,iBTiMI;EHiYV;AACF;;AY/iBE;ECrBA,oBAAa;EAAb,aAAa;EACb,mBAAe;EAAf,eAAe;EACf,mBAA0B;EAC1B,kBAAyB;AbwkB3B;;AYhjBE;EACE,eAAe;EACf,cAAc;AZmjBlB;;AYrjBE;;EAMI,gBAAgB;EAChB,eAAe;AZojBrB;;AetmBE;;;;;;EACE,kBAAkB;EAClB,WAAW;EACX,mBAA0B;EAC1B,kBAAyB;Af8mB7B;;Ae3lBM;EACE,0BAAa;EAAb,aAAa;EACb,oBAAY;EAAZ,YAAY;EACZ,eAAe;Af8lBvB;;Ae1lBQ;EF4BJ,kBAAuB;EAA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en5CQ;IFRR,uBAA8C;Eb85C9C;Eet5CQ;IFRR,gBAA8C;Ebi6C9C;Eez5CQ;IFRR,uBAA8C;Ebo6C9C;Ee55CQ;IFRR,uBAA8C;Ebu6C9C;AACF;;AgB39CA;EACE,WAAW;EACX,mBbkIW;EajIX,cbSgB;AHq9ClB;;AgBj+CA;;EAQI,gBbsVgC;EarVhC,mBAAmB;EACnB,6BbJc;AHk+ClB;;AgBx+CA;EAcI,sBAAsB;EACtB,gCbTc;AHu+ClB;;AgB7+CA;EAmBI,6Bbbc;AH2+ClB;;AgBr9CA;;EAGI,ebgU+B;AHupCnC;;AgB98CA;EACE,yBbnCgB;AHo/ClB;;AgBl9CA;;EAKI,yBbvCc;AHy/ClB;;AgBv9CA;;EAWM,wBAA4C;AhBi9ClD;;AgB58CA;;;;EAKI,SAAS;AhB88Cb;;AgBt8CA;EAEI,qCb1DW;AHkgDf;;AKvgDE;EW2EI,cbvEY;EawEZ,sCbvES;AHugDf;;AiBnhDE;;;EAII,yBCsF4D;AlB+7ClE;;AiBzhDE;;;;EAYM,qBC8E0D;AlBs8ClE;;AKzhDE;EYiBM,yBAJsC;AjBghD9C;;AiBjhDE;;EASQ,yBARoC;AjBqhD9C;;AiBziDE;;;EAII,yBCsF4D;AlBq9ClE;;AiB/iDE;;;;EAYM,qBC8E0D;AlB49ClE;;AK/iDE;EYiBM,yBAJsC;AjBsiD9C;;AiBviDE;;EASQ,yBARoC;AjB2iD9C;;AiB/jDE;;;EAII,yBCsF4D;AlB2+ClE;;AiBrkDE;;;;EAYM,qBC8E0D;AlBk/ClE;;AKrkDE;EYiBM,yBAJsC;AjB4jD9C;;AiB7jDE;;EASQ,yBARoC;AjBikD9C;;AiBrlDE;;;EAII,yBCsF4D;AlBigDlE;;AiB3lDE;;;;EAYM,qBC8E0D;AlBwgDlE;;AK3lDE;EYiBM,yBAJsC;AjBklD9C;;AiBnlDE;;EASQ,yBARoC;AjBulD9C;;AiB3mDE;;;EAII,yBCsF4D;AlBuhDlE;;AiBjnDE;;;;EAYM,qBC8E0D;AlB8hDlE;;AKjnDE;EYiBM,yBAJsC;AjBwmD9C;;AiBzmDE;;EASQ,yBARoC;AjB6mD9C;;AiBjoDE;;;EAII,yBCsF4D;AlB6iDlE;;AiBvoDE;;;;EAYM,qBC8E0D;AlBojDlE;;AKvoDE;EYiBM,yBAJsC;AjB8nD9C;;AiB/nDE;;EASQ,yBARoC;AjBmoD9C;;AiBvpDE;;;EAII,yBCsF4D;AlBmkDlE;;AiB7pDE;;;;EAYM,qBC8E0D;AlB0kDlE;;AK7pDE;EYiBM,yBAJsC;AjBopD9C;;AiBrpDE;;EASQ,yBARoC;AjBypD9C;;AiB7qDE;;;EAII,yBCsF4D;AlBylDlE;;AiBnrDE;;;;EAYM,qBC8E0D;AlBgmDlE;;AKnrDE;EYiBM,yBAJsC;AjB0qD9C;;AiB3qDE;;EASQ,yBARoC;AjB+qD9C;;AiBnsDE;;;EAII,sCdQS;AH6rDf;;AKlsDE;EYiBM,sCAJsC;AjByrD9C;;AiB1rDE;;EASQ,sCARoC;AjB8rD9C;;AgBxmDA;EAGM,Wb3GS;Ea4GT,yBbpGY;EaqGZ,qBbmQqD;AHs2C3D;;AgB9mDA;EAWM,cb5GY;Ea6GZ,yBblHY;EamHZ,qBblHY;AHytDlB;;AgBlmDA;EACE,Wb3Ha;Ea4Hb,yBbpHgB;AHytDlB;;AgBvmDA;;;EAOI,qBb+OuD;AHu3C3D;;AgB7mDA;EAWI,SAAS;AhBsmDb;;AgBjnDA;EAgBM,2Cb1IS;AH+uDf;;AK1uDE;EW4IM,WbjJO;EakJP,4CblJO;AHovDf;;AclrDI;EEiGA;IAEI,cAAc;IACd,WAAW;IACX,gBAAgB;IAChB,iCAAiC;EhBolDvC;EgBzlDG;IASK,SAAS;EhBmlDjB;AACF;;Ac9rDI;EEiGA;IAEI,cAAc;IACd,WAAW;IACX,gBAAgB;IAChB,iCAAiC;EhBgmDvC;EgBrmDG;IASK,SAAS;EhB+lDjB;AACF;;Ac1sDI;EEiGA;IAEI,cAAc;IACd,WAAW;IACX,gBAAgB;IAChB,iCAAiC;EhB4mDvC;EgBjnDG;IASK,SAAS;EhB2mDjB;AACF;;ActtDI;EEiGA;IAEI,cAAc;IACd,WAAW;IACX,gBAAgB;IAChB,iCAAiC;EhBwnDvC;EgB7nDG;IASK,SAAS;EhBunDjB;AACF;;AgBtoDA;EAOQ,cAAc;EACd,WAAW;EACX,gBAAgB;EAChB,iCAAiC;AhBmoDzC;;AgB7oDA;EAcU,SAAS;AhBmoDnB;;AmBhzDA;EACE,cAAc;EACd,WAAW;EACX,mCDuG8D;ECtG9D,yBhB4XkC;ECvQ9B,eAtCY;Ee5EhB,gBhBsR+B;EgBrR/B,gBhB0R+B;EgBzR/B,chBDgB;EgBEhB,sBhBTa;EgBUb,4BAA4B;EAC5B,yBhBPgB;EONd,sBP6OgC;EiB5O9B,wEjBof4F;AH40ClG;;AoB3zDI;EDLJ;ICMM,gBAAgB;EpB+zDpB;AACF;;AmBt0DA;EAsBI,6BAA6B;EAC7B,SAAS;AnBozDb;;AmB30DA;EA4BI,kBAAkB;EAClB,0BhBrBc;AHw0DlB;;AqBz0DE;EACE,clBAc;EkBCd,sBlBRW;EkBSX,qBlBwdsE;EkBvdtE,UAAU;EAKR,gDlBcW;AH0zDjB;;AmBx1DA;EAqCI,chB9Bc;EgBgCd,UAAU;AnBszDd;;AmB71DA;EAqCI,chB9Bc;EgBgCd,UAAU;AnBszDd;;AmB71DA;EAqCI,chB9Bc;EgBgCd,UAAU;AnBszDd;;AmB71DA;EAqCI,chB9Bc;EgBgCd,UAAU;AnBszDd;;AmB71DA;EAqCI,chB9Bc;EgBgCd,UAAU;AnBszDd;;AmB71DA;EAiDI,yBhB9Cc;EgBgDd,UAAU;AnB+yDd;;AmB3yDA;EAOI,chBtDc;EgBuDd,sBhB9DW;AHs2Df;;AmBnyDA;;EAEE,cAAc;EACd,WAAW;AnBsyDb;;AmB5xDA;EACE,iCDwB8D;ECvB9D,oCDuB8D;ECtB9D,gBAAgB;EflBd,kBAAW;EeoBb,gBhB4M+B;AHmlDjC;;AmB5xDA;EACE,+BDgB8D;ECf9D,kCDe8D;Ede1D,kBAtCY;EeUhB,gBhByI+B;AHspDjC;;AmB5xDA;EACE,gCDS8D;ECR9D,mCDQ8D;Ede1D,mBAtCY;EeiBhB,gBhBmI+B;AH4pDjC;;AmBtxDA;EACE,cAAc;EACd,WAAW;EACX,mBAA2B;EAC3B,gBAAgB;EfQZ,eAtCY;EegChB,gBhB+K+B;EgB9K/B,chB1GgB;EgB2GhB,6BAA6B;EAC7B,yBAAyB;EACzB,mBAAmC;AnByxDrC;;AmBnyDA;EAcI,gBAAgB;EAChB,eAAe;AnByxDnB;;AmB7wDA;EACE,kCD/B8D;ECgC9D,uBhBgQiC;ECjR7B,mBAtCY;EeyDhB,gBhB2F+B;EOxO7B,qBP+O+B;AH+qDnC;;AmB7wDA;EACE,gCDvC8D;ECwC9D,oBhB6PgC;ECtR5B,kBAtCY;EeiEhB,gBhBkF+B;EOvO7B,qBP8O+B;AHwrDnC;;AmB5wDA;EAGI,YAAY;AnB6wDhB;;AmBzwDA;EACE,YAAY;AnB4wDd;;AmBpwDA;EACE,mBhBsV0C;AHi7C5C;;AmBpwDA;EACE,cAAc;EACd,mBhBuU4C;AHg8C9C;;AmB/vDA;EACE,oBAAa;EAAb,aAAa;EACb,mBAAe;EAAf,eAAe;EACf,kBAA0C;EAC1C,iBAAyC;AnBkwD3C;;AmBtwDA;;EAQI,kBAA0C;EAC1C,iBAAyC;AnBmwD7C;;AmB1vDA;EACE,kBAAkB;EAClB,cAAc;EACd,qBhB4S6C;AHi9C/C;;AmB1vDA;EACE,kBAAkB;EAClB,kBhBwS2C;EgBvS3C,qBhBsS6C;AHu9C/C;;AmBhwDA;;EAQI,chBhNc;AH68DlB;;AmBzvDA;EACE,gBAAgB;AnB4vDlB;;AmBzvDA;EACE,2BAAoB;EAApB,oBAAoB;EACpB,sBAAmB;EAAnB,mBAAmB;EACnB,eAAe;EACf,qBhByR4C;AHm+C9C;;AmBhwDA;EAQI,gBAAgB;EAChB,aAAa;EACb,uBhBoR4C;EgBnR5C,cAAc;AnB4vDlB;;AqBh8DE;EACE,aAAa;EACb,WAAW;EACX,mBlB6c0C;ECpb1C,cAAW;EiBvBX,clBNa;AHy8DjB;;AqBh8DE;EACE,kBAAkB;EAClB,SAAS;EACT,UAAU;EACV,aAAa;EACb,eAAe;EACf,uBlBmyBqC;EkBlyBrC,iBAAiB;EjBoEf,mBAtCY;EiB5Bd,gBlB2O6B;EkB1O7B,WlBvDW;EkBwDX,wClBpBa;EOtCb,sBP6OgC;AHixDpC;;AqBn+DI;;;;EAsCE,cAAc;ArBo8DpB;;AqB1+DI;EA4CE,qBlBjCW;EkBoCT,oCHiCwD;EGhCxD,iRHpCmI;EGqCnI,4BAA4B;EAC5B,2DAA6D;EAC7D,gEH6BwD;AlBm6DhE;;AqBn/DI;EAuDI,qBlB5CS;EkB6CT,gDlB7CS;AH6+DjB;;AqBx/DI;EAiEI,oCHewD;EGdxD,kFHcwD;AlB66DhE;;AqB7/DI;EAyEE,qBlB9DW;EkBiET,uCHIwD;EGHxD,ujBAA8J;ArBs7DtK;;AqBngEI;EAiFI,qBlBtES;EkBuET,gDlBvES;AH6/DjB;;AqBxgEI;EA0FI,clB/ES;AHigEjB;;AqB5gEI;;;EA+FI,cAAc;ArBm7DtB;;AqBlhEI;EAuGI,clB5FS;AH2gEjB;;AqBthEI;EA0GM,qBlB/FO;AH+gEjB;;AqB1hEI;EAgHM,qBAAkC;EC1IxC,yBD2I+C;ArB86DnD;;AqB/hEI;EAuHM,gDlB5GO;AHwhEjB;;AqBniEI;EA2HM,qBlBhHO;AH4hEjB;;AqBviEI;EAqII,qBlB1HS;AHgiEjB;;AqB3iEI;EA0IM,qBlB/HO;EkBgIP,gDlBhIO;AHqiEjB;;AqBpiEE;EACE,aAAa;EACb,WAAW;EACX,mBlB6c0C;ECpb1C,cAAW;EiBvBX,clBTa;AHgjEjB;;AqBpiEE;EACE,kBAAkB;EAClB,SAAS;EACT,UAAU;EACV,aAAa;EACb,eAAe;EACf,uBlBmyBqC;EkBlyBrC,iBAAiB;EjBoEf,mBAtCY;EiB5Bd,gBlB2O6B;EkB1O7B,WlBvDW;EkBwDX,wClBvBa;EOnCb,sBP6OgC;AHq3DpC;;AqBvkEI;;;;EAsCE,cAAc;ArBwiEpB;;AqB9kEI;EA4CE,qBlBpCW;EkBuCT,oCHiCwD;EGhCxD,4UHpCmI;EGqCnI,4BAA4B;EAC5B,2DAA6D;EAC7D,gEH6BwD;AlBugEhE;;AqBvlEI;EAuDI,qBlB/CS;EkBgDT,gDlBhDS;AHolEjB;;AqB5lEI;EAiEI,oCHewD;EGdxD,kFHcwD;AlBihEhE;;AqBjmEI;EAyEE,qBlBjEW;EkBoET,uCHIwD;EGHxD,knBAA8J;ArB0hEtK;;AqBvmEI;EAiFI,qBlBzES;EkB0ET,gDlB1ES;AHomEjB;;AqB5mEI;EA0FI,clBlFS;AHwmEjB;;AqBhnEI;;;EA+FI,cAAc;ArBuhEtB;;AqBtnEI;EAuGI,clB/FS;AHknEjB;;AqB1nEI;EA0GM,qBlBlGO;AHsnEjB;;AqB9nEI;EAgHM,qBAAkC;EC1IxC,yBD2I+C;ArBkhEnD;;AqBnoEI;EAuHM,gDlB/GO;AH+nEjB;;AqBvoEI;EA2HM,qBlBnHO;AHmoEjB;;AqB3oEI;EAqII,qBlB7HS;AHuoEjB;;AqB/oEI;EA0IM,qBlBlIO;EkBmIP,gDlBnIO;AH4oEjB;;AmB36DA;EACE,oBAAa;EAAb,aAAa;EACb,uBAAmB;EAAnB,mBAAmB;EACnB,sBAAmB;EAAnB,mBAAmB;AnB86DrB;;AmBj7DA;EASI,WAAW;AnB46Df;;AcloEI;EK6MJ;IAeM,oBAAa;IAAb,aAAa;IACb,sBAAmB;IAAnB,mBAAmB;IACnB,qBAAuB;IAAvB,uBAAuB;IACvB,gBAAgB;EnB26DpB;EmB77DF;IAuBM,oBAAa;IAAb,aAAa;IACb,kBAAc;IAAd,cAAc;IACd,uBAAmB;IAAnB,mBAAmB;IACnB,sBAAmB;IAAnB,mBAAmB;IACnB,gBAAgB;EnBy6DpB;EmBp8DF;IAgCM,qBAAqB;IACrB,WAAW;IACX,sBAAsB;EnBu6D1B;EmBz8DF;IAuCM,qBAAqB;EnBq6DzB;EmB58DF;;IA4CM,WAAW;EnBo6Df;EmBh9DF;IAkDM,oBAAa;IAAb,aAAa;IACb,sBAAmB;IAAnB,mBAAmB;IACnB,qBAAuB;IAAvB,uBAAuB;IACvB,WAAW;IACX,eAAe;EnBi6DnB;EmBv9DF;IAyDM,kBAAkB;IAClB,oBAAc;IAAd,cAAc;IACd,aAAa;IACb,qBhB2LwC;IgB1LxC,cAAc;EnBi6DlB;EmB99DF;IAiEM,sBAAmB;IAAnB,mBAAmB;IACnB,qBAAuB;IAAvB,uBAAuB;EnBg6D3B;EmBl+DF;IAqEM,gBAAgB;EnBg6DpB;AACF;;AuBzuEA;EACE,qBAAqB;EAErB,gBpB0R+B;EoBzR/B,cpBMgB;EoBLhB,kBAAkB;EAElB,sBAAsB;EACtB,eAAsD;EACtD,yBAAiB;EAAjB,sBAAiB;EAAjB,qBAAiB;EAAjB,iBAAiB;EACjB,6BAA6B;EAC7B,6BAA2C;ECuF3C,yBrB8RkC;ECvQ9B,eAtCY;EoBiBhB,gBrB8L+B;EOnS7B,sBP6OgC;EiB5O9B,qIjB6b6I;AH6zDnJ;;AoBrvEI;EGLJ;IHMM,gBAAgB;EpByvEpB;AACF;;AK1vEE;EkBUE,cpBNc;EoBOd,qBAAqB;AvBovEzB;;AuBrwEA;EAsBI,UAAU;EACV,gDpBOa;AH4uEjB;;AuB1wEA;EA6BI,apBoZ6B;AH61DjC;;AuBluEA;;EAEE,oBAAoB;AvBquEtB;;AuB5tEE;ECvDA,WrBCa;EmBDX,yBnB8Ba;EqB5Bf,qBrB4Be;AH2vEjB;;AKnxEE;EmBAE,WrBLW;EmBDX,yBEDoF;EASpF,qBATyH;AxBgyE7H;;AwBpxEE;EAEE,WrBZW;EmBDX,yBEDoF;EAgBpF,qBAhByH;EAqBvH,gDAAiF;AxBkxEvF;;AwB7wEE;EAEE,WrB1BW;EqB2BX,yBrBEa;EqBDb,qBrBCa;AH8wEjB;;AwBxwEE;;EAGE,WrBtCW;EqBuCX,yBAzCuK;EA6CvK,qBA7C+M;AxBozEnN;;AwBrwEI;;EAKI,gDAAiF;AxBqwEzF;;AuBjwEE;ECvDA,WrBCa;EmBDX,yBnBOc;EqBLhB,qBrBKgB;AHuzElB;;AKxzEE;EmBAE,WrBLW;EmBDX,yBEDoF;EASpF,qBATyH;AxBq0E7H;;AwBzzEE;EAEE,WrBZW;EmBDX,yBEDoF;EAgBpF,qBAhByH;EAqBvH,iDAAiF;AxBuzEvF;;AwBlzEE;EAEE,WrB1BW;EqB2BX,yBrBrBc;EqBsBd,qBrBtBc;AH00ElB;;AwB7yEE;;EAGE,WrBtCW;EqBuCX,yBAzCuK;EA6CvK,qBA7C+M;AxBy1EnN;;AwB1yEI;;EAKI,iDAAiF;AxB0yEzF;;AuBtyEE;ECvDA,WrBCa;EmBDX,yBnBqCa;EqBnCf,qBrBmCe;AH8zEjB;;AK71EE;EmBAE,WrBLW;EmBDX,yBEDoF;EASpF,qBATyH;AxB02E7H;;AwB91EE;EAEE,WrBZW;EmBDX,yBEDoF;EAgBpF,qBAhByH;EAqBvH,+CAAiF;AxB41EvF;;AwBv1EE;EAEE,WrB1BW;EqB2BX,yBrBSa;EqBRb,qBrBQa;AHi1EjB;;AwBl1EE;;EAGE,WrBtCW;EqBuCX,yBAzCuK;EA6CvK,qBA7C+M;AxB83EnN;;AwB/0EI;;EAKI,+CAAiF;AxB+0EzF;;AuB30EE;ECvDA,WrBCa;EmBDX,yBnBuCa;EqBrCf,qBrBqCe;AHi2EjB;;AKl4EE;EmBAE,WrBLW;EmBDX,yBEDoF;EASpF,qBATyH;AxB+4E7H;;AwBn4EE;EAEE,WrBZW;EmBDX,yBEDoF;EAgBpF,qBAhByH;EAqBvH,gDAAiF;AxBi4EvF;;AwB53EE;EAEE,WrB1BW;EqB2BX,yBrBWa;EqBVb,qBrBUa;AHo3EjB;;AwBv3EE;;EAGE,WrBtCW;EqBuCX,yBAzCuK;EA6CvK,qBA7C+M;AxBm6EnN;;AwBp3EI;;EAKI,gDAAiF;AxBo3EzF;;AuBh3EE;ECvDA,crBUgB;EmBVd,yBnBoCa;EqBlCf,qBrBkCe;AHy4EjB;;AKv6EE;EmBAE,crBIc;EmBVd,yBEDoF;EASpF,qBATyH;AxBo7E7H;;AwBx6EE;EAEE,crBHc;EmBVd,yBEDoF;EAgBpF,qBAhByH;EAqBvH,gDAAiF;AxBs6EvF;;AwBj6EE;EAEE,crBjBc;EqBkBd,yBrBQa;EqBPb,qBrBOa;AH45EjB;;AwB55EE;;EAGE,crB7Bc;EqB8Bd,yBAzCuK;EA6CvK,qBA7C+M;AxBw8EnN;;AwBz5EI;;EAKI,gDAAiF;AxBy5EzF;;AuBr5EE;ECvDA,WrBCa;EmBDX,yBnBkCa;EqBhCf,qBrBgCe;AHg7EjB;;AK58EE;EmBAE,WrBLW;EmBDX,yBEDoF;EASpF,qBATyH;AxBy9E7H;;AwB78EE;EAEE,WrBZW;EmBDX,yBEDoF;EAgBpF,qBAhByH;EAqBvH,+CAAiF;AxB28EvF;;AwBt8EE;EAEE,WrB1BW;EqB2BX,yBrBMa;EqBLb,qBrBKa;AHm8EjB;;AwBj8EE;;EAGE,WrBtCW;EqBuCX,yBAzCuK;EA6CvK,qBA7C+M;AxB6+EnN;;AwB97EI;;EAKI,+CAAiF;AxB87EzF;;AuB17EE;ECvDA,crBUgB;EmBVd,yBnBEc;EqBAhB,qBrBAgB;AHq/ElB;;AKj/EE;EmBAE,crBIc;EmBVd,yBEDoF;EASpF,qBATyH;AxB8/E7H;;AwBl/EE;EAEE,crBHc;EmBVd,yBEDoF;EAgBpF,qBAhByH;EAqBvH,iDAAiF;AxBg/EvF;;AwB3+EE;EAEE,crBjBc;EqBkBd,yBrB1Bc;EqB2Bd,qBrB3Bc;AHwgFlB;;AwBt+EE;;EAGE,crB7Bc;EqB8Bd,yBAzCuK;EA6CvK,qBA7C+M;AxBkhFnN;;AwBn+EI;;EAKI,iDAAiF;AxBm+EzF;;AuB/9EE;ECvDA,WrBCa;EmBDX,yBnBSc;EqBPhB,qBrBOgB;AHmhFlB;;AKthFE;EmBAE,WrBLW;EmBDX,yBEDoF;EASpF,qBATyH;AxBmiF7H;;AwBvhFE;EAEE,WrBZW;EmBDX,yBEDoF;EAgBpF,qBAhByH;EAqBvH,8CAAiF;AxBqhFvF;;AwBhhFE;EAEE,WrB1BW;EqB2BX,yBrBnBc;EqBoBd,qBrBpBc;AHsiFlB;;AwB3gFE;;EAGE,WrBtCW;EqBuCX,yBAzCuK;EA6CvK,qBA7C+M;AxBujFnN;;AwBxgFI;;EAKI,8CAAiF;AxBwgFzF;;AuB9/EE;ECHA,crB5Be;EqB6Bf,qBrB7Be;AHkiFjB;;AK1jFE;EmBwDE,WrB7DW;EqB8DX,yBrBjCa;EqBkCb,qBrBlCa;AHwiFjB;;AwBngFE;EAEE,+CrBvCa;AH4iFjB;;AwBlgFE;EAEE,crB5Ca;EqB6Cb,6BAA6B;AxBogFjC;;AwBjgFE;;EAGE,WrBhFW;EqBiFX,yBrBpDa;EqBqDb,qBrBrDa;AHwjFjB;;AwBjgFI;;EAKI,+CrB5DS;AH6jFjB;;AuB9hFE;ECHA,crBnDgB;EqBoDhB,qBrBpDgB;AHylFlB;;AK1lFE;EmBwDE,WrB7DW;EqB8DX,yBrBxDc;EqByDd,qBrBzDc;AH+lFlB;;AwBniFE;EAEE,iDrB9Dc;AHmmFlB;;AwBliFE;EAEE,crBnEc;EqBoEd,6BAA6B;AxBoiFjC;;AwBjiFE;;EAGE,WrBhFW;EqBiFX,yBrB3Ec;EqB4Ed,qBrB5Ec;AH+mFlB;;AwBjiFI;;EAKI,iDrBnFU;AHonFlB;;AuB9jFE;ECHA,crBrBe;EqBsBf,qBrBtBe;AH2lFjB;;AK1nFE;EmBwDE,WrB7DW;EqB8DX,yBrB1Ba;EqB2Bb,qBrB3Ba;AHimFjB;;AwBnkFE;EAEE,+CrBhCa;AHqmFjB;;AwBlkFE;EAEE,crBrCa;EqBsCb,6BAA6B;AxBokFjC;;AwBjkFE;;EAGE,WrBhFW;EqBiFX,yBrB7Ca;EqB8Cb,qBrB9Ca;AHinFjB;;AwBjkFI;;EAKI,+CrBrDS;AHsnFjB;;AuB9lFE;ECHA,crBnBe;EqBoBf,qBrBpBe;AHynFjB;;AK1pFE;EmBwDE,WrB7DW;EqB8DX,yBrBxBa;EqByBb,qBrBzBa;AH+nFjB;;AwBnmFE;EAEE,gDrB9Ba;AHmoFjB;;AwBlmFE;EAEE,crBnCa;EqBoCb,6BAA6B;AxBomFjC;;AwBjmFE;;EAGE,WrBhFW;EqBiFX,yBrB3Ca;EqB4Cb,qBrB5Ca;AH+oFjB;;AwBjmFI;;EAKI,gDrBnDS;AHopFjB;;AuB9nFE;ECHA,crBtBe;EqBuBf,qBrBvBe;AH4pFjB;;AK1rFE;EmBwDE,crBpDc;EqBqDd,yBrB3Ba;EqB4Bb,qBrB5Ba;AHkqFjB;;AwBnoFE;EAEE,+CrBjCa;AHsqFjB;;AwBloFE;EAEE,crBtCa;EqBuCb,6BAA6B;AxBooFjC;;AwBjoFE;;EAGE,crBvEc;EqBwEd,yBrB9Ca;EqB+Cb,qBrB/Ca;AHkrFjB;;AwBjoFI;;EAKI,+CrBtDS;AHurFjB;;AuB9pFE;ECHA,crBxBe;EqByBf,qBrBzBe;AH8rFjB;;AK1tFE;EmBwDE,WrB7DW;EqB8DX,yBrB7Ba;EqB8Bb,qBrB9Ba;AHosFjB;;AwBnqFE;EAEE,+CrBnCa;AHwsFjB;;AwBlqFE;EAEE,crBxCa;EqByCb,6BAA6B;AxBoqFjC;;AwBjqFE;;EAGE,WrBhFW;EqBiFX,yBrBhDa;EqBiDb,qBrBjDa;AHotFjB;;AwBjqFI;;EAKI,+CrBxDS;AHytFjB;;AuB9rFE;ECHA,crBxDgB;EqByDhB,qBrBzDgB;AH8vFlB;;AK1vFE;EmBwDE,crBpDc;EqBqDd,yBrB7Dc;EqB8Dd,qBrB9Dc;AHowFlB;;AwBnsFE;EAEE,iDrBnEc;AHwwFlB;;AwBlsFE;EAEE,crBxEc;EqByEd,6BAA6B;AxBosFjC;;AwBjsFE;;EAGE,crBvEc;EqBwEd,yBrBhFc;EqBiFd,qBrBjFc;AHoxFlB;;AwBjsFI;;EAKI,iDrBxFU;AHyxFlB;;AuB9tFE;ECHA,crBjDgB;EqBkDhB,qBrBlDgB;AHuxFlB;;AK1xFE;EmBwDE,WrB7DW;EqB8DX,yBrBtDc;EqBuDd,qBrBvDc;AH6xFlB;;AwBnuFE;EAEE,8CrB5Dc;AHiyFlB;;AwBluFE;EAEE,crBjEc;EqBkEd,6BAA6B;AxBouFjC;;AwBjuFE;;EAGE,WrBhFW;EqBiFX,yBrBzEc;EqB0Ed,qBrB1Ec;AH6yFlB;;AwBjuFI;;EAKI,8CrBjFU;AHkzFlB;;AuBnvFA;EACE,gBpBoN+B;EoBnN/B,cpB5Ce;EoB6Cf,qBpBkG4C;AHopF9C;;AK3zFE;EkBwEE,cpBgG8D;EoB/F9D,0BpBgG+C;AHupFnD;;AuB9vFA;EAYI,0BpB2F+C;EoB1F/C,gBAAgB;AvBsvFpB;;AuBnwFA;EAkBI,cpBnFc;EoBoFd,oBAAoB;AvBqvFxB;;AuB1uFA;ECJE,oBrB6SgC;ECtR5B,kBAtCY;EoBiBhB,gBrBkI+B;EOvO7B,qBP8O+B;AH0mFnC;;AuB7uFA;ECRE,uBrBwSiC;ECjR7B,mBAtCY;EoBiBhB,gBrBmI+B;EOxO7B,qBP+O+B;AHgnFnC;;AuB3uFA;EACE,cAAc;EACd,WAAW;AvB8uFb;;AuBhvFA;EAMI,kBpB6T+B;AHi7EnC;;AuBzuFA;;;EAII,WAAW;AvB2uFf;;AyBn3FA;ELMM,gCjB8P2C;AHmnFjD;;AoB52FI;EKXJ;ILYM,gBAAgB;EpBg3FpB;AACF;;AyB73FA;EAII,UAAU;AzB63Fd;;AyBz3FA;EAEI,aAAa;AzB23FjB;;AyBv3FA;EACE,kBAAkB;EAClB,SAAS;EACT,gBAAgB;ELXZ,6BjB+PwC;AHuoF9C;;AoBj4FI;EKGJ;ILFM,gBAAgB;EpBq4FpB;AACF;;A0Bj5FA;;;;EAIE,kBAAkB;A1Bo5FpB;;A0Bj5FA;EACE,mBAAmB;A1Bo5FrB;;A2Bh4FI;EACE,qBAAqB;EACrB,oBxBkO0C;EwBjO1C,uBxBgO0C;EwB/N1C,WAAW;EAhCf,uBAA8B;EAC9B,qCAA4C;EAC5C,gBAAgB;EAChB,oCAA2C;A3Bo6F7C;;A2B/2FI;EACE,cAAc;A3Bk3FpB;;A0B55FA;EACE,kBAAkB;EAClB,SAAS;EACT,OAAO;EACP,avB4pBsC;EuB3pBtC,aAAa;EACb,WAAW;EACX,gBvBkuBuC;EuBjuBvC,iBAA8B;EAC9B,oBAA4B;EtBsGxB,eAtCY;EsB9DhB,cvBXgB;EuBYhB,gBAAgB;EACh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uB,kB3BqJU;AHwnGjC;;A8B5wGA;EAAsB,iB3BoJW;AH4nGjC;;A8BxwGA;EACE,oBAAa;EAAb,aAAa;EACb,sBAAmB;EAAnB,mBAAmB;EACnB,yB3BgSkC;E2B/RlC,gBAAgB;E1BwBZ,eAtCY;E0BgBhB,gB3B0L+B;E2BzL/B,gB3B8L+B;E2B7L/B,c3B7FgB;E2B8FhB,kBAAkB;EAClB,mBAAmB;EACnB,yB3BrGgB;E2BsGhB,yB3BpGgB;EONd,sBP6OgC;AHyoGpC;;A8BxxGA;;EAkBI,aAAa;A9B2wGjB;;A8BjwGA;;EAEE,gCZjB8D;AlBqxGhE;;A8BjwGA;;;;;;EAME,oB3B2QgC;ECtR5B,kBAtCY;E0BmDhB,gB3BgG+B;EOvO7B,qBP8O+B;AH8pGnC;;A8BjwGA;;EAEE,kCZlC8D;AlBsyGhE;;A8BjwGA;;;;;;EAME,uB3BqPiC;ECjR7B,mBAtCY;E0BoEhB,gB3BgF+B;EOxO7B,qBP+O+B;AH8qGnC;;A8BjwGA;;EAEE,sBAA0E;A9BowG5E;;A8BzvGA;;;;;;EpBzJI,0BoB+J4B;EpB9J5B,6BoB8J4B;A9B6vGhC;;A8B1vGA;;;;;;EpBpJI,yBoB0J2B;EpBzJ3B,4BoByJ2B;A9B8vG/B;;A+Bl7GA;EACE,kBAAkB;EAClB,cAAc;EACd,kBAA+C;EAC/C,oBAAqE;A/Bq7GvE;;A+Bl7GA;EACE,2BAAoB;EAApB,oBAAoB;EACpB,kB5B6f0C;AHw7F5C;;A+Bl7GA;EACE,kBAAkB;EAClB,OAAO;EACP,WAAW;EACX,W5Byf0C;E4Bxf1C,eAAkF;EAClF,UAAU;A/Bq7GZ;;A+B37GA;EASI,W5BvBW;E4BwBX,qB5BKa;EmB9Bb,yBnB8Ba;AHk7GjB;;A+Bj8GA;EAoBM,gD5BLW;AHs7GjB;;A+Br8GA;EAyBI,qB5B0bsE;AHs/F1E;;A+Bz8GA;EA6BI,W5B3CW;E4B4CX,yB5Bsf8E;E4Brf9E,qB5Bqf8E;AH27FlF;;A+B/8GA;EAuCM,c5B/CY;AH29GlB;;A+Bn9GA;EA0CQ,yB5BtDU;AHm+GlB;;A+Bn6GA;EACE,kBAAkB;EAClB,gBAAgB;EAEhB,mBAAmB;A/Bq6GrB;;A+Bz6GA;EASI,kBAAkB;EAClB,YAA+E;EAC/E,aAA+D;EAC/D,cAAc;EACd,W5B4bwC;E4B3bxC,Y5B2bwC;E4B1bxC,oBAAoB;EACpB,WAAW;EACX,sB5BnFW;E4BoFX,yB5BoJ6B;AHgxGjC;;A+Bt7GA;EAwBI,kBAAkB;EAClB,YAA+E;EAC/E,aAA+D;EAC/D,cAAc;EACd,W5B6awC;E4B5axC,Y5B4awC;E4B3axC,WAAW;EACX,mCAAgE;A/Bk6GpE;;A+Bz5GA;ErB5GI,sBP6OgC;AH4xGpC;;A+B75GA;EAOM,kOb5EqI;AlBs+G3I;;A+Bj6GA;EAaM,qB5B1FW;EmB9Bb,yBnB8Ba;AHm/GjB;;A+Bt6GA;EAkBM,+KbvFqI;AlB++G3I;;A+B16GA;EAwBM,wC5BrGW;AH2/GjB;;A+B96GA;EA2BM,wC5BxGW;AH+/GjB;;A+B94GA;EAGI,kB5B8Z+C;AHi/FnD;;A+Bl5GA;EAQM,8KbjHqI;AlB+/G3I;;A+Bt5GA;EAcM,wC5B/HW;AH2gHjB;;A+Bl4GA;EACE,qBAA2D;A/Bq4G7D;;A+Bt4GA;EAKM,cAAqD;EACrD,c5BsY+E;E4BrY/E,mBAAmB;EAEnB,qB5BoY4E;AHggGlF;;A+B74GA;EAaM,wBb1E0D;Ea2E1D,0Bb3E0D;Ea4E1D,uBbxD0D;EayD1D,wBbzD0D;Ea0D1D,yB5BlLY;E4BoLZ,qB5B0X4E;EiBpjB5E,iJjBsgB+H;EiBtgB/H,yIjBsgB+H;EiBtgB/H,8KjBsgB+H;AHwjGrI;;AoBzjHI;EWkKJ;IXjKM,gBAAgB;EpB6jHpB;AACF;;A+B75GA;EA0BM,sB5BhMS;E4BiMT,sCAA4E;EAA5E,8BAA4E;A/Bu4GlF;;A+Bl6GA;EAiCM,wC5B1KW;AH+iHjB;;A+Bz3GA;EACE,qBAAqB;EACrB,WAAW;EACX,mCb7G8D;Ea8G9D,0C5BwKkC;ECvQ9B,eAtCY;E2BwIhB,gB5BkE+B;E4BjE/B,gB5BsE+B;E4BrE/B,c5BrNgB;E4BsNhB,sBAAsB;EACtB,uO5BuW+I;E4BtW/I,yB5B3NgB;EONd,sBP6OgC;E4BTlC,wBAAgB;EAAhB,qBAAgB;EAAhB,gBAAgB;A/B03GlB;;A+Bz4GA;EAkBI,qB5B4PsE;E4B3PtE,UAAU;EAIR,gD5B7MW;AHqkHjB;;A+B/4GA;EAgCM,c5B5OY;E4B6OZ,sB5BpPS;AHumHf;;A+Bp5GA;EAuCI,YAAY;EACZ,sB5BoIgC;E4BnIhC,sBAAsB;A/Bi3G1B;;A+B15GA;EA6CI,c5B1Pc;E4B2Pd,yB5B/Pc;AHgnHlB;;A+B/5GA;EAmDI,aAAa;A/Bg3GjB;;A+Bn6GA;EAwDI,kBAAkB;EAClB,0B5BrQc;AHonHlB;;A+B32GA;EACE,kCbxK8D;EayK9D,oB5BsHkC;E4BrHlC,uB5BqHkC;E4BpHlC,oB5BqHiC;ECjR7B,mBAtCY;AJijHlB;;A+B32GA;EACE,gCbhL8D;EaiL9D,mB5BmHiC;E4BlHjC,sB5BkHiC;E4BjHjC,kB5BkHgC;ECtR5B,kBAtCY;AJyjHlB;;A+Bt2GA;EACE,kBAAkB;EAClB,qBAAqB;EACrB,WAAW;EACX,mCbhM8D;EaiM9D,gBAAgB;A/By2GlB;;A+Bt2GA;EACE,kBAAkB;EAClB,UAAU;EACV,WAAW;EACX,mCbxM8D;EayM9D,SAAS;EACT,UAAU;A/By2GZ;;A+B/2GA;EASI,qB5B2KsE;E4B1KtE,gD5B1Ra;AHooHjB;;A+Bp3GA;;EAgBI,yB5B3Tc;AHoqHlB;;A+Bz3GA;EAqBM,iB5BkUQ;AHsiGd;;A+B73GA;EA0BI,0BAA0B;A/Bu2G9B;;A+Bn2GA;EACE,kBAAkB;EAClB,MAAM;EACN,QAAQ;EACR,OAAO;EACP,UAAU;EACV,mCbxO8D;EayO9D,yB5B6CkC;E4B3ClC,gB5BxD+B;E4ByD/B,gB5BpD+B;E4BqD/B,c5B/UgB;E4BgVhB,sB5BvVa;E4BwVb,yB5BpVgB;EONd,sBP6OgC;AHm9GpC;;A+Bn3GA;EAkBI,kBAAkB;EAClB,MAAM;EACN,QAAQ;EACR,SAAS;EACT,UAAU;EACV,cAAc;EACd,6Bb1P4D;Ea2P5D,yB5B2BgC;E4B1BhC,gB5BpE6B;E4BqE7B,c5B/Vc;E4BgWd,iBAAiB;ETxWjB,yBnBGc;E4BuWd,oBAAoB;ErB3WpB,kCqB4WgF;A/Bq2GpF;;A+B31GA;EACE,WAAW;EACX,cbhR2B;EaiR3B,UAAU;EACV,6BAA6B;EAC7B,wBAAgB;EAAhB,qBAAgB;EAAhB,gBAAgB;A/B81GlB;;A+Bn2GA;EAQI,aAAa;A/B+1GjB;;A+Bv2GA;EAY8B,gE5BnWb;AHksHjB;;A+B32GA;EAa8B,gE5BpWb;AHssHjB;;A+B/2GA;EAc8B,gE5BrWb;AH0sHjB;;A+Bn3GA;EAkBI,SAAS;A/Bq2Gb;;A+Bv3GA;EAsBI,W5B2N6C;E4B1N7C,Y5B0N6C;E4BzN7C,oBAAyE;ET7YzE,yBnB8Ba;E4BiXb,S5B0N0C;EO1mB1C,mBP2mB6C;EiB1mB3C,oHjBsgB+H;EiBtgB/H,4GjBsgB+H;E4BnHjI,wBAAgB;EAAhB,gBAAgB;A/Bo2GpB;;AoBlvHI;EWgXJ;IX/WM,wBAAgB;IAAhB,gBAAgB;EpBsvHpB;AACF;;A+Bx4GA;ETrXI,yBnB8mB2E;AHmpG/E;;A+B54GA;EAsCI,W5BoMoC;E4BnMpC,c5BoMqC;E4BnMrC,kBAAkB;EAClB,e5BmMuC;E4BlMvC,yB5B3Zc;E4B4Zd,yBAAyB;ErBjazB,mBPomBoC;AHwqGxC;;A+Bt5GA;EAiDI,W5BgM6C;E4B/L7C,Y5B+L6C;EmBtmB7C,yBnB8Ba;E4B2Yb,S5BgM0C;EO1mB1C,mBP2mB6C;EiB1mB3C,iHjBsgB+H;EiBtgB/H,4GjBsgB+H;E4BzFjI,qBAAgB;EAAhB,gBAAgB;A/Bw2GpB;;AoBhxHI;EWgXJ;IX/WM,qBAAgB;IAAhB,gBAAgB;EpBoxHpB;AACF;;A+Bt6GA;ETrXI,yBnB8mB2E;AHirG/E;;A+B16GA;EAgEI,W5B0KoC;E4BzKpC,c5B0KqC;E4BzKrC,kBAAkB;EAClB,e5ByKuC;E4BxKvC,yB5Brbc;E4Bsbd,yBAAyB;ErB3bzB,mBPomBoC;AHssGxC;;A+Bp7GA;EA2EI,W5BsK6C;E4BrK7C,Y5BqK6C;E4BpK7C,aAAa;EACb,oB5B9D+B;E4B+D/B,mB5B/D+B;EmBrY/B,yBnB8Ba;E4Bwab,S5BmK0C;EO1mB1C,mBP2mB6C;EiB1mB3C,gHjBsgB+H;EiBtgB/H,4GjBsgB+H;E4B5DjI,gBAAgB;A/B42GpB;;AoBjzHI;EWgXJ;IX/WM,oBAAgB;IAAhB,gBAAgB;EpBqzHpB;AACF;;A+Bv8GA;ETrXI,yBnB8mB2E;AHktG/E;;A+B38GA;EA6FI,W5B6IoC;E4B5IpC,c5B6IqC;E4B5IrC,kBAAkB;EAClB,e5B4IuC;E4B3IvC,6BAA6B;EAC7B,yBAAyB;EACzB,oBAA4C;A/Bk3GhD;;A+Br9GA;EAwGI,yB5Bzdc;EOLd,mBPomBoC;AH4uGxC;;A+B19GA;EA6GI,kBAAkB;EAClB,yB5B/dc;EOLd,mBPomBoC;AHkvGxC;;A+Bh+GA;EAoHM,yB5BneY;AHm1HlB;;A+Bp+GA;EAwHM,eAAe;A/Bg3GrB;;A+Bx+GA;EA4HM,yB5B3eY;AH21HlB;;A+B5+GA;EAgIM,eAAe;A/Bg3GrB;;A+Bh/GA;EAoIM,yB5BnfY;AHm2HlB;;A+B32GA;;;EX9fM,4GjBsgB+H;AHy2GrI;;AoB12HI;EWyfJ;;;IXxfM,gBAAgB;EpBg3HpB;AACF;;AgCx3HA;EACE,oBAAa;EAAb,aAAa;EACb,mBAAe;EAAf,eAAe;EACf,eAAe;EACf,gBAAgB;EAChB,gBAAgB;AhC23HlB;;AgCx3HA;EACE,cAAc;EACd,oB7B6qBsC;AH8sGxC;;AK13HE;E2BEE,qBAAqB;AhC43HzB;;AgCj4HA;EAUI,c7BVc;E6BWd,oBAAoB;EACpB,eAAe;AhC23HnB;;AgCn3HA;EACE,gC7BxBgB;AH84HlB;;AgCv3HA;EAII,mB7B0M6B;AH6qHjC;;AgC33HA;EAQI,6BAAgD;EtB3BhD,+BPoOgC;EOnOhC,gCPmOgC;AHgrHpC;;AKl5HE;E2B6BI,qC7BnCY;AH45HlB;;AgCr4HA;EAgBM,c7BpCY;E6BqCZ,6BAA6B;EAC7B,yBAAyB;AhCy3H/B;;AgC34HA;;EAwBI,c7B3Cc;E6B4Cd,sB7BnDW;E6BoDX,kC7BpDW;AH46Hf;;AgCl5HA;EA+BI,gB7B+K6B;EOjO7B,yBsBoD4B;EtBnD5B,0BsBmD4B;AhCu3HhC;;AgC92HA;EtBtEI,sBP6OgC;AH2sHpC;;AgCl3HA;;EAOI,W7B3EW;E6B4EX,yB7B/Ca;AH+5HjB;;AgCv2HA;EAEI,kBAAc;EAAd,cAAc;EACd,kBAAkB;AhCy2HtB;;AgCr2HA;EAEI,0BAAa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C;EiC/gIG;IAwBK,uBAAmB;IAAnB,mBAAmB;EjC0/H3B;EiClhIG;IA2BO,kBAAkB;EjC0/H5B;EiCrhIG;IA+BO,qB9BgiB6B;I8B/hB7B,oB9B+hB6B;EH09GvC;EiCzhIG;;IAsCK,qBAAiB;IAAjB,iBAAiB;EjCu/HzB;EiC7hIG;IAqDK,+BAAwB;IAAxB,wBAAwB;IAGxB,6BAAgB;IAAhB,gBAAgB;EjCy+HxB;EiCjiIG;IA4DK,aAAa;EjCw+HrB;AACF;;AiC1iIA;EAyBQ,yBAAqB;EAArB,qBAAqB;EACrB,oBAA2B;EAA3B,2BAA2B;AjCqhInC;;AiC/iIA;;EAQU,gBAAgB;EAChB,eAAe;AjC4iIzB;;AiCrjIA;EA6BU,uBAAmB;EAAnB,mBAAmB;AjC4hI7B;;AiCzjIA;EAgCY,kBAAkB;AjC6hI9B;;AiC7jIA;EAoCY,qB9BgiB6B;E8B/hB7B,oB9B+hB6B;AH8/GzC;;AiClkIA;;EA2CU,qBAAiB;EAAjB,iBAAiB;AjC4hI3B;;AiCvkIA;EA0DU,+BAAwB;EAAxB,wBAAwB;EAGxB,6BAAgB;EAAhB,gBAAgB;AjC+gI1B;;AiC5kIA;EAiEU,aAAa;AjC+gIvB;;AiClgIA;EAEI,yB9B/MW;AHmtIf;;AKptIE;E4BmNI,yB9BlNS;AHutIf;;AiC1gIA;EAWM,yB9BxNS;AH2tIf;;AK5tIE;E4B4NM,yB9B3NO;AH+tIf;;AiClhIA;EAkBQ,yB9B/NO;AHmuIf;;AiCthIA;;;;EA0BM,yB9BvOS;AH0uIf;;AiC7hIA;EA+BI,yB9B5OW;E8B6OX,gC9B7OW;AH+uIf;;AiCliIA;EAoCI,+QftNuI;AlBwtI3I;;AiCtiIA;EAwCI,yB9BrPW;AHuvIf;;AiC1iIA;EA0CM,yB9BvPS;AH2vIf;;AK5vIE;E4B2PM,yB9B1PO;AH+vIf;;AiC9/HA;EAEI,W9B7QW;AH6wIf;;AKpwIE;E4BuQI,W9BhRS;AHixIf;;AiCtgIA;EAWM,+B9BtRS;AHqxIf;;AK5wIE;E4BgRM,gC9BzRO;AHyxIf;;AiC9gIA;EAkBQ,gC9B7RO;AH6xIf;;AiClhIA;;;;EA0BM,W9BrSS;AHoyIf;;AiCzhIA;EA+BI,+B9B1SW;E8B2SX,sC9B3SW;AHyyIf;;AiC9hIA;EAoCI,qRf1QuI;AlBwwI3I;;AiCliIA;EAwCI,+B9BnTW;AHizIf;;AiCtiIA;EA0CM,W9BrTS;AHqzIf;;AK5yIE;E4B+SM,W9BxTO;AHyzIf;;AkC5zIA;EACE,kBAAkB;EAClB,oBAAa;EAAb,aAAa;EACb,0BAAsB;EAAtB,sBAAsB;EACtB,YAAY;EAEZ,qBAAqB;EACrB,sB/BJa;E+BKb,2BAA2B;EAC3B,sC/BIa;EOZX,sBP6OgC;AH0lIpC;;AkCx0IA;EAaI,eAAe;EACf,cAAc;AlC+zIlB;;AkC70IA;ExBUI,+BPoOgC;EOnOhC,gCPmOgC;AHomIpC;;AkCl1IA;ExBwBI,mCPsNgC;EOrNhC,kCPqNgC;AHymIpC;;AkCzzIA;EAGE,kBAAc;EAAd,cAAc;EAGd,eAAe;EACf,gB/BsxByC;AHkiH3C;;AkCpzIA;EACE,sB/BgxBwC;AHuiH1C;;AkCpzIA;EACE,qBAA+B;EAC/B,gBAAgB;AlCuzIlB;;AkCpzIA;EACE,gBAAgB;AlCuzIlB;;AKl2IE;E6BgDE,qBAAqB;AlCszIzB;;AkCxzIA;EAMI,oB/B+vBuC;AHujH3C;;AkC9yIA;EACE,wB/BsvByC;E+BrvBzC,gBAAgB;EAEhB,qC/B3Da;E+B4Db,6C/B5Da;AH42If;;AkCrzIA;ExBnEI,0DwB2E8E;AlCizIlF;;AkCzzIA;EAaM,aAAa;AlCgzInB;;AkC3yIA;EACE,wB/BouByC;E+BnuBzC,qC/B3Ea;E+B4Eb,0C/B5Ea;AH03If;;AkCjzIA;ExBrFI,0DQ+H4D;AlB2wIhE;;AkCtyIA;EACE,uBAAiC;EACjC,uB/BmtBwC;E+BltBxC,sBAAgC;EAChC,gBAAgB;AlCyyIlB;;AkCtyIA;EACE,uBAAiC;EACjC,sBAAgC;AlCyyIlC;;AkCryIA;EACE,kBAAkB;EAClB,MAAM;EACN,QAAQ;EACR,SAAS;EACT,OAAO;EACP,gB/B8sByC;AH0lH3C;;AkCryIA;;;EAGE,oBAAc;EAAd,cAAc;EACd,WAAW;AlCwyIb;;AkCryIA;;ExBxHI,2CQsH4D;ERrH5D,4CQqH4D;AlB6yIhE;;AkCtyIA;;ExB/GI,+CQwG4D;ERvG5D,8CQuG4D;AlBmzIhE;;AkCpyIA;EAEI,mB/BurBsD;AH+mH1D;;Ac/3II;EoBuFJ;IAMI,oBAAa;IAAb,aAAa;IACb,uBAAmB;IAAnB,mBAAmB;IACnB,mB/BirBsD;I+BhrBtD,kB/BgrBsD;EHunHxD;EkChzIF;IAaM,gBAAY;IAAZ,YAAY;IACZ,kB/B2qBoD;I+B1qBpD,gBAAgB;IAChB,iB/ByqBoD;EH6nHxD;AACF;;AkC7xIA;EAII,mB/B2pBsD;AHkoH1D;;Acl5II;EoBiHJ;IAQI,oBAAa;IAAb,aAAa;IACb,uBAAmB;IAAnB,mBAAmB;ElC8xIrB;EkCvyIF;IAcM,gBAAY;IAAZ,YAAY;IACZ,gBAAgB;ElC4xIpB;EkC3yIF;IAkBQ,cAAc;IACd,cAAc;ElC4xIpB;EkC/yIF;IxBxJI,0BwBiLoC;IxBhLpC,6BwBgLoC;ElC0xItC;EkCnzIF;;IA8BY,0BAA0B;ElCyxIpC;EkCvzIF;;IAmCY,6BAA6B;ElCwxIvC;EkC3zIF;IxB1II,yBwBkLmC;IxBjLnC,4BwBiLmC;ElCuxIrC;EkC/zIF;;IA6CY,yBAAyB;ElCsxInC;EkCn0IF;;IAkDY,4BAA4B;ElCqxItC;AACF;;AkCzwIA;EAEI,sB/BglBsC;AH2rH1C;;Ac77II;EoBgLJ;IAMI,uB/B6lBiC;I+B7lBjC,oB/B6lBiC;I+B7lBjC,e/B6lBiC;I+B5lBjC,2B/B6lBuC;I+B7lBvC,wB/B6lBuC;I+B7lBvC,mB/B6lBuC;I+B5lBvC,UAAU;IACV,SAAS;ElC4wIX;EkCrxIF;IAYM,qBAAqB;IACrB,WAAW;ElC4wIf;AACF;;AkCnwIA;EAEI,gBAAgB;AlCqwIpB;;AkCvwIA;EAKM,gBAAgB;ExB5OlB,6BwB6OiC;ExB5OjC,4BwB4OiC;AlCuwIrC;;AkC7wIA;ExBrPI,yBwB+P8B;ExB9P9B,0BwB8P8B;AlCwwIlC;;AkClxIA;ExB9PI,gBwB4Q0B;EACxB,mB/BnC2B;AH2yIjC;;AmC1hJA;EACE,oBAAa;EAAb,aAAa;EACb,mBAAe;EAAf,eAAe;EACf,qBhC+hCsC;EgC9hCtC,mBhCiiCsC;EgC/hCtC,gBAAgB;EAChB,yBhCEgB;EOJd,sBP6OgC;AHkzIpC;;AmCzhJA;EAGI,oBhCqhCqC;AHqgHzC;;AmC7hJA;EAMM,qBAAqB;EACrB,qBhCihCmC;EgChhCnC,chCNY;EgCOZ,YhCshCuC;AHqgH7C;;AmCpiJA;EAoBI,0BAA0B;AnCohJ9B;;AmCxiJA;EAwBI,qBAAqB;AnCohJzB;;AmC5iJA;EA4BI,chC1Bc;AH8iJlB;;AoC3jJA;EACE,oBAAa;EAAb,aAAa;E7BGb,eAAe;EACf,gBAAgB;EGAd,sBP6OgC;AHg1IpC;;AoC5jJA;EACE,kBAAkB;EAClB,cAAc;EACd,uBjCgxBwC;EiC/wBxC,iBjCqO+B;EiCpO/B,iBjCmxBsC;EiClxBtC,cjCwBe;EiCvBf,sBjCNa;EiCOb,yBjCJgB;AHmkJlB;;AoCvkJA;EAWI,UAAU;EACV,cjCkK8D;EiCjK9D,qBAAqB;EACrB,yBjCXc;EiCYd,qBjCXc;AH2kJlB;;AoC/kJA;EAmBI,UAAU;EACV,UjC4wBiC;EiC3wBjC,gDjCSa;AHujJjB;;AoC5jJA;EAGM,cAAc;E1BChB,+BP+MgC;EO9MhC,kCP8MgC;AH+2IpC;;AoClkJA;E1BVI,gCP6NgC;EO5NhC,mCP4NgC;AHo3IpC;;AoCvkJA;EAcI,UAAU;EACV,WjCvCW;EiCwCX,yBjCXa;EiCYb,qBjCZa;AHykJjB;;AoC9kJA;EAqBI,cjCvCc;EiCwCd,oBAAoB;EAEpB,YAAY;EACZ,sBjCjDW;EiCkDX,qBjC/Cc;AH2mJlB;;AqClnJE;EACE,uBlCyxBsC;EC9pBpC,kBAtCY;EiCnFd,gBlCsO6B;AH+4IjC;;AqChnJM;E3BwBF,8BPgN+B;EO/M/B,iCP+M+B;AH64InC;;AqChnJM;E3BKF,+BP8N+B;EO7N/B,kCP6N+B;AHk5InC;;AqCloJE;EACE,uBlCuxBqC;EC5pBnC,mBAtCY;EiCnFd,gBlCuO6B;AH85IjC;;AqChoJM;E3BwBF,8BPiN+B;EOhN/B,iCPgN+B;AH45InC;;AqChoJM;E3BKF,+BP+N+B;EO9N/B,kCP8N+B;AHi6InC;;AsChpJA;EACE,qBAAqB;EACrB,qBnCw5BsC;ECv1BpC,cAAW;EkC/Db,gBnC2R+B;EmC1R/B,cAAc;EACd,kBAAkB;EAClB,mBAAmB;EACnB,wBAAwB;E5BRtB,sBP6OgC;EiB5O9B,qIjB6b6I;AH+tInJ;;AoBvpJI;EkBNJ;IlBOM,gBAAgB;EpB2pJpB;AACF;;AKxpJE;EiCGI,qBAAqB;AtCypJ3B;;AsCvqJA;EAoBI,aAAa;AtCupJjB;;AsClpJA;EACE,kBAAkB;EAClB,SAAS;AtCqpJX;;AsC9oJA;EACE,oBnC63BsC;EmC53BtC,mBnC43BsC;EOh6BpC,oBPm6BqC;AHmxHzC;;AsCzoJE;ECjDA,WpCMa;EoCLb,yBpCkCe;AH4pJjB;;AKhrJE;EkCVI,WpCCS;EoCAT,yBAAkC;AvC8rJxC;;AuCjsJU;EAQJ,UAAU;EACV,+CpCuBW;AHsqJjB;;AsCxpJE;ECjDA,WpCMa;EoCLb,yBpCWgB;AHksJlB;;AK/rJE;EkCVI,WpCCS;EoCAT,yBAAkC;AvC6sJxC;;AuChtJU;EAQJ,UAAU;EACV,iDpCAY;AH4sJlB;;AsCvqJE;ECjDA,WpCMa;EoCLb,yBpCyCe;AHmrJjB;;AK9sJE;EkCVI,WpCCS;EoCAT,yBAAkC;AvC4tJxC;;AuC/tJU;EAQJ,UAAU;EACV,+CpC8BW;AH6rJjB;;AsCtrJE;ECjDA,WpCMa;EoCLb,yBpC2Ce;AHgsJjB;;AK7tJE;EkCVI,WpCCS;EoCAT,yBAAkC;AvC2uJxC;;AuC9uJU;EAQJ,UAAU;EACV,gDpCgCW;AH0sJjB;;AsCrsJE;ECjDA,cpCegB;EoCdhB,yBpCwCe;AHktJjB;;AK5uJE;EkCVI,cpCUY;EoCTZ,yBAAkC;AvC0vJxC;;AuC7vJU;EAQJ,UAAU;EACV,+CpC6BW;AH4tJjB;;AsCptJE;ECjDA,WpCMa;EoCLb,yBpCsCe;AHmuJjB;;AK3vJE;EkCVI,WpCCS;EoCAT,yBAAkC;AvCywJxC;;AuC5wJU;EAQJ,UAAU;EACV,+CpC2BW;AH6uJjB;;AsCnuJE;ECjDA,cpCegB;EoCdhB,yBpCMgB;AHkxJlB;;AK1wJE;EkCVI,cpCUY;EoCTZ,yBAAkC;AvCwxJxC;;AuC3xJU;EAQJ,UAAU;EACV,iDpCLY;AH4xJlB;;AsClvJE;ECjDA,WpCMa;EoCLb,yBpCagB;AH0xJlB;;AKzxJE;EkCVI,WpCCS;EoCAT,yBAAkC;AvCuyJxC;;AuC1yJU;EAQJ,UAAU;EACV,8CpCEY;AHoyJlB;;AwCnzJA;EACE,kBAAoD;EACpD,mBrCqzBsC;EqCnzBtC,yBrCKgB;EOJd,qBP8O+B;AHukJnC;;Ac9vJI;E0B5DJ;IAQI,kBrC+yBoC;EHwgItC;AACF;;AwCpzJA;EACE,gBAAgB;EAChB,eAAe;E9BTb,gB8BUsB;AxCuzJ1B;;AyCl0JA;EACE,kBAAkB;EAClB,wBtCq9ByC;EsCp9BzC,mBtCq9BsC;EsCp9BtC,6BAA6C;E/BH3C,sBP6OgC;AH4lJpC;;AyCj0JA;EAEE,cAAc;AzCm0JhB;;AyC/zJA;EACE,gBtCgR+B;AHkjJjC;;AyC1zJA;EACE,mBAAsD;AzC6zJxD;;AyC9zJA;EAKI,kBAAkB;EAClB,MAAM;EACN,QAAQ;EACR,wBtCu7BuC;EsCt7BvC,cAAc;AzC6zJlB;;AyCnzJE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlBywJlE;;A0Cn2JE;EACE,yBAAqC;A1Cs2JzC;;A0Cn2JE;EACE,cAA0B;A1Cs2J9B;;AyCj0JE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlBuxJlE;;A0Cj3JE;EACE,yBAAqC;A1Co3JzC;;A0Cj3JE;EACE,cAA0B;A1Co3J9B;;AyC/0JE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlBqyJlE;;A0C/3JE;EACE,yBAAqC;A1Ck4JzC;;A0C/3JE;EACE,cAA0B;A1Ck4J9B;;AyC71JE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlBmzJlE;;A0C74JE;EACE,yBAAqC;A1Cg5JzC;;A0C74JE;EACE,cAA0B;A1Cg5J9B;;AyC32JE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlBi0JlE;;A0C35JE;EACE,yBAAqC;A1C85JzC;;A0C35JE;EACE,cAA0B;A1C85J9B;;AyCz3JE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlB+0JlE;;A0Cz6JE;EACE,yBAAqC;A1C46JzC;;A0Cz6JE;EACE,cAA0B;A1C46J9B;;AyCv4JE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlB61JlE;;A0Cv7JE;EACE,yBAAqC;A1C07JzC;;A0Cv7JE;EACE,cAA0B;A1C07J9B;;AyCr5JE;EC9CA,cxB8FgE;EIzF9D,yBJyF8D;EwB5FhE,qBxB4FgE;AlB22JlE;;A0Cr8JE;EACE,yBAAqC;A1Cw8JzC;;A0Cr8JE;EACE,cAA0B;A1Cw8J9B;;A2Ch9JE;EACE;IAAO,2BAAuC;E3Co9JhD;E2Cn9JE;IAAK,wBAAwB;E3Cs9J/B;AACF;;A2Cz9JE;EACE;IAAO,2BAAuC;E3Co9JhD;E2Cn9JE;IAAK,wBAAwB;E3Cs9J/B;AACF;;A2Cn9JA;EACE,oBAAa;EAAb,aAAa;EACb,YxC89BsC;EwC79BtC,gBAAgB;EvCoHZ,kBAtCY;EuC5EhB,yBxCJgB;EOJd,sBP6OgC;AHkvJpC;;A2Cl9JA;EACE,oBAAa;EAAb,aAAa;EACb,0BAAsB;EAAtB,sBAAsB;EACtB,qBAAuB;EAAvB,uBAAuB;EACvB,gBAAgB;EAChB,WxChBa;EwCiBb,kBAAkB;EAClB,mBAAmB;EACnB,yBxCUe;EiB9BX,2BjB0+B4C;AHggIlD;;AoBr+JI;EuBOJ;IvBNM,gBAAgB;EpBy+JpB;AACF;;A2Cx9JA;ErBaE,qMAA6I;EqBX7I,0BxCw8BsC;AHmhIxC;;A2Cv9JE;EACE,0DxC08BkD;EwC18BlD,kDxC08BkD;AHghItD;;A2Cv9JM;EAJJ;IAKM,uBAAe;IAAf,eAAe;E3C29JrB;AACF;;A4CrgKA;EACE,oBAAa;EAAb,aAAa;EACb,qBAAuB;EAAvB,uBAAuB;A5CwgKzB;;A4CrgKA;EACE,WAAO;EAAP,OAAO;A5CwgKT;;A6C1gKA;EACE,oBAAa;EAAb,aAAa;EACb,0BAAsB;EAAtB,sBAAsB;EAGtB,eAAe;EACf,gBAAgB;A7C2gKlB;;A6ClgKA;EACE,WAAW;EACX,c1CPgB;E0CQhB,mBAAmB;A7CqgKrB;;AK3gKE;EwCUE,UAAU;EACV,c1Cbc;E0Ccd,qBAAqB;EACrB,yB1CrBc;AH0hKlB;;A6C/gKA;EAcI,c1CjBc;E0CkBd,yB1CzBc;AH8hKlB;;A6C5/JA;EACE,kBAAkB;EAClB,cAAc;EACd,wB1C88ByC;E0C58BzC,sB1CzCa;E0C0Cb,sC1ChCa;AH8hKf;;A6CpgKA;EnC7BI,+BPoOgC;EOnOhC,gCPmOgC;AHk0JpC;;A6CzgKA;EnCfI,mCPsNgC;EOrNhC,kCPqNgC;AHu0JpC;;A6C9gKA;EAkBI,c1ChDc;E0CiDd,oBAAoB;EACpB,sB1CxDW;AHwjKf;;A6CphKA;EAyBI,UAAU;EACV,W1C9DW;E0C+DX,yB1ClCa;E0CmCb,qB1CnCa;AHkiKjB;;A6C3hKA;EAgCI,mBAAmB;A7C+/JvB;;A6C/hKA;EAmCM,gB1CiK2B;E0ChK3B,qB1CgK2B;AHg2JjC;;A6Cl/JI;EACE,uBAAmB;EAAnB,mBAAmB;A7Cq/JzB;;A6Ct/JI;EnCjCA,kCPsLgC;EOlMhC,0BmCmDwC;A7Cq/J5C;;A6C3/JI;EnC7CA,gCPkMgC;EOtLhC,4BmC4C0C;A7Cq/J9C;;A6ChgKI;EAeM,aAAa;A7Cq/JvB;;A6CpgKI;EAmBM,qB1C+HuB;E0C9HvB,oBAAoB;A7Cq/J9B;;A6CzgKI;EAuBQ,iB1C2HqB;E0C1HrB,sB1C0HqB;AH43JjC;;Ac/iKI;E+BiCA;IACE,uBAAmB;IAAnB,mBAAmB;E7CkhKvB;E6CnhKE;InCjCA,kCPsLgC;IOlMhC,0BmCmDwC;E7CihK1C;E6CvhKE;InC7CA,gCPkMgC;IOtLhC,4BmC4C0C;E7CghK5C;E6C3hKE;IAeM,aAAa;E7C+gKrB;E6C9hKE;IAmBM,qB1C+HuB;I0C9HvB,oBAAoB;E7C8gK5B;E6CliKE;IAuBQ,iB1C2HqB;I0C1HrB,sB1C0HqB;EHo5J/B;AACF;;AcxkKI;E+BiCA;IACE,uBAAmB;IAAnB,mBAAmB;E7C2iKvB;E6C5iKE;InCjCA,kCPsLgC;IOlMhC,0BmCmDwC;E7C0iK1C;E6ChjKE;InC7CA,gCPkMgC;IOtLhC,4BmC4C0C;E7CyiK5C;E6CpjKE;IAeM,aAAa;E7CwiKrB;E6CvjKE;IAmBM,qB1C+HuB;I0C9HvB,oBAAoB;E7CuiK5B;E6C3jKE;IAuBQ,iB1C2HqB;I0C1HrB,sB1C0HqB;EH66J/B;AACF;;AcjmKI;E+BiCA;IACE,uBAAmB;IAAnB,mBAAmB;E7CokKvB;E6CrkKE;InCjCA,kCPsLgC;IOlMhC,0BmCmDwC;E7CmkK1C;E6CzkKE;InC7CA,gCPkMgC;IOtLhC,4BmC4C0C;E7CkkK5C;E6C7kKE;IAeM,aAAa;E7CikKrB;E6ChlKE;IAmBM,qB1C+HuB;I0C9HvB,oBAAoB;E7CgkK5B;E6CplKE;IAuBQ,iB1C2HqB;I0C1HrB,sB1C0HqB;EHs8J/B;AACF;;Ac1nKI;E+BiCA;IACE,uBAAmB;IAAnB,mBAAmB;E7C6lKvB;E6C9lKE;InCjCA,kCPsLgC;IOlMhC,0BmCmDwC;E7C4lK1C;E6ClmKE;InC7CA,gCPkMgC;IOtLhC,4BmC4C0C;E7C2lK5C;E6CtmKE;IAeM,aAAa;E7C0lKrB;E6CzmKE;IAmBM,qB1C+HuB;I0C9HvB,oBAAoB;E7CylK5B;E6C7mKE;IAuBQ,iB1C2HqB;I0C1HrB,sB1C0HqB;EH+9J/B;AACF;;A6C5kKA;EAEI,qBAAqB;EACrB,oBAAoB;EnCjIpB,gBmCkIwB;A7C8kK5B;;A6CllKA;EAOM,mBAAmB;A7C+kKzB;;A6CtlKA;EAaM,sBAAsB;A7C6kK5B;;A8C1tKE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmoKlE;;AKltKE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C6tKjD;;A8CpuKE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8oKlE;;A8C1uKE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmpKlE;;AKluKE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C6uKjD;;A8CpvKE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8pKlE;;A8C1vKE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmqKlE;;AKlvKE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C6vKjD;;A8CpwKE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8qKlE;;A8C1wKE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmrKlE;;AKlwKE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C6wKjD;;A8CpxKE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8rKlE;;A8C1xKE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmsKlE;;AKlxKE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C6xKjD;;A8CpyKE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8sKlE;;A8C1yKE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmtKlE;;AKlyKE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C6yKjD;;A8CpzKE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8tKlE;;A8C1zKE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmuKlE;;AKlzKE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C6zKjD;;A8Cp0KE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8uKlE;;A8C10KE;EACE,c5B2F8D;E4B1F9D,yB5B0F8D;AlBmvKlE;;AKl0KE;EyCPM,c5BsF0D;E4BrF1D,yBAAyC;A9C60KjD;;A8Cp1KE;EAWM,W3CPO;E2CQP,yB5BgF0D;E4B/E1D,qB5B+E0D;AlB8vKlE;;A+C71KA;EACE,YAAY;E3C8HR,iBAtCY;E2CtFhB,gB5CiS+B;E4ChS/B,cAAc;EACd,W5CYa;E4CXb,yB5CCa;E4CAb,WAAW;A/Cg2Kb;;AK31KE;E0CDE,W5CMW;E4CLX,qBAAqB;A/Cg2KzB;;AK51KE;E0CCI,YAAY;A/C+1KlB;;A+Cp1KA;EACE,UAAU;EACV,6BAA6B;EAC7B,SAAS;EACT,wBAAgB;EAAhB,qBAAgB;EAAhB,gBAAgB;A/Cu1KlB;;A+Cj1KA;EACE,oBAAoB;A/Co1KtB;;AgD33KA;EACE,gB7Cy4BuC;E6Cx4BvC,gBAAgB;E5C6HZ,mBAtCY;E4CpFhB,2C7CEa;E6CDb,4BAA4B;EAC5B,oC7C04BmD;E6Cz4BnD,gD7CSa;E6CRb,mCAA2B;EAA3B,2BAA2B;EAC3B,UAAU;EtCLR,sBP64BsC;AHs/I1C;;AgDx4KA;EAcI,sB7C63BsC;AHigJ1C;;AgD54KA;EAkBI,UAAU;AhD83Kd;;AgDh5KA;EAsBI,cAAc;EACd,UAAU;AhD83Kd;;AgDr5KA;EA2BI,aAAa;AhD83KjB;;AgD13KA;EACE,oBAAa;EAAb,aAAa;EACb,sBAAmB;EAAnB,mBAAmB;EACnB,wB7Cy2BwC;E6Cx2BxC,c7CtBgB;E6CuBhB,2C7C7Ba;E6C8Bb,4BAA4B;EAC5B,4C7Ci3BoD;AH4gJtD;;AgD13KA;EACE,gB7Ci2BwC;AH4hJ1C;;AiDj6KA;EAEE,gBAAgB;AjDm6KlB;;AiDr6KA;EAKI,kBAAkB;EAClB,gBAAgB;AjDo6KpB;;AiD/5KA;EACE,eAAe;EACf,MAAM;EACN,OAAO;EACP,a9C+pBsC;E8C9pBtC,aAAa;EACb,WAAW;EACX,YAAY;EACZ,gBAAgB;EAGhB,UAAU;AjDg6KZ;;AiDz5KA;EACE,kBAAkB;EAClB,WAAW;EACX,c9C64BuC;E8C34BvC,oBAAoB;AjD25KtB;;AiDx5KE;E7BrCI,2CjB48BoD;EiB58BpD,mCjB48BoD;EiB58BpD,oEjB48BoD;E8Cr6BtD,sC9Cm6BmD;E8Cn6BnD,8B9Cm6BmD;AHw/IvD;;AoB77KI;E6BgCF;I7B/BI,gBAAgB;EpBi8KpB;AACF;;AiD/5KE;EACE,uB9Ci6BoC;E8Cj6BpC,e9Ci6BoC;AHigJxC;;AiD95KE;EACE,8B9C85B2C;E8C95B3C,sB9C85B2C;AHmgJ/C;;AiD75KA;EACE,oBAAa;EAAb,aAAa;EACb,6B/ByE8D;AlBu1KhE;;AiDl6KA;EAKI,8B/BsE4D;E+BrE5D,gBAAgB;AjDi6KpB;;AiDv6KA;;EAWI,oBAAc;EAAd,cAAc;AjDi6KlB;;AiD56KA;EAeI,gBAAgB;AjDi6KpB;;AiD75KA;EACE,oBAAa;EAAb,aAAa;EACb,sBAAmB;EAAnB,mBAAmB;EACnB,6B/BqD8D;AlB22KhE;;AiDn6KA;EAOI,cAAc;EACd,0B/BgD4D;E+B/C5D,WAAW;AjDg6Kf;;AiDz6KA;EAcI,0BAAsB;EAAtB,sBAAsB;EACtB,qBAAuB;EAAvB,uBAAuB;EACvB,YAAY;AjD+5KhB;;AiD/6KA;EAmBM,gBAAgB;AjDg6KtB;;AiDn7KA;EAuBM,aAAa;AjDg6KnB;;AiD15KA;EACE,kBAAkB;EAClB,oBAAa;EAAb,aAAa;EACb,0BAAsB;EAAtB,sBAAsB;EACtB,WAAW;EAGX,oBAAoB;EACpB,sB9C1Ga;E8C2Gb,4BAA4B;EAC5B,oC9ClGa;EOZX,qBP8O+B;E8C5HjC,UAAU;AjDy5KZ;;AiDr5KA;EACE,eAAe;EACf,MAAM;EACN,OAAO;EACP,a9CojBsC;E8CnjBtC,YAAY;EACZ,aAAa;EACb,sB9CjHa;AHygLf;;AiD/5KA;EAUW,UAAU;AjDy5KrB;;AiDn6KA;EAWW,Y9C4zB2B;AHgmJtC;;AiDv5KA;EACE,oBAAa;EAAb,aAAa;EACb,qBAAuB;EAAvB,uBAAuB;EACvB,sBAA8B;EAA9B,8BAA8B;EAC9B,kB9CyzBsC;E8CxzBtC,gC9CtIgB;EOId,0CQsH4D;ERrH5D,2CQqH4D;AlBw6KhE;;AiDj6KA;EASI,kB9CozBoC;E8ClzBpC,8BAA6F;AjD25KjG;;AiDt5KA;EACE,gBAAgB;EAChB,gB9C2I+B;AH8wKjC;;AiDp5KA;EACE,kBAAkB;EAGlB,kBAAc;EAAd,cAAc;EACd,a9CuwBsC;AH8oJxC;;AiDj5KA;EACE,oBAAa;EAAb,aAAa;EACb,mBAAe;EAAf,eAAe;EACf,sBAAmB;EAAnB,mBAAmB;EACnB,kBAAyB;EAAzB,yBAAyB;EACzB,gBAAgE;EAChE,6B9CvKgB;EOkBd,8CQwG4D;ERvG5D,6CQuG4D;AlBm8KhE;;AiD55KA;EAcI,eAAwC;AjDk5K5C;;AiD74KA;EACE,kBAAkB;EAClB,YAAY;EACZ,WAAW;EACX,YAAY;EACZ,gBAAgB;AjDg5KlB;;AcvhLI;EmCzBJ;IAuKI,gB9CmwBqC;I8ClwBrC,oBAAyC;EjD84K3C;EiDhiLF;IAsJI,+B/B3E4D;ElBw9K9D;EiDniLF;IAyJM,gC/B9E0D;ElB29K9D;EiDnhLF;IA2II,+B/BnF4D;ElB89K9D;EiDthLF;IA8IM,4B/BtF0D;ElBi+K9D;EiDn4KA;IAAY,gB9C4uB2B;EH0pJvC;AACF;;Ac7iLI;EmC0KF;;IAEE,gB9CouBqC;EHmqJvC;AACF;;AcpjLI;EmCiLF;IAAY,iB9C8tB4B;EH0qJxC;AACF;;AkDrnLA;EACE,kBAAkB;EAClB,a/CmrBsC;E+ClrBtC,cAAc;EACd,S/Cy1BmC;EgD71BnC,kMhDuRiN;EgDrRjN,kBAAkB;EAClB,gBhD+R+B;EgD9R/B,gBhDmS+B;EgDlS/B,gBAAgB;EAChB,iBAAiB;EACjB,qBAAqB;EACrB,iBAAiB;EACjB,oBAAoB;EACpB,sBAAsB;EACtB,kBAAkB;EAClB,oBAAoB;EACpB,mBAAmB;EACnB,gBAAgB;E/CgHZ,mBAtCY;E8C9EhB,qBAAqB;EACrB,UAAU;AlDkoLZ;;AkD7oLA;EAaW,Y/C60B2B;AHuzJtC;;AkDjpLA;EAgBI,kBAAkB;EAClB,cAAc;EACd,a/C60BqC;E+C50BrC,c/C60BqC;AHwzJzC;;AkDxpLA;EAsBM,kBAAkB;EAClB,WAAW;EACX,yBAAyB;EACzB,mBAAmB;AlDsoLzB;;AkDjoLA;EACE,iBAAgC;AlDooLlC;;AkDroLA;EAII,SAAS;AlDqoLb;;AkDzoLA;EAOM,MAAM;EACN,6BAAgE;EAChE,sB/CvBS;AH6pLf;;AkDjoLA;EACE,iB/CmzBuC;AHi1JzC;;AkDroLA;EAII,OAAO;EACP,a/C+yBqC;E+C9yBrC,c/C6yBqC;AHw1JzC;;AkD3oLA;EASM,QAAQ;EACR,oCAA2F;EAC3F,wB/CvCS;AH6qLf;;AkDjoLA;EACE,iBAAgC;AlDooLlC;;AkDroLA;EAII,MAAM;AlDqoLV;;AkDzoLA;EAOM,SAAS;EACT,6B/C4xBmC;E+C3xBnC,yB/CrDS;AH2rLf;;AkDjoLA;EACE,iB/CqxBuC;AH+2JzC;;AkDroLA;EAII,QAAQ;EACR,a/CixBqC;E+ChxBrC,c/C+wBqC;AHs3JzC;;AkD3oLA;EASM,OAAO;EACP,oC/C4wBmC;E+C3wBnC,uB/CrES;AH2sLf;;AkDjnLA;EACE,gB/C2uBuC;E+C1uBvC,uB/CgvBuC;E+C/uBvC,W/CvGa;E+CwGb,kBAAkB;EAClB,sB/C/Fa;EOZX,sBP6OgC;AHm/KpC;;AoDruLA;EACE,kBAAkB;EAClB,MAAM;EACN,OAAO;EACP,ajDirBsC;EiDhrBtC,cAAc;EACd,gBjD22BuC;EgDh3BvC,kMhDuRiN;EgDrRjN,kBAAkB;EAClB,gBhD+R+B;EgD9R/B,gBhDmS+B;EgDlS/B,gBAAgB;EAChB,iBAAiB;EACjB,qBAAqB;EACrB,iBAAiB;EACjB,oBAAoB;EACpB,sBAAsB;EACtB,kBAAkB;EAClB,oBAAoB;EACpB,mBAAmB;EACnB,gBAAgB;E/CgHZ,mBAtCY;EgD7EhB,qBAAqB;EACrB,sBjDNa;EiDOb,4BAA4B;EAC5B,oCjDEa;EOZX,qBP8O+B;AH+gLnC;;AoDlwLA;EAoBI,kBAAkB;EAClB,cAAc;EACd,WjD22BoC;EiD12BpC,cjD22BqC;EiD12BrC,gBjD2N+B;AHuhLnC;;AoD1wLA;EA4BM,kBAAkB;EAClB,cAAc;EACd,WAAW;EACX,yBAAyB;EACzB,mBAAmB;ApDkvLzB;;AoD7uLA;EACE,qBjD41BuC;AHo5JzC;;AoDjvLA;EAII,2BlC2F4D;AlBspLhE;;AoDrvLA;EAOM,SAAS;EACT,6BAAgE;EAChE,qCjDu1BiE;AH25JvE;;AoD3vLA;EAaM,WjD6L2B;EiD5L3B,6BAAgE;EAChE,sBjD7CS;AH+xLf;;AoD7uLA;EACE,mBjDw0BuC;AHw6JzC;;AoDjvLA;EAII,yBlCuE4D;EkCtE5D,ajDo0BqC;EiDn0BrC,YjDk0BoC;EiDj0BpC,gBAAgC;ApDivLpC;;AoDxvLA;EAUM,OAAO;EACP,oCAA2F;EAC3F,uCjDg0BiE;AHk7JvE;;AoD9vLA;EAgBM,SjDsK2B;EiDrK3B,oCAA2F;EAC3F,wBjDpES;AHszLf;;AoD7uLA;EACE,kBjDizBuC;AH+7JzC;;AoDjvLA;EAII,wBlCgD4D;AlBisLhE;;AoDrvLA;EAOM,MAAM;EACN,oCAA2F;EAC3F,wCjD4yBiE;AHs8JvE;;AoD3vLA;EAaM,QjDkJ2B;EiDjJ3B,oCAA2F;EAC3F,yBjDxFS;AH00Lf;;AoDjwLA;EAqBI,kBAAkB;EAClB,MAAM;EACN,SAAS;EACT,cAAc;EACd,WjDwxBoC;EiDvxBpC,oBAAsC;EACtC,WAAW;EACX,gCjD4wBuD;AHo+J3D;;AoD5uLA;EACE,oBjDixBuC;AH89JzC;;AoDhvLA;EAII,0BlCgB4D;EkCf5D,ajD6wBqC;EiD5wBrC,YjD2wBoC;EiD1wBpC,gBAAgC;ApDgvLpC;;AoDvvLA;EAUM,QAAQ;EACR,oCjDuwBmC;EiDtwBnC,sCjDywBiE;AHw+JvE;;AoD7vLA;EAgBM,UjD+G2B;EiD9G3B,oCjDiwBmC;EiDhwBnC,uBjD3HS;AH42Lf;;AoD3tLA;EACE,uBjDkuBwC;EiDjuBxC,gBAAgB;EhD3BZ,eAtCY;EgDoEhB,yBjD2tByD;EiD1tBzD,gCAAyE;E1ChJvE,0CQsH4D;ERrH5D,2CQqH4D;AlByvLhE;;AoDruLA;EAUI,aAAa;ApD+tLjB;;AoD3tLA;EACE,uBjDotBwC;EiDntBxC,cjDxJgB;AHs3LlB;;AqDz3LA;EACE,kBAAkB;ArD43LpB;;AqDz3LA;EACE,uBAAmB;EAAnB,mBAAmB;ArD43LrB;;AqDz3LA;EACE,kBAAkB;EAClB,WAAW;EACX,gBAAgB;ArD43LlB;;AsDn5LE;EACE,cAAc;EACd,WAAW;EACX,WAAW;AtDs5Lf;;AqD93LA;EACE,kBAAkB;EAClB,aAAa;EACb,WAAW;EACX,WAAW;EACX,mBAAmB;EACnB,mCAA2B;EAA3B,2BAA2B;EjC5BvB,8CjBikCkF;EiBjkClF,sCjBikCkF;EiBjkClF,0EjBikCkF;AH61JxF;;AoBz5LI;EiCiBJ;IjChBM,gBAAgB;EpB65LpB;AACF;;AqDp4LA;;;EAGE,cAAc;ArDu4LhB;;AqDp4LA;;EAEE,mCAA2B;EAA3B,2BAA2B;ArDu4L7B;;AqDp4LA;;EAEE,oCAA4B;EAA5B,4BAA4B;ArDu4L9B;;AqD/3LA;EAEI,UAAU;EACV,4BAA4B;EAC5B,uBAAe;EAAf,eAAe;ArDi4LnB;;AqDr4LA;;;EAUI,UAAU;EACV,UAAU;ArDi4Ld;;AqD54LA;;EAgBI,UAAU;EACV,UAAU;EjCtER,2BjBgkCkC;AHw4JxC;;AoBn8LI;EiCgDJ;;I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;AvEy5OhD;;AuEx6OQ;EAAgC,yBAA4B;AvE46OpE;;AuE36OQ;;EAEE,6BAAoC;AvE86O9C;;AuE56OQ;;EAEE,+BAAwC;AvE+6OlD;;AuE76OQ;;EAEE,gCAA0C;AvEg7OpD;;AuE96OQ;;EAEE,8BAAsC;AvEi7OhD;;AuEh8OQ;EAAgC,uBAA4B;AvEo8OpE;;AuEn8OQ;;EAEE,2BAAoC;AvEs8O9C;;AuEp8OQ;;EAEE,6BAAwC;AvEu8OlD;;AuEr8OQ;;EAEE,8BAA0C;AvEw8OpD;;AuEt8OQ;;EAEE,4BAAsC;AvEy8OhD;;AuEx9OQ;EAAgC,qBAA4B;AvE49OpE;;AuE39OQ;;EAEE,yBAAoC;AvE89O9C;;AuE59OQ;;EAEE,2BAAwC;AvE+9OlD;;AuE79OQ;;EAEE,4BAA0C;AvEg+OpD;;AuE99OQ;;EAEE,0BAAsC;AvEi+OhD;;AuEh/OQ;EAAgC,2BAA4B;AvEo/OpE;;AuEn/OQ;;EAEE,+BAAoC;AvEs/O9C;;AuEp/OQ;;EAEE,iCAAwC;AvEu/OlD;;AuEr/OQ;;EAEE,kCAA0C;AvEw/OpD;;AuEt/OQ;;EAEE,gCAAsC;AvEy/OhD;;AuExgPQ;EAAgC,0BAA4B;AvE4gPpE;;AuE3gPQ;;EAEE,8BAAoC;AvE8gP9C;;AuE5gPQ;;EAEE,gCAAwC;AvE+gPlD;;AuE7gPQ;;EAEE,iCAA0C;AvEghPpD;;AuE9gPQ;;EAEE,+BAAsC;AvEihPhD;;AuEhiPQ;EAAgC,wBAA4B;AvEoiPpE;;AuEniPQ;;EAEE,4BAAoC;AvEsiP9C;;AuEpiPQ;;EAEE,8BAAwC;AvEuiPlD;;AuEriPQ;;EAEE,+BAA0C;AvEwiPpD;;AuEtiPQ;;EAEE,6BAAsC;AvEyiPhD;;AuExjPQ;EAAgC,0BAA4B;AvE4jPpE;;AuE3jPQ;;EAEE,8BAAoC;AvE8jP9C;;AuE5jPQ;;EAEE,gCAAwC;AvE+jPlD;;AuE7jPQ;;EAEE,iCAA0C;AvEgkPpD;;AuE9jPQ;;EAEE,+BAAsC;AvEikPhD;;AuEhlPQ;EAAgC,wBAA4B;AvEolPpE;;AuEnlPQ;;EAEE,4BAAoC;AvEslP9C;;AuEplPQ;;EAEE,8BAAwC;AvEulPlD;;AuErlPQ;;EAEE,+BAA0C;AvEwlPpD;;AuEtlPQ;;EAEE,6BAAsC;AvEylPhD;;AuEjlPQ;EAAwB,2BAA2B;AvEqlP3D;;AuEplPQ;;EAEE,+BAA+B;AvEulPzC;;AuErlPQ;;EAEE,iCAAiC;AvEwlP3C;;AuEtlPQ;;EAEE,kCAAkC;AvEylP5C;;AuEvlPQ;;EAEE,gCAAgC;AvE0lP1C;;AuEzmPQ;EAAwB,0BAA2B;AvE6mP3D;;AuE5mPQ;;EAEE,8BAA+B;AvE+mPzC;;AuE7mPQ;;EAEE,gCAAiC;AvEgnP3C;;AuE9mPQ;;EAEE,iCAAkC;AvEinP5C;;AuE/mPQ;;EAEE,+BAAgC;AvEknP1C;;AuEjoPQ;EAAwB,wBAA2B;AvEqoP3D;;AuEpoPQ;;EAEE,4BAA+B;AvEuoPzC;;AuEroPQ;;EAEE,8BAAiC;AvEwoP3C;;AuEtoPQ;;EAEE,+BAAkC;AvEyoP5C;;AuEvoPQ;;EAEE,6BAAgC;AvE0oP1C;;AuEzpPQ;EAAwB,0BAA2B;AvE6pP3D;;AuE5pPQ;;EAEE,8BAA+B;AvE+pPzC;;AuE7pPQ;;EAEE,gCAAiC;AvEgqP3C;;AuE9pPQ;;EAEE,iCAAkC;AvEiqP5C;;AuE/pPQ;;EAEE,+BAAgC;AvEkqP1C;;AuEjrPQ;EAAwB,wBAA2B;AvEqrP3D;;AuEprPQ;;EAEE,4BAA+B;AvEurPzC;;AuErrPQ;;EAEE,8BAAiC;AvEwrP3C;;AuEtrPQ;;EAEE,+BAAkC;AvEyrP5C;;AuEvrPQ;;EAEE,6BAAgC;AvE0rP1C;;AuEprPI;EAAmB,uBAAuB;AvEwrP9C;;AuEvrPI;;EAEE,2BAA2B;AvE0rPjC;;AuExrPI;;EAEE,6BAA6B;AvE2rPnC;;AuEzrPI;;EAEE,8BAA8B;AvE4rPpC;;AuE1rPI;;EAEE,4BAA4B;AvE6rPlC;;ActsPI;EyDlDI;IAAgC,oBAA4B;EvE6vPlE;EuE5vPM;;IAEE,wBAAoC;EvE8vP5C;EuE5vPM;;IAEE,0BAAwC;EvE8vPhD;EuE5vPM;;IAEE,2BAA0C;EvE8vPlD;EuE5vPM;;IAEE,yBAAsC;EvE8vP9C;EuE7wPM;IAAgC,0BAA4B;EvEgxPlE;EuE/wPM;;IAEE,8BAAoC;EvEixP5C;EuE/wPM;;IAEE,gCAAwC;EvEixPhD;EuE/wPM;;IAEE,iCAA0C;EvEixPlD;EuE/wPM;;IAEE,+BAAsC;EvEixP9C;EuEhyPM;IAAgC,yBAA4B;EvEmyPlE;EuElyPM;;IAEE,6BAAoC;EvEoyP5C;EuElyPM;;IAEE,+BAAwC;EvEoyPhD;EuElyPM;;IAEE,gCAA0C;EvEoyPlD;EuElyPM;;IAEE,8BAAsC;EvEoyP9C;EuEnzPM;IAAgC,uBAA4B;EvEszPlE;EuErzPM;;IAEE,2BAAoC;EvEuzP5C;EuErzPM;;IAEE,6BAAwC;EvEuzPhD;EuErzPM;;IAEE,8BAA0C;EvEuzPlD;EuErzPM;;IAEE,4BAAsC;EvEuzP9C;EuEt0PM;IAAgC,yBAA4B;EvEy0PlE;EuEx0PM;;IAEE,6BAAoC;EvE00P5C;EuEx0PM;;IAEE,+BAAwC;EvE00PhD;EuEx0PM;;IAEE,gCAA0C;EvE00PlD;EuEx0PM;;IAEE,8BAAsC;EvE00P9C;EuEz1PM;IAAgC,uBAA4B;EvE41PlE;EuE31PM;;IAEE,2BAAoC;EvE61P5C;EuE31PM;;IAEE,6BAAwC;EvE61PhD;EuE31PM;;IAEE,8BAA0C;EvE61PlD;EuE31PM;;IAEE,4BAAsC;EvE61P9C;EuE52PM;IAAgC,qBAA4B;EvE+2PlE;EuE92PM;;IAEE,yBAAoC;EvEg3P5C;EuE92PM;;IAEE,2BAAwC;EvEg3PhD;EuE92PM;;IAEE,4BAA0C;EvEg3PlD;EuE92PM;;IAEE,0BAAsC;EvEg3P9C;EuE/3PM;IAAgC,2BAA4B;EvEk4PlE;EuEj4PM;;IAEE,+BAAoC;EvEm4P5C;EuEj4PM;;IAEE,iCAAwC;EvEm4PhD;EuEj4PM;;IAEE,kCAA0C;EvEm4PlD;EuEj4PM;;IAEE,gCAAsC;EvEm4P9C;EuEl5PM;IAAgC,0BAA4B;EvEq5PlE;EuEp5PM;;IAEE,8BAAoC;EvEs5P5C;EuEp5PM;;IAEE,gCAAwC;EvEs5PhD;EuEp5PM;;IAEE,iCAA0C;EvEs5PlD;EuEp5PM;;IAEE,+BAAsC;EvEs5P9C;EuEr6PM;IAAgC,wBAA4B;EvEw6PlE;EuEv6PM;;IAEE,4BAAoC;EvEy6P5C;EuEv6PM;;IAEE,8BAAwC;EvEy6PhD;EuEv6PM;;IAEE,+BAA0C;EvEy6PlD;EuEv6PM;;IAEE,6BAAsC;EvEy6P9C;EuEx7PM;IAAgC,0BAA4B;EvE27PlE;EuE17PM;;IAEE,8BAAoC;EvE47P5C;EuE17PM;;IAEE,gCAAwC;EvE47PhD;EuE17PM;;IAEE,iCAA0C;EvE47PlD;EuE17PM;;IAEE,+BAAsC;EvE47P9C;EuE38PM;IAAgC,wBAA4B;EvE88PlE;EuE78PM;;IAEE,4BAAoC;EvE+8P5C;EuE78PM;;IAEE,8BAAwC;EvE+8PhD;EuE78PM;;IAEE,+BAA0C;EvE+8PlD;EuE78PM;;IAEE,6BAAsC;EvE+8P9C;EuEv8PM;IAAwB,2BAA2B;EvE08PzD;EuEz8PM;;IAEE,+BAA+B;EvE28PvC;EuEz8PM;;IAEE,iCAAiC;EvE28PzC;EuEz8PM;;IAEE,kCAAkC;EvE28P1C;EuEz8PM;;IAEE,gCAAgC;EvE28PxC;EuE19PM;IAAwB,0BAA2B;EvE69PzD;EuE59PM;;IAEE,8BAA+B;EvE89PvC;EuE59PM;;IAEE,gCAAiC;EvE89PzC;EuE59PM;;IAEE,iCAAkC;EvE89P1C;EuE59PM;;IAEE,+BAAgC;EvE89PxC;EuE7+PM;IAAwB,wBAA2B;EvEg/PzD;EuE/+PM;;IAEE,4BAA+B;EvEi/PvC;EuE/+PM;;IAEE,8BAAiC;EvEi/PzC;EuE/+PM;;IAEE,+BAAkC;EvEi/P1C;EuE/+PM;;IAEE,6BAAgC;EvEi/PxC;EuEhgQM;IAAwB,0BAA2B;EvEmgQzD;EuElgQM;;IAEE,8BAA+B;EvEogQvC;EuElgQM;;IAEE,gCAAiC;EvEogQzC;EuElgQM;;IAEE,iCAAkC;EvEogQ1C;EuElgQM;;IAEE,+BAAgC;EvEogQxC;EuEnhQM;IAAwB,wBAA2B;EvEshQzD;EuErhQM;;IAEE,4BAA+B;EvEuhQvC;EuErhQM;;IAEE,8BAAiC;EvEuhQzC;EuErhQM;;IAEE,+BAAkC;EvEuhQ1C;EuErhQM;;IAEE,6BAAgC;EvEuhQxC;EuEjhQE;IAAmB,uBAAuB;EvEohQ5C;EuEnhQE;;IAEE,2BAA2B;EvEqhQ/B;EuEnhQE;;IAEE,6BAA6B;EvEqhQjC;EuEnhQE;;IAEE,8BAA8B;EvEqhQlC;EuEnhQE;;IAEE,4BAA4B;EvEqhQhC;AACF;;Ac/hQI;EyDlDI;IAAgC,oBAA4B;EvEslQlE;EuErlQM;;IAEE,wBAAoC;EvEulQ5C;EuErlQM;;IAEE,0BAAwC;EvEulQhD;EuErlQM;;IAEE,2BAA0C;EvEulQlD;EuErlQM;;IAEE,yBAAsC;EvEulQ9C;EuEtmQM;IAAgC,0BAA4B;EvEymQlE;EuExmQM;;IAEE,8BAAoC;EvE0mQ5C;EuExmQM;;IAEE,gCAAwC;EvE0mQhD;EuExmQM;;IAEE,iCAA0C;EvE0mQlD;EuExmQM;;IAEE,+BAAsC;EvE0mQ9C;EuEznQM;IAAgC,yBAA4B;EvE4nQlE;EuE3nQM;;IAEE,6BAAoC;EvE6nQ5C;EuE3nQM;;IAEE,+BAAwC;EvE6nQhD;EuE3nQM;;IAEE,gCAA0C;EvE6nQlD;EuE3nQM;;IAEE,8BAAsC;EvE6nQ9C;EuE5oQM;IAAgC,uBAA4B;EvE+oQlE;EuE9oQM;;IAEE,2BAAoC;EvEgpQ5C;EuE9oQM;;IAEE,6BAAwC;EvEgpQhD;EuE9oQM;;IAEE,8BAA0C;EvEgpQlD;EuE9oQM;;IAEE,4BAAsC;EvEgpQ9C;EuE/pQM;IAAgC,yBAA4B;EvEkqQlE;EuEjqQM;;IAEE,6BAAoC;EvEmqQ5C;EuEjqQM;;IAEE,+BAAwC;EvEmqQhD;EuEjqQM;;IAEE,gCAA0C;EvEmqQlD;EuEjqQM;;IAEE,8BAAsC;EvEmqQ9C;EuElrQM;IAAgC,uBAA4B;EvEqrQlE;EuEprQM;;IAEE,2BAAoC;EvEsrQ5C;EuEprQM;;IAEE,6BAAwC;EvEsrQhD;EuEprQM;;IAEE,8BAA0C;EvEsrQlD;EuEprQM;;IAEE,4BAAsC;EvEsrQ9C;EuErsQM;IAAgC,qBAA4B;EvEwsQlE;EuEvsQM;;IAEE,yBAAoC;EvEysQ5C;EuEvsQM;;IAEE,2BAAwC;EvEysQhD;EuEvsQM;;IAEE,4BAA0C;EvEysQlD;EuEvsQM;;IAEE,0BAAsC;EvEysQ9C;EuExtQM;IAAgC,2BAA4B;EvE2tQlE;EuE1tQM;;IAEE,+BAAoC;EvE4tQ5C;EuE1tQM;;IAEE,iCAAwC;EvE4tQhD;EuE1tQM;;IAEE,kCAA0C;EvE4tQlD;EuE1tQM;;IAEE,gCAAsC;EvE4tQ9C;EuE3uQM;IAAgC,0BAA4B;EvE8uQlE;EuE7uQM;;IAEE,8BAAoC;EvE+uQ5C;EuE7uQM;;IAEE,gCAAwC;EvE+uQhD;EuE7uQM;;IAEE,iCAA0C;EvE+uQlD;EuE7uQM;;IAEE,+BAAsC;EvE+uQ9C;EuE9vQM;IAAgC,wBAA4B;EvEiwQlE;EuEhwQM;;IAEE,4BAAoC;EvEkwQ5C;EuEhwQM;;IAEE,8BAAwC;EvEkwQhD;EuEhwQM;;IAEE,+BAA0C;EvEkwQlD;EuEhwQM;;IAEE,6BAAsC;EvEkwQ9C;EuEjxQM;IAAgC,0BAA4B;EvEoxQlE;EuEnxQM;;IAEE,8BAAoC;EvEqxQ5C;EuEnxQM;;IAEE,gCAAwC;EvEqxQhD;EuEnxQM;;IAEE,iCAA0C;EvEqxQlD;EuEnxQM;;IAEE,+BAAsC;EvEqxQ9C;EuEpyQM;IAAgC,wBAA4B;EvEuyQlE;EuEtyQM;;IAEE,4BAAoC;EvEwyQ5C;EuEtyQM;;IAEE,8BAAwC;EvEwyQhD;EuEtyQM;;IAEE,+BAA0C;EvEwyQlD;EuEtyQM;;IAEE,6BAAsC;EvEwyQ9C;EuEhyQM;IAAwB,2BAA2B;EvEmyQzD;EuElyQM;;IAEE,+BAA+B;EvEoyQvC;EuElyQM;;IAEE,iCAAiC;EvEoyQzC;EuElyQM;;IAEE,kCAAkC;EvEoyQ1C;EuElyQM;;IAEE,gCAAgC;EvEoyQxC;EuEnzQM;IAAwB,0BAA2B;EvEszQzD;EuErzQM;;IAEE,8BAA+B;EvEuzQvC;EuErzQM;;IAEE,gCAAiC;EvEuzQzC;EuErzQM;;IAEE,iCAAkC;EvEuzQ1C;EuErzQM;;IAEE,+BAAgC;EvEuzQxC;EuEt0QM;IAAwB,wBAA2B;EvEy0QzD;EuEx0QM;;IAEE,4BAA+B;EvE00QvC;EuEx0QM;;IAEE,8BAAiC;EvE00QzC;EuEx0QM;;IAEE,+BAAkC;EvE00Q1C;EuEx0QM;;IAEE,6BAAgC;EvE00QxC;EuEz1QM;IAAwB,0BAA2B;EvE41QzD;EuE31QM;;IAEE,8BAA+B;EvE61QvC;EuE31QM;;IAEE,gCAAiC;EvE61QzC;EuE31QM;;IAEE,iCAAkC;EvE61Q1C;EuE31QM;;IAEE,+BAAgC;EvE61QxC;EuE52QM;IAAwB,wBAA2B;EvE+2QzD;EuE92QM;;IAEE,4BAA+B;EvEg3QvC;EuE92QM;;IAEE,8BAAiC;EvEg3QzC;EuE92QM;;IAEE,+BAAkC;EvEg3Q1C;EuE92QM;;IAEE,6BAAgC;EvEg3QxC;EuE12QE;IAAmB,uBAAuB;EvE62Q5C;EuE52QE;;IAEE,2BAA2B;EvE82Q/B;EuE52QE;;IAEE,6BAA6B;EvE82QjC;EuE52QE;;IAEE,8BAA8B;EvE82QlC;EuE52QE;;IAEE,4BAA4B;EvE82QhC;AACF;;Acx3QI;EyDlDI;IAAgC,oBAA4B;EvE+6QlE;EuE96QM;;IAEE,wBAAoC;EvEg7Q5C;EuE96QM;;IAEE,0BAAwC;EvEg7QhD;EuE96QM;;IAEE,2BAA0C;EvEg7QlD;EuE96QM;;IAEE,yBAAsC;EvEg7Q9C;EuE/7QM;IAAgC,0BAA4B;EvEk8QlE;EuEj8QM;;IAEE,8BAAoC;EvEm8Q5C;EuEj8QM;;IAEE,gCAAwC;EvEm8QhD;EuEj8QM;;IAEE,iCAA0C;EvEm8QlD;EuEj8QM;;IAEE,+BAAsC;EvEm8Q9C;EuEl9QM;IAAgC,yBAA4B;EvEq9QlE;EuEp9QM;;IAEE,6BAAoC;EvEs9Q5C;EuEp9QM;;IAEE,+BAAwC;EvEs9QhD;EuEp9QM;;IAEE,gCAA0C;EvEs9QlD;EuEp9QM;;IAEE,8BAAsC;EvEs9Q9C;EuEr+QM;IAAgC,uBAA4B;EvEw+QlE;EuEv+QM;;IAEE,2BAAoC;EvEy+Q5C;EuEv+QM;;IAEE,6BAAwC;EvEy+QhD;EuEv+QM;;IAEE,8BAA0C;EvEy+QlD;EuEv+QM;;IAEE,4BAAsC;EvEy+Q9C;EuEx/QM;IAAgC,yBAA4B;EvE2/QlE;EuE1/QM;;IAEE,6BAAoC;EvE4/Q5C;EuE1/QM;;IAEE,+BAAwC;EvE4/QhD;EuE1/QM;;IAEE,gCAA0C;EvE4/QlD;EuE1/QM;;IAEE,8BAAsC;EvE4/Q9C;EuE3gRM;IAAgC,uBAA4B;EvE8gRlE;EuE7gRM;;IAEE,2BAAoC;EvE+gR5C;EuE7gRM;;IAEE,6BAAwC;EvE+gRhD;EuE7gRM;;IAEE,8BAA0C;EvE+gRlD;EuE7gRM;;IAEE,4BAAsC;EvE+gR9C;EuE9hRM;IAAgC,qBAA4B;EvEiiRlE;EuEhiRM;;IAEE,yBAAoC;EvEkiR5C;EuEhiRM;;IAEE,2BAAwC;EvEkiRhD;EuEhiRM;;IAEE,4BAA0C;EvEkiRlD;EuEhiRM;;IAEE,0BAAsC;EvEkiR9C;EuEjjRM;IAAgC,2BAA4B;EvEojRlE;EuEnjRM;;IAEE,+BAAoC;EvEqjR5C;EuEnjRM;;IAEE,iCAAwC;EvEqjRhD;EuEnjRM;;IAEE,kCAA0C;EvEqjRlD;EuEnjRM;;IAEE,gCAAsC;EvEqjR9C;EuEpkRM;IAAgC,0BAA4B;EvEukRlE;EuEtkRM;;IAEE,8BAAoC;EvEwkR5C;EuEtkRM;;IAEE,gCAAwC;EvEwkRhD;EuEtkRM;;IAEE,iCAA0C;EvEwkRlD;EuEtkRM;;IAEE,+BAAsC;EvEwkR9C;EuEvlRM;IAAgC,wBAA4B;EvE0lRlE;EuEzlRM;;IAEE,4BAAoC;EvE2lR5C;EuEzlRM;;IAEE,8BAAwC;EvE2lRhD;EuEzlRM;;IAEE,+BAA0C;EvE2lRlD;EuEzlRM;;IAEE,6BAAsC;EvE2lR9C;EuE1mRM;IAAgC,0BAA4B;EvE6mRlE;EuE5mRM;;IAEE,8BAAoC;EvE8mR5C;EuE5mRM;;IAEE,gCAAwC;EvE8mRhD;EuE5mRM;;IAEE,iCAA0C;EvE8mRlD;EuE5mRM;;IAEE,+BAAsC;EvE8mR9C;EuE7nRM;IAAgC,wBAA4B;EvEgoRlE;EuE/nRM;;IAEE,4BAAoC;EvEioR5C;EuE/nRM;;IAEE,8BAAwC;EvEioRhD;EuE/nRM;;IAEE,+BAA0C;EvEioRlD;EuE/nRM;;IAEE,6BAAsC;EvEioR9C;EuEznRM;IAAwB,2BAA2B;EvE4nRzD;EuE3nRM;;IAEE,+BAA+B;EvE6nRvC;EuE3nRM;;IAEE,iCAAiC;EvE6nRzC;EuE3nRM;;IAEE,kCAAkC;EvE6nR1C;EuE3nRM;;IAEE,gCAAgC;EvE6nRxC;EuE5oRM;IAAwB,0BAA2B;EvE+oRzD;EuE9oRM;;IAEE,8BAA+B;EvEgpRvC;EuE9oRM;;IAEE,gCAAiC;EvEgpRzC;EuE9oRM;;IAEE,iCAAkC;EvEgpR1C;EuE9oRM;;IAEE,+BAAgC;EvEgpRxC;EuE/pRM;IAAwB,wBAA2B;EvEkqRzD;EuEjqRM;;IAEE,4BAA+B;EvEmqRvC;EuEjqRM;;IAEE,8BAAiC;EvEmqRzC;EuEjqRM;;IAEE,+BAAkC;EvEmqR1C;EuEjqRM;;IAEE,6BAAgC;EvEmqRxC;EuElrRM;IAAwB,0BAA2B;EvEqrRzD;EuEprRM;;IAEE,8BAA+B;EvEsrRvC;EuEprRM;;IAEE,gCAAiC;EvEsrRzC;EuEprRM;;IAEE,iCAAkC;EvEsrR1C;EuEprRM;;IAEE,+BAAgC;EvEsrRxC;EuErsRM;IAAwB,wBAA2B;EvEwsRzD;EuEvsRM;;IAEE,4BAA+B;EvEysRvC;EuEvsRM;;IAEE,8BAAiC;EvEysRzC;EuEvsRM;;IAEE,+BAAkC;EvEysR1C;EuEvsRM;;IAEE,6BAAgC;EvEysRxC;EuEnsRE;IAAmB,uBAAuB;EvEssR5C;EuErsRE;;IAEE,2BAA2B;EvEusR/B;EuErsRE;;IAEE,6BAA6B;EvEusRjC;EuErsRE;;IAEE,8BAA8B;EvEusRlC;EuErsRE;;IAEE,4BAA4B;EvEusRhC;AACF;;AcjtRI;EyDlDI;IAAgC,oBAA4B;EvEwwRlE;EuEvwRM;;IAEE,wBAAoC;EvEywR5C;EuEvwRM;;IAEE,0BAAwC;EvEywRhD;EuEvwRM;;IAEE,2BAA0C;EvEywRlD;EuEvwRM;;IAEE,yBAAsC;EvEywR9C;EuExxRM;IAAgC,0BAA4B;EvE2xRlE;EuE1xRM;;IAEE,8BAAoC;EvE4xR5C;EuE1xRM;;IAEE,gCAAwC;EvE4xRhD;EuE1xRM;;IAEE,iCAA0C;EvE4xRlD;EuE1xRM;;IAEE,+BAAsC;EvE4xR9C;EuE3yRM;IAAgC,yBAA4B;EvE8yRlE;EuE7yRM;;IAEE,6BAAoC;EvE+yR5C;EuE7yRM;;IAEE,+BAAwC;EvE+yRhD;EuE7yRM;;IAEE,gCAA0C;EvE+yRlD;EuE7yRM;;IAEE,8BAAsC;EvE+yR9C;EuE9zRM;IAAgC,uBAA4B;EvEi0RlE;EuEh0RM;;IAEE,2BAAoC;EvEk0R5C;EuEh0RM;;IAEE,6BAAwC;EvEk0RhD;EuEh0RM;;IAEE,8BAA0C;EvEk0RlD;EuEh0RM;;IAEE,4BAAsC;EvEk0R9C;EuEj1RM;IAAgC,yBAA4B;EvEo1RlE;EuEn1RM;;IAEE,6BAAoC;EvEq1R5C;EuEn1RM;;IAEE,+BAAwC;EvEq1RhD;EuEn1RM;;IAEE,gCAA0C;EvEq1RlD;EuEn1RM;;IAEE,8BAAsC;EvEq1R9C;EuEp2RM;IAAgC,uBAA4B;EvEu2RlE;EuEt2RM;;IAEE,2BAAoC;EvEw2R5C;EuEt2RM;;IAEE,6BAAwC;EvEw2RhD;EuEt2RM;;IAEE,8BAA0C;EvEw2RlD;EuEt2RM;;IAEE,4BAAsC;EvEw2R9C;EuEv3RM;IAAgC,qBAA4B;EvE03RlE;EuEz3RM;;IAEE,yBAAoC;EvE23R5C;EuEz3RM;;IAEE,2BAAwC;EvE23RhD;EuEz3RM;;IAEE,4BAA0C;EvE23RlD;EuEz3RM;;IAEE,0BAAsC;EvE23R9C;EuE14RM;IAAgC,2BAA4B;EvE64RlE;EuE54RM;;IAEE,+BAAoC;EvE84R5C;EuE54RM;;IAEE,iCAAwC;EvE84RhD;EuE54RM;;IAEE,kCAA0C;EvE84RlD;EuE54RM;;IAEE,gCAAsC;EvE84R9C;EuE75RM;IAAgC,0BAA4B;EvEg6RlE;EuE/5RM;;IAEE,8BAAoC;EvEi6R5C;EuE/5RM;;IAEE,gCAAwC;EvEi6RhD;EuE/5RM;;IAEE,iCAA0C;EvEi6RlD;EuE/5RM;;IAEE,+BAAsC;EvEi6R9C;EuEh7RM;IAAgC,wBAA4B;EvEm7RlE;EuEl7RM;;IAEE,4BAAoC;EvEo7R5C;EuEl7RM;;IAEE,8BAAwC;EvEo7RhD;EuEl7RM;;IAEE,+BAA0C;EvEo7RlD;EuEl7RM;;IAEE,6BAAsC;EvEo7R9C;EuEn8RM;IAAgC,0BAA4B;EvEs8RlE;EuEr8RM;;IAEE,8BAAoC;EvEu8R5C;EuEr8RM;;IAEE,gCAAwC;EvEu8RhD;EuEr8RM;;IAEE,iCAA0C;EvEu8RlD;EuEr8RM;;IAEE,+BAAsC;EvEu8R9C;EuEt9RM;IAAgC,wBAA4B;EvEy9RlE;EuEx9RM;;IAEE,4BAAoC;EvE09R5C;EuEx9RM;;IAEE,8BAAwC;EvE09RhD;EuEx9RM;;IAEE,+BAA0C;EvE09RlD;EuEx9RM;;IAEE,6BAAsC;EvE09R9C;EuEl9RM;IAAwB,2BAA2B;EvEq9RzD;EuEp9RM;;IAEE,+BAA+B;EvEs9RvC;EuEp9RM;;IAEE,iCAAiC;EvEs9RzC;EuEp9RM;;IAEE,kCAAkC;EvEs9R1C;EuEp9RM;;IAEE,gCAAgC;EvEs9RxC;EuEr+RM;IAAwB,0BAA2B;EvEw+RzD;EuEv+RM;;IAEE,8BAA+B;EvEy+RvC;EuEv+RM;;IAEE,gCAAiC;EvEy+RzC;EuEv+RM;;IAEE,iCAAkC;EvEy+R1C;EuEv+RM;;IAEE,+BAAgC;EvEy+RxC;EuEx/RM;IAAwB,wBAA2B;EvE2/RzD;EuE1/RM;;IAEE,4BAA+B;EvE4/RvC;EuE1/RM;;IAEE,8BAAiC;EvE4/RzC;EuE1/RM;;IAEE,+BAAkC;EvE4/R1C;EuE1/RM;;IAEE,6BAAgC;EvE4/RxC;EuE3gSM;IAAwB,0BAA2B;EvE8gSzD;EuE7gSM;;IAEE,8BAA+B;EvE+gSvC;EuE7gSM;;IAEE,gCAAiC;EvE+gSzC;EuE7gSM;;IAEE,iCAAkC;EvE+gS1C;EuE7gSM;;IAEE,+BAAgC;EvE+gSxC;EuE9hSM;IAAwB,wBAA2B;EvEiiSzD;EuEhiSM;;IAEE,4BAA+B;EvEkiSvC;EuEhiSM;;IAEE,8BAAiC;EvEkiSzC;EuEhiSM;;IAEE,+BAAkC;EvEkiS1C;EuEhiSM;;IAEE,6BAAgC;EvEkiSxC;EuE5hSE;IAAmB,uBAAuB;EvE+hS5C;EuE9hSE;;IAEE,2BAA2B;EvEgiS/B;EuE9hSE;;IAEE,6BAA6B;EvEgiSjC;EuE9hSE;;IAEE,8BAA8B;EvEgiSlC;EuE9hSE;;IAEE,4BAA4B;EvEgiShC;AACF;;AwEhmSA;EAAkB,4GAA8C;AxEomShE;;AwEhmSA;EAAiB,8BAA8B;AxEomS/C;;AwEnmSA;EAAiB,8BAA8B;AxEumS/C;;AwEtmSA;EAAiB,8BAA8B;AxE0mS/C;;AwEzmSA;ECTE,gBAAgB;EAChB,uBAAuB;EACvB,mBAAmB;AzEsnSrB;;AwEvmSI;EAAwB,2BAA2B;AxE2mSvD;;AwE1mSI;EAAwB,4BAA4B;AxE8mSxD;;AwE7mSI;EAAwB,6BAA6B;AxEinSzD;;Ac5kSI;E0DvCA;IAAwB,2BAA2B;ExEwnSrD;EwEvnSE;IAAwB,4BAA4B;ExE0nStD;EwEznSE;IAAwB,6BAA6B;ExE4nSvD;AACF;;AcxlSI;E0DvCA;IAAwB,2BAA2B;ExEooSrD;EwEnoSE;IAAwB,4BAA4B;ExEsoStD;EwEroSE;IAAwB,6BAA6B;ExEwoSvD;AACF;;AcpmSI;E0DvCA;IAAwB,2BAA2B;ExEgpSrD;EwE/oSE;IAAwB,4BAA4B;ExEkpStD;EwEjpSE;IAAwB,6BAA6B;ExEopSvD;AACF;;AchnSI;E0DvCA;IAAwB,2BAA2B;ExE4pSrD;EwE3pSE;IAAwB,4BAA4B;ExE8pStD;EwE7pSE;IAAwB,6BAA6B;ExEgqSvD;AACF;;AwE3pSA;EAAmB,oCAAoC;AxE+pSvD;;AwE9pSA;EAAmB,oCAAoC;AxEkqSvD;;AwEjqSA;EAAmB,qCAAqC;AxEqqSxD;;AwEjqSA;EAAuB,2BAA0C;AxEqqSjE;;AwEpqSA;EAAuB,+BAA4C;AxEwqSnE;;AwEvqSA;EAAuB,2BAA2C;AxE2qSlE;;AwE1qSA;EAAuB,2BAAyC;AxE8qShE;;AwE7qSA;EAAuB,8BAA2C;AxEirSlE;;AwEhrSA;EAAuB,6BAA6B;AxEorSpD;;AwEhrSA;EAAc,sBAAwB;AxEorStC;;A0E3tSE;EACE,yBAAwB;A1E8tS5B;;AKptSE;EqELM,yBAA0E;A1E6tSlF;;A0EnuSE;EACE,yBAAwB;A1EsuS5B;;AK5tSE;EqELM,yBAA0E;A1EquSlF;;A0E3uSE;EACE,yBAAwB;A1E8uS5B;;AKpuSE;EqELM,yBAA0E;A1E6uSlF;;A0EnvSE;EACE,yBAAwB;A1EsvS5B;;AK5uSE;EqELM,yBAA0E;A1EqvSlF;;A0E3vSE;EACE,yBAAwB;A1E8vS5B;;AKpvSE;EqELM,yBAA0E;A1E6vSlF;;A0EnwSE;EACE,yBAAwB;A1EswS5B;;AK5vSE;EqELM,yBAA0E;A1EqwSlF;;A0E3wSE;EACE,yBAAwB;A1E8wS5B;;AKpwSE;EqELM,yBAA0E;A1E6wSlF;;A0EnxSE;EACE,yBAAwB;A1EsxS5B;;AK5wSE;EqELM,yBAA0E;A1EqxSlF;;AwE9uSA;EAAa,yBAA6B;AxEkvS1C;;AwEjvSA;EAAc,yBAA6B;AxEqvS3C;;AwEnvSA;EAAiB,oCAAkC;AxEuvSnD;;AwEtvSA;EAAiB,0CAAkC;AxE0vSnD;;AwEtvSA;EGvDE,WAAW;EACX,kBAAkB;EAClB,iBAAiB;EACjB,6BAA6B;EAC7B,SAAS;A3EizSX;;AwE1vSA;EAAwB,gCAAgC;AxE8vSxD;;AwE5vSA;EACE,iCAAiC;EACjC,oCAAoC;AxE+vStC;;AwE1vSA;EAAc,yBAAyB;AxE8vSvC;;A4E/zSA;EACE,8BAA8B;A5Ek0ShC;;A4E/zSA;EACE,6BAA6B;A5Ek0S/B;;A6El0SE;E3EOF;;;I2EDM,4BAA4B;IAE5B,2BAA2B;E7Ek0S/B;E6E/zSE;IAEI,0BAA0B;E7Eg0ShC;E6EvzSE;IACE,6BAA6B;E7EyzSjC;EE3nSF;I2E/KM,gCAAgC;E7E6ySpC;E6E3ySE;;IAEE,yB1EzCY;I0E0CZ,wBAAwB;E7E6yS5B;E6ErySE;IACE,2BAA2B;E7EuyS/B;E6EpySE;;IAEE,wBAAwB;E7EsyS5B;E6EnySE;;;IAGE,UAAU;IACV,SAAS;E7EqySb;E6ElySE;;IAEE,uBAAuB;E7EoyS3B;E6E5xSE;IACE,Q1E4hCgC;EHkwQpC;EE10SF;I2E+CM,2BAA2C;E7E8xS/C;EYp3SA;IiEyFI,2BAA2C;E7E8xS/C;EiC52SF;I4CmFM,aAAa;E7E4xSjB;EsC33SF;IuCkGM,sB1EtFS;EHk3Sb;EgB/3SF;I6DuGM,oCAAoC;E7E2xSxC;E6E5xSE;;IAKI,iCAAmC;E7E2xSzC;EgB91SF;;I6D0EQ,oCAAsC;E7EwxS5C;EgB7wSF;I6DNM,cAAc;E7EsxSlB;EiB54SA;;;;I4D4HM,qB1EvHU;EH64ShB;EgBxySF;I6DuBM,cAAc;IACd,qB1E7HY;EHi5ShB;AACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"code\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"input-group\";\n@import \"custom-forms\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"jumbotron\";\n@import \"alert\";\n@import \"progress\";\n@import \"media\";\n@import \"list-group\";\n@import \"close\";\n@import \"toasts\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"spinners\";\n@import \"utilities\";\n@import \"print\";\n","/*!\n * Bootstrap v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-weight: 500;\n line-height: 1.2;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014\\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-wrap: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container, .container-sm {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container, .container-sm, .container-md {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container, .container-sm, .container-md, .container-lg {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container, .container-sm, .container-md, .container-lg, .container-xl {\n max-width: 1140px;\n }\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.row-cols-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n}\n\n.col-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n order: -1;\n}\n\n.order-last {\n order: 13;\n}\n\n.order-0 {\n order: 0;\n}\n\n.order-1 {\n order: 1;\n}\n\n.order-2 {\n order: 2;\n}\n\n.order-3 {\n order: 3;\n}\n\n.order-4 {\n order: 4;\n}\n\n.order-5 {\n order: 5;\n}\n\n.order-6 {\n order: 6;\n}\n\n.order-7 {\n order: 7;\n}\n\n.order-8 {\n order: 8;\n}\n\n.order-9 {\n order: 9;\n}\n\n.order-10 {\n order: 10;\n}\n\n.order-11 {\n order: 11;\n}\n\n.order-12 {\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-sm-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-sm-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-sm-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-sm-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-sm-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-sm-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-sm-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n order: -1;\n }\n .order-sm-last {\n order: 13;\n }\n .order-sm-0 {\n order: 0;\n }\n .order-sm-1 {\n order: 1;\n }\n .order-sm-2 {\n order: 2;\n }\n .order-sm-3 {\n order: 3;\n }\n .order-sm-4 {\n order: 4;\n }\n .order-sm-5 {\n order: 5;\n }\n .order-sm-6 {\n order: 6;\n }\n .order-sm-7 {\n order: 7;\n }\n .order-sm-8 {\n order: 8;\n }\n .order-sm-9 {\n order: 9;\n }\n .order-sm-10 {\n order: 10;\n }\n .order-sm-11 {\n order: 11;\n }\n .order-sm-12 {\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-md-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-md-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-md-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-md-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-md-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-md-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-md-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n order: -1;\n }\n .order-md-last {\n order: 13;\n }\n .order-md-0 {\n order: 0;\n }\n .order-md-1 {\n order: 1;\n }\n .order-md-2 {\n order: 2;\n }\n .order-md-3 {\n order: 3;\n }\n .order-md-4 {\n order: 4;\n }\n .order-md-5 {\n order: 5;\n }\n .order-md-6 {\n order: 6;\n }\n .order-md-7 {\n order: 7;\n }\n .order-md-8 {\n order: 8;\n }\n .order-md-9 {\n order: 9;\n }\n .order-md-10 {\n order: 10;\n }\n .order-md-11 {\n order: 11;\n }\n .order-md-12 {\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-lg-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-lg-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-lg-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-lg-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-lg-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-lg-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-lg-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n order: -1;\n }\n .order-lg-last {\n order: 13;\n }\n .order-lg-0 {\n order: 0;\n }\n .order-lg-1 {\n order: 1;\n }\n .order-lg-2 {\n order: 2;\n }\n .order-lg-3 {\n order: 3;\n }\n .order-lg-4 {\n order: 4;\n }\n .order-lg-5 {\n order: 5;\n }\n .order-lg-6 {\n order: 6;\n }\n .order-lg-7 {\n order: 7;\n }\n .order-lg-8 {\n order: 8;\n }\n .order-lg-9 {\n order: 9;\n }\n .order-lg-10 {\n order: 10;\n }\n .order-lg-11 {\n order: 11;\n }\n .order-lg-12 {\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .row-cols-xl-1 > * {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .row-cols-xl-2 > * {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .row-cols-xl-3 > * {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .row-cols-xl-4 > * {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .row-cols-xl-5 > * {\n flex: 0 0 20%;\n max-width: 20%;\n }\n .row-cols-xl-6 > * {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-xl-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n order: -1;\n }\n .order-xl-last {\n order: 13;\n }\n .order-xl-0 {\n order: 0;\n }\n .order-xl-1 {\n order: 1;\n }\n .order-xl-2 {\n order: 2;\n }\n .order-xl-3 {\n order: 3;\n }\n .order-xl-4 {\n order: 4;\n }\n .order-xl-5 {\n order: 5;\n }\n .order-xl-6 {\n order: 6;\n }\n .order-xl-7 {\n order: 7;\n }\n .order-xl-8 {\n order: 8;\n }\n .order-xl-9 {\n order: 9;\n }\n .order-xl-10 {\n order: 10;\n }\n .order-xl-11 {\n order: 11;\n }\n .order-xl-12 {\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n margin-bottom: 1rem;\n color: #212529;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n color: #212529;\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-primary th,\n.table-primary td,\n.table-primary thead th,\n.table-primary tbody + tbody {\n border-color: #7abaff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-secondary th,\n.table-secondary td,\n.table-secondary thead th,\n.table-secondary tbody + tbody {\n border-color: #b3b7bb;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-success th,\n.table-success td,\n.table-success thead th,\n.table-success tbody + tbody {\n border-color: #8fd19e;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-info th,\n.table-info td,\n.table-info thead th,\n.table-info tbody + tbody {\n border-color: #86cfda;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-warning th,\n.table-warning td,\n.table-warning thead th,\n.table-warning tbody + tbody {\n border-color: #ffdf7e;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-danger th,\n.table-danger td,\n.table-danger thead th,\n.table-danger tbody + tbody {\n border-color: #ed969e;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-light th,\n.table-light td,\n.table-light thead th,\n.table-light tbody + tbody {\n border-color: #fbfcfc;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th,\n.table-dark tbody + tbody {\n border-color: #95999c;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #343a40;\n border-color: #454d55;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #454d55;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n color: #fff;\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:-moz-focusring {\n color: transparent;\n text-shadow: 0 0 0 #495057;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding: 0.375rem 0;\n margin-bottom: 0;\n font-size: 1rem;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm {\n height: calc(1.5em + 0.5rem + 2px);\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.form-control-lg {\n height: calc(1.5em + 1rem + 2px);\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control[size], select.form-control[multiple] {\n height: auto;\n}\n\ntextarea.form-control {\n height: auto;\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input[disabled] ~ .form-check-label,\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: inline-flex;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: .1rem;\n font-size: 0.875rem;\n line-height: 1.5;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n border-color: #28a745;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:valid, .custom-select.is-valid {\n border-color: #28a745;\n padding-right: calc(0.75em + 2.3125rem);\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px, url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n border-color: #28a745;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n border-color: #34ce57;\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: .1rem;\n font-size: 0.875rem;\n line-height: 1.5;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n border-color: #dc3545;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:invalid, .custom-select.is-invalid {\n border-color: #dc3545;\n padding-right: calc(0.75em + 2.3125rem);\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px, url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n border-color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n border-color: #e4606d;\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: flex;\n flex: 0 0 auto;\n flex-flow: row wrap;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n flex-shrink: 0;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n align-items: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n color: #212529;\n text-align: center;\n vertical-align: middle;\n cursor: pointer;\n user-select: none;\n background-color: transparent;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover {\n color: #212529;\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n text-decoration: none;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle {\n white-space: nowrap;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-left {\n right: auto;\n left: 0;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n@media (min-width: 576px) {\n .dropdown-menu-sm-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-sm-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 768px) {\n .dropdown-menu-md-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-md-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 992px) {\n .dropdown-menu-lg-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-lg-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 1200px) {\n .dropdown-menu-xl-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-xl-right {\n right: 0;\n left: auto;\n }\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n pointer-events: none;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 1 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) {\n margin-left: -1px;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n margin-top: -1px;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .form-control-plaintext,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n flex: 1 1 0%;\n min-width: 0;\n margin-bottom: 0;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .form-control-plaintext + .form-control,\n.input-group > .form-control-plaintext + .custom-select,\n.input-group > .form-control-plaintext + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {\n z-index: 3;\n}\n\n.input-group > .custom-file .custom-file-input:focus {\n z-index: 4;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: flex;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn:focus,\n.input-group-append .btn:focus {\n z-index: 3;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group-lg > .form-control:not(textarea),\n.input-group-lg > .custom-select {\n height: calc(1.5em + 1rem + 2px);\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .custom-select,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.input-group-sm > .form-control:not(textarea),\n.input-group-sm > .custom-select {\n height: calc(1.5em + 0.5rem + 2px);\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .custom-select,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.input-group-lg > .custom-select,\n.input-group-sm > .custom-select {\n padding-right: 1.75rem;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n left: 0;\n z-index: -1;\n width: 1rem;\n height: 1.25rem;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n border-color: #007bff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #80bdff;\n}\n\n.custom-control-input:not(:disabled):active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n border-color: #b3d7ff;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n position: relative;\n margin-bottom: 0;\n vertical-align: top;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n background-color: #fff;\n border: #adb5bd solid 1px;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background: no-repeat 50% / 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n border-color: #007bff;\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-switch {\n padding-left: 2.25rem;\n}\n\n.custom-switch .custom-control-label::before {\n left: -2.25rem;\n width: 1.75rem;\n pointer-events: all;\n border-radius: 0.5rem;\n}\n\n.custom-switch .custom-control-label::after {\n top: calc(0.25rem + 2px);\n left: calc(-2.25rem + 2px);\n width: calc(1rem - 4px);\n height: calc(1rem - 4px);\n background-color: #adb5bd;\n border-radius: 0.5rem;\n transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-switch .custom-control-label::after {\n transition: none;\n }\n}\n\n.custom-switch .custom-control-input:checked ~ .custom-control-label::after {\n background-color: #fff;\n transform: translateX(0.75rem);\n}\n\n.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n display: none;\n}\n\n.custom-select:-moz-focusring {\n color: transparent;\n text-shadow: 0 0 0 #495057;\n}\n\n.custom-select-sm {\n height: calc(1.5em + 0.5rem + 2px);\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n padding-left: 0.5rem;\n font-size: 0.875rem;\n}\n\n.custom-select-lg {\n height: calc(1.5em + 1rem + 2px);\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n font-size: 1.25rem;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input[disabled] ~ .custom-file-label,\n.custom-file-input:disabled ~ .custom-file-label {\n background-color: #e9ecef;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-input ~ .custom-file-label[data-browse]::after {\n content: attr(data-browse);\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 0.75rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: calc(1.5em + 0.75rem);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: inherit;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n height: 1.4rem;\n padding: 0;\n background-color: transparent;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range:focus::-webkit-slider-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-moz-range-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-ms-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-webkit-slider-thumb {\n transition: none;\n }\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-moz-range-thumb {\n transition: none;\n }\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: 0;\n margin-right: 0.2rem;\n margin-left: 0.2rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-ms-thumb {\n transition: none;\n }\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range:disabled::-webkit-slider-thumb {\n background-color: #adb5bd;\n}\n\n.custom-range:disabled::-webkit-slider-runnable-track {\n cursor: default;\n}\n\n.custom-range:disabled::-moz-range-thumb {\n background-color: #adb5bd;\n}\n\n.custom-range:disabled::-moz-range-track {\n cursor: default;\n}\n\n.custom-range:disabled::-ms-thumb {\n background-color: #adb5bd;\n}\n\n.custom-control-label::before,\n.custom-file-label,\n.custom-select {\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-control-label::before,\n .custom-file-label,\n .custom-select {\n transition: none;\n }\n}\n\n.nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n pointer-events: none;\n cursor: default;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar .container,\n.navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n flex-flow: row nowrap;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n flex: 1 1 auto;\n min-height: 1px;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n flex-shrink: 0;\n width: 100%;\n}\n\n.card-img,\n.card-img-top {\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img,\n.card-img-bottom {\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n display: flex;\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n flex: 1 0 0%;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n display: flex;\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-top,\n .card-group > .card:not(:last-child) .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-bottom,\n .card-group > .card:not(:last-child) .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-top,\n .card-group > .card:not(:first-child) .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-bottom,\n .card-group > .card:not(:first-child) .card-footer {\n border-bottom-left-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n column-count: 3;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion > .card {\n overflow: hidden;\n}\n\n.accordion > .card:not(:last-of-type) {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion > .card:not(:first-of-type) {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.accordion > .card > .card-header {\n border-radius: 0;\n margin-bottom: -1px;\n}\n\n.breadcrumb {\n display: flex;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 3;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 3;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .badge {\n transition: none;\n }\n}\n\na.badge:hover, a.badge:focus {\n text-decoration: none;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\na.badge-primary:hover, a.badge-primary:focus {\n color: #fff;\n background-color: #0062cc;\n}\n\na.badge-primary:focus, a.badge-primary.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\na.badge-secondary:hover, a.badge-secondary:focus {\n color: #fff;\n background-color: #545b62;\n}\n\na.badge-secondary:focus, a.badge-secondary.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\na.badge-success:hover, a.badge-success:focus {\n color: #fff;\n background-color: #1e7e34;\n}\n\na.badge-success:focus, a.badge-success.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\na.badge-info:hover, a.badge-info:focus {\n color: #fff;\n background-color: #117a8b;\n}\n\na.badge-info:focus, a.badge-info.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\na.badge-warning:hover, a.badge-warning:focus {\n color: #212529;\n background-color: #d39e00;\n}\n\na.badge-warning:focus, a.badge-warning.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\na.badge-danger:hover, a.badge-danger:focus {\n color: #fff;\n background-color: #bd2130;\n}\n\na.badge-danger:focus, a.badge-danger.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\na.badge-light:hover, a.badge-light:focus {\n color: #212529;\n background-color: #dae0e5;\n}\n\na.badge-light:focus, a.badge-light.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\na.badge-dark:hover, a.badge-dark:focus {\n color: #fff;\n background-color: #1d2124;\n}\n\na.badge-dark:focus, a.badge-dark.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n overflow: hidden;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-bar-animated {\n animation: none;\n }\n}\n\n.media {\n display: flex;\n align-items: flex-start;\n}\n\n.media-body {\n flex: 1;\n}\n\n.list-group {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n z-index: 1;\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n pointer-events: none;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-item + .list-group-item {\n border-top-width: 0;\n}\n\n.list-group-item + .list-group-item.active {\n margin-top: -1px;\n border-top-width: 1px;\n}\n\n.list-group-horizontal {\n flex-direction: row;\n}\n\n.list-group-horizontal .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n}\n\n.list-group-horizontal .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n}\n\n.list-group-horizontal .list-group-item.active {\n margin-top: 0;\n}\n\n.list-group-horizontal .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n}\n\n.list-group-horizontal .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n}\n\n@media (min-width: 576px) {\n .list-group-horizontal-sm {\n flex-direction: row;\n }\n .list-group-horizontal-sm .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-sm .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-sm .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-sm .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-sm .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n\n@media (min-width: 768px) {\n .list-group-horizontal-md {\n flex-direction: row;\n }\n .list-group-horizontal-md .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-md .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-md .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-md .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-md .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n\n@media (min-width: 992px) {\n .list-group-horizontal-lg {\n flex-direction: row;\n }\n .list-group-horizontal-lg .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-lg .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-lg .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-lg .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-lg .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n\n@media (min-width: 1200px) {\n .list-group-horizontal-xl {\n flex-direction: row;\n }\n .list-group-horizontal-xl .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-xl .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-xl .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-xl .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-xl .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n\n.list-group-flush .list-group-item {\n border-right-width: 0;\n border-left-width: 0;\n border-radius: 0;\n}\n\n.list-group-flush .list-group-item:first-child {\n border-top-width: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover {\n color: #000;\n text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {\n opacity: .75;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n appearance: none;\n}\n\na.close.disabled {\n pointer-events: none;\n}\n\n.toast {\n max-width: 350px;\n overflow: hidden;\n font-size: 0.875rem;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.1);\n box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);\n backdrop-filter: blur(10px);\n opacity: 0;\n border-radius: 0.25rem;\n}\n\n.toast:not(:last-child) {\n margin-bottom: 0.75rem;\n}\n\n.toast.showing {\n opacity: 1;\n}\n\n.toast.show {\n display: block;\n opacity: 1;\n}\n\n.toast.hide {\n display: none;\n}\n\n.toast-header {\n display: flex;\n align-items: center;\n padding: 0.25rem 0.75rem;\n color: #6c757d;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n}\n\n.toast-body {\n padding: 0.75rem;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1050;\n display: none;\n width: 100%;\n height: 100%;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -50px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n transform: none;\n}\n\n.modal.modal-static .modal-dialog {\n transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n display: flex;\n max-height: calc(100% - 1rem);\n}\n\n.modal-dialog-scrollable .modal-content {\n max-height: calc(100vh - 1rem);\n overflow: hidden;\n}\n\n.modal-dialog-scrollable .modal-header,\n.modal-dialog-scrollable .modal-footer {\n flex-shrink: 0;\n}\n\n.modal-dialog-scrollable .modal-body {\n overflow-y: auto;\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - 1rem);\n}\n\n.modal-dialog-centered::before {\n display: block;\n height: calc(100vh - 1rem);\n content: \"\";\n}\n\n.modal-dialog-centered.modal-dialog-scrollable {\n flex-direction: column;\n justify-content: center;\n height: 100%;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable .modal-content {\n max-height: none;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable::before {\n content: none;\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1040;\n width: 100vw;\n height: 100vh;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 1rem 1rem;\n border-bottom: 1px solid #dee2e6;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.modal-header .close {\n padding: 1rem 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n padding: 0.75rem;\n border-top: 1px solid #dee2e6;\n border-bottom-right-radius: calc(0.3rem - 1px);\n border-bottom-left-radius: calc(0.3rem - 1px);\n}\n\n.modal-footer > * {\n margin: 0.25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-scrollable {\n max-height: calc(100% - 3.5rem);\n }\n .modal-dialog-scrollable .modal-content {\n max-height: calc(100vh - 3.5rem);\n }\n .modal-dialog-centered {\n min-height: calc(100% - 3.5rem);\n }\n .modal-dialog-centered::before {\n height: calc(100vh - 3.5rem);\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg,\n .modal-xl {\n max-width: 800px;\n }\n}\n\n@media (min-width: 1200px) {\n .modal-xl {\n max-width: 1140px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top > .arrow, .bs-popover-auto[x-placement^=\"top\"] > .arrow {\n bottom: calc(-0.5rem - 1px);\n}\n\n.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^=\"top\"] > .arrow::before {\n bottom: 0;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^=\"top\"] > .arrow::after {\n bottom: 1px;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right > .arrow, .bs-popover-auto[x-placement^=\"right\"] > .arrow {\n left: calc(-0.5rem - 1px);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^=\"right\"] > .arrow::before {\n left: 0;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^=\"right\"] > .arrow::after {\n left: 1px;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow {\n top: calc(-0.5rem - 1px);\n}\n\n.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::before {\n top: 0;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::after {\n top: 1px;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left > .arrow, .bs-popover-auto[x-placement^=\"left\"] > .arrow {\n right: calc(-0.5rem - 1px);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^=\"left\"] > .arrow::before {\n right: 0;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^=\"left\"] > .arrow::after {\n right: 1px;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel.pointer-event {\n touch-action: pan-y;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-inner::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.carousel-item {\n position: relative;\n display: none;\n float: left;\n width: 100%;\n margin-right: -100%;\n backface-visibility: hidden;\n transition: transform 0.6s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next:not(.carousel-item-left),\n.active.carousel-item-right {\n transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-right),\n.active.carousel-item-left {\n transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-property: opacity;\n transform: none;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n z-index: 1;\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n z-index: 0;\n opacity: 0;\n transition: opacity 0s 0.6s;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-right {\n transition: none;\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n transition: opacity 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-control-prev,\n .carousel-control-next {\n transition: none;\n }\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: 0.9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: no-repeat 50% / 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 15;\n display: flex;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n box-sizing: content-box;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: #fff;\n background-clip: padding-box;\n border-top: 10px solid transparent;\n border-bottom: 10px solid transparent;\n opacity: .5;\n transition: opacity 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-indicators li {\n transition: none;\n }\n}\n\n.carousel-indicators .active {\n opacity: 1;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n@keyframes spinner-border {\n to {\n transform: rotate(360deg);\n }\n}\n\n.spinner-border {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: text-bottom;\n border: 0.25em solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: spinner-border .75s linear infinite;\n}\n\n.spinner-border-sm {\n width: 1rem;\n height: 1rem;\n border-width: 0.2em;\n}\n\n@keyframes spinner-grow {\n 0% {\n transform: scale(0);\n }\n 50% {\n opacity: 1;\n }\n}\n\n.spinner-grow {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: text-bottom;\n background-color: currentColor;\n border-radius: 50%;\n opacity: 0;\n animation: spinner-grow .75s linear infinite;\n}\n\n.spinner-grow-sm {\n width: 1rem;\n height: 1rem;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded-sm {\n border-radius: 0.2rem !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-lg {\n border-radius: 0.3rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-pill {\n border-radius: 50rem !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.overflow-auto {\n overflow: auto !important;\n}\n\n.overflow-hidden {\n overflow: hidden !important;\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports (position: sticky) {\n .sticky-top {\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.min-vw-100 {\n min-width: 100vw !important;\n}\n\n.min-vh-100 {\n min-height: 100vh !important;\n}\n\n.vw-100 {\n width: 100vw !important;\n}\n\n.vh-100 {\n height: 100vh !important;\n}\n\n.stretched-link::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1;\n pointer-events: auto;\n content: \"\";\n background-color: rgba(0, 0, 0, 0);\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-n1 {\n margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n margin-left: -1rem !important;\n}\n\n.m-n4 {\n margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n margin-left: -3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-n1 {\n margin: -0.25rem !important;\n }\n .mt-sm-n1,\n .my-sm-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-sm-n1,\n .mx-sm-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-sm-n1,\n .my-sm-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-sm-n1,\n .mx-sm-n1 {\n margin-left: -0.25rem !important;\n }\n .m-sm-n2 {\n margin: -0.5rem !important;\n }\n .mt-sm-n2,\n .my-sm-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-sm-n2,\n .mx-sm-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-sm-n2,\n .my-sm-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-sm-n2,\n .mx-sm-n2 {\n margin-left: -0.5rem !important;\n }\n .m-sm-n3 {\n margin: -1rem !important;\n }\n .mt-sm-n3,\n .my-sm-n3 {\n margin-top: -1rem !important;\n }\n .mr-sm-n3,\n .mx-sm-n3 {\n margin-right: -1rem !important;\n }\n .mb-sm-n3,\n .my-sm-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-sm-n3,\n .mx-sm-n3 {\n margin-left: -1rem !important;\n }\n .m-sm-n4 {\n margin: -1.5rem !important;\n }\n .mt-sm-n4,\n .my-sm-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-sm-n4,\n .mx-sm-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-sm-n4,\n .my-sm-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-sm-n4,\n .mx-sm-n4 {\n margin-left: -1.5rem !important;\n }\n .m-sm-n5 {\n margin: -3rem !important;\n }\n .mt-sm-n5,\n .my-sm-n5 {\n margin-top: -3rem !important;\n }\n .mr-sm-n5,\n .mx-sm-n5 {\n margin-right: -3rem !important;\n }\n .mb-sm-n5,\n .my-sm-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-sm-n5,\n .mx-sm-n5 {\n margin-left: -3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-n1 {\n margin: -0.25rem !important;\n }\n .mt-md-n1,\n .my-md-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-md-n1,\n .mx-md-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-md-n1,\n .my-md-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-md-n1,\n .mx-md-n1 {\n margin-left: -0.25rem !important;\n }\n .m-md-n2 {\n margin: -0.5rem !important;\n }\n .mt-md-n2,\n .my-md-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-md-n2,\n .mx-md-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-md-n2,\n .my-md-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-md-n2,\n .mx-md-n2 {\n margin-left: -0.5rem !important;\n }\n .m-md-n3 {\n margin: -1rem !important;\n }\n .mt-md-n3,\n .my-md-n3 {\n margin-top: -1rem !important;\n }\n .mr-md-n3,\n .mx-md-n3 {\n margin-right: -1rem !important;\n }\n .mb-md-n3,\n .my-md-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-md-n3,\n .mx-md-n3 {\n margin-left: -1rem !important;\n }\n .m-md-n4 {\n margin: -1.5rem !important;\n }\n .mt-md-n4,\n .my-md-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-md-n4,\n .mx-md-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-md-n4,\n .my-md-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-md-n4,\n .mx-md-n4 {\n margin-left: -1.5rem !important;\n }\n .m-md-n5 {\n margin: -3rem !important;\n }\n .mt-md-n5,\n .my-md-n5 {\n margin-top: -3rem !important;\n }\n .mr-md-n5,\n .mx-md-n5 {\n margin-right: -3rem !important;\n }\n .mb-md-n5,\n .my-md-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-md-n5,\n .mx-md-n5 {\n margin-left: -3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-n1 {\n margin: -0.25rem !important;\n }\n .mt-lg-n1,\n .my-lg-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-lg-n1,\n .mx-lg-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-lg-n1,\n .my-lg-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-lg-n1,\n .mx-lg-n1 {\n margin-left: -0.25rem !important;\n }\n .m-lg-n2 {\n margin: -0.5rem !important;\n }\n .mt-lg-n2,\n .my-lg-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-lg-n2,\n .mx-lg-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-lg-n2,\n .my-lg-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-lg-n2,\n .mx-lg-n2 {\n margin-left: -0.5rem !important;\n }\n .m-lg-n3 {\n margin: -1rem !important;\n }\n .mt-lg-n3,\n .my-lg-n3 {\n margin-top: -1rem !important;\n }\n .mr-lg-n3,\n .mx-lg-n3 {\n margin-right: -1rem !important;\n }\n .mb-lg-n3,\n .my-lg-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-lg-n3,\n .mx-lg-n3 {\n margin-left: -1rem !important;\n }\n .m-lg-n4 {\n margin: -1.5rem !important;\n }\n .mt-lg-n4,\n .my-lg-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-lg-n4,\n .mx-lg-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-lg-n4,\n .my-lg-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-lg-n4,\n .mx-lg-n4 {\n margin-left: -1.5rem !important;\n }\n .m-lg-n5 {\n margin: -3rem !important;\n }\n .mt-lg-n5,\n .my-lg-n5 {\n margin-top: -3rem !important;\n }\n .mr-lg-n5,\n .mx-lg-n5 {\n margin-right: -3rem !important;\n }\n .mb-lg-n5,\n .my-lg-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-lg-n5,\n .mx-lg-n5 {\n margin-left: -3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-n1 {\n margin: -0.25rem !important;\n }\n .mt-xl-n1,\n .my-xl-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-xl-n1,\n .mx-xl-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-xl-n1,\n .my-xl-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-xl-n1,\n .mx-xl-n1 {\n margin-left: -0.25rem !important;\n }\n .m-xl-n2 {\n margin: -0.5rem !important;\n }\n .mt-xl-n2,\n .my-xl-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-xl-n2,\n .mx-xl-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-xl-n2,\n .my-xl-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-xl-n2,\n .mx-xl-n2 {\n margin-left: -0.5rem !important;\n }\n .m-xl-n3 {\n margin: -1rem !important;\n }\n .mt-xl-n3,\n .my-xl-n3 {\n margin-top: -1rem !important;\n }\n .mr-xl-n3,\n .mx-xl-n3 {\n margin-right: -1rem !important;\n }\n .mb-xl-n3,\n .my-xl-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-xl-n3,\n .mx-xl-n3 {\n margin-left: -1rem !important;\n }\n .m-xl-n4 {\n margin: -1.5rem !important;\n }\n .mt-xl-n4,\n .my-xl-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-xl-n4,\n .mx-xl-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-xl-n4,\n .my-xl-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-xl-n4,\n .mx-xl-n4 {\n margin-left: -1.5rem !important;\n }\n .m-xl-n5 {\n margin: -3rem !important;\n }\n .mt-xl-n5,\n .my-xl-n5 {\n margin-top: -3rem !important;\n }\n .mr-xl-n5,\n .mx-xl-n5 {\n margin-right: -3rem !important;\n }\n .mb-xl-n5,\n .my-xl-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-xl-n5,\n .mx-xl-n5 {\n margin-left: -3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !important;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-wrap {\n white-space: normal !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-lighter {\n font-weight: lighter !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-weight-bolder {\n font-weight: bolder !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0056b3 !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #494f54 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #19692c !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #0f6674 !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #ba8b00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #a71d2a !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #cbd3da !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #121416 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.text-decoration-none {\n text-decoration: none !important;\n}\n\n.text-break {\n word-break: break-word !important;\n overflow-wrap: break-word !important;\n}\n\n.text-reset {\n color: inherit !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n .table-dark {\n color: inherit;\n }\n .table-dark th,\n .table-dark td,\n .table-dark thead th,\n .table-dark tbody + tbody {\n border-color: #dee2e6;\n }\n .table .thead-dark th {\n color: inherit;\n border-color: #dee2e6;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// Do not forget to update getting-started/theming.md!\n:root {\n // Custom variable values only support SassScript inside `#{}`.\n @each $color, $value in $colors {\n --#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$color}: #{$value};\n }\n\n @each $bp, $value in $grid-breakpoints {\n --breakpoint-#{$bp}: #{$value};\n }\n\n // Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --font-family-sans-serif: #{inspect($font-family-sans-serif)};\n --font-family-monospace: #{inspect($font-family-monospace)};\n}\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -webkit-tap-highlight-color: rgba($black, 0); // 5\n}\n\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\n// TODO: remove in v5\n// stylelint-disable-next-line selector-list-comma-newline-after\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline\n// on elements that programmatically receive focus but wouldn't normally show a visible\n// focus outline. In general, this would mean that the outline is only applied if the\n// interaction that led to the element receiving programmatic focus was a keyboard interaction,\n// or the browser has somehow determined that the user is primarily a keyboard user and/or\n// wants focus outlines to always be presented.\n//\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible\n// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

    `-`

    ` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

    `s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

    `s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n @include font-size(1.5rem);\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n// Color system\n\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n\n$grays: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$grays: map-merge(\n (\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n ),\n $grays\n);\n\n$blue: #007bff !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #e83e8c !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #28a745 !default;\n$teal: #20c997 !default;\n$cyan: #17a2b8 !default;\n\n$colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$colors: map-merge(\n (\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n ),\n $colors\n);\n\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-800 !default;\n\n$theme-colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$theme-colors: map-merge(\n (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n ),\n $theme-colors\n);\n\n// Set a specific jump point for requesting color jumps\n$theme-color-interval: 8% !default;\n\n// The yiq lightness value that determines when the lightness of color changes from \"dark\" to \"light\". Acceptable values are between 0 and 255.\n$yiq-contrasted-threshold: 150 !default;\n\n// Customize the light and dark text colors for use in our YIQ color contrast function.\n$yiq-text-dark: $gray-900 !default;\n$yiq-text-light: $white !default;\n\n// Characters which are escaped by the escape-svg function\n$escaped-characters: (\n (\"<\",\"%3c\"),\n (\">\",\"%3e\"),\n (\"#\",\"%23\"),\n) !default;\n\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-prefers-reduced-motion-media-query: true !default;\n$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS\n$enable-grid-classes: true !default;\n$enable-pointer-cursor-for-buttons: true !default;\n$enable-print-styles: true !default;\n$enable-responsive-font-sizes: false !default;\n$enable-validation-icons: true !default;\n$enable-deprecation-messages: true !default;\n\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n$spacer: 1rem !default;\n$spacers: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$spacers: map-merge(\n (\n 0: 0,\n 1: ($spacer * .25),\n 2: ($spacer * .5),\n 3: $spacer,\n 4: ($spacer * 1.5),\n 5: ($spacer * 3)\n ),\n $spacers\n);\n\n// This variable affects the `.h-*` and `.w-*` classes.\n$sizes: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$sizes: map-merge(\n (\n 25: 25%,\n 50: 50%,\n 75: 75%,\n 100: 100%,\n auto: auto\n ),\n $sizes\n);\n\n\n// Body\n//\n// Settings for the `` element.\n\n$body-bg: $white !default;\n$body-color: $gray-900 !default;\n\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: theme-color(\"primary\") !default;\n$link-decoration: none !default;\n$link-hover-color: darken($link-color, 15%) !default;\n$link-hover-decoration: underline !default;\n// Darken percentage for links with `.text-*` class (e.g. `.text-success`)\n$emphasized-link-hover-darken-percentage: 15% !default;\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px\n) !default;\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints, \"$grid-breakpoints\");\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px\n) !default;\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 30px !default;\n$grid-row-columns: 6 !default;\n\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n$line-height-lg: 1.5 !default;\n$line-height-sm: 1.5 !default;\n\n$border-width: 1px !default;\n$border-color: $gray-300 !default;\n\n$border-radius: .25rem !default;\n$border-radius-lg: .3rem !default;\n$border-radius-sm: .2rem !default;\n\n$rounded-pill: 50rem !default;\n\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n\n$component-active-color: $white !default;\n$component-active-bg: theme-color(\"primary\") !default;\n\n$caret-width: .3em !default;\n$caret-vertical-align: $caret-width * .85 !default;\n$caret-spacing: $caret-width * .85 !default;\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n$transition-collapse: height .35s ease !default;\n\n$embed-responsive-aspect-ratios: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$embed-responsive-aspect-ratios: join(\n (\n (21 9),\n (16 9),\n (4 3),\n (1 1),\n ),\n $embed-responsive-aspect-ratios\n);\n\n// Typography\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n$font-family-base: $font-family-sans-serif !default;\n// stylelint-enable value-keyword-case\n\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-lg: $font-size-base * 1.25 !default;\n$font-size-sm: $font-size-base * .875 !default;\n\n$font-weight-lighter: lighter !default;\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-bold: 700 !default;\n$font-weight-bolder: bolder !default;\n\n$font-weight-base: $font-weight-normal !default;\n$line-height-base: 1.5 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n\n$headings-margin-bottom: $spacer / 2 !default;\n$headings-font-family: null !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: null !default;\n\n$display1-size: 6rem !default;\n$display2-size: 5.5rem !default;\n$display3-size: 4.5rem !default;\n$display4-size: 3.5rem !default;\n\n$display1-weight: 300 !default;\n$display2-weight: 300 !default;\n$display3-weight: 300 !default;\n$display4-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n\n$lead-font-size: $font-size-base * 1.25 !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: 80% !default;\n\n$text-muted: $gray-600 !default;\n\n$blockquote-small-color: $gray-600 !default;\n$blockquote-small-font-size: $small-font-size !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n\n$hr-border-color: rgba($black, .1) !default;\n$hr-border-width: $border-width !default;\n\n$mark-padding: .2em !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default;\n$nested-kbd-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-bg: #fcf8e3 !default;\n\n$hr-margin-y: $spacer !default;\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n$table-cell-padding: .75rem !default;\n$table-cell-padding-sm: .3rem !default;\n\n$table-color: $body-color !default;\n$table-bg: null !default;\n$table-accent-bg: rgba($black, .05) !default;\n$table-hover-color: $table-color !default;\n$table-hover-bg: rgba($black, .075) !default;\n$table-active-bg: $table-hover-bg !default;\n\n$table-border-width: $border-width !default;\n$table-border-color: $border-color !default;\n\n$table-head-bg: $gray-200 !default;\n$table-head-color: $gray-700 !default;\n\n$table-dark-color: $white !default;\n$table-dark-bg: $gray-800 !default;\n$table-dark-accent-bg: rgba($white, .05) !default;\n$table-dark-hover-color: $table-dark-color !default;\n$table-dark-hover-bg: rgba($white, .075) !default;\n$table-dark-border-color: lighten($table-dark-bg, 7.5%) !default;\n\n$table-striped-order: odd !default;\n\n$table-caption-color: $text-muted !default;\n\n$table-bg-level: -9 !default;\n$table-border-level: -6 !default;\n\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-font-family: null !default;\n$input-btn-font-size: $font-size-base !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: .2rem !default;\n$input-btn-focus-color: rgba($component-active-bg, .25) !default;\n$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-font-size-sm: $font-size-sm !default;\n$input-btn-line-height-sm: $line-height-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-font-size-lg: $font-size-lg !default;\n$input-btn-line-height-lg: $line-height-lg !default;\n\n$input-btn-border-width: $border-width !default;\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-font-family: $input-btn-font-family !default;\n$btn-font-size: $input-btn-font-size !default;\n$btn-line-height: $input-btn-line-height !default;\n$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-font-size-sm: $input-btn-font-size-sm !default;\n$btn-line-height-sm: $input-btn-line-height-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-font-size-lg: $input-btn-font-size-lg !default;\n$btn-line-height-lg: $input-btn-line-height-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-disabled-color: $gray-600 !default;\n\n$btn-block-spacing-y: .5rem !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: $border-radius !default;\n$btn-border-radius-lg: $border-radius-lg !default;\n$btn-border-radius-sm: $border-radius-sm !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n\n// Forms\n\n$label-margin-bottom: .5rem !default;\n\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-font-family: $input-btn-font-family !default;\n$input-font-size: $input-btn-font-size !default;\n$input-font-weight: $font-weight-base !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-font-size-sm: $input-btn-font-size-sm !default;\n$input-line-height-sm: $input-btn-line-height-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-font-size-lg: $input-btn-font-size-lg !default;\n$input-line-height-lg: $input-btn-line-height-lg !default;\n\n$input-bg: $white !default;\n$input-disabled-bg: $gray-200 !default;\n\n$input-color: $gray-700 !default;\n$input-border-color: $gray-400 !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: inset 0 1px 1px rgba($black, .075) !default;\n\n$input-border-radius: $border-radius !default;\n$input-border-radius-lg: $border-radius-lg !default;\n$input-border-radius-sm: $border-radius-sm !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: lighten($component-active-bg, 25%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: $gray-600 !default;\n$input-plaintext-color: $body-color !default;\n\n$input-height-border: $input-border-width * 2 !default;\n\n$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;\n$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;\n$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y / 2) !default;\n\n$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;\n$input-height-sm: add($input-line-height-sm * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;\n$input-height-lg: add($input-line-height-lg * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-text-margin-top: .25rem !default;\n\n$form-check-input-gutter: 1.25rem !default;\n$form-check-input-margin-y: .3rem !default;\n$form-check-input-margin-x: .25rem !default;\n\n$form-check-inline-margin-x: .75rem !default;\n$form-check-inline-input-margin-x: .3125rem !default;\n\n$form-grid-gutter-width: 10px !default;\n$form-group-margin-bottom: 1rem !default;\n\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: $gray-200 !default;\n$input-group-addon-border-color: $input-border-color !default;\n\n$custom-forms-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$custom-control-gutter: .5rem !default;\n$custom-control-spacer-x: 1rem !default;\n$custom-control-cursor: null !default;\n\n$custom-control-indicator-size: 1rem !default;\n$custom-control-indicator-bg: $input-bg !default;\n\n$custom-control-indicator-bg-size: 50% 50% !default;\n$custom-control-indicator-box-shadow: $input-box-shadow !default;\n$custom-control-indicator-border-color: $gray-500 !default;\n$custom-control-indicator-border-width: $input-border-width !default;\n\n$custom-control-label-color: null !default;\n\n$custom-control-indicator-disabled-bg: $input-disabled-bg !default;\n$custom-control-label-disabled-color: $gray-600 !default;\n\n$custom-control-indicator-checked-color: $component-active-color !default;\n$custom-control-indicator-checked-bg: $component-active-bg !default;\n$custom-control-indicator-checked-disabled-bg: rgba(theme-color(\"primary\"), .5) !default;\n$custom-control-indicator-checked-box-shadow: none !default;\n$custom-control-indicator-checked-border-color: $custom-control-indicator-checked-bg !default;\n\n$custom-control-indicator-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-control-indicator-focus-border-color: $input-focus-border-color !default;\n\n$custom-control-indicator-active-color: $component-active-color !default;\n$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-control-indicator-active-box-shadow: none !default;\n$custom-control-indicator-active-border-color: $custom-control-indicator-active-bg !default;\n\n$custom-checkbox-indicator-border-radius: $border-radius !default;\n$custom-checkbox-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default;\n$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;\n$custom-checkbox-indicator-icon-indeterminate: url(\"data:image/svg+xml,\") !default;\n$custom-checkbox-indicator-indeterminate-box-shadow: none !default;\n$custom-checkbox-indicator-indeterminate-border-color: $custom-checkbox-indicator-indeterminate-bg !default;\n\n$custom-radio-indicator-border-radius: 50% !default;\n$custom-radio-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-switch-width: $custom-control-indicator-size * 1.75 !default;\n$custom-switch-indicator-border-radius: $custom-control-indicator-size / 2 !default;\n$custom-switch-indicator-size: subtract($custom-control-indicator-size, $custom-control-indicator-border-width * 4) !default;\n\n$custom-select-padding-y: $input-padding-y !default;\n$custom-select-padding-x: $input-padding-x !default;\n$custom-select-font-family: $input-font-family !default;\n$custom-select-font-size: $input-font-size !default;\n$custom-select-height: $input-height !default;\n$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator\n$custom-select-font-weight: $input-font-weight !default;\n$custom-select-line-height: $input-line-height !default;\n$custom-select-color: $input-color !default;\n$custom-select-disabled-color: $gray-600 !default;\n$custom-select-bg: $input-bg !default;\n$custom-select-disabled-bg: $gray-200 !default;\n$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions\n$custom-select-indicator-color: $gray-800 !default;\n$custom-select-indicator: url(\"data:image/svg+xml,\") !default;\n$custom-select-background: escape-svg($custom-select-indicator) no-repeat right $custom-select-padding-x center / $custom-select-bg-size !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)\n\n$custom-select-feedback-icon-padding-right: add(1em * .75, (2 * $custom-select-padding-y * .75) + $custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-position: center right ($custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;\n\n$custom-select-border-width: $input-border-width !default;\n$custom-select-border-color: $input-border-color !default;\n$custom-select-border-radius: $border-radius !default;\n$custom-select-box-shadow: inset 0 1px 2px rgba($black, .075) !default;\n\n$custom-select-focus-border-color: $input-focus-border-color !default;\n$custom-select-focus-width: $input-focus-width !default;\n$custom-select-focus-box-shadow: 0 0 0 $custom-select-focus-width $input-btn-focus-color !default;\n\n$custom-select-padding-y-sm: $input-padding-y-sm !default;\n$custom-select-padding-x-sm: $input-padding-x-sm !default;\n$custom-select-font-size-sm: $input-font-size-sm !default;\n$custom-select-height-sm: $input-height-sm !default;\n\n$custom-select-padding-y-lg: $input-padding-y-lg !default;\n$custom-select-padding-x-lg: $input-padding-x-lg !default;\n$custom-select-font-size-lg: $input-font-size-lg !default;\n$custom-select-height-lg: $input-height-lg !default;\n\n$custom-range-track-width: 100% !default;\n$custom-range-track-height: .5rem !default;\n$custom-range-track-cursor: pointer !default;\n$custom-range-track-bg: $gray-300 !default;\n$custom-range-track-border-radius: 1rem !default;\n$custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;\n\n$custom-range-thumb-width: 1rem !default;\n$custom-range-thumb-height: $custom-range-thumb-width !default;\n$custom-range-thumb-bg: $component-active-bg !default;\n$custom-range-thumb-border: 0 !default;\n$custom-range-thumb-border-radius: 1rem !default;\n$custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;\n$custom-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in IE/Edge\n$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-range-thumb-disabled-bg: $gray-500 !default;\n\n$custom-file-height: $input-height !default;\n$custom-file-height-inner: $input-height-inner !default;\n$custom-file-focus-border-color: $input-focus-border-color !default;\n$custom-file-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-file-disabled-bg: $input-disabled-bg !default;\n\n$custom-file-padding-y: $input-padding-y !default;\n$custom-file-padding-x: $input-padding-x !default;\n$custom-file-line-height: $input-line-height !default;\n$custom-file-font-family: $input-font-family !default;\n$custom-file-font-weight: $input-font-weight !default;\n$custom-file-color: $input-color !default;\n$custom-file-bg: $input-bg !default;\n$custom-file-border-width: $input-border-width !default;\n$custom-file-border-color: $input-border-color !default;\n$custom-file-border-radius: $input-border-radius !default;\n$custom-file-box-shadow: $input-box-shadow !default;\n$custom-file-button-color: $custom-file-color !default;\n$custom-file-button-bg: $input-group-addon-bg !default;\n$custom-file-text: (\n en: \"Browse\"\n) !default;\n\n\n// Form validation\n\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $small-font-size !default;\n$form-feedback-valid-color: theme-color(\"success\") !default;\n$form-feedback-invalid-color: theme-color(\"danger\") !default;\n\n$form-feedback-icon-valid-color: $form-feedback-valid-color !default;\n$form-feedback-icon-valid: url(\"data:image/svg+xml,\") !default;\n$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;\n$form-feedback-icon-invalid: url(\"data:image/svg+xml,\") !default;\n\n$form-validation-states: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$form-validation-states: map-merge(\n (\n \"valid\": (\n \"color\": $form-feedback-valid-color,\n \"icon\": $form-feedback-icon-valid\n ),\n \"invalid\": (\n \"color\": $form-feedback-invalid-color,\n \"icon\": $form-feedback-icon-invalid\n ),\n ),\n $form-validation-states\n);\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-modal-backdrop: 1040 !default;\n$zindex-modal: 1050 !default;\n$zindex-popover: 1060 !default;\n$zindex-tooltip: 1070 !default;\n\n\n// Navs\n\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-disabled-color: $gray-600 !default;\n\n$nav-tabs-border-color: $gray-300 !default;\n$nav-tabs-border-width: $border-width !default;\n$nav-tabs-border-radius: $border-radius !default;\n$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: $gray-700 !default;\n$nav-tabs-link-active-bg: $body-bg !default;\n$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: $border-radius !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-divider-color: $gray-200 !default;\n$nav-divider-margin-y: $spacer / 2 !default;\n\n\n// Navbar\n\n$navbar-padding-y: $spacer / 2 !default;\n$navbar-padding-x: $spacer !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n\n$navbar-dark-color: rgba($white, .5) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n\n$navbar-light-color: rgba($black, .5) !default;\n$navbar-light-hover-color: rgba($black, .7) !default;\n$navbar-light-active-color: rgba($black, .9) !default;\n$navbar-light-disabled-color: rgba($black, .3) !default;\n$navbar-light-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-light-toggler-border-color: rgba($black, .1) !default;\n\n$navbar-light-brand-color: $navbar-light-active-color !default;\n$navbar-light-brand-hover-color: $navbar-light-active-color !default;\n$navbar-dark-brand-color: $navbar-dark-active-color !default;\n$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-font-size: $font-size-base !default;\n$dropdown-color: $body-color !default;\n$dropdown-bg: $white !default;\n$dropdown-border-color: rgba($black, .15) !default;\n$dropdown-border-radius: $border-radius !default;\n$dropdown-border-width: $border-width !default;\n$dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default;\n$dropdown-divider-bg: $gray-200 !default;\n$dropdown-divider-margin-y: $nav-divider-margin-y !default;\n$dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;\n\n$dropdown-link-color: $gray-900 !default;\n$dropdown-link-hover-color: darken($gray-900, 5%) !default;\n$dropdown-link-hover-bg: $gray-100 !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: $gray-600 !default;\n\n$dropdown-item-padding-y: .25rem !default;\n$dropdown-item-padding-x: 1.5rem !default;\n\n$dropdown-header-color: $gray-600 !default;\n\n\n// Pagination\n\n$pagination-padding-y: .5rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n$pagination-line-height: 1.25 !default;\n\n$pagination-color: $link-color !default;\n$pagination-bg: $white !default;\n$pagination-border-width: $border-width !default;\n$pagination-border-color: $gray-300 !default;\n\n$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: $link-hover-color !default;\n$pagination-hover-bg: $gray-200 !default;\n$pagination-hover-border-color: $gray-300 !default;\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $pagination-active-bg !default;\n\n$pagination-disabled-color: $gray-600 !default;\n$pagination-disabled-bg: $white !default;\n$pagination-disabled-border-color: $gray-300 !default;\n\n\n// Jumbotron\n\n$jumbotron-padding: 2rem !default;\n$jumbotron-color: null !default;\n$jumbotron-bg: $gray-200 !default;\n\n\n// Cards\n\n$card-spacer-y: .75rem !default;\n$card-spacer-x: 1.25rem !default;\n$card-border-width: $border-width !default;\n$card-border-radius: $border-radius !default;\n$card-border-color: rgba($black, .125) !default;\n$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;\n$card-cap-bg: rgba($black, .03) !default;\n$card-cap-color: null !default;\n$card-height: null !default;\n$card-color: null !default;\n$card-bg: $white !default;\n\n$card-img-overlay-padding: 1.25rem !default;\n\n$card-group-margin: $grid-gutter-width / 2 !default;\n$card-deck-margin: $card-group-margin !default;\n\n$card-columns-count: 3 !default;\n$card-columns-gap: 1.25rem !default;\n$card-columns-margin: $card-spacer-y !default;\n\n\n// Tooltips\n\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: $white !default;\n$tooltip-bg: $black !default;\n$tooltip-border-radius: $border-radius !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: .25rem !default;\n$tooltip-padding-x: .5rem !default;\n$tooltip-margin: 0 !default;\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n$tooltip-arrow-color: $tooltip-bg !default;\n\n// Form tooltips must come after regular tooltips\n$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;\n$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;\n$form-feedback-tooltip-font-size: $tooltip-font-size !default;\n$form-feedback-tooltip-line-height: $line-height-base !default;\n$form-feedback-tooltip-opacity: $tooltip-opacity !default;\n$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;\n\n\n// Popovers\n\n$popover-font-size: $font-size-sm !default;\n$popover-bg: $white !default;\n$popover-max-width: 276px !default;\n$popover-border-width: $border-width !default;\n$popover-border-color: rgba($black, .2) !default;\n$popover-border-radius: $border-radius-lg !default;\n$popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default;\n$popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default;\n\n$popover-header-bg: darken($popover-bg, 3%) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: .75rem !default;\n\n$popover-body-color: $body-color !default;\n$popover-body-padding-y: $popover-header-padding-y !default;\n$popover-body-padding-x: $popover-header-padding-x !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n$popover-arrow-color: $popover-bg !default;\n\n$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;\n\n\n// Toasts\n\n$toast-max-width: 350px !default;\n$toast-padding-x: .75rem !default;\n$toast-padding-y: .25rem !default;\n$toast-font-size: .875rem !default;\n$toast-color: null !default;\n$toast-background-color: rgba($white, .85) !default;\n$toast-border-width: 1px !default;\n$toast-border-color: rgba(0, 0, 0, .1) !default;\n$toast-border-radius: .25rem !default;\n$toast-box-shadow: 0 .25rem .75rem rgba($black, .1) !default;\n\n$toast-header-color: $gray-600 !default;\n$toast-header-background-color: rgba($white, .85) !default;\n$toast-header-border-color: rgba(0, 0, 0, .05) !default;\n\n\n// Badges\n\n$badge-font-size: 75% !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-padding-y: .25em !default;\n$badge-padding-x: .4em !default;\n$badge-border-radius: $border-radius !default;\n\n$badge-transition: $btn-transition !default;\n$badge-focus-width: $input-btn-focus-width !default;\n\n$badge-pill-padding-x: .6em !default;\n// Use a higher than normal value to ensure completely rounded edges when\n// customizing padding or font-size on labels.\n$badge-pill-border-radius: 10rem !default;\n\n\n// Modals\n\n// Padding applied to the modal body\n$modal-inner-padding: 1rem !default;\n\n// Margin between elements in footer, must be lower than or equal to 2 * $modal-inner-padding\n$modal-footer-margin-between: .5rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-color: null !default;\n$modal-content-bg: $white !default;\n$modal-content-border-color: rgba($black, .2) !default;\n$modal-content-border-width: $border-width !default;\n$modal-content-border-radius: $border-radius-lg !default;\n$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;\n$modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default;\n$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n$modal-header-border-color: $border-color !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n$modal-header-padding-y: 1rem !default;\n$modal-header-padding-x: 1rem !default;\n$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility\n\n$modal-xl: 1140px !default;\n$modal-lg: 800px !default;\n$modal-md: 500px !default;\n$modal-sm: 300px !default;\n\n$modal-fade-transform: translate(0, -50px) !default;\n$modal-show-transform: none !default;\n$modal-transition: transform .3s ease-out !default;\n$modal-scale-transform: scale(1.02) !default;\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n$alert-padding-y: .75rem !default;\n$alert-padding-x: 1.25rem !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: $border-radius !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: $border-width !default;\n\n$alert-bg-level: -10 !default;\n$alert-border-level: -9 !default;\n$alert-color-level: 6 !default;\n\n\n// Progress bars\n\n$progress-height: 1rem !default;\n$progress-font-size: $font-size-base * .75 !default;\n$progress-bg: $gray-200 !default;\n$progress-border-radius: $border-radius !default;\n$progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: theme-color(\"primary\") !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n\n\n// List group\n\n$list-group-color: null !default;\n$list-group-bg: $white !default;\n$list-group-border-color: rgba($black, .125) !default;\n$list-group-border-width: $border-width !default;\n$list-group-border-radius: $border-radius !default;\n\n$list-group-item-padding-y: .75rem !default;\n$list-group-item-padding-x: 1.25rem !default;\n\n$list-group-hover-bg: $gray-100 !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: $gray-600 !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: $gray-700 !default;\n$list-group-action-hover-color: $list-group-action-color !default;\n\n$list-group-action-active-color: $body-color !default;\n$list-group-action-active-bg: $gray-200 !default;\n\n\n// Image thumbnails\n\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: $body-bg !default;\n$thumbnail-border-width: $border-width !default;\n$thumbnail-border-color: $gray-300 !default;\n$thumbnail-border-radius: $border-radius !default;\n$thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default;\n\n\n// Figures\n\n$figure-caption-font-size: 90% !default;\n$figure-caption-color: $gray-600 !default;\n\n\n// Breadcrumbs\n\n$breadcrumb-font-size: null !default;\n\n$breadcrumb-padding-y: .75rem !default;\n$breadcrumb-padding-x: 1rem !default;\n$breadcrumb-item-padding: .5rem !default;\n\n$breadcrumb-margin-bottom: 1rem !default;\n\n$breadcrumb-bg: $gray-200 !default;\n$breadcrumb-divider-color: $gray-600 !default;\n$breadcrumb-active-color: $gray-600 !default;\n$breadcrumb-divider: quote(\"/\") !default;\n\n$breadcrumb-border-radius: $border-radius !default;\n\n\n// Carousel\n\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n$carousel-control-hover-opacity: .9 !default;\n$carousel-control-transition: opacity .15s ease !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-hit-area-height: 10px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-active-bg: $white !default;\n$carousel-indicator-transition: opacity .6s ease !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n\n$carousel-control-icon-width: 20px !default;\n\n$carousel-control-prev-icon-bg: url(\"data:image/svg+xml,\") !default;\n$carousel-control-next-icon-bg: url(\"data:image/svg+xml,\") !default;\n\n$carousel-transition-duration: .6s !default;\n$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n\n\n// Spinners\n\n$spinner-width: 2rem !default;\n$spinner-height: $spinner-width !default;\n$spinner-border-width: .25em !default;\n\n$spinner-width-sm: 1rem !default;\n$spinner-height-sm: $spinner-width-sm !default;\n$spinner-border-width-sm: .2em !default;\n\n\n// Close\n\n$close-font-size: $font-size-base * 1.5 !default;\n$close-font-weight: $font-weight-bold !default;\n$close-color: $black !default;\n$close-text-shadow: 0 1px 0 $white !default;\n\n\n// Code\n\n$code-font-size: 87.5% !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .2rem !default;\n$kbd-padding-x: .4rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: $white !default;\n$kbd-bg: $gray-900 !default;\n\n$pre-color: $gray-900 !default;\n$pre-scrollable-max-height: 340px !default;\n\n\n// Utilities\n\n$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default;\n$overflows: auto, hidden !default;\n$positions: static, relative, absolute, fixed, sticky !default;\n\n\n// Printing\n\n$print-page-size: a3 !default;\n$print-body-min-width: map-get($grid-breakpoints, \"lg\") !default;\n","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated font-resizing\n//\n// See https://github.com/twbs/rfs\n\n// Configuration\n\n// Base font size\n$rfs-base-font-size: 1.25rem !default;\n$rfs-font-size-unit: rem !default;\n\n// Breakpoint at where font-size starts decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n// Resize font-size based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != \"number\" or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-responsive-font-sizes to false\n$enable-responsive-font-sizes: true !default;\n\n// Cache $rfs-base-font-size unit\n$rfs-base-font-size-unit: unit($rfs-base-font-size);\n\n// Remove px-unit from $rfs-base-font-size for calculations\n@if $rfs-base-font-size-unit == \"px\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1);\n}\n@else if $rfs-base-font-size-unit == \"rem\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1 / $rfs-rem-value);\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == \"px\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == \"rem\" or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);\n}\n\n// Responsive font-size mixin\n@mixin rfs($fs, $important: false) {\n // Cache $fs unit\n $fs-unit: if(type-of($fs) == \"number\", unit($fs), false);\n\n // Add !important suffix if needed\n $rfs-suffix: if($important, \" !important\", \"\");\n\n // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $fs-unit or $fs-unit != \"\" and $fs-unit != \"px\" and $fs-unit != \"rem\" or $fs == 0 {\n font-size: #{$fs}#{$rfs-suffix};\n }\n @else {\n // Variables for storing static and fluid rescaling\n $rfs-static: null;\n $rfs-fluid: null;\n\n // Remove px-unit from $fs for calculations\n @if $fs-unit == \"px\" {\n $fs: $fs / ($fs * 0 + 1);\n }\n @else if $fs-unit == \"rem\" {\n $fs: $fs / ($fs * 0 + 1 / $rfs-rem-value);\n }\n\n // Set default font-size\n @if $rfs-font-size-unit == rem {\n $rfs-static: #{$fs / $rfs-rem-value}rem#{$rfs-suffix};\n }\n @else if $rfs-font-size-unit == px {\n $rfs-static: #{$fs}px#{$rfs-suffix};\n }\n @else {\n @error \"`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.\";\n }\n\n // Only add media query if font-size is bigger as the minimum font-size\n // If $rfs-factor == 1, no rescaling will take place\n @if $fs > $rfs-base-font-size and $enable-responsive-font-sizes {\n $min-width: null;\n $variable-unit: null;\n\n // Calculate minimum font-size for given font-size\n $fs-min: $rfs-base-font-size + ($fs - $rfs-base-font-size) / $rfs-factor;\n\n // Calculate difference between given font-size and minimum font-size for given font-size\n $fs-diff: $fs - $fs-min;\n\n // Base font-size formatting\n // No need to check if the unit is valid, because we did that before\n $min-width: if($rfs-font-size-unit == rem, #{$fs-min / $rfs-rem-value}rem, #{$fs-min}px);\n\n // If two-dimensional, use smallest of screen width and height\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{$fs-diff * 100 / $rfs-breakpoint}#{$variable-unit};\n\n // Set the calculated font-size.\n $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};\n }\n\n // Rendering\n @if $rfs-fluid == null {\n // Only render static font-size if no fluid font-size is available\n font-size: $rfs-static;\n }\n @else {\n $mq-value: null;\n\n // RFS breakpoint formatting\n @if $rfs-breakpoint-unit == em or $rfs-breakpoint-unit == rem {\n $mq-value: #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit};\n }\n @else if $rfs-breakpoint-unit == px {\n $mq-value: #{$rfs-breakpoint}px;\n }\n @else {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n }\n\n @if $rfs-class == \"disable\" {\n // Adding an extra class increases specificity,\n // which prevents the media query to override the font size\n &,\n .disable-responsive-font-size &,\n &.disable-responsive-font-size {\n font-size: $rfs-static;\n }\n }\n @else {\n font-size: $rfs-static;\n }\n\n @if $rfs-two-dimensional {\n @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n @else {\n @media (max-width: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n }\n }\n}\n\n// The font-size & responsive-font-size mixin uses RFS to rescale font sizes\n@mixin font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n\n@mixin responsive-font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover() {\n &:hover { @content; }\n}\n\n@mixin hover-focus() {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus() {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active() {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n","// stylelint-disable declaration-no-important, selector-list-comma-newline-after\n\n//\n// Headings\n//\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1, .h1 { @include font-size($h1-font-size); }\nh2, .h2 { @include font-size($h2-font-size); }\nh3, .h3 { @include font-size($h3-font-size); }\nh4, .h4 { @include font-size($h4-font-size); }\nh5, .h5 { @include font-size($h5-font-size); }\nh6, .h6 { @include font-size($h6-font-size); }\n\n.lead {\n @include font-size($lead-font-size);\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n.display-1 {\n @include font-size($display1-size);\n font-weight: $display1-weight;\n line-height: $display-line-height;\n}\n.display-2 {\n @include font-size($display2-size);\n font-weight: $display2-weight;\n line-height: $display-line-height;\n}\n.display-3 {\n @include font-size($display3-size);\n font-weight: $display3-weight;\n line-height: $display-line-height;\n}\n.display-4 {\n @include font-size($display4-size);\n font-weight: $display4-weight;\n line-height: $display-line-height;\n}\n\n\n//\n// Horizontal rules\n//\n\nhr {\n margin-top: $hr-margin-y;\n margin-bottom: $hr-margin-y;\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n}\n\n\n//\n// Emphasis\n//\n\nsmall,\n.small {\n @include font-size($small-font-size);\n font-weight: $font-weight-normal;\n}\n\nmark,\n.mark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled();\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled();\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n @include font-size(90%);\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $spacer;\n @include font-size($blockquote-font-size);\n}\n\n.blockquote-footer {\n display: block;\n @include font-size($blockquote-small-font-size);\n color: $blockquote-small-color;\n\n &::before {\n content: \"\\2014\\00A0\"; // em dash, nbsp\n }\n}\n","// Lists\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n@mixin list-unstyled() {\n padding-left: 0;\n list-style: none;\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid();\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid();\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: $spacer / 2;\n line-height: 1;\n}\n\n.figure-caption {\n @include font-size($figure-caption-font-size);\n color: $figure-caption-color;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n@mixin img-fluid() {\n // Part 1: Set a maximum relative to the parent\n max-width: 100%;\n // Part 2: Override the height to auto, otherwise images will be stretched\n // when setting a width and height attribute on the img element.\n height: auto;\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size.\n\n@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {\n background-image: url($file-1x);\n\n // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,\n // but doesn't convert dppx=>dpi.\n // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.\n // Compatibility info: https://caniuse.com/#feat=css-media-resolution\n @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx\n only screen and (min-resolution: 2dppx) { // Standardized\n background-image: url($file-2x);\n background-size: $width-1x $height-1x;\n }\n @include deprecate(\"`img-retina()`\", \"v4.3.0\", \"v5\");\n}\n","// stylelint-disable property-blacklist\n// Single side border-radius\n\n@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) {\n @if $enable-rounded {\n border-radius: $radius;\n }\n @else if $fallback-border-radius != false {\n border-radius: $fallback-border-radius;\n }\n}\n\n@mixin border-top-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-top-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n }\n}\n\n@mixin border-top-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-right-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-left-radius($radius) {\n @if $enable-rounded {\n border-bottom-left-radius: $radius;\n }\n}\n","// Inline code\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n @include box-shadow($kbd-box-shadow);\n\n kbd {\n padding: 0;\n @include font-size(100%);\n font-weight: $nested-kbd-font-weight;\n @include box-shadow(none);\n }\n}\n\n// Blocks of code\npre {\n display: block;\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: $pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n // Single container class with breakpoint max-widths\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n @each $name, $width in $grid-breakpoints {\n @if ($container-max-width > $width or $breakpoint == $name) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n }\n }\n }\n }\n}\n\n\n// Row\n//\n// Rows contain your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container($gutter: $grid-gutter-width) {\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row($gutter: $grid-gutter-width) {\n display: flex;\n flex-wrap: wrap;\n margin-right: -$gutter / 2;\n margin-left: -$gutter / 2;\n}\n\n@mixin make-col-ready($gutter: $grid-gutter-width) {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%; // Reset earlier grid tiers\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// numberof columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n & > * {\n flex: 0 0 100% / $count;\n max-width: 100% / $count;\n }\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n != null and $n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","//\n// Basic Bootstrap table\n//\n\n.table {\n width: 100%;\n margin-bottom: $spacer;\n color: $table-color;\n background-color: $table-bg; // Reset for nesting within parents with `background-color`.\n\n th,\n td {\n padding: $table-cell-padding;\n vertical-align: top;\n border-top: $table-border-width solid $table-border-color;\n }\n\n thead th {\n vertical-align: bottom;\n border-bottom: (2 * $table-border-width) solid $table-border-color;\n }\n\n tbody + tbody {\n border-top: (2 * $table-border-width) solid $table-border-color;\n }\n}\n\n\n//\n// Condensed table w/ half padding\n//\n\n.table-sm {\n th,\n td {\n padding: $table-cell-padding-sm;\n }\n}\n\n\n// Border versions\n//\n// Add or remove borders all around the table and between all the columns.\n\n.table-bordered {\n border: $table-border-width solid $table-border-color;\n\n th,\n td {\n border: $table-border-width solid $table-border-color;\n }\n\n thead {\n th,\n td {\n border-bottom-width: 2 * $table-border-width;\n }\n }\n}\n\n.table-borderless {\n th,\n td,\n thead th,\n tbody + tbody {\n border: 0;\n }\n}\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-accent-bg;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n tbody tr {\n @include hover() {\n color: $table-hover-color;\n background-color: $table-hover-bg;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n@each $color, $value in $theme-colors {\n @include table-row-variant($color, theme-color-level($color, $table-bg-level), theme-color-level($color, $table-border-level));\n}\n\n@include table-row-variant(active, $table-active-bg);\n\n\n// Dark styles\n//\n// Same table markup, but inverted color scheme: dark background and light text.\n\n// stylelint-disable-next-line no-duplicate-selectors\n.table {\n .thead-dark {\n th {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n border-color: $table-dark-border-color;\n }\n }\n\n .thead-light {\n th {\n color: $table-head-color;\n background-color: $table-head-bg;\n border-color: $table-border-color;\n }\n }\n}\n\n.table-dark {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n\n th,\n td,\n thead th {\n border-color: $table-dark-border-color;\n }\n\n &.table-bordered {\n border: 0;\n }\n\n &.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-dark-accent-bg;\n }\n }\n\n &.table-hover {\n tbody tr {\n @include hover() {\n color: $table-dark-hover-color;\n background-color: $table-dark-hover-bg;\n }\n }\n }\n}\n\n\n// Responsive tables\n//\n// Generate series of `.table-responsive-*` classes for configuring the screen\n// size of where your table will overflow.\n\n.table-responsive {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $next: breakpoint-next($breakpoint, $grid-breakpoints);\n $infix: breakpoint-infix($next, $grid-breakpoints);\n\n &#{$infix} {\n @include media-breakpoint-down($breakpoint) {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n\n // Prevent double border on horizontal scroll due to use of `display: block;`\n > .table-bordered {\n border: 0;\n }\n }\n }\n }\n}\n","// Tables\n\n@mixin table-row-variant($state, $background, $border: null) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table-#{$state} {\n &,\n > th,\n > td {\n background-color: $background;\n }\n\n @if $border != null {\n th,\n td,\n thead th,\n tbody + tbody {\n border-color: $border;\n }\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover {\n $hover-background: darken($background, 5%);\n\n .table-#{$state} {\n @include hover() {\n background-color: $hover-background;\n\n > td,\n > th {\n background-color: $hover-background;\n }\n }\n }\n }\n}\n","// Bootstrap functions\n//\n// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.\n\n// Ascending\n// Used to evaluate Sass maps like our grid breakpoints.\n@mixin _assert-ascending($map, $map-name) {\n $prev-key: null;\n $prev-num: null;\n @each $key, $num in $map {\n @if $prev-num == null or unit($num) == \"%\" or unit($prev-num) == \"%\" {\n // Do nothing\n } @else if not comparable($prev-num, $num) {\n @warn \"Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !\";\n } @else if $prev-num >= $num {\n @warn \"Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !\";\n }\n $prev-key: $key;\n $prev-num: $num;\n }\n}\n\n// Starts at zero\n// Used to ensure the min-width of the lowest breakpoint starts at 0.\n@mixin _assert-starts-at-zero($map, $map-name: \"$grid-breakpoints\") {\n $values: map-values($map);\n $first-value: nth($values, 1);\n @if $first-value != 0 {\n @warn \"First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.\";\n }\n}\n\n// Replace `$search` with `$replace` in `$string`\n// Used on our SVG icon backgrounds for custom forms.\n//\n// @author Hugo Giraudel\n// @param {String} $string - Initial string\n// @param {String} $search - Substring to replace\n// @param {String} $replace ('') - New value\n// @return {String} - Updated string\n@function str-replace($string, $search, $replace: \"\") {\n $index: str-index($string, $search);\n\n @if $index {\n @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);\n }\n\n @return $string;\n}\n\n// See https://codepen.io/kevinweber/pen/dXWoRw\n@function escape-svg($string) {\n @if str-index($string, \"data:image/svg+xml\") {\n @each $char, $encoded in $escaped-characters {\n $string: str-replace($string, $char, $encoded);\n }\n }\n\n @return $string;\n}\n\n// Color contrast\n@function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) {\n $r: red($color);\n $g: green($color);\n $b: blue($color);\n\n $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;\n\n @if ($yiq >= $yiq-contrasted-threshold) {\n @return $dark;\n } @else {\n @return $light;\n }\n}\n\n// Retrieve color Sass maps\n@function color($key: \"blue\") {\n @return map-get($colors, $key);\n}\n\n@function theme-color($key: \"primary\") {\n @return map-get($theme-colors, $key);\n}\n\n@function gray($key: \"100\") {\n @return map-get($grays, $key);\n}\n\n// Request a theme color level\n@function theme-color-level($color-name: \"primary\", $level: 0) {\n $color: theme-color($color-name);\n $color-base: if($level > 0, $black, $white);\n $level: abs($level);\n\n @return mix($color-base, $color, $level * $theme-color-interval);\n}\n\n// Return valid calc\n@function add($value1, $value2, $return-calc: true) {\n @if $value1 == null {\n @return $value2;\n }\n\n @if $value2 == null {\n @return $value1;\n }\n\n @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {\n @return $value1 + $value2;\n }\n\n @return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(\" + \") + $value2);\n}\n\n@function subtract($value1, $value2, $return-calc: true) {\n @if $value1 == null and $value2 == null {\n @return null;\n }\n\n @if $value1 == null {\n @return -$value2;\n }\n\n @if $value2 == null {\n @return $value1;\n }\n\n @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {\n @return $value1 - $value2;\n }\n\n @return if($return-calc == true, calc(#{$value1} - #{$value2}), $value1 + unquote(\" - \") + $value2);\n}\n","// stylelint-disable selector-no-qualifying-type\n\n//\n// Textual form controls\n//\n\n.form-control {\n display: block;\n width: 100%;\n height: $input-height;\n padding: $input-padding-y $input-padding-x;\n font-family: $input-font-family;\n @include font-size($input-font-size);\n font-weight: $input-font-weight;\n line-height: $input-line-height;\n color: $input-color;\n background-color: $input-bg;\n background-clip: padding-box;\n border: $input-border-width solid $input-border-color;\n\n // Note: This has no effect on `s in CSS.\n @include border-radius($input-border-radius, 0);\n\n @include box-shadow($input-box-shadow);\n @include transition($input-transition);\n\n // Unstyle the caret on ` receives focus\n // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to\n // match the appearance of the native widget.\n // See https://github.com/twbs/bootstrap/issues/19398.\n color: $input-color;\n background-color: $input-bg;\n }\n}\n\n// Make file inputs better match text inputs by forcing them to new lines.\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n\n//\n// Labels\n//\n\n// For use with horizontal and inline forms, when you need the label (or legend)\n// text to align with the form controls.\n.col-form-label {\n padding-top: add($input-padding-y, $input-border-width);\n padding-bottom: add($input-padding-y, $input-border-width);\n margin-bottom: 0; // Override the `
    ',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:Se,popperConfig:null},Fe="show",Ue="out",We={HIDE:"hide"+Oe,HIDDEN:"hidden"+Oe,SHOW:"show"+Oe,SHOWN:"shown"+Oe,INSERTED:"inserted"+Oe,CLICK:"click"+Oe,FOCUSIN:"focusin"+Oe,FOCUSOUT:"focusout"+Oe,MOUSEENTER:"mouseenter"+Oe,MOUSELEAVE:"mouseleave"+Oe},qe="fade",Me="show",Ke=".tooltip-inner",Qe=".arrow",Be="hover",Ve="focus",Ye="click",ze="manual",Xe=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(Me))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._hideModalHandler),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(qe);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,this._getPopperConfig(a)),g(o).addClass(Me),"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===Ue&&e._leave(null,e)};if(g(this.tip).hasClass(qe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){function e(){n._hoverState!==Fe&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),g(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()}var n=this,i=this.getTipElement(),o=g.Event(this.constructor.Event.HIDE);if(g(this.element).trigger(o),!o.isDefaultPrevented()){if(g(i).removeClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ye]=!1,this._activeTrigger[Ve]=!1,this._activeTrigger[Be]=!1,g(this.tip).hasClass(qe)){var r=_.getTransitionDurationFromElement(i);g(i).one(_.TRANSITION_END,e).emulateTransitionEnd(r)}else e();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(Pe+"-"+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(Ke)),this.getTitle()),g(t).removeClass(qe+" "+Me)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=we(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._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Qe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},{},this.config.popperConfig)},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 Re[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!==ze){var e=t===Be?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Be?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)})}}),this._hideModalHandler=function(){i.element&&i.hide()},g(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),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?Ve:Be]=!0),g(e.getTipElement()).hasClass(Me)||e._hoverState===Fe?e._hoverState=Fe:(clearTimeout(e._timeout),e._hoverState=Fe,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Fe&&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?Ve:Be]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ue,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ue&&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!==je.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(Ae,t,this.constructor.DefaultType),t.sanitize&&(t.template=we(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(Le);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(qe),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(Ne),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ne,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.4.1"}},{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return Ae}},{key:"DATA_KEY",get:function(){return Ne}},{key:"Event",get:function(){return We}},{key:"EVENT_KEY",get:function(){return Oe}},{key:"DefaultType",get:function(){return He}}]),i}();g.fn[Ae]=Xe._jQueryInterface,g.fn[Ae].Constructor=Xe,g.fn[Ae].noConflict=function(){return g.fn[Ae]=ke,Xe._jQueryInterface};var $e="popover",Ge="bs.popover",Je="."+Ge,Ze=g.fn[$e],tn="bs-popover",en=new RegExp("(^|\\s)"+tn+"\\S+","g"),nn=l({},Xe.Default,{placement:"right",trigger:"click",content:"",template:''}),on=l({},Xe.DefaultType,{content:"(string|element|function)"}),rn="fade",sn="show",an=".popover-header",ln=".popover-body",cn={HIDE:"hide"+Je,HIDDEN:"hidden"+Je,SHOW:"show"+Je,SHOWN:"shown"+Je,INSERTED:"inserted"+Je,CLICK:"click"+Je,FOCUSIN:"focusin"+Je,FOCUSOUT:"focusout"+Je,MOUSEENTER:"mouseenter"+Je,MOUSELEAVE:"mouseleave"+Je},hn=function(t){function i(){return t.apply(this,arguments)||this}!function(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}(i,t);var e=i.prototype;return e.isWithContent=function(){return this.getTitle()||this._getContent()},e.addAttachmentClass=function(t){g(this.getTipElement()).addClass(tn+"-"+t)},e.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},e.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(an),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ln),e),t.removeClass(rn+" "+sn)},e._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},e._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(en);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 jQueryDetection() {\n if (typeof $ === 'undefined') {\n throw new TypeError('Bootstrap\\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\\'s JavaScript.')\n }\n\n const version = $.fn.jquery.split(' ')[0].split('.')\n const minMajor = 1\n const ltMajor = 2\n const minMinor = 9\n const minPatch = 1\n const maxMajor = 4\n\n if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {\n throw new Error('Bootstrap\\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0')\n }\n }\n}\n\nUtil.jQueryDetection()\nsetTransitionEndSupport()\n\nexport default Util\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.4.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.4.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.4.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.4.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_TOGGLES : '[data-toggle=\"buttons\"]',\n DATA_TOGGLE : '[data-toggle=\"button\"]',\n DATA_TOGGLES_BUTTONS : '[data-toggle=\"buttons\"] .btn',\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 LOAD_DATA_API : `load${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_TOGGLES\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 } else if (input.type === 'checkbox') {\n if (this._element.tagName === 'LABEL' && input.checked === this._element.classList.contains(ClassName.ACTIVE)) {\n triggerChangeEvent = false\n }\n } else {\n // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input\n triggerChangeEvent = false\n }\n\n if (triggerChangeEvent) {\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 (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) {\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\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 let button = event.target\n\n if (!$(button).hasClass(ClassName.BUTTON)) {\n button = $(button).closest(Selector.BUTTON)[0]\n }\n\n if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) {\n event.preventDefault() // work around Firefox bug #1540995\n } else {\n const inputBtn = button.querySelector(Selector.INPUT)\n\n if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) {\n event.preventDefault() // work around Firefox bug #1540995\n return\n }\n\n Button._jQueryInterface.call($(button), 'toggle')\n }\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$(window).on(Event.LOAD_DATA_API, () => {\n // ensure correct active class is set to match the controls' actual values/states\n\n // find all checkboxes/readio buttons inside data-toggle groups\n let buttons = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLES_BUTTONS))\n for (let i = 0, len = buttons.length; i < len; i++) {\n const button = buttons[i]\n const input = button.querySelector(Selector.INPUT)\n if (input.checked || input.hasAttribute('checked')) {\n button.classList.add(ClassName.ACTIVE)\n } else {\n button.classList.remove(ClassName.ACTIVE)\n }\n }\n\n // find all button toggles\n buttons = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n for (let i = 0, len = buttons.length; i < len; i++) {\n const button = buttons[i]\n if (button.getAttribute('aria-pressed') === 'true') {\n button.classList.add(ClassName.ACTIVE)\n } else {\n button.classList.remove(ClassName.ACTIVE)\n }\n }\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.4.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.4.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 this.touchDeltaX = 0\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.4.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.4.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.4.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.4.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 popperConfig : null\n}\n\nconst DefaultType = {\n offset : '(number|string|function)',\n flip : 'boolean',\n boundary : '(string|element)',\n reference : '(string|element)',\n display : 'string',\n popperConfig : '(null|object)'\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 isActive = $(this._menu).hasClass(ClassName.SHOW)\n\n Dropdown._clearMenus()\n\n if (isActive) {\n return\n }\n\n this.show(true)\n }\n\n show(usePopper = false) {\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 // Disable totally Popper.js for Dropdown in Navbar\n if (!this._inNavbar && usePopper) {\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 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 if (this._popper) {\n this._popper.destroy()\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 {\n ...popperConfig,\n ...this._config.popperConfig\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) {\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 if (context._popper) {\n context._popper.destroy()\n }\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 && event.which === ESCAPE_KEYCODE) {\n return\n }\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 .filter((item) => $(item).is(':visible'))\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.4.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.4.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 HIDE_PREVENTED : `hidePrevented${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 STATIC : 'modal-static'\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 _triggerBackdropTransition() {\n if (this._config.backdrop === 'static') {\n const hideEventPrevented = $.Event(Event.HIDE_PREVENTED)\n\n $(this._element).trigger(hideEventPrevented)\n if (hideEventPrevented.defaultPrevented) {\n return\n }\n\n this._element.classList.add(ClassName.STATIC)\n\n const modalTransitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element).one(Util.TRANSITION_END, () => {\n this._element.classList.remove(ClassName.STATIC)\n })\n .emulateTransitionEnd(modalTransitionDuration)\n this._element.focus()\n } else {\n this.hide()\n }\n }\n\n _showElement(relatedTarget) {\n const transition = $(this._element).hasClass(ClassName.FADE)\n const modalBody = this._dialog ? this._dialog.querySelector(Selector.MODAL_BODY) : null\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) && modalBody) {\n modalBody.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 this._triggerBackdropTransition()\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\n this._triggerBackdropTransition()\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.4.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.4.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.4.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 popperConfig : '(null|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 popperConfig : null\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 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', this._hideModalHandler)\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) {\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, this._getPopperConfig(attachment))\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 _getPopperConfig(attachment) {\n const defaultBsConfig = {\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 return {\n ...defaultBsConfig,\n ...this.config.popperConfig\n }\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 _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._hideModalHandler = () => {\n if (this.element) {\n this.hide()\n }\n }\n\n $(this.element).closest('.modal').on(\n 'hide.bs.modal',\n this._hideModalHandler\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.4.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.4.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.4.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.4.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
      and